UNPKG

@dialpad/dialtone-vue

Version:

Vue component library for Dialpad's design system Dialtone

376 lines (375 loc) 12.6 kB
import { TOOLTIP_DELAY_MS as i, TOOLTIP_KIND_MODIFIERS as s, TOOLTIP_STICKY_VALUES as a, TOOLTIP_DIRECTIONS as h } from "./tooltip-constants.js"; import { POPOVER_APPEND_TO_VALUES as l } from "../popover/popover-constants.js"; import { flushPromises as d, getUniqueString as c } from "../../common/utils/index.js"; import { getPopperOptions as r, createTippy as u, getAnchor as p } from "../popover/tippy-utils.js"; import { n as f } from "../../_plugin-vue2_normalizer-DSLOjnn3.js"; const m = { name: "DtTooltip", props: { /** * The id of the tooltip */ id: { type: String, default() { return c(); } }, /** * 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: !1 }, /** * 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(t) { return h.includes(t); } }, /** * 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: !0, validator: (t) => a.includes(t) }, /** * 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: (t) => l.includes(t) || t 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: !0 }, /** * 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: !0 }, /** * Whether the tooltip will have a delay when being focused or moused over. * @values true, false */ delay: { type: Boolean, default: !0 }, /** * 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: s, tip: null, inTimer: null, // Internal state for whether the tooltip is shown. Changing the prop // will update this. internalShow: !1, // 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: { tippyProps() { return { offset: this.offset, delay: this.delay ? i : !1, placement: this.placement, sticky: this.sticky, theme: this.inverted ? "inverted" : this.theme, animation: this.transition ? "fade" : !1, // onShown only triggers when transition is truthy onShown: (t) => this.onShow(t, "onShown"), // onShown will always be called, but it will be called before the animation is complete onShow: (t) => this.onShow(t, "onShow"), onHidden: this.onHide, popperOptions: r({ fallbackPlacements: this.fallbackPlacements, hasHideModifierEnabled: !0 }) }; }, anchor() { return this.externalAnchor ? document.body.querySelector(this.externalAnchor) : p(this.$refs.anchor); } }, watch: { tippyProps: { handler: "setProps", deep: !0 }, show: { handler: function(t) { t !== null && this.enabled && (this.internalShow = t); }, immediate: !0 }, internalShow(t) { t ? (this.setProps(), this.tip.show()) : this.tip.hide(); }, sticky(t) { this.tip.setProps({ sticky: t }); } }, async mounted() { !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 = u(this.anchor, this.initOptions()), this.externalAnchor && (await d(), this.addExternalAnchorEventListeners()); }, beforeDestroy() { var t, e; this.externalAnchor && this.removeExternalAnchorEventListeners(), (t = this.anchor) != null && t._tippy && ((e = this.tip) == null || e.destroy()); }, methods: { calculateAnchorZindex() { return this.$el.getRootNode().querySelector( `.d-modal[aria-hidden="false"], .d-modal--transparent[aria-hidden="false"], .d-modal:not([aria-hidden]), .d-modal--transparent:not([aria-hidden])` ) || // 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") ? 651 : 400; }, hasVisibleFocus() { return this.anchor.matches(":focus-visible"); }, onEnterAnchor(t) { this.enabled && (this.delay && this.inTimer === null ? this.inTimer = setTimeout(() => { this.triggerShow(t); }, i) : this.triggerShow(t)); }, triggerShow(t) { t.type === "focusin" ? this.show === null && this.hasVisibleFocus() && (this.internalShow = !0) : this.show === null && (this.internalShow = !0); }, onLeaveAnchor(t) { t.type === "keydown" && t.code !== "Escape" || (clearTimeout(this.inTimer), this.inTimer = null, this.triggerHide()); }, triggerHide() { this.show === null && (this.internalShow = !1); }, onChangePlacement(t) { this.currentPlacement = t; }, onHide() { var t; (t = this.tip) == null || t.unmount(), this.$emit("shown", !1), this.show !== null && this.$emit("update:show", !1); }, onShow(t, e) { if (!this.tooltipHasContent(t)) return !1; this.transition && e === "onShow" || (this.$emit("shown", !0), this.show !== null && this.$emit("update:show", !0)); }, setProps() { var t, e; 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" ? (e = (t = this.anchor) == null ? void 0 : t.getRootNode()) == null ? void 0 : e.querySelector("body") : this.appendTo, zIndex: this.calculateAnchorZindex() }); }, onMount() { this.setProps(); }, tooltipHasContent(t) { return t.props.content.textContent.trim().length !== 0; }, // set initial options here. If any of the options need to dynamically change, they should be put in // tippyProps instead. initOptions() { return { content: this.$refs.content, 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: !1, trigger: "manual", hideOnClick: !1, // disable tooltip from displaying on touch devices touch: !1, onMount: this.onMount, showOnCreate: this.internalShow, popperOptions: r({ hasHideModifierEnabled: !0 }) }; }, addExternalAnchorEventListeners() { ["focusin", "mouseenter"].forEach((t) => { var e; (e = this.anchor) == null || e.addEventListener(t, (n) => this.onEnterAnchor(n)); }), ["focusout", "mouseleave", "keydown"].forEach((t) => { var e; (e = this.anchor) == null || e.addEventListener(t, (n) => this.onLeaveAnchor(n)); }); }, removeExternalAnchorEventListeners() { ["focusin", "mouseenter"].forEach((t) => { var e; (e = this.anchor) == null || e.removeEventListener(t, (n) => this.onEnterAnchor(n)); }), ["focusout", "mouseleave", "keydown"].forEach((t) => { var e; (e = this.anchor) == null || e.removeEventListener(t, (n) => this.onLeaveAnchor(n)); }); } } }; var y = function() { var e = this, n = e._self._c; return n("div", { attrs: { "data-qa": "dt-tooltip-container" } }, [e.externalAnchor ? e._e() : n("span", { ref: "anchor", attrs: { "data-qa": "dt-tooltip-anchor" }, on: { focusin: e.onEnterAnchor, focusout: e.onLeaveAnchor, mouseenter: e.onEnterAnchor, mouseleave: e.onLeaveAnchor, keydown: function(o) { return !o.type.indexOf("key") && e._k(o.keyCode, "esc", 27, o.key, ["Esc", "Escape"]) ? null : e.onLeaveAnchor.apply(null, arguments); } } }, [e._t("anchor")], 2), n("div", e._g({ ref: "content", class: [ // eslint-disable-next-line vue/no-restricted-class "d-tooltip", { [e.TOOLTIP_KIND_MODIFIERS.inverted]: e.inverted }, e.contentClass ], attrs: { id: e.id, "data-qa": "dt-tooltip" } }, e.$listeners), [e._t("default", function() { return [e._v(" " + e._s(e.message) + " ")]; })], 2)]); }, w = [], v = /* @__PURE__ */ f( m, y, w ); const T = v.exports; export { T as default }; //# sourceMappingURL=tooltip.js.map