maz-ui
Version:
A standalone components library for Vue.Js 3 & Nuxt.Js 3
162 lines (161 loc) • 5.5 kB
JavaScript
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
};