import { ref, onMounted, onBeforeUnmount } from 'vue'; import { FOCUSOUT_PREVENTED, FOCUSOUT_PREVENTED_OPTS } from './tokens.mjs'; const focusReason = ref(); const lastUserFocusTimestamp = ref(0); const lastAutomatedFocusTimestamp = ref(0); let focusReasonUserCount = 0; const obtainAllFocusableElements = (element) => { const nodes = []; const walker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT, { acceptNode: (node) => { const isHiddenInput = node.tagName === "INPUT" && node.type === "hidden"; if (node.disabled || node.hidden || isHiddenInput) return NodeFilter.FILTER_SKIP; return node.tabIndex >= 0 || node === document.activeElement ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP; } }); while (walker.nextNode()) nodes.push(walker.currentNode); return nodes; }; const getVisibleElement = (elements, container) => { for (const element of elements) { if (!isHidden(element, container)) return element; } }; const isHidden = (element, container) => { if (process.env.NODE_ENV === "test") return false; if (getComputedStyle(element).visibility === "hidden") return true; while (element) { if (container && element === container) return false; if (getComputedStyle(element).display === "none") return true; element = element.parentElement; } return false; }; const getEdges = (container) => { const focusable = obtainAllFocusableElements(container); const first = getVisibleElement(focusable, container); const last = getVisibleElement(focusable.reverse(), container); return [first, last]; }; const isSelectable = (element) => { return element instanceof HTMLInputElement && "select" in element; }; const tryFocus = (element, shouldSelect) => { if (element && element.focus) { const prevFocusedElement = document.activeElement; element.focus({ preventScroll: true }); lastAutomatedFocusTimestamp.value = window.performance.now(); if (element !== prevFocusedElement && isSelectable(element) && shouldSelect) { element.select(); } } }; function removeFromStack(list, item) { const copy = [...list]; const idx = list.indexOf(item); if (idx !== -1) { copy.splice(idx, 1); } return copy; } const createFocusableStack = () => { let stack = []; const push = (layer) => { const currentLayer = stack[0]; if (currentLayer && layer !== currentLayer) { currentLayer.pause(); } stack = removeFromStack(stack, layer); stack.unshift(layer); }; const remove = (layer) => { var _a, _b; stack = removeFromStack(stack, layer); (_b = (_a = stack[0]) == null ? void 0 : _a.resume) == null ? void 0 : _b.call(_a); }; return { push, remove }; }; const focusFirstDescendant = (elements, shouldSelect = false) => { const prevFocusedElement = document.activeElement; for (const element of elements) { tryFocus(element, shouldSelect); if (document.activeElement !== prevFocusedElement) return; } }; const focusableStack = createFocusableStack(); const isFocusCausedByUserEvent = () => { return lastUserFocusTimestamp.value > lastAutomatedFocusTimestamp.value; }; const notifyFocusReasonPointer = () => { focusReason.value = "pointer"; lastUserFocusTimestamp.value = window.performance.now(); }; const notifyFocusReasonKeydown = () => { focusReason.value = "keyboard"; lastUserFocusTimestamp.value = window.performance.now(); }; const useFocusReason = () => { onMounted(() => { if (focusReasonUserCount === 0) { document.addEventListener("mousedown", notifyFocusReasonPointer); document.addEventListener("touchstart", notifyFocusReasonPointer); document.addEventListener("keydown", notifyFocusReasonKeydown); } focusReasonUserCount++; }); onBeforeUnmount(() => { focusReasonUserCount--; if (focusReasonUserCount <= 0) { document.removeEventListener("mousedown", notifyFocusReasonPointer); document.removeEventListener("touchstart", notifyFocusReasonPointer); document.removeEventListener("keydown", notifyFocusReasonKeydown); } }); return { focusReason, lastUserFocusTimestamp, lastAutomatedFocusTimestamp }; }; const createFocusOutPreventedEvent = (detail) => { return new CustomEvent(FOCUSOUT_PREVENTED, { ...FOCUSOUT_PREVENTED_OPTS, detail }); }; export { createFocusOutPreventedEvent, focusFirstDescendant, focusableStack, getEdges, getVisibleElement, isFocusCausedByUserEvent, isHidden, obtainAllFocusableElements, tryFocus, useFocusReason }; //# sourceMappingURL=utils.mjs.map