@mortenbrudvik/waveui 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/README.md +357 -72
  2. package/dist/components/button/Toolbar.cjs +1 -1
  3. package/dist/components/button/Toolbar.cjs.map +1 -1
  4. package/dist/components/button/Toolbar.mjs +25 -30
  5. package/dist/components/button/Toolbar.mjs.map +1 -1
  6. package/dist/components/button/Toolbar.navigation.cjs +2 -0
  7. package/dist/components/button/Toolbar.navigation.cjs.map +1 -0
  8. package/dist/components/button/Toolbar.navigation.mjs +17 -0
  9. package/dist/components/button/Toolbar.navigation.mjs.map +1 -0
  10. package/dist/components/data-display/Avatar.cjs +1 -1
  11. package/dist/components/data-display/Avatar.cjs.map +1 -1
  12. package/dist/components/data-display/Avatar.context.cjs +2 -0
  13. package/dist/components/data-display/Avatar.context.cjs.map +1 -0
  14. package/dist/components/data-display/Avatar.context.mjs +9 -0
  15. package/dist/components/data-display/Avatar.context.mjs.map +1 -0
  16. package/dist/components/data-display/Avatar.mjs +66 -61
  17. package/dist/components/data-display/Avatar.mjs.map +1 -1
  18. package/dist/components/data-display/InteractionTag.cjs +2 -0
  19. package/dist/components/data-display/InteractionTag.cjs.map +1 -0
  20. package/dist/components/data-display/InteractionTag.mjs +234 -0
  21. package/dist/components/data-display/InteractionTag.mjs.map +1 -0
  22. package/dist/components/data-display/Tag.cjs +1 -1
  23. package/dist/components/data-display/Tag.cjs.map +1 -1
  24. package/dist/components/data-display/Tag.mjs +222 -90
  25. package/dist/components/data-display/Tag.mjs.map +1 -1
  26. package/dist/components/data-display/TagGroup.cjs +2 -0
  27. package/dist/components/data-display/TagGroup.cjs.map +1 -0
  28. package/dist/components/data-display/TagGroup.context.cjs +2 -0
  29. package/dist/components/data-display/TagGroup.context.cjs.map +1 -0
  30. package/dist/components/data-display/TagGroup.context.mjs +113 -0
  31. package/dist/components/data-display/TagGroup.context.mjs.map +1 -0
  32. package/dist/components/data-display/TagGroup.mjs +149 -0
  33. package/dist/components/data-display/TagGroup.mjs.map +1 -0
  34. package/dist/components/data-display/tagStyles.cjs +2 -0
  35. package/dist/components/data-display/tagStyles.cjs.map +1 -0
  36. package/dist/components/data-display/tagStyles.mjs +108 -0
  37. package/dist/components/data-display/tagStyles.mjs.map +1 -0
  38. package/dist/components/feedback/Toaster.cjs +1 -1
  39. package/dist/components/feedback/Toaster.cjs.map +1 -1
  40. package/dist/components/feedback/Toaster.mjs +213 -181
  41. package/dist/components/feedback/Toaster.mjs.map +1 -1
  42. package/dist/components/input/Combobox.expand.cjs +1 -1
  43. package/dist/components/input/Combobox.expand.cjs.map +1 -1
  44. package/dist/components/input/Combobox.expand.mjs +7 -7
  45. package/dist/components/input/Combobox.expand.mjs.map +1 -1
  46. package/dist/components/input/Option.cjs +1 -1
  47. package/dist/components/input/Option.cjs.map +1 -1
  48. package/dist/components/input/Option.mjs +16 -11
  49. package/dist/components/input/Option.mjs.map +1 -1
  50. package/dist/components/input/SwatchPicker.cjs.map +1 -1
  51. package/dist/components/input/SwatchPicker.context.cjs.map +1 -1
  52. package/dist/components/input/SwatchPicker.context.mjs.map +1 -1
  53. package/dist/components/input/SwatchPicker.mjs.map +1 -1
  54. package/dist/components/input/TagPicker.cjs +1 -1
  55. package/dist/components/input/TagPicker.cjs.map +1 -1
  56. package/dist/components/input/TagPicker.context.cjs +2 -0
  57. package/dist/components/input/TagPicker.context.cjs.map +1 -0
  58. package/dist/components/input/TagPicker.context.mjs +20 -0
  59. package/dist/components/input/TagPicker.context.mjs.map +1 -0
  60. package/dist/components/input/TagPicker.create.cjs +2 -0
  61. package/dist/components/input/TagPicker.create.cjs.map +1 -0
  62. package/dist/components/input/TagPicker.create.mjs +24 -0
  63. package/dist/components/input/TagPicker.create.mjs.map +1 -0
  64. package/dist/components/input/TagPicker.mjs +13 -279
  65. package/dist/components/input/TagPicker.mjs.map +1 -1
  66. package/dist/components/input/TagPicker.option.cjs +2 -0
  67. package/dist/components/input/TagPicker.option.cjs.map +1 -0
  68. package/dist/components/input/TagPicker.option.mjs +45 -0
  69. package/dist/components/input/TagPicker.option.mjs.map +1 -0
  70. package/dist/components/input/TagPicker.overflow.cjs +2 -0
  71. package/dist/components/input/TagPicker.overflow.cjs.map +1 -0
  72. package/dist/components/input/TagPicker.overflow.mjs +153 -0
  73. package/dist/components/input/TagPicker.overflow.mjs.map +1 -0
  74. package/dist/components/input/TagPicker.parts.cjs +2 -0
  75. package/dist/components/input/TagPicker.parts.cjs.map +1 -0
  76. package/dist/components/input/TagPicker.parts.mjs +425 -0
  77. package/dist/components/input/TagPicker.parts.mjs.map +1 -0
  78. package/dist/components/input/TagPicker.root.cjs +2 -0
  79. package/dist/components/input/TagPicker.root.cjs.map +1 -0
  80. package/dist/components/input/TagPicker.root.mjs +416 -0
  81. package/dist/components/input/TagPicker.root.mjs.map +1 -0
  82. package/dist/components/layout/Accordion.cjs.map +1 -1
  83. package/dist/components/layout/Accordion.mjs.map +1 -1
  84. package/dist/components/navigation/Hamburger.cjs +2 -0
  85. package/dist/components/navigation/Hamburger.cjs.map +1 -0
  86. package/dist/components/navigation/Hamburger.mjs +54 -0
  87. package/dist/components/navigation/Hamburger.mjs.map +1 -0
  88. package/dist/components/navigation/Nav.cjs +1 -1
  89. package/dist/components/navigation/Nav.cjs.map +1 -1
  90. package/dist/components/navigation/Nav.context.cjs +2 -0
  91. package/dist/components/navigation/Nav.context.cjs.map +1 -0
  92. package/dist/components/navigation/Nav.context.mjs +137 -0
  93. package/dist/components/navigation/Nav.context.mjs.map +1 -0
  94. package/dist/components/navigation/Nav.mjs +151 -258
  95. package/dist/components/navigation/Nav.mjs.map +1 -1
  96. package/dist/components/navigation/Nav.parts.cjs +2 -0
  97. package/dist/components/navigation/Nav.parts.cjs.map +1 -0
  98. package/dist/components/navigation/Nav.parts.mjs +77 -0
  99. package/dist/components/navigation/Nav.parts.mjs.map +1 -0
  100. package/dist/components/navigation/Nav.root.cjs +2 -0
  101. package/dist/components/navigation/Nav.root.cjs.map +1 -0
  102. package/dist/components/navigation/Nav.root.mjs +78 -0
  103. package/dist/components/navigation/Nav.root.mjs.map +1 -0
  104. package/dist/components/navigation/NavDrawer.cjs +2 -0
  105. package/dist/components/navigation/NavDrawer.cjs.map +1 -0
  106. package/dist/components/navigation/NavDrawer.context.cjs +2 -0
  107. package/dist/components/navigation/NavDrawer.context.cjs.map +1 -0
  108. package/dist/components/navigation/NavDrawer.context.mjs +11 -0
  109. package/dist/components/navigation/NavDrawer.context.mjs.map +1 -0
  110. package/dist/components/navigation/NavDrawer.mjs +179 -0
  111. package/dist/components/navigation/NavDrawer.mjs.map +1 -0
  112. package/dist/components/overlays/Dialog.cjs +1 -1
  113. package/dist/components/overlays/Dialog.cjs.map +1 -1
  114. package/dist/components/overlays/Dialog.mjs +190 -125
  115. package/dist/components/overlays/Dialog.mjs.map +1 -1
  116. package/dist/components/overlays/Dialog.shared.cjs +1 -1
  117. package/dist/components/overlays/Dialog.shared.cjs.map +1 -1
  118. package/dist/components/overlays/Dialog.shared.mjs +157 -81
  119. package/dist/components/overlays/Dialog.shared.mjs.map +1 -1
  120. package/dist/components/overlays/Dialog.slots.cjs +2 -0
  121. package/dist/components/overlays/Dialog.slots.cjs.map +1 -0
  122. package/dist/components/overlays/Dialog.slots.mjs +129 -0
  123. package/dist/components/overlays/Dialog.slots.mjs.map +1 -0
  124. package/dist/components/overlays/Drawer.cjs +1 -1
  125. package/dist/components/overlays/Drawer.cjs.map +1 -1
  126. package/dist/components/overlays/Drawer.context.cjs +2 -0
  127. package/dist/components/overlays/Drawer.context.cjs.map +1 -0
  128. package/dist/components/overlays/Drawer.context.mjs +29 -0
  129. package/dist/components/overlays/Drawer.context.mjs.map +1 -0
  130. package/dist/components/overlays/Drawer.mjs +12 -187
  131. package/dist/components/overlays/Drawer.mjs.map +1 -1
  132. package/dist/components/overlays/Drawer.panel.cjs +2 -0
  133. package/dist/components/overlays/Drawer.panel.cjs.map +1 -0
  134. package/dist/components/overlays/Drawer.panel.mjs +159 -0
  135. package/dist/components/overlays/Drawer.panel.mjs.map +1 -0
  136. package/dist/components/overlays/Drawer.parts.cjs +2 -0
  137. package/dist/components/overlays/Drawer.parts.cjs.map +1 -0
  138. package/dist/components/overlays/Drawer.parts.mjs +152 -0
  139. package/dist/components/overlays/Drawer.parts.mjs.map +1 -0
  140. package/dist/components/overlays/Drawer.root.cjs +2 -0
  141. package/dist/components/overlays/Drawer.root.cjs.map +1 -0
  142. package/dist/components/overlays/Drawer.root.mjs +207 -0
  143. package/dist/components/overlays/Drawer.root.mjs.map +1 -0
  144. package/dist/components/overlays/Drawer.scroll.cjs +2 -0
  145. package/dist/components/overlays/Drawer.scroll.cjs.map +1 -0
  146. package/dist/components/overlays/Drawer.scroll.mjs +42 -0
  147. package/dist/components/overlays/Drawer.scroll.mjs.map +1 -0
  148. package/dist/hooks/useDismiss.cjs.map +1 -1
  149. package/dist/hooks/useDismiss.mjs.map +1 -1
  150. package/dist/hooks/useFocusTrap.cjs +1 -1
  151. package/dist/hooks/useFocusTrap.cjs.map +1 -1
  152. package/dist/hooks/useFocusTrap.mjs +115 -98
  153. package/dist/hooks/useFocusTrap.mjs.map +1 -1
  154. package/dist/hooks/useModalLayer.cjs +1 -1
  155. package/dist/hooks/useModalLayer.cjs.map +1 -1
  156. package/dist/hooks/useModalLayer.mjs +35 -22
  157. package/dist/hooks/useModalLayer.mjs.map +1 -1
  158. package/dist/hooks/useRestoreFocus.cjs +1 -1
  159. package/dist/hooks/useRestoreFocus.cjs.map +1 -1
  160. package/dist/hooks/useRestoreFocus.mjs +91 -78
  161. package/dist/hooks/useRestoreFocus.mjs.map +1 -1
  162. package/dist/hooks/useRovingTabIndex.cjs +1 -1
  163. package/dist/hooks/useRovingTabIndex.cjs.map +1 -1
  164. package/dist/hooks/useRovingTabIndex.mjs +102 -78
  165. package/dist/hooks/useRovingTabIndex.mjs.map +1 -1
  166. package/dist/index.cjs +1 -1
  167. package/dist/index.d.cts +2226 -221
  168. package/dist/index.d.ts +2226 -221
  169. package/dist/index.mjs +115 -107
  170. package/dist/lib/classNames.cjs +1 -1
  171. package/dist/lib/classNames.cjs.map +1 -1
  172. package/dist/lib/classNames.mjs +137 -77
  173. package/dist/lib/classNames.mjs.map +1 -1
  174. package/dist/lib/icons.cjs +1 -1
  175. package/dist/lib/icons.cjs.map +1 -1
  176. package/dist/lib/icons.mjs +13 -6
  177. package/dist/lib/icons.mjs.map +1 -1
  178. package/dist/lib/layers.cjs +1 -1
  179. package/dist/lib/layers.cjs.map +1 -1
  180. package/dist/lib/layers.mjs +53 -47
  181. package/dist/lib/layers.mjs.map +1 -1
  182. package/dist/styles.css +1 -1
  183. package/package.json +1 -1
  184. package/src/styles/tokens.css +25 -0
