{"version":3,"file":"utils.cjs","sources":["../../src/FocusScope/utils.ts"],"sourcesContent":["import { getActiveElement } from '@/shared'\n\nexport const AUTOFOCUS_ON_MOUNT = 'focusScope.autoFocusOnMount'\nexport const AUTOFOCUS_ON_UNMOUNT = 'focusScope.autoFocusOnUnmount'\nexport const EVENT_OPTIONS = { bubbles: false, cancelable: true }\n\ntype FocusableTarget = HTMLElement | { focus: () => void }\n\n/**\n * Attempts focusing the first element in a list of candidates.\n * Stops when focus has actually moved.\n */\nexport function focusFirst(candidates: HTMLElement[], { select = false } = {}) {\n const previouslyFocusedElement = getActiveElement()\n for (const candidate of candidates) {\n focus(candidate, { select })\n if (getActiveElement() !== previouslyFocusedElement)\n return true\n }\n}\n\n/**\n * Returns the first and last tabbable elements inside a container.\n */\nexport function getTabbableEdges(container: HTMLElement) {\n const candidates = getTabbableCandidates(container)\n const first = findVisible(candidates, container)\n const last = findVisible(candidates.reverse(), container)\n return [first, last] as const\n}\n\n/**\n * Returns a list of potential tabbable candidates.\n *\n * NOTE: This is only a close approximation. For example it doesn't take into account cases like when\n * elements are not visible. This cannot be worked out easily by just reading a property, but rather\n * necessitate runtime knowledge (computed styles, etc). We deal with these cases separately.\n *\n * See: https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker\n * Credit: https://github.com/discord/focus-layers/blob/master/src/util/wrapFocus.tsx#L1\n */\nexport function getTabbableCandidates(container: HTMLElement) {\n const nodes: HTMLElement[] = []\n const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {\n acceptNode: (node: any) => {\n const isHiddenInput = node.tagName === 'INPUT' && node.type === 'hidden'\n if (node.disabled || node.hidden || isHiddenInput)\n return NodeFilter.FILTER_SKIP\n // `.tabIndex` is not the same as the `tabindex` attribute. It works on the\n // runtime's understanding of tabbability, so this automatically accounts\n // for any kind of element that could be tabbed to.\n return node.tabIndex >= 0\n ? NodeFilter.FILTER_ACCEPT\n : NodeFilter.FILTER_SKIP\n },\n })\n while (walker.nextNode()) nodes.push(walker.currentNode as HTMLElement)\n // we do not take into account the order of nodes with positive `tabIndex` as it\n // hinders accessibility to have tab order different from visual order.\n return nodes\n}\n\n/**\n * Returns the first visible element in a list.\n * NOTE: Only checks visibility up to the `container`.\n */\nexport function findVisible(elements: HTMLElement[], container: HTMLElement) {\n for (const element of elements) {\n // we stop checking if it's hidden at the `container` level (excluding)\n if (!isHidden(element, { upTo: container }))\n return element\n }\n}\n\nexport function isHidden(node: HTMLElement, { upTo }: { upTo?: HTMLElement }) {\n if (getComputedStyle(node).visibility === 'hidden')\n return true\n while (node) {\n // we stop at `upTo` (excluding it)\n if (upTo !== undefined && node === upTo)\n return false\n if (getComputedStyle(node).display === 'none')\n return true\n node = node.parentElement as HTMLElement\n }\n return false\n}\n\nexport function isSelectableInput(\n element: any,\n): element is FocusableTarget & { select: () => void } {\n return element instanceof HTMLInputElement && 'select' in element\n}\n\nexport function focus(\n element?: FocusableTarget | null,\n { select = false } = {},\n) {\n // only focus if that element is focusable\n if (element && element.focus) {\n const previouslyFocusedElement = getActiveElement()\n // NOTE: we prevent scrolling on focus, to minimize jarring transitions for users\n element.focus({ preventScroll: true })\n // only select if its not the same element, it supports selection and we need to select\n if (\n element !== previouslyFocusedElement\n && isSelectableInput(element)\n && select\n ) {\n element.select()\n }\n }\n}\n"],"names":["getActiveElement"],"mappings":"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"}