{"version":3,"sources":["../../../src/components/List/listNavigation.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useFluent_unstable } from '@fluentui/react-shared-contexts';\nimport {\n  isHTMLElement,\n  useAnimationFrame,\n  useEventCallback,\n  useIsomorphicLayoutEffect,\n} from '@fluentui/react-utilities';\nimport type { ListNavigationMode } from './List.types';\n\nconst authorTabIndices = new WeakMap<HTMLElement, { attribute: string | null; tabIndex: number }>();\nconst editableOrMediaSelector =\n  '[contenteditable=\"\"], [contenteditable=\"true\" i], [contenteditable=\"plaintext-only\" i], audio[controls], video[controls]';\nconst naturallyFocusableSelector = `a[href], area[href], button, input, select, textarea, iframe, object, embed, summary, ${editableOrMediaSelector}`;\n\nconst getAuthorTabIndex = (element: HTMLElement): number =>\n  !element.hasAttribute('tabindex') && element.matches(editableOrMediaSelector) ? 0 : element.tabIndex;\n\nconst updateAuthorTabIndices = (root: HTMLElement, records: MutationRecord[]): void => {\n  for (const record of records) {\n    if (\n      isHTMLElement(record.target) &&\n      record.target.closest('[data-list]') === root &&\n      authorTabIndices.has(record.target)\n    ) {\n      if (record.attributeName === 'tabindex') {\n        authorTabIndices.set(record.target, {\n          attribute: record.target.getAttribute('tabindex'),\n          tabIndex: getAuthorTabIndex(record.target),\n        });\n      } else if (['contenteditable', 'controls', 'href', 'type'].includes(record.attributeName ?? '')) {\n        const original = authorTabIndices.get(record.target);\n        if (original?.attribute === null) {\n          original.tabIndex = record.target.matches(naturallyFocusableSelector) ? 0 : -1;\n        }\n      }\n    }\n  }\n};\n\nconst isAvailable = (element: HTMLElement, boundary: HTMLElement): boolean => {\n  if (element.matches(':disabled, input[type=\"hidden\"]') || element.closest('[hidden], [inert]')) {\n    return false;\n  }\n  const view = element.ownerDocument.defaultView;\n  for (let current: HTMLElement | null = element; current; current = current.parentElement) {\n    const style = view?.getComputedStyle(current);\n    if (style?.display === 'none' || style?.visibility === 'hidden' || style?.visibility === 'collapse') {\n      return false;\n    }\n    if (current === boundary) {\n      break;\n    }\n  }\n  return true;\n};\n\nexport const getListFocusableElements = (root: HTMLElement): HTMLElement[] =>\n  Array.from(root.querySelectorAll<HTMLElement>(`[tabindex], ${naturallyFocusableSelector}`)).filter(\n    element =>\n      (authorTabIndices.get(element)?.tabIndex ?? getAuthorTabIndex(element)) >= 0 && isAvailable(element, root),\n  );\n\n/**\n * Composite grids need a roving tab stop until focusgroup supports their row/control model.\n * Keep author tabindex values separate so explicit opt-outs survive focus movement and cleanup.\n */\nexport const useCompositeListNavigation = (\n  navigationMode: ListNavigationMode | undefined,\n  rootRef: React.RefObject<HTMLElement | null>,\n): React.FocusEventHandler<HTMLElement> => {\n  const { targetDocument } = useFluent_unstable();\n  const current = React.useRef<HTMLElement | null>(null);\n  const managed = React.useRef(new Set<HTMLElement>());\n  const observer = React.useRef<MutationObserver | undefined>(undefined);\n  const pendingRecords = React.useRef<MutationRecord[]>([]);\n  const [requestFrame, cancelFrame] = useAnimationFrame();\n\n  const reconcile = useEventCallback(() => {\n    const root = rootRef.current;\n    if (!root || navigationMode !== 'composite') {\n      return;\n    }\n    cancelFrame();\n    updateAuthorTabIndices(root, [...pendingRecords.current.splice(0), ...(observer.current?.takeRecords() ?? [])]);\n\n    const rows = Array.from(root.querySelectorAll<HTMLElement>('[data-navigable]')).filter(\n      element =>\n        element.closest('[data-list]') === root &&\n        (authorTabIndices.get(element)?.tabIndex ?? getAuthorTabIndex(element)) >= 0 &&\n        isAvailable(element, root),\n    );\n    const elements = rows.flatMap(row => [\n      row,\n      ...getListFocusableElements(row).filter(\n        element => element.closest('[data-navigable]') === row && element.closest('[data-list]') === root,\n      ),\n    ]);\n    const nextManaged = new Set(elements);\n\n    for (const element of managed.current) {\n      if (!nextManaged.has(element)) {\n        const original = authorTabIndices.get(element);\n        if (original?.attribute === null) {\n          element.removeAttribute('tabindex');\n        } else if (original) {\n          element.setAttribute('tabindex', original.attribute);\n        }\n        authorTabIndices.delete(element);\n      }\n    }\n\n    if (!current.current || !nextManaged.has(current.current)) {\n      current.current = rows[0] ?? null;\n    }\n    for (const element of elements) {\n      if (!authorTabIndices.has(element)) {\n        authorTabIndices.set(element, {\n          attribute: element.getAttribute('tabindex'),\n          tabIndex: getAuthorTabIndex(element),\n        });\n      }\n      const tabIndex = element === current.current ? 0 : -1;\n      // Natural editables/media can report -1 while still being native tab stops.\n      if (element.getAttribute('tabindex') !== String(tabIndex)) {\n        element.tabIndex = tabIndex;\n      }\n    }\n    managed.current = nextManaged;\n    observer.current?.takeRecords();\n  });\n\n  useIsomorphicLayoutEffect(() => {\n    const root = rootRef.current;\n    if (navigationMode !== 'composite' || !root) {\n      return;\n    }\n    const MutationObserver = targetDocument?.defaultView?.MutationObserver;\n    if (MutationObserver) {\n      observer.current = new MutationObserver(records => {\n        pendingRecords.current.push(...records);\n        requestFrame(() => reconcile());\n      });\n      observer.current.observe(root, {\n        childList: true,\n        subtree: true,\n        attributes: true,\n        attributeFilter: [\n          'tabindex',\n          'disabled',\n          'hidden',\n          'inert',\n          'href',\n          'type',\n          'contenteditable',\n          'controls',\n          'style',\n          'class',\n        ],\n      });\n    }\n    reconcile();\n\n    return () => {\n      updateAuthorTabIndices(root, [...pendingRecords.current.splice(0), ...(observer.current?.takeRecords() ?? [])]);\n      observer.current?.disconnect();\n      observer.current = undefined;\n      cancelFrame();\n      pendingRecords.current = [];\n      for (const element of managed.current) {\n        const original = authorTabIndices.get(element);\n        if (original?.attribute === null) {\n          element.removeAttribute('tabindex');\n        } else if (original) {\n          element.setAttribute('tabindex', original.attribute);\n        }\n        authorTabIndices.delete(element);\n      }\n      managed.current.clear();\n      current.current = null;\n    };\n  }, [cancelFrame, navigationMode, reconcile, requestFrame, rootRef, targetDocument]);\n\n  return useEventCallback(event => {\n    const root = rootRef.current;\n    if (navigationMode !== 'composite' || !root || !isHTMLElement(event.target)) {\n      return;\n    }\n    const row = event.target.closest<HTMLElement>('[data-navigable]');\n    if (row?.closest('[data-list]') === root) {\n      current.current = managed.current.has(event.target) ? event.target : row;\n      reconcile();\n    }\n  });\n};\n"],"names":["getListFocusableElements","useCompositeListNavigation","authorTabIndices","WeakMap","editableOrMediaSelector","naturallyFocusableSelector","getAuthorTabIndex","element","hasAttribute","matches","tabIndex","updateAuthorTabIndices","root","records","record","isHTMLElement","target","closest","has","attributeName","set","attribute","getAttribute","includes","original","get","isAvailable","boundary","view","ownerDocument","defaultView","current","parentElement","style","getComputedStyle","display","visibility","Array","from","querySelectorAll","filter","navigationMode","rootRef","targetDocument","useFluent_unstable","React","useRef","managed","Set","observer","undefined","pendingRecords","requestFrame","cancelFrame","useAnimationFrame","reconcile","useEventCallback","splice","takeRecords","rows","elements","flatMap","row","nextManaged","removeAttribute","setAttribute","delete","String","useIsomorphicLayoutEffect","MutationObserver","push","observe","childList","subtree","attributes","attributeFilter","disconnect","clear","event"],"mappings":"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"}