@@ -1 +1 @@
1
- {"version":3,"file":"useRestoreFocus.mjs","names":["React","FOLLOWING","PORTAL_SELECTOR","isValidTarget","el","isFocusable","isInsideOtherOpenModal","recordPosition","position","body","node","captureAnchors","opener","layers","getOpenLayers","isInsideLayerTree","layer","compareLayers","b","a","anchors","anchor","container","entry","getSurfaceEntries","tracker","getFocusTracker","isClosingByHover","seen","other","isHoverCloseOf","options","getGlobalRegistry","ELEMENT_NODE","isElementTarget","target","recordFocus","doc","refreshFocusRecord","last","retainFocusTracker","onFocusIn","event","onFocusOut","unsubscribe","subscribeLayers","retained","getLostFocus","element","captureOpener","isCandidate","trigger","triggerRef","active","lost","getLayerTargets","targets","first","getFirstTabbable","isAfterPoint","point","ref","placement","follows","resolvePoint","index","path","root","next","previous","getNearbyTargets","resolved","levels","level","candidates","getTabbableElements","inLevel","after","before","shouldRestore","onBody","isHoverClose","getReplacementTargets","restoreCandidates","latest","replacements","parentLayer","parentLayerId","getLayer","outsideParent","getTopmostLayer","restore","candidate","focusElement","useRestoreFocus","DismissLayerContext","latestRef","useRef","capturedRef","insertionEnabledRef","layoutEnabledRef","scheduledRef","entryRef","useLayoutEffect","release","surfaces","useInsertionEffect","wasEnabled","enabled","captured","acts","scheduled","entryHolder","snapshot","token"],"sources":["../../src/hooks/useRestoreFocus.ts"],"sourcesContent":["import * as React from 'react';\nimport { useInsertionEffect, useLayoutEffect, useRef } from 'react';\nimport { focusElement, getFirstTabbable, getTabbableElements, isFocusable } from '../lib/focus';\nimport { getGlobalRegistry } from '../lib/globalRegistry';\nimport {\n ALLOW_OUTSIDE_SELECTOR,\n compareLayers,\n getLayer,\n getOpenLayers,\n getTopmostLayer,\n isInsideLayerTree,\n isInsideOtherOpenModal,\n subscribeLayers,\n type LayerRecord,\n} from '../lib/layers';\nimport { DismissLayerContext } from './useDismiss';\n\n/** Options of {@link useRestoreFocus}. */\nexport interface UseRestoreFocusOptions {\n /** `true` while the surface is open. */\n enabled: boolean;\n /** The layer surface: opener candidates inside it are ignored. */\n container?: HTMLElement | null;\n /** The trigger: preferred restore target. */\n triggerRef?: React.RefObject<HTMLElement | null>;\n /** Consumer override: focused first when valid. */\n finalFocusRef?: React.RefObject<HTMLElement | null>;\n /** Used when neither the override, the opener nor the trigger can take focus. */\n fallback?: () => HTMLElement | null;\n /**\n * Popovers: restore only when focus was inside the surface (or lost to `<body>`) at close, so a\n * click that moved focus elsewhere is respected. @default false\n */\n onlyIfFocusInside?: boolean;\n /**\n * Read when the surface closes, or unmounts while open: `true` when the hover intent closed it\n * (Popover and Menu `openOnHover`, a submenu closing by hover). A hover close never moves focus:\n * it restores only while focus is inside the surface, so focus on `<body>` (where a hover-opened\n * surface leaves it) or elsewhere stays where it is. A surface opened from inside one that closes\n * by hover (its opener, else its trigger, lies in that surface: a submenu, a hover card in a\n * hover card, at any depth) is taken along, and its close counts as a hover close too. Every\n * other close keeps the rules above.\n */\n isHoverClose?: () => boolean;\n}\n\ninterface Latest {\n options: UseRestoreFocusOptions;\n parentLayerId: string | null;\n}\n\n/** Where an element sat in the document, recorded so a spot near it can be found once it is gone. */\ninterface DomPosition {\n /** The element and its ancestors up to `<body>`, innermost first. */\n path: Element[];\n /** The next element sibling of each `path` entry when it was recorded. */\n next: Array<Element | null>;\n /** The previous element sibling of each `path` entry when it was recorded. */\n previous: Array<Element | null>;\n}\n\n/** What is captured about the opener when the surface opens. */\ninterface OpenerRecord {\n element: HTMLElement;\n /** Its place in the document, used when it is removed or can no longer take focus. */\n position: DomPosition;\n /** The anchors (triggers) of the open layers whose tree held it, innermost layer first. */\n anchors: Array<{ element: HTMLElement; position: DomPosition }>;\n}\n\n/** A point in document order: just before `ref`, or just after `ref` and its subtree. */\ninterface DomPoint {\n ref: Element;\n placement: 'before' | 'after';\n}\n\nconst FOLLOWING = 4; // Node.DOCUMENT_POSITION_FOLLOWING\nconst PORTAL_SELECTOR = '[data-wave-portal]';\n\nfunction isValidTarget(el: HTMLElement | null | undefined): el is HTMLElement {\n if (!el || !el.isConnected) return false;\n if (!isFocusable(el)) return false; // also rejects [inert] ancestors, hidden and disabled\n if (el.closest('[aria-hidden=\"true\"]')) return false;\n return !isInsideOtherOpenModal(el);\n}\n\nfunction recordPosition(el: Element): DomPosition {\n const position: DomPosition = { path: [], next: [], previous: [] };\n const body = el.ownerDocument.body;\n for (let node: Element | null = el; node; node = node.parentElement) {\n position.path.push(node);\n position.next.push(node.nextElementSibling);\n position.previous.push(node.previousElementSibling);\n if (node === body) break;\n }\n return position;\n}\n\n/**\n * The anchors of the open layers whose tree contains `opener` (a menu item's menu button),\n * innermost layer first. An opener in an allow-listed region (a toast) belongs to no layer.\n */\nfunction captureAnchors(\n opener: HTMLElement,\n container: HTMLElement | null | undefined,\n): OpenerRecord['anchors'] {\n if (opener.closest(ALLOW_OUTSIDE_SELECTOR)) return [];\n const layers = getOpenLayers()\n .filter((layer) => isInsideLayerTree(layer.id, opener))\n .sort((a, b) => compareLayers(b, a));\n const anchors: OpenerRecord['anchors'] = [];\n for (const layer of layers) {\n const anchor = layer.getAnchor();\n if (!anchor || anchor === opener || (container && container.contains(anchor))) continue;\n if (anchors.some((entry) => entry.element === anchor)) continue;\n anchors.push({ element: anchor, position: recordPosition(anchor) });\n }\n return anchors;\n}\n\n// ---------------------------------------------------------------------------\n// Focus tracker\n// ---------------------------------------------------------------------------\n\n/**\n * Remembers the last focused element, with its place in the document and the anchors of the\n * overlays that held it, recorded while it is still in the document. A surface captures its opener\n * in the mutation phase, and by then React has already deleted anything earlier in the tree that\n * the same commit removes. A popover rendered before a dialog, whose menu item closes the popover\n * and opens the dialog, has lost its menu item (and focus is on `<body>`) when the dialog captures.\n * The record still leads the restore to the popover's trigger. It lives in\n * `getGlobalRegistry('restoreFocusTracker')`, so two copies of the library share one set of\n * listeners.\n */\ninterface FocusTracker {\n /** Mounted `useRestoreFocus` instances: the listeners are installed while there is one. */\n users: number;\n /** The last focused element; `null` once focus was moved away from it on purpose. */\n last: OpenerRecord | null;\n /** Removes the listeners; `null` while none are installed. */\n uninstall: (() => void) | null;\n /**\n * The mounted surfaces, as the other instances see them (see {@link isClosingByHover}). Created\n * on first use: a tracker made by another copy of the library does not have it.\n */\n surfaces?: Set<SurfaceEntry>;\n}\n\n/** A mounted `useRestoreFocus`, as the other instances see it. */\ninterface SurfaceEntry {\n /** Whether the surface is closing by hover (its `isHoverClose`). */\n isHoverClose(): boolean;\n /** The surface element, while there is one. */\n getContainer(): HTMLElement | null;\n /** The element the surface was opened from: its captured opener, else its trigger. */\n getOpener(): HTMLElement | null;\n}\n\nfunction getSurfaceEntries(): Set<SurfaceEntry> {\n const tracker = getFocusTracker();\n tracker.surfaces ??= new Set();\n return tracker.surfaces;\n}\n\n/**\n * Whether the close of `entry` is a hover close: its own, or that of a surface holding the element\n * it was opened from (a submenu in a menu that closes by hover, a hover card inside a hover card,\n * at any depth), which takes it along.\n */\nfunction isClosingByHover(entry: SurfaceEntry, seen = new Set<SurfaceEntry>()): boolean {\n if (seen.has(entry)) return false;\n seen.add(entry);\n if (entry.isHoverClose()) return true;\n const opener = entry.getOpener();\n if (!opener) return false;\n for (const other of getSurfaceEntries()) {\n const container = other === entry ? null : other.getContainer();\n if (container && container.contains(opener) && isClosingByHover(other, seen)) return true;\n }\n return false;\n}\n\n/** Whether this close is a hover close: the surface's own, or that of a surface it was opened from. */\nfunction isHoverCloseOf(entry: SurfaceEntry | null, options: UseRestoreFocusOptions): boolean {\n return entry ? isClosingByHover(entry) : options.isHoverClose?.() === true;\n}\n\nfunction getFocusTracker(): FocusTracker {\n return getGlobalRegistry<FocusTracker>('restoreFocusTracker', () => ({\n users: 0,\n last: null,\n uninstall: null,\n }));\n}\n\nconst ELEMENT_NODE = 1;\n\nfunction isElementTarget(target: EventTarget | null): target is HTMLElement {\n return !!target && (target as Partial<Node>).nodeType === ELEMENT_NODE;\n}\n\nfunction recordFocus(tracker: FocusTracker, el: HTMLElement): void {\n const doc = el.ownerDocument;\n if (el === doc.body || el === doc.documentElement) {\n tracker.last = null;\n return;\n }\n tracker.last = { element: el, position: recordPosition(el), anchors: captureAnchors(el, null) };\n}\n\n/**\n * A layer opened or closed: re-record the focused element's place and overlays while it is still in\n * the document. An element focused as its popover opens (autoFocus) gets focus before the\n * popover's layer registers. Anchors found earlier are kept (a layer unregistering as its content\n * is deleted no longer reports it).\n */\nfunction refreshFocusRecord(tracker: FocusTracker): void {\n const last = tracker.last;\n if (!last || !last.element.isConnected) return;\n const anchors = captureAnchors(last.element, null);\n for (const anchor of last.anchors) {\n if (!anchors.some((entry) => entry.element === anchor.element)) anchors.push(anchor);\n }\n tracker.last = { element: last.element, position: recordPosition(last.element), anchors };\n}\n\n/** Installs the tracker's listeners while at least one `useRestoreFocus` is mounted. */\nfunction retainFocusTracker(): () => void {\n if (typeof document === 'undefined') return () => {};\n const tracker = getFocusTracker();\n tracker.users += 1;\n if (!tracker.uninstall) {\n const doc = document;\n const onFocusIn = (event: FocusEvent) => {\n if (isElementTarget(event.target)) recordFocus(tracker, event.target);\n };\n // Focus leaves for nowhere when the element is removed (some browsers fire focusout then) or\n // when the user presses a non-focusable spot. Decide once the current task has run: an element\n // still in the document that can take focus was left on purpose and is no opener to return to.\n const onFocusOut = (event: FocusEvent) => {\n const target = event.target;\n if (event.relatedTarget || !isElementTarget(target)) return;\n queueMicrotask(() => {\n if (tracker.last?.element !== target) return;\n if (!target.isConnected || !isFocusable(target)) return;\n if (target.ownerDocument.activeElement !== target) tracker.last = null;\n });\n };\n doc.addEventListener('focusin', onFocusIn, true);\n doc.addEventListener('focusout', onFocusOut, true);\n const unsubscribe = subscribeLayers(() => refreshFocusRecord(tracker));\n if (isElementTarget(doc.activeElement)) recordFocus(tracker, doc.activeElement);\n tracker.uninstall = () => {\n doc.removeEventListener('focusin', onFocusIn, true);\n doc.removeEventListener('focusout', onFocusOut, true);\n unsubscribe();\n tracker.last = null;\n };\n }\n let retained = true;\n return () => {\n if (!retained) return;\n retained = false;\n tracker.users = Math.max(0, tracker.users - 1);\n if (tracker.users > 0) return;\n tracker.uninstall?.();\n tracker.uninstall = null;\n };\n}\n\n/**\n * The tracker's record of the element focus was lost from: it no longer has focus and was removed,\n * or can no longer take focus (disabled, hidden, made inert). `null` when focus was moved away from\n * it on purpose, or it still has focus.\n */\nfunction getLostFocus(): OpenerRecord | null {\n const last = getFocusTracker().last;\n if (!last) return null;\n const { element } = last;\n if (element.ownerDocument.activeElement === element) return null;\n if (element.isConnected && isFocusable(element)) return null;\n return last;\n}\n\n/**\n * The element to restore to when the surface opens: the trigger, else the focused element, else —\n * focus already lost to `<body>` — the element the focus tracker saw lose it.\n */\nfunction captureOpener(options: UseRestoreFocusOptions): OpenerRecord | null {\n if (typeof document === 'undefined') return null;\n const { container, triggerRef } = options;\n const isCandidate = (el: Element | null | undefined): el is HTMLElement =>\n !!el &&\n el !== el.ownerDocument.body &&\n el !== el.ownerDocument.documentElement &&\n !(container && container.contains(el));\n const trigger = triggerRef?.current;\n const active = document.activeElement;\n let element: HTMLElement | null = null;\n if (isCandidate(trigger)) element = trigger;\n else if (isCandidate(active)) element = active;\n if (element) {\n return {\n element,\n position: recordPosition(element),\n anchors: captureAnchors(element, container),\n };\n }\n // The opener was removed earlier in this commit (a menu item in a popover that closed as this\n // surface opened), or can no longer take focus: use what the tracker recorded while it could.\n const lost = getLostFocus();\n if (!lost || !isCandidate(lost.element)) return null;\n return {\n element: lost.element,\n position: lost.position,\n anchors: lost.anchors.filter((anchor) => !(container && container.contains(anchor.element))),\n };\n}\n\n/** A layer's surface (or its first tabbable element), for the last-resort fallbacks. */\nfunction getLayerTargets(layer: LayerRecord | null): HTMLElement[] {\n if (!layer) return [];\n const targets: HTMLElement[] = [];\n for (const el of layer.getElements()) {\n if (!el) continue;\n targets.push(el);\n const first = getFirstTabbable(el);\n if (first) targets.push(first);\n }\n return targets;\n}\n\nfunction isAfterPoint(el: Element, point: DomPoint): boolean {\n const { ref, placement } = point;\n if (el === ref) return placement === 'before';\n const follows = (ref.compareDocumentPosition(el) & FOLLOWING) !== 0;\n return placement === 'before' ? follows : follows && !ref.contains(el);\n}\n\n/**\n * Where to look once the recorded element is gone (or cannot take focus): its nearest ancestor\n * still in the document, and the point where the element used to be. `null` when its whole overlay\n * (the portal wrapper) was removed: that spot means nothing on the page.\n */\nfunction resolvePoint(position: DomPosition): { root: Element; point: DomPoint } | null {\n const { path } = position;\n const index = path.findIndex((el) => el.isConnected);\n if (index === -1) return null;\n if (index === 0) {\n const root = path[0].parentElement;\n return root ? { root, point: { ref: path[0], placement: 'after' } } : null;\n }\n if (path.slice(0, index).some((el) => el.matches(PORTAL_SELECTOR))) return null;\n const root = path[index];\n const next = position.next[index - 1];\n if (next && next.parentElement === root) {\n return { root, point: { ref: next, placement: 'before' } };\n }\n const previous = position.previous[index - 1];\n if (previous && previous.parentElement === root) {\n return { root, point: { ref: previous, placement: 'after' } };\n }\n return { root, point: { ref: root, placement: 'before' } };\n}\n\n/**\n * Tabbable elements near a recorded position, nearest first. In the innermost surviving ancestor\n * that holds a valid one: the first after the old spot (where Tab would have gone from there),\n * else the last before it; then the same one level up. The search stops at the element's overlay\n * (its portal wrapper) and never reaches behind an open modal.\n */\nfunction getNearbyTargets(\n position: DomPosition,\n container: HTMLElement | null | undefined,\n): HTMLElement[] {\n const resolved = resolvePoint(position);\n if (!resolved) return [];\n const { root, point } = resolved;\n const body = root.ownerDocument.body;\n const levels: Element[] = [];\n for (let level: Element | null = root; level; level = level.parentElement) {\n if (isInsideOtherOpenModal(level)) break;\n levels.push(level);\n if (level === body || level.matches(PORTAL_SELECTOR)) break;\n }\n if (levels.length === 0) return [];\n const candidates = getTabbableElements(levels[levels.length - 1])\n .filter((el) => !(container && container.contains(el)) && isValidTarget(el))\n .sort((a, b) => ((a.compareDocumentPosition(b) & FOLLOWING) !== 0 ? -1 : 1));\n const targets: HTMLElement[] = [];\n for (const level of levels) {\n const inLevel = candidates.filter((el) => level.contains(el) && !targets.includes(el));\n const after = inLevel.find((el) => isAfterPoint(el, point));\n const before = inLevel.filter((el) => !isAfterPoint(el, point)).pop();\n if (after) targets.push(after);\n if (before) targets.push(before);\n }\n return targets;\n}\n\n/**\n * Whether the restore acts for this close: always while focus is inside the surface; otherwise\n * never for a hover close (`isClosingByHover`, asked only then), and with `onlyIfFocusInside` only\n * when focus was lost to `<body>`.\n */\nfunction shouldRestore(options: UseRestoreFocusOptions, isHoverClose: () => boolean): boolean {\n if (typeof document === 'undefined') return false;\n const active = document.activeElement;\n const onBody = !active || active === document.body || active === document.documentElement;\n if (!onBody && options.container?.contains(active)) return true;\n if (!onBody && options.onlyIfFocusInside) return false;\n return !isHoverClose();\n}\n\n/**\n * Where to go when the opener was removed or can no longer take focus, nearest first: the tabbable\n * element next to where it was, else the trigger of the overlay it was in (a menu item's menu\n * button), else the element next to that trigger.\n */\nfunction* getReplacementTargets(\n opener: OpenerRecord,\n container: HTMLElement | null | undefined,\n): Generator<HTMLElement> {\n yield* getNearbyTargets(opener.position, container);\n for (const anchor of opener.anchors) yield anchor.element;\n for (const anchor of opener.anchors) yield* getNearbyTargets(anchor.position, container);\n}\n\n/** Restore targets in order of preference, computed lazily (the later ones cost more). */\nfunction* restoreCandidates(\n latest: Latest,\n opener: OpenerRecord | null,\n): Generator<HTMLElement | null | undefined> {\n const { options, parentLayerId } = latest;\n yield options.finalFocusRef?.current;\n yield opener?.element;\n yield options.triggerRef?.current;\n yield options.fallback?.();\n const replacements = opener ? getReplacementTargets(opener, options.container) : null;\n const parentLayer = parentLayerId === null ? null : getLayer(parentLayerId);\n const outsideParent: HTMLElement[] = [];\n if (parentLayer) {\n // The surface was rendered inside a layer that stays open (a confirm Dialog in a Drawer): the\n // opener's replacement inside that layer (the next row after a delete) comes before the\n // layer's surface, so where the surface sits in the React tree does not change the result.\n for (const target of replacements ?? []) {\n if (isInsideLayerTree(parentLayer.id, target)) yield target;\n else outsideParent.push(target);\n }\n yield* getLayerTargets(parentLayer);\n yield* outsideParent;\n } else if (replacements) {\n yield* replacements;\n }\n // Still nothing: into the modal that stays open, never onto the inert page behind it.\n yield* getLayerTargets(getTopmostLayer((layer) => layer.kind === 'modal'));\n}\n\nfunction restore(latest: Latest, opener: OpenerRecord | null): void {\n const container = latest.options.container;\n for (const candidate of restoreCandidates(latest, opener)) {\n if (container && candidate && container.contains(candidate)) continue;\n if (isValidTarget(candidate) && focusElement(candidate, { preventScroll: true })) return;\n }\n}\n\n/**\n * Returns focus to where it came from when a surface closes (dialogs, drawers, popovers, menus,\n * the DatePicker calendar, TeachingPopover).\n *\n * - **Capture**: when `enabled` flips to `true`, the opener is captured in an insertion effect —\n * before React applies `autoFocus` inside the new surface — preferring `triggerRef.current` and\n * ignoring `<body>` and elements inside `container`. Its place in the document and the triggers\n * of the overlays it sits in are recorded with it. When focus is already on `<body>` because\n * the focused element was removed earlier in the same commit (a menu item in a popover that\n * closes as the dialog it opens appears, the popover coming first in the tree), a document\n * focus tracker supplies that element with its recorded place and overlay triggers. The tracker\n * runs while any instance of the hook is mounted, and it forgets an element the user moved focus\n * away from on purpose.\n * - **Restore** when `enabled` flips to `false` (layout phase) and when the component unmounts\n * while enabled. The unmount restore runs in a microtask and is cancelled if the same instance\n * mounts again, so React StrictMode's simulated unmount does not pull focus out of a surface\n * that just opened.\n * - **Targets**, first valid wins:\n * 1. `finalFocusRef` → the captured opener → `triggerRef` → `fallback()`.\n * 2. When the opener was removed or cannot take focus, its replacement, nearest first: the\n * tabbable element next to where it was (in the innermost surviving ancestor that holds one,\n * the next one — the next row's action after a delete — else the previous one; then the same\n * one level up), else the trigger of the overlay it was in (a menu item's menu button), else\n * the element next to that trigger. When the surface is rendered inside another open layer\n * (its parent layer, from `DismissLayerContext`: a confirm Dialog in a Drawer's content),\n * only the replacements inside that layer's tree are tried here.\n * 3. The parent layer's surface, then the replacements outside the parent layer.\n * 4. The surface of the modal layer that stays open.\n *\n * So a confirm Dialog rendered in a Drawer's content and one rendered next to the Drawer both\n * return focus to the adjacent row, not to the Drawer's panel. A target is valid when it is\n * connected, focusable, not inside `[inert]` or `[aria-hidden=\"true\"]`, and not cut off behind\n * another open modal layer — so focus never lands on the inert page behind a Drawer that stays\n * open, and is not dropped on `<body>` while a valid target exists. Focus uses `preventScroll`.\n * - `onlyIfFocusInside` (popovers): restores only when focus is inside the surface or was lost to\n * `<body>`.\n * - `isHoverClose`: a close by the hover intent restores only when focus is inside the surface, so\n * a hover close never moves focus from `<body>` or from where the user put it. The same holds for\n * a surface opened from inside one that closes by hover (a submenu, a hover card in a hover\n * card): every mounted instance is known to the others through the focus tracker's record.\n */\nexport function useRestoreFocus(options: UseRestoreFocusOptions): void {\n const parentLayerId = React.useContext(DismissLayerContext);\n const latestRef = useRef<Latest>({ options, parentLayerId });\n const capturedRef = useRef<OpenerRecord | null>(null);\n const insertionEnabledRef = useRef(false);\n const layoutEnabledRef = useRef(false);\n const scheduledRef = useRef<object | null>(null);\n const entryRef = useRef<SurfaceEntry | null>(null);\n const { enabled } = options;\n\n // Track focus while mounted, so an opener removed in the commit that opens the surface is known.\n // And be known to the other instances: a surface closing by hover takes along the surfaces opened\n // from inside it, whose closes are then hover closes too.\n useLayoutEffect(() => {\n const release = retainFocusTracker();\n const entry: SurfaceEntry = {\n isHoverClose: () => latestRef.current.options.isHoverClose?.() === true,\n getContainer: () => latestRef.current.options.container ?? null,\n getOpener: () =>\n capturedRef.current?.element ?? latestRef.current.options.triggerRef?.current ?? null,\n };\n entryRef.current = entry;\n const surfaces = getSurfaceEntries();\n surfaces.add(entry);\n return () => {\n release();\n // Kept to the end of the commit: a surface opened from inside this one, closing along with\n // it, still asks about it.\n queueMicrotask(() => surfaces.delete(entry));\n };\n }, []);\n\n // Mutation phase: record the latest options and capture the opener when `enabled` turns on,\n // before autoFocus (layout phase) or a focus trap's initial focus moves focus into the surface.\n useInsertionEffect(() => {\n latestRef.current = { options, parentLayerId };\n const wasEnabled = insertionEnabledRef.current;\n insertionEnabledRef.current = options.enabled;\n if (options.enabled && !wasEnabled) capturedRef.current = captureOpener(options);\n });\n\n // `enabled` true → false: restore in the layout phase (after every cleanup of this commit, so a\n // modal's isolation is already gone).\n useLayoutEffect(() => {\n const wasEnabled = layoutEnabledRef.current;\n layoutEnabledRef.current = enabled;\n if (!wasEnabled || enabled) return;\n const latest = latestRef.current;\n const captured = capturedRef.current;\n const entry = entryRef.current;\n // Decided before the opener is dropped: a hover close is also recognised through it.\n const acts = shouldRestore(latest.options, () => isHoverCloseOf(entry, latest.options));\n capturedRef.current = null;\n if (acts) restore(latest, captured);\n }, [enabled]);\n\n // Unmount while enabled: restore in a microtask, cancelled by a remount of this instance.\n useLayoutEffect(() => {\n const latest = latestRef;\n const captured = capturedRef;\n const scheduled = scheduledRef;\n const entryHolder = entryRef;\n scheduled.current = null;\n return () => {\n const snapshot = latest.current;\n if (!snapshot.options.enabled) return;\n // Decide now, while the surface is still in the document.\n const entry = entryHolder.current;\n if (!shouldRestore(snapshot.options, () => isHoverCloseOf(entry, snapshot.options))) return;\n const token = {};\n scheduled.current = token;\n const opener = captured.current;\n queueMicrotask(() => {\n if (scheduled.current !== token) return;\n scheduled.current = null;\n const active = typeof document === 'undefined' ? null : document.activeElement;\n const container = snapshot.options.container;\n // Focus placed elsewhere in the meantime (another surface opened) wins.\n if (\n active &&\n active !== document.body &&\n active !== document.documentElement &&\n !(container && container.contains(active))\n ) {\n return;\n }\n restore(snapshot, opener);\n });\n };\n }, []);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useRestoreFocus.mjs","names":["React","FOLLOWING","PORTAL_SELECTOR","isValidTarget","el","isFocusable","isInsideOtherOpenModal","recordPosition","position","body","node","captureAnchors","opener","layers","getOpenLayers","isInsideLayerTree","layer","compareLayers","b","a","anchors","anchor","container","entry","getSurfaceEntries","tracker","getFocusTracker","isClosingByHover","seen","other","isHoverCloseOf","options","getGlobalRegistry","ELEMENT_NODE","isElementTarget","target","recordFocus","doc","refreshFocusRecord","last","retainFocusTracker","onFocusIn","event","onFocusOut","unsubscribe","subscribeLayers","retained","getLostFocus","element","captureOpener","isCandidate","trigger","triggerRef","active","lost","getLayerTargets","targets","first","getFirstTabbable","isAfterPoint","point","ref","placement","follows","resolvePoint","index","path","root","next","previous","getNearbyTargets","resolved","levels","level","candidates","getTabbableElements","inLevel","after","before","isFocusLostFromInside","isInLayerOpenedFrom","layerId","isDescendantLayer","shouldRestore","onBody","isHoverClose","getReplacementTargets","restoreCandidates","latest","replacements","parentLayer","parentLayerId","getLayer","outsideParent","getTopmostLayer","restore","candidate","focusElement","useRestoreFocus","DismissLayerContext","latestRef","useRef","capturedRef","insertionEnabledRef","layoutEnabledRef","scheduledRef","entryRef","useLayoutEffect","release","surfaces","useInsertionEffect","wasEnabled","enabled","captured","acts","scheduled","entryHolder","snapshot","token"],"sources":["../../src/hooks/useRestoreFocus.ts"],"sourcesContent":["import * as React from 'react';\nimport { useInsertionEffect, useLayoutEffect, useRef } from 'react';\nimport { focusElement, getFirstTabbable, getTabbableElements, isFocusable } from '../lib/focus';\nimport { getGlobalRegistry } from '../lib/globalRegistry';\nimport {\n ALLOW_OUTSIDE_SELECTOR,\n compareLayers,\n getLayer,\n getOpenLayers,\n getTopmostLayer,\n isDescendantLayer,\n isInsideLayerTree,\n isInsideOtherOpenModal,\n subscribeLayers,\n type LayerRecord,\n} from '../lib/layers';\nimport { DismissLayerContext } from './useDismiss';\n\n/** Options of {@link useRestoreFocus}. */\nexport interface UseRestoreFocusOptions {\n /** `true` while the surface is open. */\n enabled: boolean;\n /** The layer surface: opener candidates inside it are ignored. */\n container?: HTMLElement | null;\n /** The trigger: preferred restore target. */\n triggerRef?: React.RefObject<HTMLElement | null>;\n /** Consumer override: focused first when valid. */\n finalFocusRef?: React.RefObject<HTMLElement | null>;\n /** Used when neither the override, the opener nor the trigger can take focus. */\n fallback?: () => HTMLElement | null;\n /**\n * Popovers: restore only when focus was inside the surface (or lost to `<body>`) at close, so a\n * click that moved focus elsewhere is respected. @default false\n */\n onlyIfFocusInside?: boolean;\n /**\n * Read when the surface closes, or unmounts while open: `true` when the hover intent closed it\n * (Popover and Menu `openOnHover`, a submenu closing by hover). A hover close never moves focus:\n * it restores only while focus is inside the surface, so focus on `<body>` (where a hover-opened\n * surface leaves it) or elsewhere stays where it is. A surface opened from inside one that closes\n * by hover (its opener, else its trigger, lies in that surface: a submenu, a hover card in a\n * hover card, at any depth) is taken along, and its close counts as a hover close too. Every\n * other close keeps the rules above.\n */\n isHoverClose?: () => boolean;\n /**\n * Surfaces that do not trap focus (a non-modal Dialog or Drawer, an inline Drawer): restore only\n * when focus is inside the surface at close, or was lost to `<body>` from an element inside it\n * (removed, or made unable to take focus). Focus elsewhere, or on `<body>` for another reason\n * (nothing had focus, a press on a spot that takes no focus, a close from code), stays where it\n * is. Wins over `onlyIfFocusInside`. Focus in a layer opened from the surface (a Popover or a\n * confirm Dialog, portaled outside it) counts as inside: a descendant layer of `layerId`, or a\n * layer anchored to an element inside the surface; so does focus lost to `<body>` from one.\n * @default false\n */\n requireFocusInside?: boolean;\n /**\n * The surface's own dismiss layer, with `requireFocusInside`: focus in one of its descendant\n * layers counts as inside the surface, also after the surface's layer has unregistered (in the\n * commit that closes it).\n */\n layerId?: string;\n}\n\ninterface Latest {\n options: UseRestoreFocusOptions;\n parentLayerId: string | null;\n}\n\n/** Where an element sat in the document, recorded so a spot near it can be found once it is gone. */\ninterface DomPosition {\n /** The element and its ancestors up to `<body>`, innermost first. */\n path: Element[];\n /** The next element sibling of each `path` entry when it was recorded. */\n next: Array<Element | null>;\n /** The previous element sibling of each `path` entry when it was recorded. */\n previous: Array<Element | null>;\n}\n\n/** What is captured about the opener when the surface opens. */\ninterface OpenerRecord {\n element: HTMLElement;\n /** Its place in the document, used when it is removed or can no longer take focus. */\n position: DomPosition;\n /** The anchors (triggers) of the open layers whose tree held it, innermost layer first. */\n anchors: Array<{ element: HTMLElement; position: DomPosition }>;\n}\n\n/** A point in document order: just before `ref`, or just after `ref` and its subtree. */\ninterface DomPoint {\n ref: Element;\n placement: 'before' | 'after';\n}\n\nconst FOLLOWING = 4; // Node.DOCUMENT_POSITION_FOLLOWING\nconst PORTAL_SELECTOR = '[data-wave-portal]';\n\nfunction isValidTarget(el: HTMLElement | null | undefined): el is HTMLElement {\n if (!el || !el.isConnected) return false;\n if (!isFocusable(el)) return false; // also rejects [inert] ancestors, hidden and disabled\n if (el.closest('[aria-hidden=\"true\"]')) return false;\n return !isInsideOtherOpenModal(el);\n}\n\nfunction recordPosition(el: Element): DomPosition {\n const position: DomPosition = { path: [], next: [], previous: [] };\n const body = el.ownerDocument.body;\n for (let node: Element | null = el; node; node = node.parentElement) {\n position.path.push(node);\n position.next.push(node.nextElementSibling);\n position.previous.push(node.previousElementSibling);\n if (node === body) break;\n }\n return position;\n}\n\n/**\n * The anchors of the open layers whose tree contains `opener` (a menu item's menu button),\n * innermost layer first. An opener in an allow-listed region (a toast) belongs to no layer.\n */\nfunction captureAnchors(\n opener: HTMLElement,\n container: HTMLElement | null | undefined,\n): OpenerRecord['anchors'] {\n if (opener.closest(ALLOW_OUTSIDE_SELECTOR)) return [];\n const layers = getOpenLayers()\n .filter((layer) => isInsideLayerTree(layer.id, opener))\n .sort((a, b) => compareLayers(b, a));\n const anchors: OpenerRecord['anchors'] = [];\n for (const layer of layers) {\n const anchor = layer.getAnchor();\n if (!anchor || anchor === opener || (container && container.contains(anchor))) continue;\n if (anchors.some((entry) => entry.element === anchor)) continue;\n anchors.push({ element: anchor, position: recordPosition(anchor) });\n }\n return anchors;\n}\n\n// ---------------------------------------------------------------------------\n// Focus tracker\n// ---------------------------------------------------------------------------\n\n/**\n * Remembers the last focused element, with its place in the document and the anchors of the\n * overlays that held it, recorded while it is still in the document. A surface captures its opener\n * in the mutation phase, and by then React has already deleted anything earlier in the tree that\n * the same commit removes. A popover rendered before a dialog, whose menu item closes the popover\n * and opens the dialog, has lost its menu item (and focus is on `<body>`) when the dialog captures.\n * The record still leads the restore to the popover's trigger. It lives in\n * `getGlobalRegistry('restoreFocusTracker')`, so two copies of the library share one set of\n * listeners.\n */\ninterface FocusTracker {\n /** Mounted `useRestoreFocus` instances: the listeners are installed while there is one. */\n users: number;\n /** The last focused element; `null` once focus was moved away from it on purpose. */\n last: OpenerRecord | null;\n /** Removes the listeners; `null` while none are installed. */\n uninstall: (() => void) | null;\n /**\n * The mounted surfaces, as the other instances see them (see {@link isClosingByHover}). Created\n * on first use: a tracker made by another copy of the library does not have it.\n */\n surfaces?: Set<SurfaceEntry>;\n}\n\n/** A mounted `useRestoreFocus`, as the other instances see it. */\ninterface SurfaceEntry {\n /** Whether the surface is closing by hover (its `isHoverClose`). */\n isHoverClose(): boolean;\n /** The surface element, while there is one. */\n getContainer(): HTMLElement | null;\n /** The element the surface was opened from: its captured opener, else its trigger. */\n getOpener(): HTMLElement | null;\n}\n\nfunction getSurfaceEntries(): Set<SurfaceEntry> {\n const tracker = getFocusTracker();\n tracker.surfaces ??= new Set();\n return tracker.surfaces;\n}\n\n/**\n * Whether the close of `entry` is a hover close: its own, or that of a surface holding the element\n * it was opened from (a submenu in a menu that closes by hover, a hover card inside a hover card,\n * at any depth), which takes it along.\n */\nfunction isClosingByHover(entry: SurfaceEntry, seen = new Set<SurfaceEntry>()): boolean {\n if (seen.has(entry)) return false;\n seen.add(entry);\n if (entry.isHoverClose()) return true;\n const opener = entry.getOpener();\n if (!opener) return false;\n for (const other of getSurfaceEntries()) {\n const container = other === entry ? null : other.getContainer();\n if (container && container.contains(opener) && isClosingByHover(other, seen)) return true;\n }\n return false;\n}\n\n/** Whether this close is a hover close: the surface's own, or that of a surface it was opened from. */\nfunction isHoverCloseOf(entry: SurfaceEntry | null, options: UseRestoreFocusOptions): boolean {\n return entry ? isClosingByHover(entry) : options.isHoverClose?.() === true;\n}\n\nfunction getFocusTracker(): FocusTracker {\n return getGlobalRegistry<FocusTracker>('restoreFocusTracker', () => ({\n users: 0,\n last: null,\n uninstall: null,\n }));\n}\n\nconst ELEMENT_NODE = 1;\n\nfunction isElementTarget(target: EventTarget | null): target is HTMLElement {\n return !!target && (target as Partial<Node>).nodeType === ELEMENT_NODE;\n}\n\nfunction recordFocus(tracker: FocusTracker, el: HTMLElement): void {\n const doc = el.ownerDocument;\n if (el === doc.body || el === doc.documentElement) {\n tracker.last = null;\n return;\n }\n tracker.last = { element: el, position: recordPosition(el), anchors: captureAnchors(el, null) };\n}\n\n/**\n * A layer opened or closed: re-record the focused element's place and overlays while it is still in\n * the document. An element focused as its popover opens (autoFocus) gets focus before the\n * popover's layer registers. Anchors found earlier are kept (a layer unregistering as its content\n * is deleted no longer reports it).\n */\nfunction refreshFocusRecord(tracker: FocusTracker): void {\n const last = tracker.last;\n if (!last || !last.element.isConnected) return;\n const anchors = captureAnchors(last.element, null);\n for (const anchor of last.anchors) {\n if (!anchors.some((entry) => entry.element === anchor.element)) anchors.push(anchor);\n }\n tracker.last = { element: last.element, position: recordPosition(last.element), anchors };\n}\n\n/** Installs the tracker's listeners while at least one `useRestoreFocus` is mounted. */\nfunction retainFocusTracker(): () => void {\n if (typeof document === 'undefined') return () => {};\n const tracker = getFocusTracker();\n tracker.users += 1;\n if (!tracker.uninstall) {\n const doc = document;\n const onFocusIn = (event: FocusEvent) => {\n if (isElementTarget(event.target)) recordFocus(tracker, event.target);\n };\n // Focus leaves for nowhere when the element is removed (some browsers fire focusout then) or\n // when the user presses a non-focusable spot. Decide once the current task has run: an element\n // still in the document that can take focus was left on purpose and is no opener to return to.\n const onFocusOut = (event: FocusEvent) => {\n const target = event.target;\n if (event.relatedTarget || !isElementTarget(target)) return;\n queueMicrotask(() => {\n if (tracker.last?.element !== target) return;\n if (!target.isConnected || !isFocusable(target)) return;\n if (target.ownerDocument.activeElement !== target) tracker.last = null;\n });\n };\n doc.addEventListener('focusin', onFocusIn, true);\n doc.addEventListener('focusout', onFocusOut, true);\n const unsubscribe = subscribeLayers(() => refreshFocusRecord(tracker));\n if (isElementTarget(doc.activeElement)) recordFocus(tracker, doc.activeElement);\n tracker.uninstall = () => {\n doc.removeEventListener('focusin', onFocusIn, true);\n doc.removeEventListener('focusout', onFocusOut, true);\n unsubscribe();\n tracker.last = null;\n };\n }\n let retained = true;\n return () => {\n if (!retained) return;\n retained = false;\n tracker.users = Math.max(0, tracker.users - 1);\n if (tracker.users > 0) return;\n tracker.uninstall?.();\n tracker.uninstall = null;\n };\n}\n\n/**\n * The tracker's record of the element focus was lost from: it no longer has focus and was removed,\n * or can no longer take focus (disabled, hidden, made inert). `null` when focus was moved away from\n * it on purpose, or it still has focus.\n */\nfunction getLostFocus(): OpenerRecord | null {\n const last = getFocusTracker().last;\n if (!last) return null;\n const { element } = last;\n if (element.ownerDocument.activeElement === element) return null;\n if (element.isConnected && isFocusable(element)) return null;\n return last;\n}\n\n/**\n * The element to restore to when the surface opens: the trigger, else the focused element, else —\n * focus already lost to `<body>` — the element the focus tracker saw lose it.\n */\nfunction captureOpener(options: UseRestoreFocusOptions): OpenerRecord | null {\n if (typeof document === 'undefined') return null;\n const { container, triggerRef } = options;\n const isCandidate = (el: Element | null | undefined): el is HTMLElement =>\n !!el &&\n el !== el.ownerDocument.body &&\n el !== el.ownerDocument.documentElement &&\n !(container && container.contains(el));\n const trigger = triggerRef?.current;\n const active = document.activeElement;\n let element: HTMLElement | null = null;\n if (isCandidate(trigger)) element = trigger;\n else if (isCandidate(active)) element = active;\n if (element) {\n return {\n element,\n position: recordPosition(element),\n anchors: captureAnchors(element, container),\n };\n }\n // The opener was removed earlier in this commit (a menu item in a popover that closed as this\n // surface opened), or can no longer take focus: use what the tracker recorded while it could.\n const lost = getLostFocus();\n if (!lost || !isCandidate(lost.element)) return null;\n return {\n element: lost.element,\n position: lost.position,\n anchors: lost.anchors.filter((anchor) => !(container && container.contains(anchor.element))),\n };\n}\n\n/** A layer's surface (or its first tabbable element), for the last-resort fallbacks. */\nfunction getLayerTargets(layer: LayerRecord | null): HTMLElement[] {\n if (!layer) return [];\n const targets: HTMLElement[] = [];\n for (const el of layer.getElements()) {\n if (!el) continue;\n targets.push(el);\n const first = getFirstTabbable(el);\n if (first) targets.push(first);\n }\n return targets;\n}\n\nfunction isAfterPoint(el: Element, point: DomPoint): boolean {\n const { ref, placement } = point;\n if (el === ref) return placement === 'before';\n const follows = (ref.compareDocumentPosition(el) & FOLLOWING) !== 0;\n return placement === 'before' ? follows : follows && !ref.contains(el);\n}\n\n/**\n * Where to look once the recorded element is gone (or cannot take focus): its nearest ancestor\n * still in the document, and the point where the element used to be. `null` when its whole overlay\n * (the portal wrapper) was removed: that spot means nothing on the page.\n */\nfunction resolvePoint(position: DomPosition): { root: Element; point: DomPoint } | null {\n const { path } = position;\n const index = path.findIndex((el) => el.isConnected);\n if (index === -1) return null;\n if (index === 0) {\n const root = path[0].parentElement;\n return root ? { root, point: { ref: path[0], placement: 'after' } } : null;\n }\n if (path.slice(0, index).some((el) => el.matches(PORTAL_SELECTOR))) return null;\n const root = path[index];\n const next = position.next[index - 1];\n if (next && next.parentElement === root) {\n return { root, point: { ref: next, placement: 'before' } };\n }\n const previous = position.previous[index - 1];\n if (previous && previous.parentElement === root) {\n return { root, point: { ref: previous, placement: 'after' } };\n }\n return { root, point: { ref: root, placement: 'before' } };\n}\n\n/**\n * Tabbable elements near a recorded position, nearest first. In the innermost surviving ancestor\n * that holds a valid one: the first after the old spot (where Tab would have gone from there),\n * else the last before it; then the same one level up. The search stops at the element's overlay\n * (its portal wrapper) and never reaches behind an open modal.\n */\nfunction getNearbyTargets(\n position: DomPosition,\n container: HTMLElement | null | undefined,\n): HTMLElement[] {\n const resolved = resolvePoint(position);\n if (!resolved) return [];\n const { root, point } = resolved;\n const body = root.ownerDocument.body;\n const levels: Element[] = [];\n for (let level: Element | null = root; level; level = level.parentElement) {\n if (isInsideOtherOpenModal(level)) break;\n levels.push(level);\n if (level === body || level.matches(PORTAL_SELECTOR)) break;\n }\n if (levels.length === 0) return [];\n const candidates = getTabbableElements(levels[levels.length - 1])\n .filter((el) => !(container && container.contains(el)) && isValidTarget(el))\n .sort((a, b) => ((a.compareDocumentPosition(b) & FOLLOWING) !== 0 ? -1 : 1));\n const targets: HTMLElement[] = [];\n for (const level of levels) {\n const inLevel = candidates.filter((el) => level.contains(el) && !targets.includes(el));\n const after = inLevel.find((el) => isAfterPoint(el, point));\n const before = inLevel.filter((el) => !isAfterPoint(el, point)).pop();\n if (after) targets.push(after);\n if (before) targets.push(before);\n }\n return targets;\n}\n\n/**\n * Whether focus was lost to `<body>` from an element inside `container`: the focus tracker's record\n * of the element that lost it ({@link getLostFocus}) lies in the container (its recorded path holds\n * it), also when the element has been removed since.\n */\nfunction isFocusLostFromInside(container: HTMLElement | null | undefined): boolean {\n if (!container) return false;\n const lost = getLostFocus();\n if (!lost) return false;\n if (lost.position.path.includes(container)) return true;\n // Lost from a layer opened from the surface: the overlays that held it are anchored inside.\n return lost.anchors.some((anchor) => anchor.position.path.includes(container));\n}\n\n/**\n * Whether `active` is in a layer opened from the surface (portaled outside it): a descendant layer\n * of the surface's layer (`layerId`), or an open layer whose tree holds it and whose anchor (its\n * trigger) is inside the surface. The toast region counts as inside every layer, never here.\n */\nfunction isInLayerOpenedFrom(\n active: Element,\n container: HTMLElement | null | undefined,\n layerId: string | undefined,\n): boolean {\n if (!container || active.closest(ALLOW_OUTSIDE_SELECTOR)) return false;\n return getOpenLayers().some((layer) => {\n if (layer.id === layerId || !isInsideLayerTree(layer.id, active)) return false;\n if (layerId !== undefined && isDescendantLayer(layer.id, layerId)) return true;\n const anchor = layer.getAnchor();\n return !!anchor && container.contains(anchor);\n });\n}\n\n/**\n * Whether the restore acts for this close: always while focus is inside the surface; otherwise\n * never for a hover close (`isClosingByHover`, asked only then), and with `onlyIfFocusInside` only\n * when focus was lost to `<body>`. With `requireFocusInside`, also while focus is in a layer opened\n * from the surface, from `<body>` only when focus was lost there from inside the surface (or from\n * such a layer), and never from anywhere else.\n */\nfunction shouldRestore(options: UseRestoreFocusOptions, isHoverClose: () => boolean): boolean {\n if (typeof document === 'undefined') return false;\n const active = document.activeElement;\n const onBody = !active || active === document.body || active === document.documentElement;\n if (!onBody && options.container?.contains(active)) return true;\n if (options.requireFocusInside) {\n if (!onBody) return isInLayerOpenedFrom(active, options.container, options.layerId);\n return isFocusLostFromInside(options.container) && !isHoverClose();\n }\n if (!onBody && options.onlyIfFocusInside) return false;\n return !isHoverClose();\n}\n\n/**\n * Where to go when the opener was removed or can no longer take focus, nearest first: the tabbable\n * element next to where it was, else the trigger of the overlay it was in (a menu item's menu\n * button), else the element next to that trigger.\n */\nfunction* getReplacementTargets(\n opener: OpenerRecord,\n container: HTMLElement | null | undefined,\n): Generator<HTMLElement> {\n yield* getNearbyTargets(opener.position, container);\n for (const anchor of opener.anchors) yield anchor.element;\n for (const anchor of opener.anchors) yield* getNearbyTargets(anchor.position, container);\n}\n\n/** Restore targets in order of preference, computed lazily (the later ones cost more). */\nfunction* restoreCandidates(\n latest: Latest,\n opener: OpenerRecord | null,\n): Generator<HTMLElement | null | undefined> {\n const { options, parentLayerId } = latest;\n yield options.finalFocusRef?.current;\n yield opener?.element;\n yield options.triggerRef?.current;\n yield options.fallback?.();\n const replacements = opener ? getReplacementTargets(opener, options.container) : null;\n const parentLayer = parentLayerId === null ? null : getLayer(parentLayerId);\n const outsideParent: HTMLElement[] = [];\n if (parentLayer) {\n // The surface was rendered inside a layer that stays open (a confirm Dialog in a Drawer): the\n // opener's replacement inside that layer (the next row after a delete) comes before the\n // layer's surface, so where the surface sits in the React tree does not change the result.\n for (const target of replacements ?? []) {\n if (isInsideLayerTree(parentLayer.id, target)) yield target;\n else outsideParent.push(target);\n }\n yield* getLayerTargets(parentLayer);\n yield* outsideParent;\n } else if (replacements) {\n yield* replacements;\n }\n // Still nothing: into the modal that stays open, never onto the inert page behind it.\n yield* getLayerTargets(getTopmostLayer((layer) => layer.kind === 'modal'));\n}\n\nfunction restore(latest: Latest, opener: OpenerRecord | null): void {\n const container = latest.options.container;\n for (const candidate of restoreCandidates(latest, opener)) {\n if (container && candidate && container.contains(candidate)) continue;\n if (isValidTarget(candidate) && focusElement(candidate, { preventScroll: true })) return;\n }\n}\n\n/**\n * Returns focus to where it came from when a surface closes (dialogs, drawers, popovers, menus,\n * the DatePicker calendar, TeachingPopover).\n *\n * - **Capture**: when `enabled` flips to `true`, the opener is captured in an insertion effect —\n * before React applies `autoFocus` inside the new surface — preferring `triggerRef.current` and\n * ignoring `<body>` and elements inside `container`. When that finds nothing (a surface that\n * mounts open, before the refs of its commit are attached), it is captured again in the layout\n * phase of the same commit. Its place in the document and the triggers of the overlays it sits\n * in are recorded with it. When focus is already on `<body>` because\n * the focused element was removed earlier in the same commit (a menu item in a popover that\n * closes as the dialog it opens appears, the popover coming first in the tree), a document\n * focus tracker supplies that element with its recorded place and overlay triggers. The tracker\n * runs while any instance of the hook is mounted, and it forgets an element the user moved focus\n * away from on purpose.\n * - **Restore** when `enabled` flips to `false` (layout phase) and when the component unmounts\n * while enabled. The unmount restore runs in a microtask and is cancelled if the same instance\n * mounts again, so React StrictMode's simulated unmount does not pull focus out of a surface\n * that just opened.\n * - **Targets**, first valid wins:\n * 1. `finalFocusRef` → the captured opener → `triggerRef` → `fallback()`.\n * 2. When the opener was removed or cannot take focus, its replacement, nearest first: the\n * tabbable element next to where it was (in the innermost surviving ancestor that holds one,\n * the next one — the next row's action after a delete — else the previous one; then the same\n * one level up), else the trigger of the overlay it was in (a menu item's menu button), else\n * the element next to that trigger. When the surface is rendered inside another open layer\n * (its parent layer, from `DismissLayerContext`: a confirm Dialog in a Drawer's content),\n * only the replacements inside that layer's tree are tried here.\n * 3. The parent layer's surface, then the replacements outside the parent layer.\n * 4. The surface of the modal layer that stays open.\n *\n * So a confirm Dialog rendered in a Drawer's content and one rendered next to the Drawer both\n * return focus to the adjacent row, not to the Drawer's panel. A target is valid when it is\n * connected, focusable, not inside `[inert]` or `[aria-hidden=\"true\"]`, and not cut off behind\n * another open modal layer — so focus never lands on the inert page behind a Drawer that stays\n * open, and is not dropped on `<body>` while a valid target exists. Focus uses `preventScroll`.\n * - `onlyIfFocusInside` (popovers): restores only when focus is inside the surface or was lost to\n * `<body>`.\n * - `requireFocusInside` (surfaces that do not trap focus: a non-modal Dialog or Drawer, an inline\n * Drawer): restores only when focus is inside the surface or in a layer opened from it (a\n * descendant layer of `layerId`, or a layer anchored inside the surface), or was lost to\n * `<body>` from an element in either; a close from code while focus is on `<body>` or on the\n * page moves nothing.\n * - `isHoverClose`: a close by the hover intent restores only when focus is inside the surface, so\n * a hover close never moves focus from `<body>` or from where the user put it. The same holds for\n * a surface opened from inside one that closes by hover (a submenu, a hover card in a hover\n * card): every mounted instance is known to the others through the focus tracker's record.\n */\nexport function useRestoreFocus(options: UseRestoreFocusOptions): void {\n const parentLayerId = React.useContext(DismissLayerContext);\n const latestRef = useRef<Latest>({ options, parentLayerId });\n const capturedRef = useRef<OpenerRecord | null>(null);\n const insertionEnabledRef = useRef(false);\n const layoutEnabledRef = useRef(false);\n const scheduledRef = useRef<object | null>(null);\n const entryRef = useRef<SurfaceEntry | null>(null);\n const { enabled } = options;\n\n // Track focus while mounted, so an opener removed in the commit that opens the surface is known.\n // And be known to the other instances: a surface closing by hover takes along the surfaces opened\n // from inside it, whose closes are then hover closes too.\n useLayoutEffect(() => {\n const release = retainFocusTracker();\n const entry: SurfaceEntry = {\n isHoverClose: () => latestRef.current.options.isHoverClose?.() === true,\n getContainer: () => latestRef.current.options.container ?? null,\n getOpener: () =>\n capturedRef.current?.element ?? latestRef.current.options.triggerRef?.current ?? null,\n };\n entryRef.current = entry;\n const surfaces = getSurfaceEntries();\n surfaces.add(entry);\n return () => {\n release();\n // Kept to the end of the commit: a surface opened from inside this one, closing along with\n // it, still asks about it.\n queueMicrotask(() => surfaces.delete(entry));\n };\n }, []);\n\n // Mutation phase: record the latest options and capture the opener when `enabled` turns on,\n // before autoFocus (layout phase) or a focus trap's initial focus moves focus into the surface.\n useInsertionEffect(() => {\n latestRef.current = { options, parentLayerId };\n const wasEnabled = insertionEnabledRef.current;\n insertionEnabledRef.current = options.enabled;\n if (options.enabled && !wasEnabled) capturedRef.current = captureOpener(options);\n });\n\n // `enabled` false → true with nothing captured: capture again in the layout phase. A surface\n // that mounts open (an inline Drawer with `defaultOpen`) captured before the refs of this commit\n // were attached, so its trigger was not known yet.\n // `enabled` true → false: restore in the layout phase (after every cleanup of this commit, so a\n // modal's isolation is already gone).\n useLayoutEffect(() => {\n const wasEnabled = layoutEnabledRef.current;\n layoutEnabledRef.current = enabled;\n if (enabled && !wasEnabled && !capturedRef.current) {\n capturedRef.current = captureOpener(latestRef.current.options);\n }\n if (!wasEnabled || enabled) return;\n const latest = latestRef.current;\n const captured = capturedRef.current;\n const entry = entryRef.current;\n // Decided before the opener is dropped: a hover close is also recognised through it.\n const acts = shouldRestore(latest.options, () => isHoverCloseOf(entry, latest.options));\n capturedRef.current = null;\n if (acts) restore(latest, captured);\n }, [enabled]);\n\n // Unmount while enabled: restore in a microtask, cancelled by a remount of this instance.\n useLayoutEffect(() => {\n const latest = latestRef;\n const captured = capturedRef;\n const scheduled = scheduledRef;\n const entryHolder = entryRef;\n scheduled.current = null;\n return () => {\n const snapshot = latest.current;\n if (!snapshot.options.enabled) return;\n // Decide now, while the surface is still in the document.\n const entry = entryHolder.current;\n if (!shouldRestore(snapshot.options, () => isHoverCloseOf(entry, snapshot.options))) return;\n const token = {};\n scheduled.current = token;\n const opener = captured.current;\n queueMicrotask(() => {\n if (scheduled.current !== token) return;\n scheduled.current = null;\n const active = typeof document === 'undefined' ? null : document.activeElement;\n const container = snapshot.options.container;\n // Focus placed elsewhere in the meantime (another surface opened) wins.\n if (\n active &&\n active !== document.body &&\n active !== document.documentElement &&\n !(container && container.contains(active))\n ) {\n return;\n }\n restore(snapshot, opener);\n });\n };\n }, []);\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use client";const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../lib/dev.cjs"),n=require("../lib/mergeRefs.cjs"),r=require("./useEventCallback.cjs"),i=require("../lib/direction.cjs"),a=require("../lib/focus.cjs"),o=require("./useTypeahead.cjs");let s=require("react");s=e.__toESM(s,1);var c=`[data-roving-value]`,l=new Set([`radiogroup`,`listbox`,`grid`,`treegrid`,`tablist`,`menu`,`menubar`,`tree`,`spinbutton`]),u=new Set([`slider`,`spinbutton`,`textbox`,`searchbox`]),d=new Set([`button`,`checkbox`,`color`,`file`,`hidden`,`image`,`radio`,`reset`,`submit`]),f=[`disabled`,`aria-disabled`,`data-disabled-focusable`,`data-roving-disabled`,`data-roving-value`,`data-roving-container`,`data-roving-transparent`,`tabindex`,`hidden`,`inert`,`role`,`type`,`contenteditable`];function p(e,t){if(e.hasAttribute(`data-roving-container`))return!0;if(!t||e.hasAttribute(`data-roving-transparent`))return!1;let n=e.getAttribute(`role`);return n!==null&&l.has(n)}function m(e,t,n){for(let r=e;r&&r!==t;r=r.parentElement)if(p(r,n))return!0;return!1}function h(e){return e.tabIndex>=0&&!e.hasAttribute(`disabled`)}function g(e){let{element:t}=e;if(!e.nested||h(t))return t;let n=t.querySelector(`[tabindex="0"]`);if(n)return n;let r=a.getFirstTabbable(t);return r instanceof HTMLElement?r:null}function _(e){let t=e.getAttribute(`data-roving-disabled`);return t!==null&&t!==`false`||v(e)?!0:!e.hasAttribute(`data-disabled-focusable`)&&e.getAttribute(`aria-disabled`)===`true`}function v(e){if(e.disabled===!0)return!0;try{return e.matches(`:disabled`)}catch{return!1}}function y(e){if(!e||e.nodeType!==1)return!1;let t=e,n=t.getAttribute(`role`);if(n!==null&&u.has(n))return!0;let r=t.localName;if(r===`textarea`||r===`select`)return!0;if(r===`input`)return!d.has(t.type);let i=t.closest(`[contenteditable]`);return i!==null&&i.getAttribute(`contenteditable`)!==`false`}function b(e){return!e.nested&&!y(e.element)}function x(e,t,n){let r=e.ownerDocument.defaultView;if(!r)return!1;for(let i=e;i&&i!==t;i=i.parentElement){let e=n.get(i);if(e===void 0){let t=i.parentElement;e=r.getComputedStyle(i).display===`none`||t!==null&&t.localName===`details`&&!t.hasAttribute(`open`)&&Array.from(t.children).find(e=>e.localName===`summary`)!==i,n.set(i,e)}if(e)return!0}let{visibility:i}=r.getComputedStyle(e);return i!==`hidden`&&i!==`collapse`?!1:r.getComputedStyle(t).visibility===`visible`}function S(e){let t=e.getRootNode().activeElement;return!!t&&e.contains(t)}function C(e){try{return document.createDocumentFragment().querySelector(e),!0}catch{return!1}}function w(e){return[e.value,e.disabled?0:b(e)?1:2]}function T(e,t,n,r){let i=e.some(([,e])=>e===1)?1:2,a=e.filter(([,e])=>e===i).map(([e])=>e);return n===`last-focused`&&r!==null&&a.includes(r)?r:t!==null&&t!==``&&a.includes(t)?t:a[0]??null}function E(e,t){if(!t)return e;let n=new Map(e.map(e=>[e.value,e])),r=[];for(let e of t){let t=n.get(e);t&&r.push(t)}return r}var D=class{container=null;lastFocused=null;options={itemSelector:c,manageTabIndex:!1,items:void 0,activeValue:null,tabStop:`active`,roleComposites:!0};stamped=new WeakMap;autoValues=new WeakMap;autoCounter=0;notify=null;observer=null;dirty=!0;snapshot=``;subscribe=e=>(this.notify=e,this.observe(),()=>{this.notify=null,this.disconnect()});getSnapshot=()=>this.container?(this.dirty&&(this.dirty=!1,this.snapshot=JSON.stringify(this.resolveOwn().map(w))),this.snapshot):``;getServerSnapshot=()=>``;setContainer(e){e!==this.container&&(this.disconnect(),this.container=e,this.dirty=!0,this.notify&&(this.observe(),this.notify()))}recordFocus(e){b(e)&&(this.lastFocused=e.value)}setOptions(e){let t=this.options;this.options=e,(t.itemSelector!==e.itemSelector||t.manageTabIndex!==e.manageTabIndex||t.roleComposites!==e.roleComposites)&&(this.dirty=!0,this.notify?.())}resolve(e=this.container){return E(this.resolveOwn(e),this.options.items)}resolveOwn(e=this.container){if(!e)return[];let{itemSelector:t,manageTabIndex:n,roleComposites:r}=this.options,i;try{i=Array.from(e.querySelectorAll(t))}catch{return[]}let o=[],s=new Set,c=new Map;for(let t of i){let i=null,l=!1;for(let n=t;n&&n!==e;n=n.parentElement)p(n,r)&&(i=n),(n.hidden||n.hasAttribute(`inert`))&&(l=!0);if(l)continue;let u=i??t;if(s.has(u))continue;let d=i!==null;if(!d&&n&&(t.getAttribute(`tabindex`)===`-1`&&!this.stamped.has(t)||a.isHiddenInput(t)))continue;s.add(u);let f={value:this.valueOf(u,d),element:u,nested:d,disabled:_(u)};d&&!f.disabled&&!g(f)&&(f.disabled=!0),n&&!f.disabled&&x(u,e,c)&&(f.disabled=!0),o.push(f)}return o}stamp(){if(!this.options.manageTabIndex||!this.container)return;this.release(this.container);let e=this.resolve(),t=this.options.tabStop===`none`?null:T(e.map(w),this.options.activeValue,this.options.tabStop,this.lastFocused);for(let n of e){if(n.nested)continue;let{element:e}=n,r=this.stamped.get(e);r||(r={tabIndex:e.getAttribute(`tabindex`),addedValue:!e.hasAttribute(`data-roving-value`)},this.stamped.set(e,r));let i=n.value===t?`0`:`-1`;e.getAttribute(`tabindex`)!==i&&e.setAttribute(`tabindex`,i),e.hasAttribute(`data-roving-value`)||e.setAttribute(`data-roving-value`,n.value)}}release(e){let t;try{t=Array.from(e.querySelectorAll(this.options.itemSelector))}catch{return}for(let n of t){let t=this.stamped.get(n);t&&m(n,e,this.options.roleComposites)&&(this.stamped.delete(n),t.tabIndex===null?n.removeAttribute(`tabindex`):n.setAttribute(`tabindex`,t.tabIndex),t.addedValue&&n.removeAttribute(`data-roving-value`))}}valueOf(e,t){let n=t?null:e.getAttribute(`data-roving-value`);if(n!==null)return n;let r=this.autoValues.get(e);return r===void 0&&(this.autoCounter+=1,r=`auto-${this.autoCounter}`,this.autoValues.set(e,r)),r}observe(){this.container&&!this.observer&&typeof MutationObserver<`u`&&(this.observer=new MutationObserver(()=>{this.stamp(),this.dirty=!0,this.notify?.()}),this.observer.observe(this.container,{subtree:!0,childList:!0,attributes:!0,attributeFilter:f}),this.dirty=!0)}disconnect(){this.observer?.disconnect(),this.observer=null}};function ee(e){if(e===``)return null;try{return JSON.parse(e)}catch{return null}}function O(e){return e.getAttribute(`data-roving-text`)??e.textContent??``}function k(e,t){return!t||typeof t.nodeType!=`number`?!1:t===e||e.contains(t)}function A(e,t,n){if(e.length===0||!t||typeof t.nodeType!=`number`)return null;let r=new Map(e.map(e=>[e.element,e]));for(let e=t;e&&e!==n;e=e.parentNode){let t=r.get(e);if(t)return t}return null}function j(e,t,n){return e?t===g(e):t===n}function M(e,a){let l=a===void 0?void 0:e,{activeValue:u=null,items:d,orientation:f=`horizontal`,loop:p=!0,dir:m,itemSelector:h=c,typeahead:_=!1,homeEndKeys:v=!0,tabStop:x=`active`,manageTabIndex:w=!1,onFocusMove:E}=a??e,[M]=s.useState(()=>new D),N=s.useSyncExternalStore(M.subscribe,M.getSnapshot,M.getServerSnapshot),[P,F]=s.useState(null),[I,L]=s.useState(null),R=l===void 0&&d===void 0;s.useLayoutEffect(()=>{M.setOptions({itemSelector:h,manageTabIndex:w,items:d,activeValue:u,tabStop:x,roleComposites:R}),l&&M.setContainer(l.current),M.stamp()}),s.useEffect(()=>{t.isDev&&!C(h)&&t.warnOnce(`useRovingTabIndex:itemSelector:${h}`,`useRovingTabIndex: itemSelector "${h}" is not a valid CSS selector in this browser; no item is navigable or tabbable.`)},[h]);let z=s.useMemo(()=>ee(N),[N]),B=s.useMemo(()=>{if(!d)return z;if(z){let e=new Map(z),t=d.flatMap(t=>{let n=e.get(t);return n===void 0?[]:[[t,n]]});if(t.length>0)return t}return d.map(e=>[e,1])},[d,z]),V=x===`none`?null:B?T(B,u,x,I):u||null,H=s.useCallback(e=>e===V?0:-1,[V]),U=s.useCallback(e=>{M.setContainer(e),l&&n.setRef(l,e)},[M,l]),W=s.useRef([]),G=s.useRef([]),{onTypeahead:K,isSearching:q}=o.useTypeahead({getItems:()=>W.current,onMatch:(e,t)=>{G.current=t}}),J=e=>{if(!e||e.disabled)return!1;let t=g(e);return!t||(t.focus(),!S(t))?!1:(M.recordFocus(e),!0)},Y=e=>{for(let t of e)if(J(t))return},X=r.useEventCallback((e,t)=>{let n=e.metaKey||(e.altKey||e.ctrlKey)&&!o.isAltGraphCharacter(e);if(e.defaultPrevented||n||y(e.target))return;let r=M.container??e.currentTarget;if(!k(r,e.target))return;let a=M.resolve(r),s=a.filter(e=>!e.disabled);if(s.length===0)return;let c=A(a,e.target,r),l=!1,u=[],d=t?null:i.getArrowIntent(e.key,{orientation:f,dir:m??i.getDirection(e.currentTarget)});if(d){if(l=!0,!c)u=d===`next`?s:[...s].reverse();else{let e=d===`next`?1:-1,t=a.length,n=a.indexOf(c);for(let r=1;r<t;r++){if(n+=e,n<0||n>=t){if(!p)break;n=(n+t)%t}a[n].disabled||u.push(a[n])}}}else!t&&v&&(e.key===`Home`||e.key===`End`)?(l=!0,u=e.key===`Home`?s:[...s].reverse()):_&&j(c,e.target,r)&&(W.current=a.map(e=>({value:e.value,text:O(e.element),disabled:e.disabled})),G.current=[],K(e,c?.value??null)&&(l=!0,u=G.current.flatMap(e=>s.filter(t=>t.value===e))));if(l){e.preventDefault();for(let t of u){if(t===c)return;if(J(t)){E?.(t.value,e);return}}}}),Z=r.useEventCallback(e=>X(e,!1)),Q=r.useEventCallback(e=>{_&&e.key===` `&&q()&&X(e,!0)}),$=r.useEventCallback(e=>{let t=M.container??e.currentTarget;if(!k(t,e.target))return;let n=A(M.resolve(t),e.target,t);n&&(M.recordFocus(n),F(n.value),b(n)&&L(n.value),M.stamp())}),te=r.useEventCallback(e=>{J(M.resolve().find(t=>t.value===e))}),ne=r.useEventCallback(()=>{Y(M.resolve().filter(e=>!e.disabled))}),re=r.useEventCallback(()=>{Y(M.resolve().filter(e=>!e.disabled).reverse())});return{containerProps:s.useMemo(()=>({ref:U,"data-roving-container":``,onKeyDown:Z,onKeyDownCapture:Q,onFocus:$}),[U,Z,Q,$]),handleKeyDown:Z,handleKeyDownCapture:Q,handleFocus:$,getTabIndex:H,focusedValue:P,focusValue:te,focusFirst:ne,focusLast:re}}exports.isHiddenByCss=x,exports.useRovingTabIndex=M;
1
+ "use client";const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../lib/dev.cjs"),n=require("../lib/mergeRefs.cjs"),r=require("./useEventCallback.cjs"),i=require("../lib/direction.cjs"),a=require("../lib/focus.cjs"),o=require("./useTypeahead.cjs");let s=require("react");s=e.__toESM(s,1);var c=`[data-roving-value]`,l=new Set([`radiogroup`,`listbox`,`grid`,`treegrid`,`tablist`,`menu`,`menubar`,`tree`,`spinbutton`]),u=new Set([`slider`,`spinbutton`,`textbox`,`searchbox`]),d=new Set([`button`,`checkbox`,`color`,`file`,`hidden`,`image`,`radio`,`reset`,`submit`]),f=[`disabled`,`aria-disabled`,`data-disabled-focusable`,`data-roving-disabled`,`data-roving-value`,`data-roving-container`,`data-roving-transparent`,`tabindex`,`hidden`,`inert`,`role`,`type`,`contenteditable`];function p(e,t){if(e.hasAttribute(`data-roving-container`))return!0;if(!t||e.hasAttribute(`data-roving-transparent`))return!1;let n=e.getAttribute(`role`);return n!==null&&l.has(n)}function m(e,t,n){for(let r=e;r&&r!==t;r=r.parentElement)if(p(r,n))return!0;return!1}var h=`[tabindex], [data-roving-value]`;function g(e,t){try{return e.matches(t)}catch{return!1}}function _(e){let t=new Map;for(let n of e)n.type===`attributes`&&n.attributeName===`tabindex`&&t.set(n.target,n);let n=new Set;for(let[e,r]of t)r.oldValue===e.getAttribute(`tabindex`)&&n.add(e);return n}function v(e){return e.tabIndex>=0&&!e.hasAttribute(`disabled`)}function y(e){let{element:t}=e;if(!e.nested||v(t))return t;let n=t.querySelector(`[tabindex="0"]`);if(n)return n;let r=a.getFirstTabbable(t);return r instanceof HTMLElement?r:null}function b(e){let t=e.getAttribute(`data-roving-disabled`);return t!==null&&t!==`false`||x(e)?!0:!e.hasAttribute(`data-disabled-focusable`)&&e.getAttribute(`aria-disabled`)===`true`}function x(e){if(e.disabled===!0)return!0;try{return e.matches(`:disabled`)}catch{return!1}}function S(e){if(!e||e.nodeType!==1)return!1;let t=e,n=t.getAttribute(`role`);if(n!==null&&u.has(n))return!0;let r=t.localName;if(r===`textarea`||r===`select`)return!0;if(r===`input`)return!d.has(t.type);let i=t.closest(`[contenteditable]`);return i!==null&&i.getAttribute(`contenteditable`)!==`false`}function C(e){return!e.nested&&!S(e.element)}function w(e,t,n){let r=e.ownerDocument.defaultView;if(!r)return!1;for(let i=e;i&&i!==t;i=i.parentElement){let e=n.get(i);if(e===void 0){let t=i.parentElement;e=r.getComputedStyle(i).display===`none`||t!==null&&t.localName===`details`&&!t.hasAttribute(`open`)&&Array.from(t.children).find(e=>e.localName===`summary`)!==i,n.set(i,e)}if(e)return!0}let{visibility:i}=r.getComputedStyle(e);return i!==`hidden`&&i!==`collapse`?!1:r.getComputedStyle(t).visibility===`visible`}function T(e){let t=e.getRootNode().activeElement;return!!t&&e.contains(t)}function E(e){try{return document.createDocumentFragment().querySelector(e),!0}catch{return!1}}function D(e){return[e.value,e.disabled?0:C(e)?1:2]}function O(e,t,n,r){let i=e.some(([,e])=>e===1)?1:2,a=e.filter(([,e])=>e===i).map(([e])=>e);return n===`last-focused`&&r!==null&&a.includes(r)?r:t!==null&&t!==``&&a.includes(t)?t:a[0]??null}function k(e,t){if(!t)return e;let n=new Map(e.map(e=>[e.value,e])),r=[];for(let e of t){let t=n.get(e);t&&r.push(t)}return r}var A=class{container=null;lastFocused=null;options={itemSelector:c,manageTabIndex:!1,items:void 0,activeValue:null,tabStop:`active`,roleComposites:!0};stamped=new WeakMap;autoValues=new WeakMap;autoCounter=0;notify=null;observer=null;dirty=!0;snapshot=``;subscribe=e=>(this.notify=e,this.observe(),()=>{this.notify=null,this.disconnect()});getSnapshot=()=>this.container?(this.dirty&&(this.dirty=!1,this.snapshot=JSON.stringify(this.resolveOwn().map(D))),this.snapshot):``;getServerSnapshot=()=>``;setContainer(e){if(e===this.container)return;let t=this.takeRecords();this.disconnect(),this.container&&this.options.manageTabIndex&&this.release(this.container,`all`,t),this.container=e,this.dirty=!0,this.notify&&(this.observe(),this.notify())}recordFocus(e){C(e)&&(this.lastFocused=e.value)}setOptions(e){let t=this.options;this.options=e,t.manageTabIndex&&!e.manageTabIndex&&this.container&&this.release(this.container,`all`,this.takeRecords()),(t.itemSelector!==e.itemSelector||t.manageTabIndex!==e.manageTabIndex||t.roleComposites!==e.roleComposites)&&(this.dirty=!0,this.notify?.())}resolve(e=this.container){return k(this.resolveOwn(e),this.options.items)}resolveOwn(e=this.container){if(!e)return[];let{itemSelector:t,manageTabIndex:n,roleComposites:r}=this.options,i;try{i=Array.from(e.querySelectorAll(t))}catch{return[]}let o=[],s=new Set,c=new Map;for(let t of i){let i=null,l=!1;for(let n=t;n&&n!==e;n=n.parentElement)p(n,r)&&(i=n),(n.hidden||n.hasAttribute(`inert`))&&(l=!0);if(l)continue;let u=i??t;if(s.has(u))continue;let d=i!==null;if(!d&&n&&(t.getAttribute(`tabindex`)===`-1`&&!this.stamped.has(t)||a.isHiddenInput(t)))continue;s.add(u);let f={value:this.valueOf(u,d),element:u,nested:d,disabled:b(u)};d&&!f.disabled&&!y(f)&&(f.disabled=!0),n&&!f.disabled&&w(u,e,c)&&(f.disabled=!0),o.push(f)}return o}stamp(){if(!this.options.manageTabIndex||!this.container)return;this.release(this.container,`stale`);let e=this.resolve(),t=this.options.tabStop===`none`?null:O(e.map(D),this.options.activeValue,this.options.tabStop,this.lastFocused);for(let n of e){if(n.nested)continue;let{element:e}=n,r=n.value===t?`0`:`-1`,i=e.getAttribute(`tabindex`),a=this.stamped.get(e);a?(i!==a.stamped&&(a.tabIndex=i),a.stamped=r):(a={tabIndex:i,stamped:r,addedValue:null},this.stamped.set(e,a)),i!==r&&e.setAttribute(`tabindex`,r),e.hasAttribute(`data-roving-value`)||(e.setAttribute(`data-roving-value`,n.value),a.addedValue=n.value)}}release(e,t,n=[]){let r=_(n),{itemSelector:i,roleComposites:a}=this.options;for(let n of Array.from(e.querySelectorAll(h))){let o=this.stamped.get(n);if(!o||t===`stale`&&g(n,i)&&!m(n,e,a))continue;this.stamped.delete(n);let s=n.getAttribute(`tabindex`);!(s!==o.stamped||r.has(n))&&s!==o.tabIndex&&(o.tabIndex===null?n.removeAttribute(`tabindex`):n.setAttribute(`tabindex`,o.tabIndex)),o.addedValue!==null&&n.getAttribute(`data-roving-value`)===o.addedValue&&n.removeAttribute(`data-roving-value`)}}adopt(e){for(let t of _(e)){let e=this.stamped.get(t);e&&(e.tabIndex=t.getAttribute(`tabindex`))}}takeRecords(){return this.observer?.takeRecords()??[]}valueOf(e,t){let n=t?null:e.getAttribute(`data-roving-value`);if(n!==null)return n;let r=this.autoValues.get(e);return r===void 0&&(this.autoCounter+=1,r=`auto-${this.autoCounter}`,this.autoValues.set(e,r)),r}observe(){this.container&&!this.observer&&typeof MutationObserver<`u`&&(this.observer=new MutationObserver(e=>{this.adopt(e),this.stamp(),this.dirty=!0,this.notify?.()}),this.observer.observe(this.container,{subtree:!0,childList:!0,attributes:!0,attributeOldValue:!0,attributeFilter:f}),this.dirty=!0)}disconnect(){this.observer?.disconnect(),this.observer=null}};function j(e){if(e===``)return null;try{return JSON.parse(e)}catch{return null}}function M(e){return e.getAttribute(`data-roving-text`)??e.textContent??``}function N(e,t){return!t||typeof t.nodeType!=`number`?!1:t===e||e.contains(t)}function P(e,t,n){if(e.length===0||!t||typeof t.nodeType!=`number`)return null;let r=new Map(e.map(e=>[e.element,e]));for(let e=t;e&&e!==n;e=e.parentNode){let t=r.get(e);if(t)return t}return null}function F(e,t,n){return e?t===y(e):t===n}function I(e,a){let l=a===void 0?void 0:e,{activeValue:u=null,items:d,orientation:f=`horizontal`,loop:p=!0,dir:m,itemSelector:h=c,typeahead:g=!1,homeEndKeys:_=!0,tabStop:v=`active`,manageTabIndex:b=!1,onFocusMove:x}=a??e,[w]=s.useState(()=>new A),D=s.useSyncExternalStore(w.subscribe,w.getSnapshot,w.getServerSnapshot),[k,I]=s.useState(null),[L,R]=s.useState(null),z=l===void 0&&d===void 0;s.useLayoutEffect(()=>{w.setOptions({itemSelector:h,manageTabIndex:b,items:d,activeValue:u,tabStop:v,roleComposites:z}),l&&w.setContainer(l.current),w.stamp()}),s.useEffect(()=>{t.isDev&&!E(h)&&t.warnOnce(`useRovingTabIndex:itemSelector:${h}`,`useRovingTabIndex: itemSelector "${h}" is not a valid CSS selector in this browser; no item is navigable or tabbable.`)},[h]);let B=s.useMemo(()=>j(D),[D]),V=s.useMemo(()=>{if(!d)return B;if(B){let e=new Map(B),t=d.flatMap(t=>{let n=e.get(t);return n===void 0?[]:[[t,n]]});if(t.length>0)return t}return d.map(e=>[e,1])},[d,B]),H=v===`none`?null:V?O(V,u,v,L):u||null,ee=s.useCallback(e=>e===H?0:-1,[H]),U=s.useCallback(e=>{w.setContainer(e),l&&n.setRef(l,e)},[w,l]),W=s.useRef([]),G=s.useRef([]),{onTypeahead:K,isSearching:q}=o.useTypeahead({getItems:()=>W.current,onMatch:(e,t)=>{G.current=t}}),J=e=>{if(!e||e.disabled)return!1;let t=y(e);return!t||(t.focus(),!T(t))?!1:(w.recordFocus(e),!0)},Y=e=>{for(let t of e)if(J(t))return},X=r.useEventCallback((e,t)=>{let n=e.metaKey||(e.altKey||e.ctrlKey)&&!o.isAltGraphCharacter(e);if(e.defaultPrevented||n||S(e.target))return;let r=w.container??e.currentTarget;if(!N(r,e.target))return;let a=w.resolve(r),s=a.filter(e=>!e.disabled);if(s.length===0)return;let c=P(a,e.target,r),l=!1,u=[],d=t?null:i.getArrowIntent(e.key,{orientation:f,dir:m??i.getDirection(e.currentTarget)});if(d){if(l=!0,!c)u=d===`next`?s:[...s].reverse();else{let e=d===`next`?1:-1,t=a.length,n=a.indexOf(c);for(let r=1;r<t;r++){if(n+=e,n<0||n>=t){if(!p)break;n=(n+t)%t}a[n].disabled||u.push(a[n])}}}else!t&&_&&(e.key===`Home`||e.key===`End`)?(l=!0,u=e.key===`Home`?s:[...s].reverse()):g&&F(c,e.target,r)&&(W.current=a.map(e=>({value:e.value,text:M(e.element),disabled:e.disabled})),G.current=[],K(e,c?.value??null)&&(l=!0,u=G.current.flatMap(e=>s.filter(t=>t.value===e))));if(l){e.preventDefault();for(let t of u){if(t===c)return;if(J(t)){x?.(t.value,e);return}}}}),Z=r.useEventCallback(e=>X(e,!1)),Q=r.useEventCallback(e=>{g&&e.key===` `&&q()&&X(e,!0)}),$=r.useEventCallback(e=>{let t=w.container??e.currentTarget;if(!N(t,e.target))return;let n=P(w.resolve(t),e.target,t);n&&(w.recordFocus(n),I(n.value),C(n)&&R(n.value),w.stamp())}),te=r.useEventCallback(e=>{J(w.resolve().find(t=>t.value===e))}),ne=r.useEventCallback(()=>{Y(w.resolve().filter(e=>!e.disabled))}),re=r.useEventCallback(()=>{Y(w.resolve().filter(e=>!e.disabled).reverse())});return{containerProps:s.useMemo(()=>({ref:U,"data-roving-container":``,onKeyDown:Z,onKeyDownCapture:Q,onFocus:$}),[U,Z,Q,$]),handleKeyDown:Z,handleKeyDownCapture:Q,handleFocus:$,getTabIndex:ee,focusedValue:k,focusValue:te,focusFirst:ne,focusLast:re}}exports.isHiddenByCss=w,exports.useRovingTabIndex=I;
2
2
  //# sourceMappingURL=useRovingTabIndex.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useRovingTabIndex.cjs","names":["getFirstTabbable","isHiddenInput","React","isDev","useTypeahead","useEventCallback","isAltGraphCharacter","getArrowIntent","getDirection","require_runtime","require_dev","require_mergeRefs","require_useEventCallback","require_direction","require_focus","require_useTypeahead","react","DEFAULT_ITEM_SELECTOR","COMPOSITE_ROLES","ARROW_KEY_OWNER_ROLES","NON_TEXT_INPUT_TYPES","OBSERVED_ATTRIBUTES","isNestedComposite","el","roleComposites","role","isInNestedComposite","node","container","isFocusableSelf","getFocusTarget","item","element","ownStop","tabbable","isDisabledElement","marker","isNativelyDisabled","ownsArrowKeys","target","tag","editable","canHoldTabStop","isHiddenByCss","view","hidden","cache","parent","child","visibility","hasFocus","active","isValidSelector","selector","toEntry","resolveTabStop","rank","entries","r","values","value","tabStop","lastFocused","activeValue","orderByItems","items","resolved","byValue","ordered","RovingStore","notify","previous","options","candidates","itemSelector","result","seen","cssHidden","candidate","composite","nested","manageTabIndex","stop","original","tabIndex","own","generated","parseSnapshot","snapshot","textOf","isInContainer","findOwningItem","byElement","startsOnItem","useRovingTabIndex","legacyRef","maybeOptions","refOrOptions","store","focusedValue","setFocusedValue","focusedStop","setFocusedStop","domItems","stopEntries","ranks","present","tabStopValue","getTabIndex","containerRef","typeaheadItemsRef","typeaheadMatchesRef","matches","focusItem","focusFirstOf","handleKeys","shortcut","enabled","current","handled","targets","intent","spaceOnly","dir","step","count","index","i","loop","homeEndKeys","typeahead","onTypeahead","next","onFocusMove","handleKeyDown","handleKeyDownCapture","isSearching","handleFocus","focusValue","focusFirst","focusLast"],"sources":["../../src/hooks/useRovingTabIndex.ts"],"sourcesContent":["import * as React from 'react';\nimport type { WaveDir } from '../components/provider/WaveProvider';\nimport { isDev, warnOnce } from '../lib/dev';\nimport { getArrowIntent, getDirection } from '../lib/direction';\nimport { getFirstTabbable, isHiddenInput } from '../lib/focus';\nimport { setRef } from '../lib/mergeRefs';\nimport { useEventCallback } from './useEventCallback';\nimport { isAltGraphCharacter, useTypeahead, type TypeaheadItem } from './useTypeahead';\n\n/**\n * Configuration options for the {@link useRovingTabIndex} hook. Disabled items are skipped; items\n * marked `data-disabled-focusable` (the `disabledFocusable` prop of WaveUI buttons, links and\n * choice controls) stay reachable and may hold the tab stop: APG allows focusable disabled items.\n */\nexport interface UseRovingTabIndexOptions {\n /**\n * The selected value. It may be absent, `''` or `null` (nothing selected) and does not have to be\n * one of the items.\n */\n activeValue?: string | null;\n /**\n * Explicit item order (0.4 call shape). Omitted ⇒ the DOM order of the elements matching\n * `itemSelector`. Disabled items are skipped either way, and so are listed values with no\n * element in the container (not rendered, or `hidden`/`inert`): they never hold the tab stop.\n */\n items?: readonly string[];\n /**\n * Arrow key axis: `'horizontal'` uses Left/Right (mirrored in RTL), `'vertical'` uses Up/Down,\n * `'both'` uses all four.\n * @default 'horizontal'\n */\n orientation?: 'horizontal' | 'vertical' | 'both';\n /**\n * Whether arrow keys wrap around at the ends.\n * @default true\n */\n loop?: boolean;\n /** Text direction. Defaults to the direction of the container at key time (`getDirection`). */\n dir?: WaveDir;\n /**\n * Selector for the item elements inside the container. A selector the browser rejects matches\n * nothing (no item is navigable or tabbable) and logs a development warning.\n * @default '[data-roving-value]'\n */\n itemSelector?: string;\n /**\n * Printable characters move focus to the next item whose text (`data-roving-text`, else its\n * text content) starts with them. A Space typed within 500 ms of another character continues\n * the search instead of activating the focused item: `containerProps.onKeyDownCapture` calls\n * `preventDefault()` on it before an item's own keydown handler runs, so that handler must skip\n * default-prevented events (`composeEventHandlers` does). Keys typed in content nested inside an\n * item (a row's action button) are not typeahead. A character typed with AltGr (which Windows\n * reports as Ctrl+Alt, e.g. Polish `ł`) is typeahead text; other Ctrl, Alt or Meta\n * combinations, Ctrl+Alt+Space included, are shortcuts and ignored. A match whose `focus()`\n * leaves focus where it was is passed over for the next item that matches.\n * @default false\n */\n typeahead?: boolean;\n /**\n * Whether Home/End move to the first/last enabled item.\n * @default true\n */\n homeEndKeys?: boolean;\n /** Which item holds the tab stop. 'active' (default): the enabled activeValue item, else the first\n * enabled item — APG Tabs/Radio/Listbox/Tree. 'last-focused': the last focused enabled item, else\n * 'active' — APG Toolbar (and the static Menu). Either way a nested composite or a control that\n * uses the arrow keys itself (text field, select, slider, spin button, editable combobox) holds\n * it only when no other item can: arrows pressed there never lead back to the other items, so\n * focusing one keeps the tab stop on the last focused other item. 'none': no item holds it\n * (every item gets tabIndex -1; a nested composite keeps its own tab stop, and one marked\n * `data-roving-transparent` joins the group's arrow order), for a composite reached only by\n * the arrow keys of an enclosing widget or by a script (a tree row's actions); the keys,\n * `focusValue`, `focusFirst` and `focusLast` keep working. */\n tabStop?: 'active' | 'last-focused' | 'none';\n /** true ⇒ the hook writes tabIndex 0/-1 onto item elements itself and assigns\n * data-roving-value=\"auto-<n>\" to items that lack one. For containers that do not render their\n * items (Toolbar children, Menu items, Tree rows). Default false (items call getTabIndex).\n * Matches that cannot take focus are no items: `input[type=hidden]` is skipped, and a control\n * hidden by CSS inside the container (`display: none` or `visibility: hidden` on it or on an\n * ancestor inside the container) is treated as disabled (skipped, never the tab stop, stamped\n * -1). A container hidden as a whole from outside keeps its items and tab stop. */\n manageTabIndex?: boolean;\n /** Called after an arrow, Home/End or typeahead key moved focus to `value`. */\n onFocusMove?: (value: string, event: React.KeyboardEvent) => void;\n}\n\n/** Props to spread onto the container element. */\nexport interface RovingContainerProps {\n ref: React.RefCallback<HTMLElement>;\n 'data-roving-container': '';\n onKeyDown: React.KeyboardEventHandler;\n /** With `typeahead`: default-prevents a Space that continues a search, before item handlers. */\n onKeyDownCapture: React.KeyboardEventHandler;\n onFocus: React.FocusEventHandler;\n}\n\n/** Result of {@link useRovingTabIndex}. */\nexport interface UseRovingTabIndexResult {\n /** Spread onto the container: ref, `data-roving-container` marker and the key/focus handlers. */\n containerProps: RovingContainerProps;\n /** The container's keydown handler (same function as `containerProps.onKeyDown`). */\n handleKeyDown: (e: React.KeyboardEvent) => void;\n /**\n * The container's capture-phase keydown handler (same function as\n * `containerProps.onKeyDownCapture`). With `typeahead`, it extends the search with a Space that\n * continues one and calls `preventDefault()`, before an item's own keydown handler runs: an item\n * that activates on Space must skip default-prevented events (`composeEventHandlers` does).\n */\n handleKeyDownCapture: (e: React.KeyboardEvent) => void;\n /** The container's focus handler (same function as `containerProps.onFocus`). */\n handleFocus: (e: React.FocusEvent) => void;\n /** `0` for the item that holds the tab stop, `-1` for every other item. */\n getTabIndex: (value: string) => 0 | -1;\n /** The value of the item that last received focus, or `null`. */\n focusedValue: string | null;\n /** Focuses the item with this value (ignored when it is disabled or unknown). */\n focusValue: (value: string) => void;\n /** Focuses the first enabled item. */\n focusFirst: () => void;\n /** Focuses the last enabled item. */\n focusLast: () => void;\n}\n\ninterface ResolvedItem {\n value: string;\n /** The item element (for a nested composite: its root). */\n element: HTMLElement;\n /** A nested composite (own roving container or composite role) counts as one item. */\n nested: boolean;\n /**\n * Skipped by the keys and never the tab stop: disabled, a nested composite with nothing to\n * focus, or (`manageTabIndex`) hidden by CSS.\n */\n disabled: boolean;\n}\n\ninterface StoreOptions {\n itemSelector: string;\n manageTabIndex: boolean;\n items: readonly string[] | undefined;\n activeValue: string | null;\n tabStop: 'active' | 'last-focused' | 'none';\n /**\n * Whether an element with a composite role but no roving container of its own is a nested\n * composite (one item). Off in the 0.4 call shape and with explicit `items`, where such an element\n * is usually the widget's own root inside a wrapper that carries the container ref.\n */\n roleComposites: boolean;\n}\n\nconst DEFAULT_ITEM_SELECTOR = '[data-roving-value]';\n\n/** Roles of widgets that manage their own children: an element with one of them is one item. */\nconst COMPOSITE_ROLES = new Set([\n 'radiogroup',\n 'listbox',\n 'grid',\n 'treegrid',\n 'tablist',\n 'menu',\n 'menubar',\n 'tree',\n 'spinbutton',\n]);\n\n/**\n * Roles whose widgets use the arrow keys themselves. A combobox does only when it is editable (an\n * `<input>` or contenteditable, caught by the native checks): a select-only combobox such as\n * Dropdown's `<button role=\"combobox\">` uses Up/Down, Home and End, never Left/Right.\n */\nconst ARROW_KEY_OWNER_ROLES = new Set(['slider', 'spinbutton', 'textbox', 'searchbox']);\n\n/** `<input>` types that do not use the arrow keys for text editing. */\nconst NON_TEXT_INPUT_TYPES = new Set([\n 'button',\n 'checkbox',\n 'color',\n 'file',\n 'hidden',\n 'image',\n 'radio',\n 'reset',\n 'submit',\n]);\n\nconst OBSERVED_ATTRIBUTES = [\n 'disabled',\n 'aria-disabled',\n 'data-disabled-focusable',\n 'data-roving-disabled',\n 'data-roving-value',\n 'data-roving-container',\n 'data-roving-transparent',\n 'tabindex',\n 'hidden',\n 'inert',\n 'role',\n // Whether an item uses the arrow keys itself (text input types, contenteditable).\n 'type',\n 'contenteditable',\n];\n\n/**\n * Whether `el` is a nested composite: it has its own roving container, or (with `roleComposites`)\n * a composite role. An element marked `data-roving-transparent` is never one by its role (a\n * `role=\"radiogroup\"` whose radios are items of the enclosing container, `Toolbar.RadioGroup`);\n * its own `data-roving-container` still makes it one.\n */\nfunction isNestedComposite(el: Element, roleComposites: boolean): boolean {\n if (el.hasAttribute('data-roving-container')) return true;\n if (!roleComposites || el.hasAttribute('data-roving-transparent')) return false;\n const role = el.getAttribute('role');\n return role !== null && COMPOSITE_ROLES.has(role);\n}\n\n/** Whether `el` is a nested composite or inside one, below `container`. */\nfunction isInNestedComposite(el: Element, container: Element, roleComposites: boolean): boolean {\n for (let node: Element | null = el; node && node !== container; node = node.parentElement) {\n if (isNestedComposite(node, roleComposites)) return true;\n }\n return false;\n}\n\n/** What an element looked like before the hook stamped it, to hand it back unchanged. */\ninterface StampedOriginal {\n /**\n * The author's own `tabindex` attribute, or `null`, read at the first stamp. A `tabIndex` the\n * author renders differently while the element is stamped is not seen (the stamps overwrite it),\n * so the hand-back restores the first value.\n */\n tabIndex: string | null;\n /** Whether the hook added the element's `data-roving-value`. */\n addedValue: boolean;\n}\n\nfunction isFocusableSelf(el: HTMLElement): boolean {\n return el.tabIndex >= 0 && !el.hasAttribute('disabled');\n}\n\n/** The element to focus for an item: the item itself, or a nested composite's own tab stop. */\nfunction getFocusTarget(item: ResolvedItem): HTMLElement | null {\n const { element } = item;\n if (!item.nested || isFocusableSelf(element)) return element;\n const ownStop = element.querySelector<HTMLElement>('[tabindex=\"0\"]');\n if (ownStop) return ownStop;\n const tabbable = getFirstTabbable(element);\n return tabbable instanceof HTMLElement ? tabbable : null;\n}\n\n/**\n * Whether the keys skip an item, in this order: the composite's own `data-roving-disabled` marker\n * (not `\"false\"`) and native `disabled` skip it; `data-disabled-focusable` keeps it (a\n * `disabledFocusable` control: APG allows focusable disabled items); `aria-disabled=\"true\"` skips\n * it.\n */\nfunction isDisabledElement(el: HTMLElement): boolean {\n const marker = el.getAttribute('data-roving-disabled');\n if (marker !== null && marker !== 'false') return true;\n if (isNativelyDisabled(el)) return true;\n if (el.hasAttribute('data-disabled-focusable')) return false;\n return el.getAttribute('aria-disabled') === 'true';\n}\n\nfunction isNativelyDisabled(el: HTMLElement): boolean {\n if ((el as { disabled?: unknown }).disabled === true) return true;\n try {\n return el.matches(':disabled');\n } catch {\n return false;\n }\n}\n\n/** Text-entry fields and widgets that use the arrow keys themselves (APG Toolbar). */\nfunction ownsArrowKeys(target: EventTarget | null): boolean {\n if (!target || (target as Node).nodeType !== 1) return false;\n const el = target as HTMLElement;\n const role = el.getAttribute('role');\n if (role !== null && ARROW_KEY_OWNER_ROLES.has(role)) return true;\n const tag = el.localName;\n if (tag === 'textarea' || tag === 'select') return true;\n if (tag === 'input') return !NON_TEXT_INPUT_TYPES.has((el as HTMLInputElement).type);\n const editable = el.closest('[contenteditable]');\n return editable !== null && editable.getAttribute('contenteditable') !== 'false';\n}\n\n/**\n * Whether an item can hold the container's tab stop: not a nested composite (it keeps a tab stop\n * of its own) and not a control that uses the arrow keys itself. From either, the arrow keys never\n * lead to the other items, so a tab stop there would leave them reachable by keyboard only through\n * it (or not at all).\n */\nfunction canHoldTabStop(item: ResolvedItem): boolean {\n return !item.nested && !ownsArrowKeys(item.element);\n}\n\n/**\n * Whether CSS inside `container` keeps `el` from taking focus (browsers ignore `focus()` on it):\n * `display: none` on it or on an ancestor inside `container`, `visibility: hidden`/`collapse` set\n * inside `container`, or a place inside a closed `<details>` other than its summary. Hiding from\n * outside the container (a surface that stays invisible until it is positioned, a consumer panel)\n * hides the whole item set and does not count: showing it again changes nothing the hook observes,\n * so nothing would re-stamp. `cache` holds, for one resolution, whether an element hides its whole\n * subtree (the `display` and `<details>` checks).\n *\n * Internal: shared with Tree's treegrid navigation; not exported from the package.\n */\nexport function isHiddenByCss(\n el: Element,\n container: Element,\n cache: Map<Element, boolean>,\n): boolean {\n const view = el.ownerDocument.defaultView;\n if (!view) return false;\n for (let node: Element | null = el; node && node !== container; node = node.parentElement) {\n let hidden = cache.get(node);\n if (hidden === undefined) {\n const parent = node.parentElement;\n hidden =\n view.getComputedStyle(node).display === 'none' ||\n (parent !== null &&\n parent.localName === 'details' &&\n !parent.hasAttribute('open') &&\n Array.from(parent.children).find((child) => child.localName === 'summary') !== node);\n cache.set(node, hidden);\n }\n if (hidden) return true;\n }\n const { visibility } = view.getComputedStyle(el);\n if (visibility !== 'hidden' && visibility !== 'collapse') return false;\n // Visibility is inherited, so `el` also reads hidden when an ancestor of the container hides the\n // whole container. It counts only when the container itself is visible, i.e. when the hiding is\n // set inside it (on `el` or on a wrapper between them).\n return view.getComputedStyle(container).visibility === 'visible';\n}\n\n/** Whether focus is on `el` or inside it (in its document or shadow root). */\nfunction hasFocus(el: HTMLElement): boolean {\n const active = (el.getRootNode() as Node & { activeElement?: Element | null }).activeElement;\n return !!active && el.contains(active);\n}\n\nfunction isValidSelector(selector: string): boolean {\n try {\n document.createDocumentFragment().querySelector(selector);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * How an item takes part in the tab stop: `0` never (disabled), `1` can hold it, `2` holds it only\n * when no item can (a nested composite, or a control that uses the arrow keys itself).\n */\ntype TabStopRank = 0 | 1 | 2;\n\n/** One item of the published DOM snapshot, and the input of {@link resolveTabStop}. */\ntype SnapshotEntry = [value: string, rank: TabStopRank];\n\nfunction toEntry(item: ResolvedItem): SnapshotEntry {\n return [item.value, item.disabled ? 0 : canHoldTabStop(item) ? 1 : 2];\n}\n\n/**\n * The value that holds the tab stop, among the items of the best rank present (`1`, else `2`):\n * with 'last-focused' the last focused one, else the `activeValue` item, else the first one.\n */\nfunction resolveTabStop(\n entries: readonly SnapshotEntry[],\n activeValue: string | null,\n tabStop: 'active' | 'last-focused',\n lastFocused: string | null,\n): string | null {\n const rank = entries.some(([, r]) => r === 1) ? 1 : 2;\n const values = entries.filter(([, r]) => r === rank).map(([value]) => value);\n if (tabStop === 'last-focused' && lastFocused !== null && values.includes(lastFocused)) {\n return lastFocused;\n }\n if (activeValue !== null && activeValue !== '' && values.includes(activeValue)) {\n return activeValue;\n }\n return values[0] ?? null;\n}\n\nfunction orderByItems(\n resolved: ResolvedItem[],\n items: readonly string[] | undefined,\n): ResolvedItem[] {\n if (!items) return resolved;\n const byValue = new Map(resolved.map((item) => [item.value, item]));\n const ordered: ResolvedItem[] = [];\n for (const value of items) {\n const item = byValue.get(value);\n if (item) ordered.push(item);\n }\n return ordered;\n}\n\n/**\n * Per-hook store: resolves items from the DOM, publishes the enabled set to React through\n * `useSyncExternalStore` (a MutationObserver on the container notifies it), and stamps tabIndex in\n * `manageTabIndex` mode.\n */\nclass RovingStore {\n container: HTMLElement | null = null;\n lastFocused: string | null = null;\n options: StoreOptions = {\n itemSelector: DEFAULT_ITEM_SELECTOR,\n manageTabIndex: false,\n items: undefined,\n activeValue: null,\n tabStop: 'active',\n roleComposites: true,\n };\n\n private readonly stamped = new WeakMap<Element, StampedOriginal>();\n private readonly autoValues = new WeakMap<Element, string>();\n private autoCounter = 0;\n private notify: (() => void) | null = null;\n private observer: MutationObserver | null = null;\n private dirty = true;\n private snapshot = '';\n\n subscribe = (notify: () => void): (() => void) => {\n this.notify = notify;\n this.observe();\n return () => {\n this.notify = null;\n this.disconnect();\n };\n };\n\n getSnapshot = (): string => {\n if (!this.container) return '';\n if (this.dirty) {\n this.dirty = false;\n // DOM facts only (order and tab stop rank); `items` is applied during render.\n this.snapshot = JSON.stringify(this.resolveOwn().map(toEntry));\n }\n return this.snapshot;\n };\n\n getServerSnapshot = (): string => '';\n\n setContainer(container: HTMLElement | null): void {\n if (container === this.container) return;\n this.disconnect();\n this.container = container;\n this.dirty = true;\n if (this.notify) {\n this.observe();\n this.notify();\n }\n }\n\n /**\n * Records a focused item for the 'last-focused' tab stop. An item that cannot hold the tab stop\n * (a nested composite, a control that uses the arrow keys itself) leaves the last one in place.\n */\n recordFocus(item: ResolvedItem): void {\n if (canHoldTabStop(item)) this.lastFocused = item.value;\n }\n\n setOptions(options: StoreOptions): void {\n const previous = this.options;\n this.options = options;\n if (\n previous.itemSelector !== options.itemSelector ||\n previous.manageTabIndex !== options.manageTabIndex ||\n previous.roleComposites !== options.roleComposites\n ) {\n this.dirty = true;\n this.notify?.();\n }\n }\n\n /** Own items in navigation order: the explicit `items` order when given, else DOM order. */\n resolve(container: HTMLElement | null = this.container): ResolvedItem[] {\n return orderByItems(this.resolveOwn(container), this.options.items);\n }\n\n /** Own items of the container in DOM order (items of nested composites excluded). */\n resolveOwn(container: HTMLElement | null = this.container): ResolvedItem[] {\n if (!container) return [];\n const { itemSelector, manageTabIndex, roleComposites } = this.options;\n let candidates: HTMLElement[];\n try {\n candidates = Array.from(container.querySelectorAll<HTMLElement>(itemSelector));\n } catch {\n // An invalid selector (warned from an effect in development): this runs during render\n // (getSnapshot), where throwing would take the whole tree down.\n return [];\n }\n\n const result: ResolvedItem[] = [];\n const seen = new Set<Element>();\n const cssHidden = new Map<Element, boolean>();\n for (const candidate of candidates) {\n let composite: HTMLElement | null = null;\n let hidden = false;\n for (\n let node: HTMLElement | null = candidate;\n node && node !== container;\n node = node.parentElement\n ) {\n if (isNestedComposite(node, roleComposites)) composite = node;\n if (node.hidden || node.hasAttribute('inert')) hidden = true;\n }\n if (hidden) continue;\n\n const element = composite ?? candidate;\n if (seen.has(element)) continue;\n const nested = composite !== null;\n if (!nested && manageTabIndex) {\n // An author tabindex=\"-1\" (a SpinButton stepper, SplitButton internals) opts out.\n if (candidate.getAttribute('tabindex') === '-1' && !this.stamped.has(candidate)) continue;\n // A hidden form input (HiddenInput's `type=\"hidden\"`) is never focusable.\n if (isHiddenInput(candidate)) continue;\n }\n seen.add(element);\n\n const item: ResolvedItem = {\n value: this.valueOf(element, nested),\n element,\n nested,\n disabled: isDisabledElement(element),\n };\n if (nested && !item.disabled && !getFocusTarget(item)) item.disabled = true;\n // Consumer children hidden by CSS (a responsive `hidden md:inline-flex` control). Treated as\n // disabled rather than dropped, so it is stamped -1 and is no extra Tab stop once the CSS\n // shows it again (a CSS change triggers no mutation, hence no re-stamp).\n if (manageTabIndex && !item.disabled && isHiddenByCss(element, container, cssHidden)) {\n item.disabled = true;\n }\n result.push(item);\n }\n return result;\n }\n\n /**\n * manageTabIndex: writes tabIndex 0 on the tab stop and -1 on every other own item. An element\n * it stamped that now belongs to a nested composite (a `data-roving-transparent` marker removed)\n * gets its own `tabindex` back, and loses the `data-roving-value` the hook added.\n */\n stamp(): void {\n if (!this.options.manageTabIndex || !this.container) return;\n this.release(this.container);\n const resolved = this.resolve();\n const stop =\n this.options.tabStop === 'none'\n ? null\n : resolveTabStop(\n resolved.map(toEntry),\n this.options.activeValue,\n this.options.tabStop,\n this.lastFocused,\n );\n for (const item of resolved) {\n if (item.nested) continue;\n const { element } = item;\n let original = this.stamped.get(element);\n if (!original) {\n original = {\n tabIndex: element.getAttribute('tabindex'),\n addedValue: !element.hasAttribute('data-roving-value'),\n };\n this.stamped.set(element, original);\n }\n const tabIndex = item.value === stop ? '0' : '-1';\n if (element.getAttribute('tabindex') !== tabIndex) element.setAttribute('tabindex', tabIndex);\n if (!element.hasAttribute('data-roving-value')) {\n element.setAttribute('data-roving-value', item.value);\n }\n }\n }\n\n /** Hands the elements it stamped that now belong to a nested composite back to their author. */\n private release(container: HTMLElement): void {\n let candidates: HTMLElement[];\n try {\n candidates = Array.from(container.querySelectorAll<HTMLElement>(this.options.itemSelector));\n } catch {\n return;\n }\n for (const candidate of candidates) {\n const original = this.stamped.get(candidate);\n if (!original) continue;\n if (!isInNestedComposite(candidate, container, this.options.roleComposites)) continue;\n this.stamped.delete(candidate);\n if (original.tabIndex === null) candidate.removeAttribute('tabindex');\n else candidate.setAttribute('tabindex', original.tabIndex);\n if (original.addedValue) candidate.removeAttribute('data-roving-value');\n }\n }\n\n private valueOf(element: HTMLElement, nested: boolean): string {\n const own = nested ? null : element.getAttribute('data-roving-value');\n if (own !== null) return own;\n let generated = this.autoValues.get(element);\n if (generated === undefined) {\n this.autoCounter += 1;\n generated = `auto-${this.autoCounter}`;\n this.autoValues.set(element, generated);\n }\n return generated;\n }\n\n private observe(): void {\n if (!this.container || this.observer || typeof MutationObserver === 'undefined') return;\n this.observer = new MutationObserver(() => {\n this.stamp();\n this.dirty = true;\n this.notify?.();\n });\n this.observer.observe(this.container, {\n subtree: true,\n childList: true,\n attributes: true,\n attributeFilter: OBSERVED_ATTRIBUTES,\n });\n this.dirty = true;\n }\n\n private disconnect(): void {\n this.observer?.disconnect();\n this.observer = null;\n }\n}\n\nfunction parseSnapshot(snapshot: string): SnapshotEntry[] | null {\n if (snapshot === '') return null;\n try {\n return JSON.parse(snapshot) as SnapshotEntry[];\n } catch {\n return null;\n }\n}\n\nfunction textOf(element: HTMLElement): string {\n return element.getAttribute('data-roving-text') ?? element.textContent ?? '';\n}\n\n/**\n * Whether an event target is the container or inside its DOM. React bubbles events from a portal\n * (a Menu.Popover or Popover.Content a Toolbar item renders) through the container although their\n * target is elsewhere in the document; those events are not the container's to handle.\n */\nfunction isInContainer(container: Node, target: EventTarget | null): boolean {\n if (!target || typeof (target as Node).nodeType !== 'number') return false;\n return target === container || container.contains(target as Node);\n}\n\n/**\n * The item an event started in: the innermost item element that is the target or one of its\n * ancestors (up to the container). Items can nest (an APG treeitem contains the group of its child\n * treeitems), so the first containing item in DOM order would be the outermost ancestor.\n */\nfunction findOwningItem(\n items: readonly ResolvedItem[],\n target: EventTarget | null,\n container: Node,\n): ResolvedItem | null {\n if (items.length === 0 || !target || typeof (target as Node).nodeType !== 'number') return null;\n const byElement = new Map<Node, ResolvedItem>(items.map((item) => [item.element, item]));\n for (let node: Node | null = target as Node; node && node !== container; node = node.parentNode) {\n const item = byElement.get(node);\n if (item) return item;\n }\n return null;\n}\n\n/**\n * Whether a key started on the item itself (on the container when it started on no item), not on\n * content nested inside the item, such as a row's action button.\n */\nfunction startsOnItem(item: ResolvedItem | null, target: EventTarget | null, container: Node) {\n return item ? target === getFocusTarget(item) : target === container;\n}\n\n/**\n * Implements the WAI-ARIA roving tabindex pattern for composite widgets: one item holds the tab\n * stop (`tabIndex=0`, all others `-1`), arrow keys move focus between items, Home/End jump to the\n * ends, and (optionally) typeahead jumps by text. Used by RadioGroup, Rating, SwatchPicker, List,\n * TabList, Tree, Menu and Toolbar.\n *\n * - **Items** come from the DOM at event time: elements matching `itemSelector` (default\n * `[data-roving-value]`) whose nearest roving container is this one. A nested composite (an\n * element with its own `data-roving-container`, or a radiogroup/listbox/grid/tablist/menu/tree/\n * spinbutton role) counts as one item whose focus target is its own tab stop; the hook never\n * writes tabindex inside it, so it keeps its own Tab stop. An element marked\n * `data-roving-transparent` is never a nested composite by its role: its items are items of this\n * container (the radios of `Toolbar.RadioGroup`); it must not carry a `data-roving-container`.\n * Pass `items` to fix the order instead.\n * In the 0.4 call shape and with explicit `items`, only elements with their own roving container\n * are nested composites: a role-only `radiogroup`/`tablist`/… element between the container and\n * the items (the widget's root inside a wrapper that holds the ref) does not swallow them.\n * - **Disabled items** (`data-roving-disabled`, `disabled`, `aria-disabled=\"true\"`) are skipped and\n * never hold the tab stop. Items marked `data-disabled-focusable` (the `disabledFocusable` prop\n * of WaveUI buttons, links and choice controls) stay reachable and may hold the tab stop: APG\n * allows focusable disabled items. The attribute only overrides `aria-disabled`: an item that is\n * natively disabled or marked `data-roving-disabled` is still skipped. The enabled set is\n * tracked with a MutationObserver, so an item that disables itself moves the tab stop without\n * the owner re-rendering. With `manageTabIndex`, a control hidden by CSS counts as disabled and\n * `input[type=hidden]` is no item.\n * - **Tab stop** (`tabStop`): `'active'` — the enabled `activeValue` item, else the first enabled\n * item; `'last-focused'` — the last focused enabled item, else the `'active'` rule. A nested\n * composite or a control that uses the arrow keys itself holds it only when no other item can;\n * `'none'` — no item: every item is `tabIndex=-1` (a nested composite keeps its own tab stop;\n * mark it `data-roving-transparent` to join the group's arrow order), and focus reaches the\n * items only through the keys or a script.\n * - **Keys** are ignored when another handler already called `preventDefault()`; with Meta; with\n * Alt or Ctrl, except a single character typed with AltGr (Ctrl+Alt on Windows), which is\n * typeahead text (arrows, Home, End and Space with Ctrl+Alt stay ignored); and when they start\n * in a text field, select, contenteditable, slider, spinbutton or editable combobox (Left/Right\n * keep moving the caret; a select-only combobox such as Dropdown's button does not keep them).\n * Left/Right are mirrored in RTL (`dir`, else the direction of the container at key time).\n * Arrows and typeahead move from the item the key started in — the innermost one when items\n * nest, as treeitems do inside their parent's group; when it started on no item (focus on the\n * container itself) next goes to the first enabled item and prev to the last.\n * An item whose `focus()` leaves focus where it was (the browser ignores it on an element CSS\n * hides) is passed over for the next one, and is never recorded or reported to `onFocusMove`.\n * Handled keys call `preventDefault()`. Keys and focus from outside the container's DOM — a\n * popup an item renders through a portal, whose events React bubbles through the container —\n * are ignored, so focus never leaves an open menu or popover for the container's items.\n *\n * @example\n * const { containerProps, getTabIndex } = useRovingTabIndex({ activeValue: value, orientation: 'both' });\n * <div role=\"radiogroup\" {...containerProps}>\n * <button role=\"radio\" data-roving-value=\"a\" tabIndex={getTabIndex('a')} />\n * </div>\n *\n * The 0.4 call shape `useRovingTabIndex(containerRef, options)` still works: pass the container's\n * ref object and attach `handleKeyDown` (and ideally `handleFocus`, and with `typeahead`\n * `handleKeyDownCapture` as `onKeyDownCapture`) yourself.\n *\n * @param options - See {@link UseRovingTabIndexOptions}.\n * @returns See {@link UseRovingTabIndexResult}.\n */\nexport function useRovingTabIndex(options: UseRovingTabIndexOptions): UseRovingTabIndexResult;\nexport function useRovingTabIndex(\n containerRef: React.RefObject<HTMLElement | null>,\n options: UseRovingTabIndexOptions,\n): UseRovingTabIndexResult;\nexport function useRovingTabIndex(\n refOrOptions: React.RefObject<HTMLElement | null> | UseRovingTabIndexOptions,\n maybeOptions?: UseRovingTabIndexOptions,\n): UseRovingTabIndexResult {\n const legacyRef =\n maybeOptions !== undefined ? (refOrOptions as React.RefObject<HTMLElement | null>) : undefined;\n const options = (maybeOptions ?? refOrOptions) as UseRovingTabIndexOptions;\n const {\n activeValue = null,\n items,\n orientation = 'horizontal',\n loop = true,\n dir,\n itemSelector = DEFAULT_ITEM_SELECTOR,\n typeahead = false,\n homeEndKeys = true,\n tabStop = 'active',\n manageTabIndex = false,\n onFocusMove,\n } = options;\n\n const [store] = React.useState(() => new RovingStore());\n const snapshot = React.useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getServerSnapshot,\n );\n const [focusedValue, setFocusedValue] = React.useState<string | null>(null);\n // The last focused item that can hold the tab stop ('last-focused' during render).\n const [focusedStop, setFocusedStop] = React.useState<string | null>(null);\n\n // 0.4 call shape or explicit `items`: only elements with their own roving container are nested\n // composites (a role=radiogroup/tablist/… around the items is the widget's own root).\n const roleComposites = legacyRef === undefined && items === undefined;\n\n React.useLayoutEffect(() => {\n store.setOptions({ itemSelector, manageTabIndex, items, activeValue, tabStop, roleComposites });\n if (legacyRef) store.setContainer(legacyRef.current);\n store.stamp();\n });\n\n // C-DEV: `resolveOwn` treats a selector the browser rejects as \"no items\" (it runs during render,\n // where it must not throw), so the developer hears about it here.\n React.useEffect(() => {\n if (isDev && !isValidSelector(itemSelector)) {\n warnOnce(\n `useRovingTabIndex:itemSelector:${itemSelector}`,\n `useRovingTabIndex: itemSelector \"${itemSelector}\" is not a valid CSS selector in this browser; no item is navigable or tabbable.`,\n );\n }\n }, [itemSelector]);\n\n // Tab stop during render, from the DOM snapshot (null before the container is known).\n const domItems = React.useMemo(() => parseSnapshot(snapshot), [snapshot]);\n const stopEntries = React.useMemo<readonly SnapshotEntry[] | null>(() => {\n if (!items) return domItems;\n // Explicit `items`: the listed values the DOM has, in the listed order. Every listed value\n // before the container is known, or while the DOM has none of them yet (items rendered in the\n // commit the snapshot has not caught up with).\n if (domItems) {\n const ranks = new Map(domItems);\n const present = items.flatMap((value): SnapshotEntry[] => {\n const rank = ranks.get(value);\n return rank === undefined ? [] : [[value, rank]];\n });\n if (present.length > 0) return present;\n }\n return items.map((value): SnapshotEntry => [value, 1]);\n }, [items, domItems]);\n const tabStopValue =\n tabStop === 'none'\n ? null\n : stopEntries\n ? resolveTabStop(stopEntries, activeValue, tabStop, focusedStop)\n : activeValue || null;\n\n const getTabIndex = React.useCallback(\n (value: string): 0 | -1 => (value === tabStopValue ? 0 : -1),\n [tabStopValue],\n );\n\n const containerRef = React.useCallback(\n (node: HTMLElement | null) => {\n store.setContainer(node);\n if (legacyRef) setRef(legacyRef, node);\n },\n [store, legacyRef],\n );\n\n const typeaheadItemsRef = React.useRef<TypeaheadItem[]>([]);\n // Every item matching the typed characters, in search order (the first is the match).\n const typeaheadMatchesRef = React.useRef<readonly string[]>([]);\n const { onTypeahead, isSearching } = useTypeahead({\n getItems: () => typeaheadItemsRef.current,\n onMatch: (_value, matches) => {\n typeaheadMatchesRef.current = matches;\n },\n });\n\n /** Focuses an item; `true` only when focus actually moved there (then it is recorded). */\n const focusItem = (item: ResolvedItem | undefined): boolean => {\n if (!item || item.disabled) return false;\n const target = getFocusTarget(item);\n if (!target) return false;\n target.focus();\n // Browsers ignore focus() on an element CSS hides (display: none, visibility: hidden).\n if (!hasFocus(target)) return false;\n store.recordFocus(item);\n return true;\n };\n\n /** Focuses the first item of `candidates` that takes focus. */\n const focusFirstOf = (candidates: readonly ResolvedItem[]): void => {\n for (const item of candidates) {\n if (focusItem(item)) return;\n }\n };\n\n // `spaceOnly` (the capture phase) handles nothing but a Space that continues a typeahead search.\n const handleKeys = useEventCallback((e: React.KeyboardEvent, spaceOnly: boolean) => {\n // Ctrl, Alt and Meta combinations are shortcuts, except a single character typed with AltGr\n // (Ctrl+Alt on Windows), which is typeahead text: arrows, Home, End and Space with it stay\n // shortcuts.\n const shortcut = e.metaKey || ((e.altKey || e.ctrlKey) && !isAltGraphCharacter(e));\n if (e.defaultPrevented || shortcut) return;\n if (ownsArrowKeys(e.target)) return;\n const container = store.container ?? (e.currentTarget as HTMLElement);\n // A key from a portaled popup is the popup's: never pull focus back out of it.\n if (!isInContainer(container, e.target)) return;\n const ordered = store.resolve(container);\n const enabled = ordered.filter((item) => !item.disabled);\n if (enabled.length === 0) return;\n\n // The (innermost) item the key started in. None (the container itself has focus): next → first,\n // prev → last.\n const current = findOwningItem(ordered, e.target, container);\n\n let handled = false;\n // Where focus goes, in order of preference: an item whose focus() does not move focus (hidden\n // by CSS) passes the key on to the next one.\n let targets: ResolvedItem[] = [];\n const intent = spaceOnly\n ? null\n : getArrowIntent(e.key, {\n orientation,\n dir: dir ?? getDirection(e.currentTarget as Element),\n });\n if (intent) {\n handled = true;\n if (!current) {\n targets = intent === 'next' ? enabled : [...enabled].reverse();\n } else {\n const step = intent === 'next' ? 1 : -1;\n const count = ordered.length;\n let index = ordered.indexOf(current);\n for (let i = 1; i < count; i++) {\n index += step;\n if (index < 0 || index >= count) {\n if (!loop) break;\n index = (index + count) % count;\n }\n if (!ordered[index].disabled) targets.push(ordered[index]);\n }\n }\n } else if (!spaceOnly && homeEndKeys && (e.key === 'Home' || e.key === 'End')) {\n handled = true;\n targets = e.key === 'Home' ? enabled : [...enabled].reverse();\n } else if (typeahead && startsOnItem(current, e.target, container)) {\n typeaheadItemsRef.current = ordered.map((item) => ({\n value: item.value,\n text: textOf(item.element),\n disabled: item.disabled,\n }));\n typeaheadMatchesRef.current = [];\n if (onTypeahead(e, current?.value ?? null)) {\n handled = true;\n // The match first, then the next ones, for a match that does not take focus.\n targets = typeaheadMatchesRef.current.flatMap((value) =>\n enabled.filter((item) => item.value === value),\n );\n }\n }\n\n if (!handled) return;\n e.preventDefault();\n for (const next of targets) {\n // Back at the item the key started on (Home on the first item): focus stays.\n if (next === current) return;\n if (focusItem(next)) {\n onFocusMove?.(next.value, e);\n return;\n }\n }\n });\n\n const handleKeyDown = useEventCallback((e: React.KeyboardEvent) => handleKeys(e, false));\n\n // A Space that continues a typeahead search belongs to the search, not to the focused item: it is\n // handled and default-prevented in the capture phase, so the item's own keydown handler, which\n // skips default-prevented events, does not activate the item.\n const handleKeyDownCapture = useEventCallback((e: React.KeyboardEvent) => {\n if (typeahead && e.key === ' ' && isSearching()) handleKeys(e, true);\n });\n\n const handleFocus = useEventCallback((e: React.FocusEvent) => {\n const container = store.container ?? (e.currentTarget as HTMLElement);\n if (!isInContainer(container, e.target)) return;\n const item = findOwningItem(store.resolve(container), e.target, container);\n if (!item) return;\n store.recordFocus(item);\n setFocusedValue(item.value);\n if (canHoldTabStop(item)) setFocusedStop(item.value);\n store.stamp();\n });\n\n const focusValue = useEventCallback((value: string) => {\n focusItem(store.resolve().find((item) => item.value === value));\n });\n const focusFirst = useEventCallback(() => {\n focusFirstOf(store.resolve().filter((item) => !item.disabled));\n });\n const focusLast = useEventCallback(() => {\n focusFirstOf(\n store\n .resolve()\n .filter((item) => !item.disabled)\n .reverse(),\n );\n });\n\n const containerProps = React.useMemo<RovingContainerProps>(\n () => ({\n ref: containerRef,\n 'data-roving-container': '',\n onKeyDown: handleKeyDown,\n onKeyDownCapture: handleKeyDownCapture,\n onFocus: handleFocus,\n }),\n [containerRef, handleKeyDown, handleKeyDownCapture, handleFocus],\n );\n\n return {\n containerProps,\n handleKeyDown,\n handleKeyDownCapture,\n handleFocus,\n getTabIndex,\n focusedValue,\n focusValue,\n focusFirst,\n focusLast,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useRovingTabIndex.cjs","names":["getFirstTabbable","isHiddenInput","React","isDev","useTypeahead","useEventCallback","isAltGraphCharacter","getArrowIntent","getDirection","require_runtime","require_dev","require_mergeRefs","require_useEventCallback","require_direction","require_focus","require_useTypeahead","react","DEFAULT_ITEM_SELECTOR","COMPOSITE_ROLES","ARROW_KEY_OWNER_ROLES","NON_TEXT_INPUT_TYPES","OBSERVED_ATTRIBUTES","isNestedComposite","el","roleComposites","role","isInNestedComposite","node","container","STAMPED_SELECTOR","matchesSelector","selector","rewrittenTabIndex","last","record","records","rewritten","target","isFocusableSelf","getFocusTarget","item","element","ownStop","tabbable","isDisabledElement","marker","isNativelyDisabled","ownsArrowKeys","tag","editable","canHoldTabStop","isHiddenByCss","view","hidden","cache","parent","child","visibility","hasFocus","active","isValidSelector","toEntry","resolveTabStop","rank","entries","r","values","value","tabStop","lastFocused","activeValue","orderByItems","items","resolved","byValue","ordered","RovingStore","notify","previous","options","candidates","itemSelector","result","seen","cssHidden","candidate","composite","nested","manageTabIndex","stop","tabIndex","current","original","scope","own","generated","parseSnapshot","snapshot","textOf","isInContainer","findOwningItem","byElement","startsOnItem","useRovingTabIndex","legacyRef","maybeOptions","refOrOptions","store","focusedValue","setFocusedValue","focusedStop","setFocusedStop","domItems","stopEntries","ranks","present","tabStopValue","getTabIndex","containerRef","typeaheadItemsRef","typeaheadMatchesRef","matches","focusItem","focusFirstOf","handleKeys","shortcut","enabled","handled","targets","intent","spaceOnly","dir","step","count","index","i","loop","homeEndKeys","typeahead","onTypeahead","next","onFocusMove","handleKeyDown","handleKeyDownCapture","isSearching","handleFocus","focusValue","focusFirst","focusLast"],"sources":["../../src/hooks/useRovingTabIndex.ts"],"sourcesContent":["import * as React from 'react';\nimport type { WaveDir } from '../components/provider/WaveProvider';\nimport { isDev, warnOnce } from '../lib/dev';\nimport { getArrowIntent, getDirection } from '../lib/direction';\nimport { getFirstTabbable, isHiddenInput } from '../lib/focus';\nimport { setRef } from '../lib/mergeRefs';\nimport { useEventCallback } from './useEventCallback';\nimport { isAltGraphCharacter, useTypeahead, type TypeaheadItem } from './useTypeahead';\n\n/**\n * Configuration options for the {@link useRovingTabIndex} hook. Disabled items are skipped; items\n * marked `data-disabled-focusable` (the `disabledFocusable` prop of WaveUI buttons, links and\n * choice controls) stay reachable and may hold the tab stop: APG allows focusable disabled items.\n */\nexport interface UseRovingTabIndexOptions {\n /**\n * The selected value. It may be absent, `''` or `null` (nothing selected) and does not have to be\n * one of the items.\n */\n activeValue?: string | null;\n /**\n * Explicit item order (0.4 call shape). Omitted ⇒ the DOM order of the elements matching\n * `itemSelector`. Disabled items are skipped either way, and so are listed values with no\n * element in the container (not rendered, or `hidden`/`inert`): they never hold the tab stop.\n */\n items?: readonly string[];\n /**\n * Arrow key axis: `'horizontal'` uses Left/Right (mirrored in RTL), `'vertical'` uses Up/Down,\n * `'both'` uses all four.\n * @default 'horizontal'\n */\n orientation?: 'horizontal' | 'vertical' | 'both';\n /**\n * Whether arrow keys wrap around at the ends.\n * @default true\n */\n loop?: boolean;\n /** Text direction. Defaults to the direction of the container at key time (`getDirection`). */\n dir?: WaveDir;\n /**\n * Selector for the item elements inside the container. A selector the browser rejects matches\n * nothing (no item is navigable or tabbable) and logs a development warning.\n * @default '[data-roving-value]'\n */\n itemSelector?: string;\n /**\n * Printable characters move focus to the next item whose text (`data-roving-text`, else its\n * text content) starts with them. A Space typed within 500 ms of another character continues\n * the search instead of activating the focused item: `containerProps.onKeyDownCapture` calls\n * `preventDefault()` on it before an item's own keydown handler runs, so that handler must skip\n * default-prevented events (`composeEventHandlers` does). Keys typed in content nested inside an\n * item (a row's action button) are not typeahead. A character typed with AltGr (which Windows\n * reports as Ctrl+Alt, e.g. Polish `ł`) is typeahead text; other Ctrl, Alt or Meta\n * combinations, Ctrl+Alt+Space included, are shortcuts and ignored. A match whose `focus()`\n * leaves focus where it was is passed over for the next item that matches.\n * @default false\n */\n typeahead?: boolean;\n /**\n * Whether Home/End move to the first/last enabled item.\n * @default true\n */\n homeEndKeys?: boolean;\n /** Which item holds the tab stop. 'active' (default): the enabled activeValue item, else the first\n * enabled item — APG Tabs/Radio/Listbox/Tree. 'last-focused': the last focused enabled item, else\n * 'active' — APG Toolbar (and the static Menu). Either way a nested composite or a control that\n * uses the arrow keys itself (text field, select, slider, spin button, editable combobox) holds\n * it only when no other item can: arrows pressed there never lead back to the other items, so\n * focusing one keeps the tab stop on the last focused other item. 'none': no item holds it\n * (every item gets tabIndex -1; a nested composite keeps its own tab stop, and one marked\n * `data-roving-transparent` joins the group's arrow order), for a composite reached only by\n * the arrow keys of an enclosing widget or by a script (a tree row's actions); the keys,\n * `focusValue`, `focusFirst` and `focusLast` keep working. */\n tabStop?: 'active' | 'last-focused' | 'none';\n /** true ⇒ the hook writes tabIndex 0/-1 onto item elements itself and assigns\n * data-roving-value=\"auto-<n>\" to items that lack one. For containers that do not render their\n * items (Toolbar children, Menu items, Tree rows). Default false (items call getTabIndex).\n * Matches that cannot take focus are no items: `input[type=hidden]` is skipped, and a control\n * hidden by CSS inside the container (`display: none` or `visibility: hidden` on it or on an\n * ancestor inside the container) is treated as disabled (skipped, never the tab stop, stamped\n * -1). A container hidden as a whole from outside keeps its items and tab stop.\n * When the container stops being one (its ref detaches, as when `containerProps` are no longer\n * spread, or moves to another element) or `manageTabIndex` turns false, every element the hook\n * stamped gets its own `tabindex` back (the attribute it had at its first stamp, or none; a\n * `tabIndex` its author rendered since, the commit that detaches the container included, stays)\n * and loses a `data-roving-value` the hook added (one React rendered stays). An element that\n * stops being an item (`itemSelector`, or a changed selector, no longer matches it) is handed\n * back the same way. The last focused item and the generated values are kept, so a container\n * that attaches again starts where it was. */\n manageTabIndex?: boolean;\n /** Called after an arrow, Home/End or typeahead key moved focus to `value`. */\n onFocusMove?: (value: string, event: React.KeyboardEvent) => void;\n}\n\n/** Props to spread onto the container element. */\nexport interface RovingContainerProps {\n ref: React.RefCallback<HTMLElement>;\n 'data-roving-container': '';\n onKeyDown: React.KeyboardEventHandler;\n /** With `typeahead`: default-prevents a Space that continues a search, before item handlers. */\n onKeyDownCapture: React.KeyboardEventHandler;\n onFocus: React.FocusEventHandler;\n}\n\n/** Result of {@link useRovingTabIndex}. */\nexport interface UseRovingTabIndexResult {\n /** Spread onto the container: ref, `data-roving-container` marker and the key/focus handlers. */\n containerProps: RovingContainerProps;\n /** The container's keydown handler (same function as `containerProps.onKeyDown`). */\n handleKeyDown: (e: React.KeyboardEvent) => void;\n /**\n * The container's capture-phase keydown handler (same function as\n * `containerProps.onKeyDownCapture`). With `typeahead`, it extends the search with a Space that\n * continues one and calls `preventDefault()`, before an item's own keydown handler runs: an item\n * that activates on Space must skip default-prevented events (`composeEventHandlers` does).\n */\n handleKeyDownCapture: (e: React.KeyboardEvent) => void;\n /** The container's focus handler (same function as `containerProps.onFocus`). */\n handleFocus: (e: React.FocusEvent) => void;\n /** `0` for the item that holds the tab stop, `-1` for every other item. */\n getTabIndex: (value: string) => 0 | -1;\n /** The value of the item that last received focus, or `null`. */\n focusedValue: string | null;\n /** Focuses the item with this value (ignored when it is disabled or unknown). */\n focusValue: (value: string) => void;\n /** Focuses the first enabled item. */\n focusFirst: () => void;\n /** Focuses the last enabled item. */\n focusLast: () => void;\n}\n\ninterface ResolvedItem {\n value: string;\n /** The item element (for a nested composite: its root). */\n element: HTMLElement;\n /** A nested composite (own roving container or composite role) counts as one item. */\n nested: boolean;\n /**\n * Skipped by the keys and never the tab stop: disabled, a nested composite with nothing to\n * focus, or (`manageTabIndex`) hidden by CSS.\n */\n disabled: boolean;\n}\n\ninterface StoreOptions {\n itemSelector: string;\n manageTabIndex: boolean;\n items: readonly string[] | undefined;\n activeValue: string | null;\n tabStop: 'active' | 'last-focused' | 'none';\n /**\n * Whether an element with a composite role but no roving container of its own is a nested\n * composite (one item). Off in the 0.4 call shape and with explicit `items`, where such an element\n * is usually the widget's own root inside a wrapper that carries the container ref.\n */\n roleComposites: boolean;\n}\n\nconst DEFAULT_ITEM_SELECTOR = '[data-roving-value]';\n\n/** Roles of widgets that manage their own children: an element with one of them is one item. */\nconst COMPOSITE_ROLES = new Set([\n 'radiogroup',\n 'listbox',\n 'grid',\n 'treegrid',\n 'tablist',\n 'menu',\n 'menubar',\n 'tree',\n 'spinbutton',\n]);\n\n/**\n * Roles whose widgets use the arrow keys themselves. A combobox does only when it is editable (an\n * `<input>` or contenteditable, caught by the native checks): a select-only combobox such as\n * Dropdown's `<button role=\"combobox\">` uses Up/Down, Home and End, never Left/Right.\n */\nconst ARROW_KEY_OWNER_ROLES = new Set(['slider', 'spinbutton', 'textbox', 'searchbox']);\n\n/** `<input>` types that do not use the arrow keys for text editing. */\nconst NON_TEXT_INPUT_TYPES = new Set([\n 'button',\n 'checkbox',\n 'color',\n 'file',\n 'hidden',\n 'image',\n 'radio',\n 'reset',\n 'submit',\n]);\n\nconst OBSERVED_ATTRIBUTES = [\n 'disabled',\n 'aria-disabled',\n 'data-disabled-focusable',\n 'data-roving-disabled',\n 'data-roving-value',\n 'data-roving-container',\n 'data-roving-transparent',\n 'tabindex',\n 'hidden',\n 'inert',\n 'role',\n // Whether an item uses the arrow keys itself (text input types, contenteditable).\n 'type',\n 'contenteditable',\n];\n\n/**\n * Whether `el` is a nested composite: it has its own roving container, or (with `roleComposites`)\n * a composite role. An element marked `data-roving-transparent` is never one by its role (a\n * `role=\"radiogroup\"` whose radios are items of the enclosing container, `Toolbar.RadioGroup`);\n * its own `data-roving-container` still makes it one.\n */\nfunction isNestedComposite(el: Element, roleComposites: boolean): boolean {\n if (el.hasAttribute('data-roving-container')) return true;\n if (!roleComposites || el.hasAttribute('data-roving-transparent')) return false;\n const role = el.getAttribute('role');\n return role !== null && COMPOSITE_ROLES.has(role);\n}\n\n/** Whether `el` is a nested composite or inside one, below `container`. */\nfunction isInNestedComposite(el: Element, container: Element, roleComposites: boolean): boolean {\n for (let node: Element | null = el; node && node !== container; node = node.parentElement) {\n if (isNestedComposite(node, roleComposites)) return true;\n }\n return false;\n}\n\n/** What the hook knows of an element it stamped, to hand it back to its author. */\ninterface StampedOriginal {\n /**\n * The author's own `tabindex` attribute, or `null`: read at the first stamp, and replaced by the\n * one the author writes while the element is stamped (React rendering another `tabIndex`; the\n * next stamp overwrites it in the DOM).\n */\n tabIndex: string | null;\n /** The `tabindex` the hook left on the element at its last stamp. */\n stamped: string;\n /** The `data-roving-value` the hook added (a generated value), or `null` when it added none. */\n addedValue: string | null;\n}\n\n/**\n * Every element the hook stamps carries both attributes, whatever `itemSelector` matched it: the\n * hand-back finds its elements through them, also one the selector no longer matches.\n */\nconst STAMPED_SELECTOR = '[tabindex], [data-roving-value]';\n\nfunction matchesSelector(el: Element, selector: string): boolean {\n try {\n return el.matches(selector);\n } catch {\n // A selector the browser rejects matches nothing.\n return false;\n }\n}\n\n/**\n * The elements whose last `tabindex` write in `records` left the value it found: the author\n * (through React) wrote the value the stamp had left. The hook only writes to change a value, so\n * such a write is never its own. Needs records with `attributeOldValue`.\n */\nfunction rewrittenTabIndex(records: readonly MutationRecord[]): Set<Node> {\n const last = new Map<Node, MutationRecord>();\n for (const record of records) {\n if (record.type === 'attributes' && record.attributeName === 'tabindex') {\n last.set(record.target, record);\n }\n }\n const rewritten = new Set<Node>();\n for (const [target, record] of last) {\n if (record.oldValue === (target as Element).getAttribute('tabindex')) rewritten.add(target);\n }\n return rewritten;\n}\n\nfunction isFocusableSelf(el: HTMLElement): boolean {\n return el.tabIndex >= 0 && !el.hasAttribute('disabled');\n}\n\n/** The element to focus for an item: the item itself, or a nested composite's own tab stop. */\nfunction getFocusTarget(item: ResolvedItem): HTMLElement | null {\n const { element } = item;\n if (!item.nested || isFocusableSelf(element)) return element;\n const ownStop = element.querySelector<HTMLElement>('[tabindex=\"0\"]');\n if (ownStop) return ownStop;\n const tabbable = getFirstTabbable(element);\n return tabbable instanceof HTMLElement ? tabbable : null;\n}\n\n/**\n * Whether the keys skip an item, in this order: the composite's own `data-roving-disabled` marker\n * (not `\"false\"`) and native `disabled` skip it; `data-disabled-focusable` keeps it (a\n * `disabledFocusable` control: APG allows focusable disabled items); `aria-disabled=\"true\"` skips\n * it.\n */\nfunction isDisabledElement(el: HTMLElement): boolean {\n const marker = el.getAttribute('data-roving-disabled');\n if (marker !== null && marker !== 'false') return true;\n if (isNativelyDisabled(el)) return true;\n if (el.hasAttribute('data-disabled-focusable')) return false;\n return el.getAttribute('aria-disabled') === 'true';\n}\n\nfunction isNativelyDisabled(el: HTMLElement): boolean {\n if ((el as { disabled?: unknown }).disabled === true) return true;\n try {\n return el.matches(':disabled');\n } catch {\n return false;\n }\n}\n\n/** Text-entry fields and widgets that use the arrow keys themselves (APG Toolbar). */\nfunction ownsArrowKeys(target: EventTarget | null): boolean {\n if (!target || (target as Node).nodeType !== 1) return false;\n const el = target as HTMLElement;\n const role = el.getAttribute('role');\n if (role !== null && ARROW_KEY_OWNER_ROLES.has(role)) return true;\n const tag = el.localName;\n if (tag === 'textarea' || tag === 'select') return true;\n if (tag === 'input') return !NON_TEXT_INPUT_TYPES.has((el as HTMLInputElement).type);\n const editable = el.closest('[contenteditable]');\n return editable !== null && editable.getAttribute('contenteditable') !== 'false';\n}\n\n/**\n * Whether an item can hold the container's tab stop: not a nested composite (it keeps a tab stop\n * of its own) and not a control that uses the arrow keys itself. From either, the arrow keys never\n * lead to the other items, so a tab stop there would leave them reachable by keyboard only through\n * it (or not at all).\n */\nfunction canHoldTabStop(item: ResolvedItem): boolean {\n return !item.nested && !ownsArrowKeys(item.element);\n}\n\n/**\n * Whether CSS inside `container` keeps `el` from taking focus (browsers ignore `focus()` on it):\n * `display: none` on it or on an ancestor inside `container`, `visibility: hidden`/`collapse` set\n * inside `container`, or a place inside a closed `<details>` other than its summary. Hiding from\n * outside the container (a surface that stays invisible until it is positioned, a consumer panel)\n * hides the whole item set and does not count: showing it again changes nothing the hook observes,\n * so nothing would re-stamp. `cache` holds, for one resolution, whether an element hides its whole\n * subtree (the `display` and `<details>` checks).\n *\n * Internal: shared with Tree's treegrid navigation; not exported from the package.\n */\nexport function isHiddenByCss(\n el: Element,\n container: Element,\n cache: Map<Element, boolean>,\n): boolean {\n const view = el.ownerDocument.defaultView;\n if (!view) return false;\n for (let node: Element | null = el; node && node !== container; node = node.parentElement) {\n let hidden = cache.get(node);\n if (hidden === undefined) {\n const parent = node.parentElement;\n hidden =\n view.getComputedStyle(node).display === 'none' ||\n (parent !== null &&\n parent.localName === 'details' &&\n !parent.hasAttribute('open') &&\n Array.from(parent.children).find((child) => child.localName === 'summary') !== node);\n cache.set(node, hidden);\n }\n if (hidden) return true;\n }\n const { visibility } = view.getComputedStyle(el);\n if (visibility !== 'hidden' && visibility !== 'collapse') return false;\n // Visibility is inherited, so `el` also reads hidden when an ancestor of the container hides the\n // whole container. It counts only when the container itself is visible, i.e. when the hiding is\n // set inside it (on `el` or on a wrapper between them).\n return view.getComputedStyle(container).visibility === 'visible';\n}\n\n/** Whether focus is on `el` or inside it (in its document or shadow root). */\nfunction hasFocus(el: HTMLElement): boolean {\n const active = (el.getRootNode() as Node & { activeElement?: Element | null }).activeElement;\n return !!active && el.contains(active);\n}\n\nfunction isValidSelector(selector: string): boolean {\n try {\n document.createDocumentFragment().querySelector(selector);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * How an item takes part in the tab stop: `0` never (disabled), `1` can hold it, `2` holds it only\n * when no item can (a nested composite, or a control that uses the arrow keys itself).\n */\ntype TabStopRank = 0 | 1 | 2;\n\n/** One item of the published DOM snapshot, and the input of {@link resolveTabStop}. */\ntype SnapshotEntry = [value: string, rank: TabStopRank];\n\nfunction toEntry(item: ResolvedItem): SnapshotEntry {\n return [item.value, item.disabled ? 0 : canHoldTabStop(item) ? 1 : 2];\n}\n\n/**\n * The value that holds the tab stop, among the items of the best rank present (`1`, else `2`):\n * with 'last-focused' the last focused one, else the `activeValue` item, else the first one.\n */\nfunction resolveTabStop(\n entries: readonly SnapshotEntry[],\n activeValue: string | null,\n tabStop: 'active' | 'last-focused',\n lastFocused: string | null,\n): string | null {\n const rank = entries.some(([, r]) => r === 1) ? 1 : 2;\n const values = entries.filter(([, r]) => r === rank).map(([value]) => value);\n if (tabStop === 'last-focused' && lastFocused !== null && values.includes(lastFocused)) {\n return lastFocused;\n }\n if (activeValue !== null && activeValue !== '' && values.includes(activeValue)) {\n return activeValue;\n }\n return values[0] ?? null;\n}\n\nfunction orderByItems(\n resolved: ResolvedItem[],\n items: readonly string[] | undefined,\n): ResolvedItem[] {\n if (!items) return resolved;\n const byValue = new Map(resolved.map((item) => [item.value, item]));\n const ordered: ResolvedItem[] = [];\n for (const value of items) {\n const item = byValue.get(value);\n if (item) ordered.push(item);\n }\n return ordered;\n}\n\n/**\n * Per-hook store: resolves items from the DOM, publishes the enabled set to React through\n * `useSyncExternalStore` (a MutationObserver on the container notifies it), and stamps tabIndex in\n * `manageTabIndex` mode.\n */\nclass RovingStore {\n container: HTMLElement | null = null;\n lastFocused: string | null = null;\n options: StoreOptions = {\n itemSelector: DEFAULT_ITEM_SELECTOR,\n manageTabIndex: false,\n items: undefined,\n activeValue: null,\n tabStop: 'active',\n roleComposites: true,\n };\n\n private readonly stamped = new WeakMap<Element, StampedOriginal>();\n private readonly autoValues = new WeakMap<Element, string>();\n private autoCounter = 0;\n private notify: (() => void) | null = null;\n private observer: MutationObserver | null = null;\n private dirty = true;\n private snapshot = '';\n\n subscribe = (notify: () => void): (() => void) => {\n this.notify = notify;\n this.observe();\n return () => {\n this.notify = null;\n this.disconnect();\n };\n };\n\n getSnapshot = (): string => {\n if (!this.container) return '';\n if (this.dirty) {\n this.dirty = false;\n // DOM facts only (order and tab stop rank); `items` is applied during render.\n this.snapshot = JSON.stringify(this.resolveOwn().map(toEntry));\n }\n return this.snapshot;\n };\n\n getServerSnapshot = (): string => '';\n\n /**\n * Attaches the store to `container` (`null` detaches it). A container that stops being one (its\n * ref detached, or the store moved to another element) first hands every element it stamped back\n * to its author, with the writes its observer has not delivered yet (the `tabIndex` React\n * rendered in the commit that detaches it). Only a store that manages tabIndex has stamped\n * anything: `setOptions` hands the stamps back the moment `manageTabIndex` turns off.\n */\n setContainer(container: HTMLElement | null): void {\n if (container === this.container) return;\n // Disconnecting drops the pending records anyway; the hand-back reads them first.\n const records = this.takeRecords();\n this.disconnect();\n if (this.container && this.options.manageTabIndex) {\n this.release(this.container, 'all', records);\n }\n this.container = container;\n this.dirty = true;\n if (this.notify) {\n this.observe();\n this.notify();\n }\n }\n\n /**\n * Records a focused item for the 'last-focused' tab stop. An item that cannot hold the tab stop\n * (a nested composite, a control that uses the arrow keys itself) leaves the last one in place.\n */\n recordFocus(item: ResolvedItem): void {\n if (canHoldTabStop(item)) this.lastFocused = item.value;\n }\n\n setOptions(options: StoreOptions): void {\n const previous = this.options;\n this.options = options;\n // `manageTabIndex` turned off: the elements stamped so far go back to their author. The records\n // taken here are not delivered; the notification below stands in for them. (A changed\n // `itemSelector` hands back what it no longer matches at the next stamp.)\n if (previous.manageTabIndex && !options.manageTabIndex && this.container) {\n this.release(this.container, 'all', this.takeRecords());\n }\n if (\n previous.itemSelector !== options.itemSelector ||\n previous.manageTabIndex !== options.manageTabIndex ||\n previous.roleComposites !== options.roleComposites\n ) {\n this.dirty = true;\n this.notify?.();\n }\n }\n\n /** Own items in navigation order: the explicit `items` order when given, else DOM order. */\n resolve(container: HTMLElement | null = this.container): ResolvedItem[] {\n return orderByItems(this.resolveOwn(container), this.options.items);\n }\n\n /** Own items of the container in DOM order (items of nested composites excluded). */\n resolveOwn(container: HTMLElement | null = this.container): ResolvedItem[] {\n if (!container) return [];\n const { itemSelector, manageTabIndex, roleComposites } = this.options;\n let candidates: HTMLElement[];\n try {\n candidates = Array.from(container.querySelectorAll<HTMLElement>(itemSelector));\n } catch {\n // An invalid selector (warned from an effect in development): this runs during render\n // (getSnapshot), where throwing would take the whole tree down.\n return [];\n }\n\n const result: ResolvedItem[] = [];\n const seen = new Set<Element>();\n const cssHidden = new Map<Element, boolean>();\n for (const candidate of candidates) {\n let composite: HTMLElement | null = null;\n let hidden = false;\n for (\n let node: HTMLElement | null = candidate;\n node && node !== container;\n node = node.parentElement\n ) {\n if (isNestedComposite(node, roleComposites)) composite = node;\n if (node.hidden || node.hasAttribute('inert')) hidden = true;\n }\n if (hidden) continue;\n\n const element = composite ?? candidate;\n if (seen.has(element)) continue;\n const nested = composite !== null;\n if (!nested && manageTabIndex) {\n // An author tabindex=\"-1\" (a SpinButton stepper, SplitButton internals) opts out.\n if (candidate.getAttribute('tabindex') === '-1' && !this.stamped.has(candidate)) continue;\n // A hidden form input (HiddenInput's `type=\"hidden\"`) is never focusable.\n if (isHiddenInput(candidate)) continue;\n }\n seen.add(element);\n\n const item: ResolvedItem = {\n value: this.valueOf(element, nested),\n element,\n nested,\n disabled: isDisabledElement(element),\n };\n if (nested && !item.disabled && !getFocusTarget(item)) item.disabled = true;\n // Consumer children hidden by CSS (a responsive `hidden md:inline-flex` control). Treated as\n // disabled rather than dropped, so it is stamped -1 and is no extra Tab stop once the CSS\n // shows it again (a CSS change triggers no mutation, hence no re-stamp).\n if (manageTabIndex && !item.disabled && isHiddenByCss(element, container, cssHidden)) {\n item.disabled = true;\n }\n result.push(item);\n }\n return result;\n }\n\n /**\n * manageTabIndex: writes tabIndex 0 on the tab stop and -1 on every other own item. An element\n * it stamped that is no longer one of its items (it now belongs to a nested composite, as when a\n * `data-roving-transparent` marker is removed, or `itemSelector` no longer matches it) is handed\n * back first. A `tabindex` the author rendered since the last stamp becomes the element's own.\n */\n stamp(): void {\n if (!this.options.manageTabIndex || !this.container) return;\n this.release(this.container, 'stale');\n const resolved = this.resolve();\n const stop =\n this.options.tabStop === 'none'\n ? null\n : resolveTabStop(\n resolved.map(toEntry),\n this.options.activeValue,\n this.options.tabStop,\n this.lastFocused,\n );\n for (const item of resolved) {\n if (item.nested) continue;\n const { element } = item;\n const tabIndex = item.value === stop ? '0' : '-1';\n const current = element.getAttribute('tabindex');\n let original = this.stamped.get(element);\n if (!original) {\n original = { tabIndex: current, stamped: tabIndex, addedValue: null };\n this.stamped.set(element, original);\n } else {\n // Another value than the last stamp left: the author rendered it, so it is the element's own.\n if (current !== original.stamped) original.tabIndex = current;\n original.stamped = tabIndex;\n }\n if (current !== tabIndex) element.setAttribute('tabindex', tabIndex);\n if (!element.hasAttribute('data-roving-value')) {\n element.setAttribute('data-roving-value', item.value);\n original.addedValue = item.value;\n }\n }\n }\n\n /**\n * Hands elements it stamped inside `container` back to their author and drops their records, so\n * a later stamp reads them afresh. The `tabindex` the record holds as the author's is put back\n * (removed when there was none), unless the author wrote one since the last stamp, which stays:\n * another value than the stamp's, or the stamp's value written again (seen in the pending\n * `records`). A `data-roving-value` the hook added is removed while it still holds the generated\n * value (one React renders stays). The generated values and the last focused item are kept: a\n * container that attaches again starts where it was. `'stale'`: only the elements that are no\n * longer its items, because they belong to a nested composite or `itemSelector` no longer matches\n * them (every stamp); `'all'`: every element (the container detaches or is replaced, or\n * `manageTabIndex` turns off).\n */\n private release(\n container: HTMLElement,\n scope: 'stale' | 'all',\n records: readonly MutationRecord[] = [],\n ): void {\n const rewritten = rewrittenTabIndex(records);\n const { itemSelector, roleComposites } = this.options;\n for (const element of Array.from(container.querySelectorAll<HTMLElement>(STAMPED_SELECTOR))) {\n const original = this.stamped.get(element);\n if (!original) continue;\n if (\n scope === 'stale' &&\n matchesSelector(element, itemSelector) &&\n !isInNestedComposite(element, container, roleComposites)\n ) {\n continue;\n }\n this.stamped.delete(element);\n const current = element.getAttribute('tabindex');\n const authorWrote = current !== original.stamped || rewritten.has(element);\n // Written only to change the value: the hook's own writes never keep one (`rewrittenTabIndex`).\n if (!authorWrote && current !== original.tabIndex) {\n if (original.tabIndex === null) element.removeAttribute('tabindex');\n else element.setAttribute('tabindex', original.tabIndex);\n }\n if (\n original.addedValue !== null &&\n element.getAttribute('data-roving-value') === original.addedValue\n ) {\n element.removeAttribute('data-roving-value');\n }\n }\n }\n\n /**\n * Takes over, as the element's own, a `tabindex` the author wrote with the value the stamp had\n * left (`rewrittenTabIndex`): the next stamp cannot tell it from its own. Another value is taken\n * over by the next stamp.\n */\n private adopt(records: readonly MutationRecord[]): void {\n for (const target of rewrittenTabIndex(records)) {\n const original = this.stamped.get(target as Element);\n if (original) original.tabIndex = (target as Element).getAttribute('tabindex');\n }\n }\n\n /** The records the observer has not delivered yet; it delivers none of them afterwards. */\n private takeRecords(): MutationRecord[] {\n return this.observer?.takeRecords() ?? [];\n }\n\n private valueOf(element: HTMLElement, nested: boolean): string {\n const own = nested ? null : element.getAttribute('data-roving-value');\n if (own !== null) return own;\n let generated = this.autoValues.get(element);\n if (generated === undefined) {\n this.autoCounter += 1;\n generated = `auto-${this.autoCounter}`;\n this.autoValues.set(element, generated);\n }\n return generated;\n }\n\n private observe(): void {\n if (!this.container || this.observer || typeof MutationObserver === 'undefined') return;\n this.observer = new MutationObserver((records) => {\n this.adopt(records);\n this.stamp();\n this.dirty = true;\n this.notify?.();\n });\n this.observer.observe(this.container, {\n subtree: true,\n childList: true,\n attributes: true,\n // With the old value, a write that keeps a value shows as the author's (`rewrittenTabIndex`).\n attributeOldValue: true,\n attributeFilter: OBSERVED_ATTRIBUTES,\n });\n this.dirty = true;\n }\n\n private disconnect(): void {\n this.observer?.disconnect();\n this.observer = null;\n }\n}\n\nfunction parseSnapshot(snapshot: string): SnapshotEntry[] | null {\n if (snapshot === '') return null;\n try {\n return JSON.parse(snapshot) as SnapshotEntry[];\n } catch {\n return null;\n }\n}\n\nfunction textOf(element: HTMLElement): string {\n return element.getAttribute('data-roving-text') ?? element.textContent ?? '';\n}\n\n/**\n * Whether an event target is the container or inside its DOM. React bubbles events from a portal\n * (a Menu.Popover or Popover.Content a Toolbar item renders) through the container although their\n * target is elsewhere in the document; those events are not the container's to handle.\n */\nfunction isInContainer(container: Node, target: EventTarget | null): boolean {\n if (!target || typeof (target as Node).nodeType !== 'number') return false;\n return target === container || container.contains(target as Node);\n}\n\n/**\n * The item an event started in: the innermost item element that is the target or one of its\n * ancestors (up to the container). Items can nest (an APG treeitem contains the group of its child\n * treeitems), so the first containing item in DOM order would be the outermost ancestor.\n */\nfunction findOwningItem(\n items: readonly ResolvedItem[],\n target: EventTarget | null,\n container: Node,\n): ResolvedItem | null {\n if (items.length === 0 || !target || typeof (target as Node).nodeType !== 'number') return null;\n const byElement = new Map<Node, ResolvedItem>(items.map((item) => [item.element, item]));\n for (let node: Node | null = target as Node; node && node !== container; node = node.parentNode) {\n const item = byElement.get(node);\n if (item) return item;\n }\n return null;\n}\n\n/**\n * Whether a key started on the item itself (on the container when it started on no item), not on\n * content nested inside the item, such as a row's action button.\n */\nfunction startsOnItem(item: ResolvedItem | null, target: EventTarget | null, container: Node) {\n return item ? target === getFocusTarget(item) : target === container;\n}\n\n/**\n * Implements the WAI-ARIA roving tabindex pattern for composite widgets: one item holds the tab\n * stop (`tabIndex=0`, all others `-1`), arrow keys move focus between items, Home/End jump to the\n * ends, and (optionally) typeahead jumps by text. Used by RadioGroup, Rating, SwatchPicker, List,\n * TabList, Tree, Menu and Toolbar.\n *\n * - **Items** come from the DOM at event time: elements matching `itemSelector` (default\n * `[data-roving-value]`) whose nearest roving container is this one. A nested composite (an\n * element with its own `data-roving-container`, or a radiogroup/listbox/grid/tablist/menu/tree/\n * spinbutton role) counts as one item whose focus target is its own tab stop; the hook never\n * writes tabindex inside it, so it keeps its own Tab stop. An element marked\n * `data-roving-transparent` is never a nested composite by its role: its items are items of this\n * container (the radios of `Toolbar.RadioGroup`); it must not carry a `data-roving-container`.\n * Pass `items` to fix the order instead.\n * In the 0.4 call shape and with explicit `items`, only elements with their own roving container\n * are nested composites: a role-only `radiogroup`/`tablist`/… element between the container and\n * the items (the widget's root inside a wrapper that holds the ref) does not swallow them.\n * - **Disabled items** (`data-roving-disabled`, `disabled`, `aria-disabled=\"true\"`) are skipped and\n * never hold the tab stop. Items marked `data-disabled-focusable` (the `disabledFocusable` prop\n * of WaveUI buttons, links and choice controls) stay reachable and may hold the tab stop: APG\n * allows focusable disabled items. The attribute only overrides `aria-disabled`: an item that is\n * natively disabled or marked `data-roving-disabled` is still skipped. The enabled set is\n * tracked with a MutationObserver, so an item that disables itself moves the tab stop without\n * the owner re-rendering. With `manageTabIndex`, a control hidden by CSS counts as disabled and\n * `input[type=hidden]` is no item.\n * - **Hand-back** (`manageTabIndex`): a container that stops being one (its ref detaches or moves\n * to another element) or that stops managing tabIndex gives every element it stamped its own\n * `tabindex` back (the one it had, or the one its author rendered since) and removes the\n * `data-roving-value` it added, so each control is its own Tab stop again; attached again, it\n * stamps again from the last focused item. An element `itemSelector` no longer matches is handed\n * back at once.\n * - **Tab stop** (`tabStop`): `'active'` — the enabled `activeValue` item, else the first enabled\n * item; `'last-focused'` — the last focused enabled item, else the `'active'` rule. A nested\n * composite or a control that uses the arrow keys itself holds it only when no other item can;\n * `'none'` — no item: every item is `tabIndex=-1` (a nested composite keeps its own tab stop;\n * mark it `data-roving-transparent` to join the group's arrow order), and focus reaches the\n * items only through the keys or a script.\n * - **Keys** are ignored when another handler already called `preventDefault()`; with Meta; with\n * Alt or Ctrl, except a single character typed with AltGr (Ctrl+Alt on Windows), which is\n * typeahead text (arrows, Home, End and Space with Ctrl+Alt stay ignored); and when they start\n * in a text field, select, contenteditable, slider, spinbutton or editable combobox (Left/Right\n * keep moving the caret; a select-only combobox such as Dropdown's button does not keep them).\n * Left/Right are mirrored in RTL (`dir`, else the direction of the container at key time).\n * Arrows and typeahead move from the item the key started in — the innermost one when items\n * nest, as treeitems do inside their parent's group; when it started on no item (focus on the\n * container itself) next goes to the first enabled item and prev to the last.\n * An item whose `focus()` leaves focus where it was (the browser ignores it on an element CSS\n * hides) is passed over for the next one, and is never recorded or reported to `onFocusMove`.\n * Handled keys call `preventDefault()`. Keys and focus from outside the container's DOM — a\n * popup an item renders through a portal, whose events React bubbles through the container —\n * are ignored, so focus never leaves an open menu or popover for the container's items.\n *\n * @example\n * const { containerProps, getTabIndex } = useRovingTabIndex({ activeValue: value, orientation: 'both' });\n * <div role=\"radiogroup\" {...containerProps}>\n * <button role=\"radio\" data-roving-value=\"a\" tabIndex={getTabIndex('a')} />\n * </div>\n *\n * The 0.4 call shape `useRovingTabIndex(containerRef, options)` still works: pass the container's\n * ref object and attach `handleKeyDown` (and ideally `handleFocus`, and with `typeahead`\n * `handleKeyDownCapture` as `onKeyDownCapture`) yourself.\n *\n * @param options - See {@link UseRovingTabIndexOptions}.\n * @returns See {@link UseRovingTabIndexResult}.\n */\nexport function useRovingTabIndex(options: UseRovingTabIndexOptions): UseRovingTabIndexResult;\nexport function useRovingTabIndex(\n containerRef: React.RefObject<HTMLElement | null>,\n options: UseRovingTabIndexOptions,\n): UseRovingTabIndexResult;\nexport function useRovingTabIndex(\n refOrOptions: React.RefObject<HTMLElement | null> | UseRovingTabIndexOptions,\n maybeOptions?: UseRovingTabIndexOptions,\n): UseRovingTabIndexResult {\n const legacyRef =\n maybeOptions !== undefined ? (refOrOptions as React.RefObject<HTMLElement | null>) : undefined;\n const options = (maybeOptions ?? refOrOptions) as UseRovingTabIndexOptions;\n const {\n activeValue = null,\n items,\n orientation = 'horizontal',\n loop = true,\n dir,\n itemSelector = DEFAULT_ITEM_SELECTOR,\n typeahead = false,\n homeEndKeys = true,\n tabStop = 'active',\n manageTabIndex = false,\n onFocusMove,\n } = options;\n\n const [store] = React.useState(() => new RovingStore());\n const snapshot = React.useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getServerSnapshot,\n );\n const [focusedValue, setFocusedValue] = React.useState<string | null>(null);\n // The last focused item that can hold the tab stop ('last-focused' during render).\n const [focusedStop, setFocusedStop] = React.useState<string | null>(null);\n\n // 0.4 call shape or explicit `items`: only elements with their own roving container are nested\n // composites (a role=radiogroup/tablist/… around the items is the widget's own root).\n const roleComposites = legacyRef === undefined && items === undefined;\n\n React.useLayoutEffect(() => {\n store.setOptions({ itemSelector, manageTabIndex, items, activeValue, tabStop, roleComposites });\n if (legacyRef) store.setContainer(legacyRef.current);\n store.stamp();\n });\n\n // C-DEV: `resolveOwn` treats a selector the browser rejects as \"no items\" (it runs during render,\n // where it must not throw), so the developer hears about it here.\n React.useEffect(() => {\n if (isDev && !isValidSelector(itemSelector)) {\n warnOnce(\n `useRovingTabIndex:itemSelector:${itemSelector}`,\n `useRovingTabIndex: itemSelector \"${itemSelector}\" is not a valid CSS selector in this browser; no item is navigable or tabbable.`,\n );\n }\n }, [itemSelector]);\n\n // Tab stop during render, from the DOM snapshot (null before the container is known).\n const domItems = React.useMemo(() => parseSnapshot(snapshot), [snapshot]);\n const stopEntries = React.useMemo<readonly SnapshotEntry[] | null>(() => {\n if (!items) return domItems;\n // Explicit `items`: the listed values the DOM has, in the listed order. Every listed value\n // before the container is known, or while the DOM has none of them yet (items rendered in the\n // commit the snapshot has not caught up with).\n if (domItems) {\n const ranks = new Map(domItems);\n const present = items.flatMap((value): SnapshotEntry[] => {\n const rank = ranks.get(value);\n return rank === undefined ? [] : [[value, rank]];\n });\n if (present.length > 0) return present;\n }\n return items.map((value): SnapshotEntry => [value, 1]);\n }, [items, domItems]);\n const tabStopValue =\n tabStop === 'none'\n ? null\n : stopEntries\n ? resolveTabStop(stopEntries, activeValue, tabStop, focusedStop)\n : activeValue || null;\n\n const getTabIndex = React.useCallback(\n (value: string): 0 | -1 => (value === tabStopValue ? 0 : -1),\n [tabStopValue],\n );\n\n const containerRef = React.useCallback(\n (node: HTMLElement | null) => {\n store.setContainer(node);\n if (legacyRef) setRef(legacyRef, node);\n },\n [store, legacyRef],\n );\n\n const typeaheadItemsRef = React.useRef<TypeaheadItem[]>([]);\n // Every item matching the typed characters, in search order (the first is the match).\n const typeaheadMatchesRef = React.useRef<readonly string[]>([]);\n const { onTypeahead, isSearching } = useTypeahead({\n getItems: () => typeaheadItemsRef.current,\n onMatch: (_value, matches) => {\n typeaheadMatchesRef.current = matches;\n },\n });\n\n /** Focuses an item; `true` only when focus actually moved there (then it is recorded). */\n const focusItem = (item: ResolvedItem | undefined): boolean => {\n if (!item || item.disabled) return false;\n const target = getFocusTarget(item);\n if (!target) return false;\n target.focus();\n // Browsers ignore focus() on an element CSS hides (display: none, visibility: hidden).\n if (!hasFocus(target)) return false;\n store.recordFocus(item);\n return true;\n };\n\n /** Focuses the first item of `candidates` that takes focus. */\n const focusFirstOf = (candidates: readonly ResolvedItem[]): void => {\n for (const item of candidates) {\n if (focusItem(item)) return;\n }\n };\n\n // `spaceOnly` (the capture phase) handles nothing but a Space that continues a typeahead search.\n const handleKeys = useEventCallback((e: React.KeyboardEvent, spaceOnly: boolean) => {\n // Ctrl, Alt and Meta combinations are shortcuts, except a single character typed with AltGr\n // (Ctrl+Alt on Windows), which is typeahead text: arrows, Home, End and Space with it stay\n // shortcuts.\n const shortcut = e.metaKey || ((e.altKey || e.ctrlKey) && !isAltGraphCharacter(e));\n if (e.defaultPrevented || shortcut) return;\n if (ownsArrowKeys(e.target)) return;\n const container = store.container ?? (e.currentTarget as HTMLElement);\n // A key from a portaled popup is the popup's: never pull focus back out of it.\n if (!isInContainer(container, e.target)) return;\n const ordered = store.resolve(container);\n const enabled = ordered.filter((item) => !item.disabled);\n if (enabled.length === 0) return;\n\n // The (innermost) item the key started in. None (the container itself has focus): next → first,\n // prev → last.\n const current = findOwningItem(ordered, e.target, container);\n\n let handled = false;\n // Where focus goes, in order of preference: an item whose focus() does not move focus (hidden\n // by CSS) passes the key on to the next one.\n let targets: ResolvedItem[] = [];\n const intent = spaceOnly\n ? null\n : getArrowIntent(e.key, {\n orientation,\n dir: dir ?? getDirection(e.currentTarget as Element),\n });\n if (intent) {\n handled = true;\n if (!current) {\n targets = intent === 'next' ? enabled : [...enabled].reverse();\n } else {\n const step = intent === 'next' ? 1 : -1;\n const count = ordered.length;\n let index = ordered.indexOf(current);\n for (let i = 1; i < count; i++) {\n index += step;\n if (index < 0 || index >= count) {\n if (!loop) break;\n index = (index + count) % count;\n }\n if (!ordered[index].disabled) targets.push(ordered[index]);\n }\n }\n } else if (!spaceOnly && homeEndKeys && (e.key === 'Home' || e.key === 'End')) {\n handled = true;\n targets = e.key === 'Home' ? enabled : [...enabled].reverse();\n } else if (typeahead && startsOnItem(current, e.target, container)) {\n typeaheadItemsRef.current = ordered.map((item) => ({\n value: item.value,\n text: textOf(item.element),\n disabled: item.disabled,\n }));\n typeaheadMatchesRef.current = [];\n if (onTypeahead(e, current?.value ?? null)) {\n handled = true;\n // The match first, then the next ones, for a match that does not take focus.\n targets = typeaheadMatchesRef.current.flatMap((value) =>\n enabled.filter((item) => item.value === value),\n );\n }\n }\n\n if (!handled) return;\n e.preventDefault();\n for (const next of targets) {\n // Back at the item the key started on (Home on the first item): focus stays.\n if (next === current) return;\n if (focusItem(next)) {\n onFocusMove?.(next.value, e);\n return;\n }\n }\n });\n\n const handleKeyDown = useEventCallback((e: React.KeyboardEvent) => handleKeys(e, false));\n\n // A Space that continues a typeahead search belongs to the search, not to the focused item: it is\n // handled and default-prevented in the capture phase, so the item's own keydown handler, which\n // skips default-prevented events, does not activate the item.\n const handleKeyDownCapture = useEventCallback((e: React.KeyboardEvent) => {\n if (typeahead && e.key === ' ' && isSearching()) handleKeys(e, true);\n });\n\n const handleFocus = useEventCallback((e: React.FocusEvent) => {\n const container = store.container ?? (e.currentTarget as HTMLElement);\n if (!isInContainer(container, e.target)) return;\n const item = findOwningItem(store.resolve(container), e.target, container);\n if (!item) return;\n store.recordFocus(item);\n setFocusedValue(item.value);\n if (canHoldTabStop(item)) setFocusedStop(item.value);\n store.stamp();\n });\n\n const focusValue = useEventCallback((value: string) => {\n focusItem(store.resolve().find((item) => item.value === value));\n });\n const focusFirst = useEventCallback(() => {\n focusFirstOf(store.resolve().filter((item) => !item.disabled));\n });\n const focusLast = useEventCallback(() => {\n focusFirstOf(\n store\n .resolve()\n .filter((item) => !item.disabled)\n .reverse(),\n );\n });\n\n const containerProps = React.useMemo<RovingContainerProps>(\n () => ({\n ref: containerRef,\n 'data-roving-container': '',\n onKeyDown: handleKeyDown,\n onKeyDownCapture: handleKeyDownCapture,\n onFocus: handleFocus,\n }),\n [containerRef, handleKeyDown, handleKeyDownCapture, handleFocus],\n );\n\n return {\n containerProps,\n handleKeyDown,\n handleKeyDownCapture,\n handleFocus,\n getTabIndex,\n focusedValue,\n focusValue,\n focusFirst,\n focusLast,\n };\n}\n"],"mappings":"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"}