UNPKG

maz-ui

Version:

A standalone components library for Vue.Js 3 & Nuxt.Js 3

162 lines (161 loc) 5.5 kB
import { ref, nextTick, watch, h } from "vue"; import MazPopover from "../components/MazPopover.js"; import { useMountComponent } from "../composables/useMountComponent.js"; const tooltipInstances = /* @__PURE__ */ new WeakMap(); class TooltipHandler { defaultProps; constructor(options = {}) { this.defaultProps = { open: !1, position: "top", trigger: "hover", role: "tooltip", closeOnClickOutside: !1, closeOnEscape: !1, color: "contrast", ...options }; } getTooltipProps(binding) { const baseOptions = { ...this.defaultProps }; return typeof binding.value == "string" ? { ...baseOptions, text: binding.value, position: this.getPositionFromModifiers(binding) || baseOptions.position || "top" } : { ...baseOptions, ...binding.value, position: this.getPositionFromModifiers(binding) || binding.value.position || baseOptions.position || "top" }; } getPositionFromModifiers(binding) { const modifiers = Object.keys(binding.modifiers), validPositions = [ "top", "bottom", "left", "right", "top-start", "top-end", "bottom-start", "bottom-end", "left-start", "left-end", "right-start", "right-end", "auto" ]; for (const modifier of modifiers) if (validPositions.includes(modifier)) return modifier; } mount(el, binding) { const tooltipProps = this.getTooltipProps(binding); if (!tooltipProps.text && !tooltipProps.html) { console.warn("[maz-ui](vTooltip) No text or html content provided"); return; } const isOpen = ref(!!tooltipProps.open); let vNodeInstance = null; const createTooltip = () => { const popoverProps = { ...tooltipProps, panelClass: [ "m-tooltip-panel", "maz-text-sm", "maz-whitespace-pre-wrap", "maz-break-words", "maz-p-2", "maz-max-w-xs", tooltipProps.panelClass ].filter(Boolean).join(" "), modelValue: isOpen.value, positionReference: el, onAfterCloseAnimation: () => { vNodeInstance?.destroy(), vNodeInstance = null; } }; vNodeInstance = useMountComponent(MazPopover, { props: popoverProps, children: { default: () => tooltipProps.html ? h("div", { innerHTML: tooltipProps.html }) : tooltipProps.text || "" }, element: el }); }; function isTouchDevice() { return "ontouchstart" in globalThis || navigator.maxTouchPoints > 0; } function getEffectiveTrigger() { return tooltipProps.trigger === "adaptive" ? isTouchDevice() ? "click" : "hover" : tooltipProps.trigger || "hover"; } let mouseEnterHandler = null, mouseLeaveHandler = null, clickHandler = null; function setupTriggers() { cleanupTriggers(); const effectiveTrigger = getEffectiveTrigger(); effectiveTrigger === "hover" ? (mouseEnterHandler = () => { isOpen.value = !0; }, mouseLeaveHandler = () => { isOpen.value = !1; }, el.addEventListener("mouseenter", mouseEnterHandler), el.addEventListener("mouseleave", mouseLeaveHandler)) : effectiveTrigger === "click" && (clickHandler = () => { isOpen.value = !isOpen.value; }, el.addEventListener("click", clickHandler)); } function cleanupTriggers() { mouseEnterHandler && (el.removeEventListener("mouseenter", mouseEnterHandler), mouseEnterHandler = null), mouseLeaveHandler && (el.removeEventListener("mouseleave", mouseLeaveHandler), mouseLeaveHandler = null), clickHandler && (el.removeEventListener("click", clickHandler), clickHandler = null); } nextTick(() => { setupTriggers(); }), watch(isOpen, (value) => { value ? createTooltip() : vNodeInstance && vNodeInstance?.vNode.component?.exposed?.close(); }, { immediate: !0 }); function destroy() { cleanupTriggers(), vNodeInstance && (vNodeInstance.destroy(), vNodeInstance = null); } function updateProps(newProps) { const oldTrigger = tooltipProps.trigger; Object.assign(tooltipProps, newProps), isOpen.value = !!newProps.open, oldTrigger !== newProps.trigger && setupTriggers(), createTooltip(); } tooltipInstances.set(el, { destroy, updateProps, isOpen }); } update(el, binding) { const instance = tooltipInstances.get(el); if (instance) { const newProps = this.getTooltipProps(binding); instance.updateProps(newProps); } else this.mount(el, binding); } unmount(el) { const instance = tooltipInstances.get(el); instance && (instance.destroy(), tooltipInstances.delete(el)); } } let globalHandler; const directive = { mounted(el, binding) { return globalHandler || (globalHandler = new TooltipHandler()), globalHandler.mount(el, binding); }, updated(el, binding) { return globalHandler || (globalHandler = new TooltipHandler()), globalHandler.update(el, binding); }, unmounted(el) { globalHandler && globalHandler.unmount(el); } }, plugin = { install: (app, options) => { const handler = new TooltipHandler(options); app.directive("tooltip", { beforeMount: handler.mount.bind(handler), updated: handler.update.bind(handler), unmounted: handler.unmount.bind(handler) }); } }; export { directive as vTooltip, plugin as vTooltipInstall };