{"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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"}