{"version":3,"file":"utils.mjs","names":[],"sources":["../../../../../../packages/components/focus-trap/src/utils.ts"],"sourcesContent":["import { onBeforeUnmount, onMounted, ref } from 'vue'\nimport { focusElement } from '@element-plus/utils'\nimport { FOCUSOUT_PREVENTED, FOCUSOUT_PREVENTED_OPTS } from './tokens'\n\nconst focusReason = ref<'pointer' | 'keyboard'>()\nconst lastUserFocusTimestamp = ref<number>(0)\nconst lastAutomatedFocusTimestamp = ref<number>(0)\nlet focusReasonUserCount = 0\n\nexport type FocusLayer = {\n  paused: boolean\n  pause: () => void\n  resume: () => void\n}\n\nexport type FocusStack = FocusLayer[]\n\nexport const obtainAllFocusableElements = (\n  element: HTMLElement\n): HTMLElement[] => {\n  const nodes: HTMLElement[] = []\n  const walker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT, {\n    acceptNode: (\n      node: Element & {\n        disabled: boolean\n        hidden: boolean\n        type: string\n        tabIndex: number\n      }\n    ) => {\n      const isHiddenInput = node.tagName === 'INPUT' && node.type === 'hidden'\n      if (node.disabled || node.hidden || isHiddenInput)\n        return NodeFilter.FILTER_SKIP\n      return node.tabIndex >= 0 || node === document.activeElement\n        ? NodeFilter.FILTER_ACCEPT\n        : NodeFilter.FILTER_SKIP\n    },\n  })\n  while (walker.nextNode()) nodes.push(walker.currentNode as HTMLElement)\n\n  return nodes\n}\n\nexport const getVisibleElement = (\n  elements: HTMLElement[],\n  container: HTMLElement\n) => {\n  for (const element of elements) {\n    if (!isHidden(element, container)) return element\n  }\n}\n\nexport const isHidden = (element: HTMLElement, container: HTMLElement) => {\n  if (process.env.NODE_ENV === 'test') return false\n  if (getComputedStyle(element).visibility === 'hidden') return true\n\n  while (element) {\n    if (container && element === container) return false\n    if (getComputedStyle(element).display === 'none') return true\n    element = element.parentElement as HTMLElement\n  }\n\n  return false\n}\n\nexport const getEdges = (container: HTMLElement) => {\n  const focusable = obtainAllFocusableElements(container)\n  const first = getVisibleElement(focusable, container)\n  const last = getVisibleElement(focusable.reverse(), container)\n  return [first, last]\n}\n\nconst isSelectable = (\n  element: any\n): element is HTMLInputElement & { select: () => void } => {\n  return element instanceof HTMLInputElement && 'select' in element\n}\n\nexport const tryFocus = (\n  element?: HTMLElement | { focus: () => void } | null,\n  shouldSelect?: boolean\n) => {\n  if (element) {\n    const prevFocusedElement = document.activeElement\n\n    focusElement(element, { preventScroll: true })\n    lastAutomatedFocusTimestamp.value = window.performance.now()\n\n    if (\n      element !== prevFocusedElement &&\n      isSelectable(element) &&\n      shouldSelect\n    ) {\n      element.select()\n    }\n  }\n}\n\nfunction removeFromStack<T>(list: T[], item: T) {\n  const copy = [...list]\n\n  const idx = list.indexOf(item)\n\n  if (idx !== -1) {\n    copy.splice(idx, 1)\n  }\n  return copy\n}\n\nconst createFocusableStack = () => {\n  let stack = [] as FocusStack\n\n  const push = (layer: FocusLayer) => {\n    const currentLayer = stack[0]\n\n    if (currentLayer && layer !== currentLayer) {\n      currentLayer.pause()\n    }\n\n    stack = removeFromStack(stack, layer)\n    stack.unshift(layer)\n  }\n\n  const remove = (layer: FocusLayer) => {\n    stack = removeFromStack(stack, layer)\n    stack[0]?.resume?.()\n  }\n\n  return {\n    push,\n    remove,\n  }\n}\n\nexport const focusFirstDescendant = (\n  elements: HTMLElement[],\n  shouldSelect = false\n) => {\n  const prevFocusedElement = document.activeElement\n  for (const element of elements) {\n    tryFocus(element, shouldSelect)\n    if (document.activeElement !== prevFocusedElement) return\n  }\n}\n\nexport const focusableStack = createFocusableStack()\n\nexport const isFocusCausedByUserEvent = (): boolean => {\n  return lastUserFocusTimestamp.value > lastAutomatedFocusTimestamp.value\n}\n\nconst notifyFocusReasonPointer = () => {\n  focusReason.value = 'pointer'\n  lastUserFocusTimestamp.value = window.performance.now()\n}\n\nconst notifyFocusReasonKeydown = () => {\n  focusReason.value = 'keyboard'\n  lastUserFocusTimestamp.value = window.performance.now()\n}\n\nexport const useFocusReason = (): {\n  focusReason: typeof focusReason\n  lastUserFocusTimestamp: typeof lastUserFocusTimestamp\n  lastAutomatedFocusTimestamp: typeof lastAutomatedFocusTimestamp\n} => {\n  onMounted(() => {\n    if (focusReasonUserCount === 0) {\n      document.addEventListener('mousedown', notifyFocusReasonPointer)\n      document.addEventListener('touchstart', notifyFocusReasonPointer)\n      document.addEventListener('keydown', notifyFocusReasonKeydown)\n    }\n    focusReasonUserCount++\n  })\n\n  onBeforeUnmount(() => {\n    focusReasonUserCount--\n    if (focusReasonUserCount <= 0) {\n      document.removeEventListener('mousedown', notifyFocusReasonPointer)\n      document.removeEventListener('touchstart', notifyFocusReasonPointer)\n      document.removeEventListener('keydown', notifyFocusReasonKeydown)\n    }\n  })\n\n  return {\n    focusReason,\n    lastUserFocusTimestamp,\n    lastAutomatedFocusTimestamp,\n  }\n}\n\nexport const createFocusOutPreventedEvent = (\n  detail: CustomEventInit['detail']\n) => {\n  return new CustomEvent(FOCUSOUT_PREVENTED, {\n    ...FOCUSOUT_PREVENTED_OPTS,\n    detail,\n  })\n}\n"],"mappings":"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"}