UNPKG

@inkline/inkline

Version:

Inkline is the intuitive UI Components library that gives you a developer-friendly foundation for building high-quality, accessible, and customizable Vue.js 3 Design Systems.

220 lines (219 loc) 6.59 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.usePopupControl = usePopupControl; var _vue = require("vue"); var _utils = require("@grozav/utils"); var _dom = require("@floating-ui/dom"); var _utils2 = require("@inkline/inkline/utils"); function usePopupControl(props) { const visible = (0, _vue.ref)(props.componentProps.value.visible); const instance = (0, _vue.ref)(); const animating = (0, _vue.ref)(false); const triggerStack = (0, _vue.ref)(0); (0, _vue.onMounted)(() => { addEventListeners(); }); (0, _vue.onUnmounted)(() => { removeEventListeners(); }); (0, _vue.watch)(() => props.componentProps.value.visible, value => { if (value) { show(); } else { hide(); } }); function addEventListeners() { const triggerRef = (0, _utils2.extractRefHTMLElement)(props.triggerRef); const popupRef = (0, _utils2.extractRefHTMLElement)(props.popupRef); if (!triggerRef || !popupRef) { return; } [].concat(props.componentProps.value.events).forEach(trigger => { switch (trigger) { case "hover": (0, _utils.on)(triggerRef, "mouseenter", props.componentProps.value.interactable ? hoverShow : show); (0, _utils.on)(triggerRef, "mouseleave", props.componentProps.value.interactable ? hoverHide : hide); if (props.componentProps.value.interactable) { (0, _utils.on)(popupRef, "mouseenter", hoverShow); (0, _utils.on)(popupRef, "mouseleave", hoverHide); } break; case "click": (0, _utils.on)(triggerRef, "click", onClick); break; case "focus": for (const child of triggerRef.children) { (0, _utils.on)(child, "focus", show); (0, _utils.on)(child, "blur", hide); } break; default: break; } }); } function removeEventListeners() { const triggerRef = (0, _utils2.extractRefHTMLElement)(props.triggerRef); const popupRef = (0, _utils2.extractRefHTMLElement)(props.popupRef); if (!triggerRef || !popupRef) { return; } [].concat(props.componentProps.value.events).forEach(trigger => { switch (trigger) { case "hover": (0, _utils.off)(triggerRef, "mouseenter", props.componentProps.value.interactable ? hoverShow : show); (0, _utils.off)(triggerRef, "mouseleave", props.componentProps.value.interactable ? hoverHide : hide); if (props.componentProps.value.interactable) { (0, _utils.off)(popupRef, "mouseenter", hoverShow); (0, _utils.off)(popupRef, "mouseleave", hoverHide); } break; case "click": (0, _utils.off)(triggerRef, "click", onClick); break; case "focus": for (const child of triggerRef.children) { (0, _utils.off)(child, "focus", show); (0, _utils.off)(child, "blur", hide); } break; default: break; } }); } function show() { if (props.componentProps.value.disabled || props.componentProps.value.readonly || visible.value) { return; } triggerStack.value += 1; visible.value = true; createPopup(); props.emit("update:visible", true); } function hide() { if (props.componentProps.value.disabled || props.componentProps.value.readonly || !visible.value) { return; } triggerStack.value -= 1; if (triggerStack.value <= 0) { triggerStack.value = 0; visible.value = false; props.emit("update:visible", false); setTimeout(() => destroyPopup(), props.componentProps.value.animationDuration); } } function onClick() { if (visible.value) { hide(); } else { show(); } } function onClickOutside() { props.emit("click:outside"); if (!props.componentProps.value.visible) { hide(); } } function onKeyEscape() { hide(); } function hoverShow() { animating.value = false; show(); } function hoverHide() { animating.value = true; setTimeout(() => { if (animating.value) { hide(); } }, props.componentProps.value.hoverHideDelay); } function createPopup() { if (typeof window === "undefined") { return; } const triggerRef = (0, _utils2.extractRefHTMLElement)(props.triggerRef); const popupRef = (0, _utils2.extractRefHTMLElement)(props.popupRef); const arrowRef = (0, _utils2.extractRefHTMLElement)(props.arrowRef); if (!triggerRef || !popupRef) { throw new Error("Trigger and popup elements are required."); } instance.value = (0, _dom.autoUpdate)(triggerRef, popupRef, () => { (0, _dom.computePosition)(triggerRef, popupRef, { strategy: "absolute", placement: props.componentProps.value.placement, middleware: [(0, _dom.offset)(props.componentProps.value.offset), (0, _dom.flip)(), (0, _dom.shift)({ padding: 6 })].concat(arrowRef ? [(0, _dom.arrow)({ element: arrowRef })] : []), ...props.componentProps.value.popupOptions }).then(({ x, y, placement, middlewareData }) => { Object.assign(popupRef.style, { left: `${x}px`, top: `${y}px` }); popupRef?.setAttribute("data-popup-placement", placement); if (arrowRef) { const { x: arrowX, y: arrowY } = middlewareData.arrow; const staticSide = { top: "bottom", right: "left", bottom: "top", left: "right" }[placement.split("-")[0]]; Object.assign(arrowRef.style, { left: arrowX !== null ? `${arrowX}px` : "", top: arrowY !== null ? `${arrowY}px` : "", right: "", bottom: "", [staticSide]: "-6px" }); } }); }); } function destroyPopup() { if (instance.value) { instance.value(); instance.value = void 0; } } function focusTrigger() { const triggerRef = (0, _utils2.extractRefHTMLElement)(props.triggerRef); if (!triggerRef) { return; } for (const child of triggerRef.children) { if ((0, _utils.focusFirstDescendant)(child)) { child.focus(); break; } } } return { visible, show, hide, onClick, onClickOutside, onKeyEscape, focusTrigger, createPopup, destroyPopup }; }