@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
JavaScript
;
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
};
}