UNPKG

@dialpad/dialtone

Version:

Dialpad's Dialtone design system monorepo

434 lines (433 loc) 14.1 kB
"use strict"; Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } }); const tooltip_constants = require("./tooltip_constants.cjs"); const popover_constants = require("../popover/popover_constants.cjs"); const common_utils = require("../../common/utils.cjs"); const tippy_utils = require("../popover/tippy_utils.cjs"); const _pluginVue2_normalizer = require("../../_virtual/_plugin-vue2_normalizer.cjs"); const _sfc_main = { name: "DtTooltip", props: { /** * The id of the tooltip */ id: { type: String, default() { return common_utils.getUniqueString(); } }, /** * If the popover does not fit in the direction described by "placement", * it will attempt to change its direction to the "fallbackPlacements" * if defined, otherwise it will automatically position to a new location * as it sees best fit. See * <a * class="d-link" * href="https://popper.js.org/docs/v2/modifiers/flip/#fallbackplacements" * target="_blank" * > * Popper.js docs * </a> * */ fallbackPlacements: { type: Array, default: () => ["auto"] }, /** * If true, applies inverted styles to the tooltip * @values true, false */ inverted: { type: Boolean, default: false }, /** * Displaces the tooltip from its reference element * by the specified number of pixels. See * <a * class="d-link" * href="https://atomiks.github.io/tippyjs/v6/all-props/#offset" * target="_blank" * > * Tippy.js docs * </a> */ offset: { type: Array, default: () => [0, 12] }, /** * The direction the popover displays relative to the anchor. See * <a * class="d-link" * href="https://atomiks.github.io/tippyjs/v6/all-props/#placement" * target="_blank" * > * Tippy.js docs * </a> * @values top, top-start, top-end, * right, right-start, right-end, * left, left-start, left-end, * bottom, bottom-start, bottom-end, * auto, auto-start, auto-end */ placement: { type: String, default: "top", validator(placement) { return tooltip_constants.TOOLTIP_DIRECTIONS.includes(placement); } }, /** * If the tooltip sticks to the anchor. This is usually not needed, but can be needed * if the reference element's position is animating, or to automatically update the popover * position in those cases the DOM layout changes the reference element's position. * `true` enables it, `reference` only checks the "reference" rect for changes and `popper` only * checks the "popper" rect for changes. See * <a * class="d-link" * href="https://atomiks.github.io/tippyjs/v6/all-props/#sticky" * target="_blank" * > * Tippy.js docs * </a> * @values true, false, reference, popper */ sticky: { type: [Boolean, String], default: true, validator: (sticky) => { return tooltip_constants.TOOLTIP_STICKY_VALUES.includes(sticky); } }, /** * Sets the element to which the tooltip is going to append to. * 'body' will append to the nearest body (supports shadow DOM). * This prop is not reactive, must be set on initial render. * @values 'body', 'parent', HTMLElement, */ appendTo: { type: [HTMLElement, String], default: "body", validator: (appendTo) => { return popover_constants.POPOVER_APPEND_TO_VALUES.includes(appendTo) || appendTo instanceof HTMLElement; } }, /** * Additional css classes for the tooltip content element. * Can accept all of String, Object, and Array, i.e. has the * same api as Vue's built-in handling of the class attribute. */ contentClass: { type: [String, Object, Array], default: "" }, /** * A provided message for the tooltip content */ message: { type: String, default: "" }, /** * Controls whether hover/focus causes the tooltip to appear. * Cannot be combined with the show prop. show value will be ignored. * by default this is true, if you override with false, the tooltip will never show up. */ enabled: { type: Boolean, default: true }, /** * Controls whether the tooltip is shown. Leaving this null will have the tooltip trigger on mouseover by default. * If you set this value, the default mouseover behavior will be disabled and you can control it as you need. * Supports .sync modifier * @values null, true, false */ show: { type: Boolean, default: null }, /** * Whether the tooltip should have a transition effect (fade). */ transition: { type: Boolean, default: true }, /** * Whether the tooltip will have a delay when being focused or moused over. * @values true, false */ delay: { type: Boolean, default: true }, /** * Set a custom theme on the tooltip. See https://atomiks.github.io/tippyjs/v6/themes/ */ theme: { type: String, default: null }, /** * External anchor id to use in those cases the anchor can't be provided via the slot. * For instance, using the combobox's input as the anchor for the popover. */ externalAnchor: { type: String, default: null } }, emits: [ /** * Emitted when tooltip is shown or hidden * * @event shown * @type {Boolean} */ "shown", /** * Sync show value * * @event update:show */ "update:show" ], data() { return { TOOLTIP_KIND_MODIFIERS: tooltip_constants.TOOLTIP_KIND_MODIFIERS, tip: null, inTimer: null, // Internal state for whether the tooltip is shown. Changing the prop // will update this. internalShow: false, // this is where the placement currently is, this can be different than // the placement prop when there is not enough available room for the tip // to display and it uses a fallback placement. currentPlacement: this.placement }; }, computed: { // eslint-disable-next-line complexity tippyProps() { return { offset: this.offset, delay: this.delay ? tooltip_constants.TOOLTIP_DELAY_MS : false, placement: this.placement, sticky: this.sticky, theme: this.inverted ? "inverted" : this.theme, animation: this.transition ? "fade" : false, // onShown only triggers when transition is truthy onShown: (tooltipInstance) => this.onShow(tooltipInstance, "onShown"), // onShown will always be called, but it will be called before the animation is complete onShow: (tooltipInstance) => this.onShow(tooltipInstance, "onShow"), onHidden: this.onHide, popperOptions: tippy_utils.getPopperOptions({ fallbackPlacements: this.fallbackPlacements, hasHideModifierEnabled: true, onChangePlacement: this.onChangePlacement }) }; }, anchor() { return this.externalAnchor ? document.body.querySelector(this.externalAnchor) : tippy_utils.getAnchor(this.$refs.anchor); } }, watch: { tippyProps: { handler: "setProps", deep: true }, show: { handler: function(show) { if (show !== null && this.enabled) { this.internalShow = show; } }, immediate: true }, internalShow(value) { if (value) { this.setProps(); this.tip.show(); } else { this.tip.hide(); } }, sticky(sticky) { this.tip.setProps({ sticky }); } }, async mounted() { if (!this.enabled && this.show != null) { console.warn("Tooltip: You cannot use both the enabled and show props at the same time."); console.warn("The show prop will be ignored."); } this.tip = tippy_utils.createTippy(this.anchor, this.initOptions()); if (this.externalAnchor) { await common_utils.flushPromises(); this.addExternalAnchorEventListeners(); } }, beforeDestroy() { var _a, _b; this.externalAnchor && this.removeExternalAnchorEventListeners(); if ((_a = this.anchor) == null ? void 0 : _a._tippy) { (_b = this.tip) == null ? void 0 : _b.destroy(); } }, methods: { calculateAnchorZindex() { if (this.$el.getRootNode().querySelector('.d-modal[aria-hidden="false"], .d-modal--transparent[aria-hidden="false"]') || // Special case because we don't have any dialtone drawer component yet. Render at 651 when // anchor of popover is within a drawer. this.$el.closest(".d-zi-drawer")) { return 651; } else { return 400; } }, hasVisibleFocus() { return this.anchor.matches(":focus-visible"); }, onEnterAnchor(e) { if (!this.enabled) return; if (this.delay && this.inTimer === null) { this.inTimer = setTimeout(() => { this.triggerShow(e); }, tooltip_constants.TOOLTIP_DELAY_MS); } else { this.triggerShow(e); } }, triggerShow(e) { if (e.type === "focusin") { if (this.show === null && this.hasVisibleFocus()) { this.internalShow = true; } } else { if (this.show === null) this.internalShow = true; } }, onLeaveAnchor(e) { if (e.type === "keydown" && e.code !== "Escape") return; clearTimeout(this.inTimer); this.inTimer = null; this.triggerHide(); }, triggerHide() { if (this.show === null) this.internalShow = false; }, onChangePlacement(placement) { this.currentPlacement = placement; }, onHide() { var _a; (_a = this.tip) == null ? void 0 : _a.unmount(); this.$emit("shown", false); if (this.show !== null) { this.$emit("update:show", false); } }, onShow(tooltipInstance, callingMethod) { if (!this.tooltipHasContent(tooltipInstance)) { return false; } if (this.transition && callingMethod === "onShow") { return; } this.$emit("shown", true); if (this.show !== null) { this.$emit("update:show", true); } }, setProps() { var _a, _b; if (this.tip && this.tip.setProps) { this.tip.setProps({ ...this.tippyProps, // these need to be set here rather than in tippyProps because they are non-reactive appendTo: this.appendTo === "body" ? (_b = (_a = this.anchor) == null ? void 0 : _a.getRootNode()) == null ? void 0 : _b.querySelector("body") : this.appendTo, zIndex: this.calculateAnchorZindex() }); } }, onMount() { this.setProps(); }, tooltipHasContent(tooltipInstance) { if (tooltipInstance.props.content.textContent.trim().length === 0) { return false; } return true; }, // set initial options here. If any of the options need to dynamically change, they should be put in // tippyProps instead. initOptions() { const template = this.$refs.content; return { content: template, arrow: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="7"><path d="M 14.5,7 8,0 1.5,7 Z"/></svg>', // transition duration - same as our custom fade delay in dialtone-globals.less duration: 180, interactive: false, trigger: "manual", hideOnClick: false, // disable tooltip from displaying on touch devices touch: false, onMount: this.onMount, showOnCreate: this.internalShow, popperOptions: tippy_utils.getPopperOptions({ hasHideModifierEnabled: true }) }; }, addExternalAnchorEventListeners() { ["focusin", "mouseenter"].forEach((listener) => { var _a; (_a = this.anchor) == null ? void 0 : _a.addEventListener(listener, (event) => this.onEnterAnchor(event)); }); ["focusout", "mouseleave", "keydown"].forEach((listener) => { var _a; (_a = this.anchor) == null ? void 0 : _a.addEventListener(listener, (event) => this.onLeaveAnchor(event)); }); }, removeExternalAnchorEventListeners() { ["focusin", "mouseenter"].forEach((listener) => { var _a; (_a = this.anchor) == null ? void 0 : _a.removeEventListener(listener, (event) => this.onEnterAnchor(event)); }); ["focusout", "mouseleave", "keydown"].forEach((listener) => { var _a; (_a = this.anchor) == null ? void 0 : _a.removeEventListener(listener, (event) => this.onLeaveAnchor(event)); }); } } }; var _sfc_render = function render() { var _vm = this, _c = _vm._self._c; return _c("div", { attrs: { "data-qa": "dt-tooltip-container" } }, [!_vm.externalAnchor ? _c("span", { ref: "anchor", attrs: { "data-qa": "dt-tooltip-anchor" }, on: { "focusin": _vm.onEnterAnchor, "focusout": _vm.onLeaveAnchor, "mouseenter": _vm.onEnterAnchor, "mouseleave": _vm.onLeaveAnchor, "keydown": function($event) { if (!$event.type.indexOf("key") && _vm._k($event.keyCode, "esc", 27, $event.key, ["Esc", "Escape"])) return null; return _vm.onLeaveAnchor.apply(null, arguments); } } }, [_vm._t("anchor")], 2) : _vm._e(), _c("div", _vm._g({ ref: "content", class: [ "d-tooltip", { [_vm.TOOLTIP_KIND_MODIFIERS.inverted]: _vm.inverted }, _vm.contentClass ], attrs: { "id": _vm.id, "data-qa": "dt-tooltip" } }, _vm.$listeners), [_vm._t("default", function() { return [_vm._v(" " + _vm._s(_vm.message) + " ")]; })], 2)]); }; var _sfc_staticRenderFns = []; var __component__ = /* @__PURE__ */ _pluginVue2_normalizer.default( _sfc_main, _sfc_render, _sfc_staticRenderFns ); const DtTooltip = __component__.exports; exports.default = DtTooltip; //# sourceMappingURL=tooltip.vue.cjs.map