@mortenbrudvik/waveui 0.10.0 → 0.12.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 (258) hide show
  1. package/README.md +454 -74
  2. package/dist/components/button/Link.cjs +1 -1
  3. package/dist/components/button/Link.cjs.map +1 -1
  4. package/dist/components/button/Link.mjs +4 -4
  5. package/dist/components/button/Toolbar.cjs +1 -1
  6. package/dist/components/button/Toolbar.cjs.map +1 -1
  7. package/dist/components/button/Toolbar.mjs +25 -30
  8. package/dist/components/button/Toolbar.mjs.map +1 -1
  9. package/dist/components/button/Toolbar.navigation.cjs +2 -0
  10. package/dist/components/button/Toolbar.navigation.cjs.map +1 -0
  11. package/dist/components/button/Toolbar.navigation.mjs +17 -0
  12. package/dist/components/button/Toolbar.navigation.mjs.map +1 -0
  13. package/dist/components/data-display/Avatar.cjs +1 -1
  14. package/dist/components/data-display/Avatar.cjs.map +1 -1
  15. package/dist/components/data-display/Avatar.context.cjs +2 -0
  16. package/dist/components/data-display/Avatar.context.cjs.map +1 -0
  17. package/dist/components/data-display/Avatar.context.mjs +9 -0
  18. package/dist/components/data-display/Avatar.context.mjs.map +1 -0
  19. package/dist/components/data-display/Avatar.mjs +66 -61
  20. package/dist/components/data-display/Avatar.mjs.map +1 -1
  21. package/dist/components/data-display/AvatarGroup.cjs +1 -1
  22. package/dist/components/data-display/AvatarGroup.cjs.map +1 -1
  23. package/dist/components/data-display/AvatarGroup.mjs +10 -10
  24. package/dist/components/data-display/InfoButton.cjs +1 -1
  25. package/dist/components/data-display/InfoButton.cjs.map +1 -1
  26. package/dist/components/data-display/InfoButton.mjs +12 -12
  27. package/dist/components/data-display/InteractionTag.cjs +2 -0
  28. package/dist/components/data-display/InteractionTag.cjs.map +1 -0
  29. package/dist/components/data-display/InteractionTag.mjs +234 -0
  30. package/dist/components/data-display/InteractionTag.mjs.map +1 -0
  31. package/dist/components/data-display/Tag.cjs +1 -1
  32. package/dist/components/data-display/Tag.cjs.map +1 -1
  33. package/dist/components/data-display/Tag.mjs +222 -90
  34. package/dist/components/data-display/Tag.mjs.map +1 -1
  35. package/dist/components/data-display/TagGroup.cjs +2 -0
  36. package/dist/components/data-display/TagGroup.cjs.map +1 -0
  37. package/dist/components/data-display/TagGroup.context.cjs +2 -0
  38. package/dist/components/data-display/TagGroup.context.cjs.map +1 -0
  39. package/dist/components/data-display/TagGroup.context.mjs +113 -0
  40. package/dist/components/data-display/TagGroup.context.mjs.map +1 -0
  41. package/dist/components/data-display/TagGroup.mjs +149 -0
  42. package/dist/components/data-display/TagGroup.mjs.map +1 -0
  43. package/dist/components/data-display/tagStyles.cjs +2 -0
  44. package/dist/components/data-display/tagStyles.cjs.map +1 -0
  45. package/dist/components/data-display/tagStyles.mjs +108 -0
  46. package/dist/components/data-display/tagStyles.mjs.map +1 -0
  47. package/dist/components/feedback/Toaster.cjs +1 -1
  48. package/dist/components/feedback/Toaster.cjs.map +1 -1
  49. package/dist/components/feedback/Toaster.mjs +287 -255
  50. package/dist/components/feedback/Toaster.mjs.map +1 -1
  51. package/dist/components/input/Combobox.cjs +1 -1
  52. package/dist/components/input/Combobox.cjs.map +1 -1
  53. package/dist/components/input/Combobox.expand.cjs +1 -1
  54. package/dist/components/input/Combobox.expand.cjs.map +1 -1
  55. package/dist/components/input/Combobox.expand.mjs +7 -7
  56. package/dist/components/input/Combobox.expand.mjs.map +1 -1
  57. package/dist/components/input/Combobox.mjs +176 -175
  58. package/dist/components/input/Combobox.mjs.map +1 -1
  59. package/dist/components/input/DatePicker.cjs +1 -1
  60. package/dist/components/input/DatePicker.cjs.map +1 -1
  61. package/dist/components/input/DatePicker.mjs +188 -187
  62. package/dist/components/input/DatePicker.mjs.map +1 -1
  63. package/dist/components/input/Dropdown.cjs +1 -1
  64. package/dist/components/input/Dropdown.cjs.map +1 -1
  65. package/dist/components/input/Dropdown.mjs +41 -40
  66. package/dist/components/input/Dropdown.mjs.map +1 -1
  67. package/dist/components/input/Option.cjs +1 -1
  68. package/dist/components/input/Option.cjs.map +1 -1
  69. package/dist/components/input/Option.mjs +101 -94
  70. package/dist/components/input/Option.mjs.map +1 -1
  71. package/dist/components/input/SwatchPicker.cjs.map +1 -1
  72. package/dist/components/input/SwatchPicker.context.cjs.map +1 -1
  73. package/dist/components/input/SwatchPicker.context.mjs.map +1 -1
  74. package/dist/components/input/SwatchPicker.mjs.map +1 -1
  75. package/dist/components/input/TagPicker.cjs +1 -1
  76. package/dist/components/input/TagPicker.cjs.map +1 -1
  77. package/dist/components/input/TagPicker.context.cjs +2 -0
  78. package/dist/components/input/TagPicker.context.cjs.map +1 -0
  79. package/dist/components/input/TagPicker.context.mjs +20 -0
  80. package/dist/components/input/TagPicker.context.mjs.map +1 -0
  81. package/dist/components/input/TagPicker.create.cjs +2 -0
  82. package/dist/components/input/TagPicker.create.cjs.map +1 -0
  83. package/dist/components/input/TagPicker.create.mjs +24 -0
  84. package/dist/components/input/TagPicker.create.mjs.map +1 -0
  85. package/dist/components/input/TagPicker.mjs +13 -279
  86. package/dist/components/input/TagPicker.mjs.map +1 -1
  87. package/dist/components/input/TagPicker.option.cjs +2 -0
  88. package/dist/components/input/TagPicker.option.cjs.map +1 -0
  89. package/dist/components/input/TagPicker.option.mjs +45 -0
  90. package/dist/components/input/TagPicker.option.mjs.map +1 -0
  91. package/dist/components/input/TagPicker.overflow.cjs +2 -0
  92. package/dist/components/input/TagPicker.overflow.cjs.map +1 -0
  93. package/dist/components/input/TagPicker.overflow.mjs +153 -0
  94. package/dist/components/input/TagPicker.overflow.mjs.map +1 -0
  95. package/dist/components/input/TagPicker.parts.cjs +2 -0
  96. package/dist/components/input/TagPicker.parts.cjs.map +1 -0
  97. package/dist/components/input/TagPicker.parts.mjs +425 -0
  98. package/dist/components/input/TagPicker.parts.mjs.map +1 -0
  99. package/dist/components/input/TagPicker.root.cjs +2 -0
  100. package/dist/components/input/TagPicker.root.cjs.map +1 -0
  101. package/dist/components/input/TagPicker.root.mjs +417 -0
  102. package/dist/components/input/TagPicker.root.mjs.map +1 -0
  103. package/dist/components/input/TimePicker.cjs +1 -1
  104. package/dist/components/input/TimePicker.cjs.map +1 -1
  105. package/dist/components/input/TimePicker.mjs +156 -154
  106. package/dist/components/input/TimePicker.mjs.map +1 -1
  107. package/dist/components/layout/Accordion.cjs.map +1 -1
  108. package/dist/components/layout/Accordion.mjs.map +1 -1
  109. package/dist/components/navigation/Hamburger.cjs +2 -0
  110. package/dist/components/navigation/Hamburger.cjs.map +1 -0
  111. package/dist/components/navigation/Hamburger.mjs +54 -0
  112. package/dist/components/navigation/Hamburger.mjs.map +1 -0
  113. package/dist/components/navigation/Menu.popover.cjs +1 -1
  114. package/dist/components/navigation/Menu.popover.cjs.map +1 -1
  115. package/dist/components/navigation/Menu.popover.mjs +137 -127
  116. package/dist/components/navigation/Menu.popover.mjs.map +1 -1
  117. package/dist/components/navigation/Menu.root.cjs +1 -1
  118. package/dist/components/navigation/Menu.root.cjs.map +1 -1
  119. package/dist/components/navigation/Menu.root.mjs +4 -4
  120. package/dist/components/navigation/Nav.cjs +1 -1
  121. package/dist/components/navigation/Nav.cjs.map +1 -1
  122. package/dist/components/navigation/Nav.context.cjs +2 -0
  123. package/dist/components/navigation/Nav.context.cjs.map +1 -0
  124. package/dist/components/navigation/Nav.context.mjs +137 -0
  125. package/dist/components/navigation/Nav.context.mjs.map +1 -0
  126. package/dist/components/navigation/Nav.mjs +151 -258
  127. package/dist/components/navigation/Nav.mjs.map +1 -1
  128. package/dist/components/navigation/Nav.parts.cjs +2 -0
  129. package/dist/components/navigation/Nav.parts.cjs.map +1 -0
  130. package/dist/components/navigation/Nav.parts.mjs +77 -0
  131. package/dist/components/navigation/Nav.parts.mjs.map +1 -0
  132. package/dist/components/navigation/Nav.root.cjs +2 -0
  133. package/dist/components/navigation/Nav.root.cjs.map +1 -0
  134. package/dist/components/navigation/Nav.root.mjs +78 -0
  135. package/dist/components/navigation/Nav.root.mjs.map +1 -0
  136. package/dist/components/navigation/NavDrawer.cjs +2 -0
  137. package/dist/components/navigation/NavDrawer.cjs.map +1 -0
  138. package/dist/components/navigation/NavDrawer.context.cjs +2 -0
  139. package/dist/components/navigation/NavDrawer.context.cjs.map +1 -0
  140. package/dist/components/navigation/NavDrawer.context.mjs +11 -0
  141. package/dist/components/navigation/NavDrawer.context.mjs.map +1 -0
  142. package/dist/components/navigation/NavDrawer.mjs +179 -0
  143. package/dist/components/navigation/NavDrawer.mjs.map +1 -0
  144. package/dist/components/overlays/Dialog.cjs +1 -1
  145. package/dist/components/overlays/Dialog.cjs.map +1 -1
  146. package/dist/components/overlays/Dialog.mjs +188 -123
  147. package/dist/components/overlays/Dialog.mjs.map +1 -1
  148. package/dist/components/overlays/Dialog.shared.cjs +1 -1
  149. package/dist/components/overlays/Dialog.shared.cjs.map +1 -1
  150. package/dist/components/overlays/Dialog.shared.mjs +157 -81
  151. package/dist/components/overlays/Dialog.shared.mjs.map +1 -1
  152. package/dist/components/overlays/Dialog.slots.cjs +2 -0
  153. package/dist/components/overlays/Dialog.slots.cjs.map +1 -0
  154. package/dist/components/overlays/Dialog.slots.mjs +129 -0
  155. package/dist/components/overlays/Dialog.slots.mjs.map +1 -0
  156. package/dist/components/overlays/Drawer.cjs +1 -1
  157. package/dist/components/overlays/Drawer.cjs.map +1 -1
  158. package/dist/components/overlays/Drawer.context.cjs +2 -0
  159. package/dist/components/overlays/Drawer.context.cjs.map +1 -0
  160. package/dist/components/overlays/Drawer.context.mjs +29 -0
  161. package/dist/components/overlays/Drawer.context.mjs.map +1 -0
  162. package/dist/components/overlays/Drawer.mjs +12 -187
  163. package/dist/components/overlays/Drawer.mjs.map +1 -1
  164. package/dist/components/overlays/Drawer.panel.cjs +2 -0
  165. package/dist/components/overlays/Drawer.panel.cjs.map +1 -0
  166. package/dist/components/overlays/Drawer.panel.mjs +159 -0
  167. package/dist/components/overlays/Drawer.panel.mjs.map +1 -0
  168. package/dist/components/overlays/Drawer.parts.cjs +2 -0
  169. package/dist/components/overlays/Drawer.parts.cjs.map +1 -0
  170. package/dist/components/overlays/Drawer.parts.mjs +152 -0
  171. package/dist/components/overlays/Drawer.parts.mjs.map +1 -0
  172. package/dist/components/overlays/Drawer.root.cjs +2 -0
  173. package/dist/components/overlays/Drawer.root.cjs.map +1 -0
  174. package/dist/components/overlays/Drawer.root.mjs +207 -0
  175. package/dist/components/overlays/Drawer.root.mjs.map +1 -0
  176. package/dist/components/overlays/Drawer.scroll.cjs +2 -0
  177. package/dist/components/overlays/Drawer.scroll.cjs.map +1 -0
  178. package/dist/components/overlays/Drawer.scroll.mjs +42 -0
  179. package/dist/components/overlays/Drawer.scroll.mjs.map +1 -0
  180. package/dist/components/overlays/Popover.cjs +1 -1
  181. package/dist/components/overlays/Popover.cjs.map +1 -1
  182. package/dist/components/overlays/Popover.mjs +173 -162
  183. package/dist/components/overlays/Popover.mjs.map +1 -1
  184. package/dist/components/overlays/TeachingPopover.cjs +1 -1
  185. package/dist/components/overlays/TeachingPopover.cjs.map +1 -1
  186. package/dist/components/overlays/TeachingPopover.mjs +96 -91
  187. package/dist/components/overlays/TeachingPopover.mjs.map +1 -1
  188. package/dist/components/overlays/Tooltip.cjs +1 -1
  189. package/dist/components/overlays/Tooltip.cjs.map +1 -1
  190. package/dist/components/overlays/Tooltip.mjs +47 -47
  191. package/dist/components/overlays/Tooltip.mjs.map +1 -1
  192. package/dist/components/portal/Portal.cjs +1 -1
  193. package/dist/components/portal/Portal.cjs.map +1 -1
  194. package/dist/components/portal/Portal.mjs +5 -5
  195. package/dist/components/table/DataGrid.cjs +1 -1
  196. package/dist/components/table/DataGrid.cjs.map +1 -1
  197. package/dist/components/table/DataGrid.mjs +1 -1
  198. package/dist/hooks/useDismiss.cjs.map +1 -1
  199. package/dist/hooks/useDismiss.mjs.map +1 -1
  200. package/dist/hooks/useFocusTrap.cjs +1 -1
  201. package/dist/hooks/useFocusTrap.cjs.map +1 -1
  202. package/dist/hooks/useFocusTrap.mjs +115 -98
  203. package/dist/hooks/useFocusTrap.mjs.map +1 -1
  204. package/dist/hooks/useFocusVisible.cjs +2 -0
  205. package/dist/hooks/useFocusVisible.cjs.map +1 -0
  206. package/dist/hooks/useFocusVisible.mjs +49 -0
  207. package/dist/hooks/useFocusVisible.mjs.map +1 -0
  208. package/dist/hooks/useFocusableGroup.cjs +2 -0
  209. package/dist/hooks/useFocusableGroup.cjs.map +1 -0
  210. package/dist/hooks/useFocusableGroup.mjs +70 -0
  211. package/dist/hooks/useFocusableGroup.mjs.map +1 -0
  212. package/dist/hooks/useModalLayer.cjs +1 -1
  213. package/dist/hooks/useModalLayer.cjs.map +1 -1
  214. package/dist/hooks/useModalLayer.mjs +34 -21
  215. package/dist/hooks/useModalLayer.mjs.map +1 -1
  216. package/dist/hooks/useModalOpen.cjs +2 -0
  217. package/dist/hooks/useModalOpen.cjs.map +1 -0
  218. package/dist/hooks/useModalOpen.mjs +14 -0
  219. package/dist/hooks/useModalOpen.mjs.map +1 -0
  220. package/dist/hooks/usePopupPosition.cjs +1 -1
  221. package/dist/hooks/usePopupPosition.cjs.map +1 -1
  222. package/dist/hooks/usePopupPosition.mjs +82 -53
  223. package/dist/hooks/usePopupPosition.mjs.map +1 -1
  224. package/dist/hooks/useResolvedTarget.cjs +2 -0
  225. package/dist/hooks/useResolvedTarget.cjs.map +1 -0
  226. package/dist/hooks/useResolvedTarget.mjs +37 -0
  227. package/dist/hooks/useResolvedTarget.mjs.map +1 -0
  228. package/dist/hooks/useRestoreFocus.cjs +1 -1
  229. package/dist/hooks/useRestoreFocus.cjs.map +1 -1
  230. package/dist/hooks/useRestoreFocus.mjs +91 -78
  231. package/dist/hooks/useRestoreFocus.mjs.map +1 -1
  232. package/dist/hooks/useRovingGrid.cjs +1 -1
  233. package/dist/hooks/useRovingGrid.cjs.map +1 -1
  234. package/dist/hooks/useRovingGrid.mjs +8 -57
  235. package/dist/hooks/useRovingGrid.mjs.map +1 -1
  236. package/dist/hooks/useRovingTabIndex.cjs +1 -1
  237. package/dist/hooks/useRovingTabIndex.cjs.map +1 -1
  238. package/dist/hooks/useRovingTabIndex.mjs +180 -121
  239. package/dist/hooks/useRovingTabIndex.mjs.map +1 -1
  240. package/dist/index.cjs +1 -1
  241. package/dist/index.d.cts +3070 -269
  242. package/dist/index.d.ts +3070 -269
  243. package/dist/index.mjs +127 -107
  244. package/dist/lib/classNames.cjs +1 -1
  245. package/dist/lib/classNames.cjs.map +1 -1
  246. package/dist/lib/classNames.mjs +137 -77
  247. package/dist/lib/classNames.mjs.map +1 -1
  248. package/dist/lib/icons.cjs +1 -1
  249. package/dist/lib/icons.cjs.map +1 -1
  250. package/dist/lib/icons.mjs +13 -6
  251. package/dist/lib/icons.mjs.map +1 -1
  252. package/dist/lib/layers.cjs +1 -1
  253. package/dist/lib/layers.cjs.map +1 -1
  254. package/dist/lib/layers.mjs +120 -111
  255. package/dist/lib/layers.mjs.map +1 -1
  256. package/dist/styles.css +1 -1
  257. package/package.json +1 -1
  258. 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("./useEventCallback.cjs"),n=require("./useRovingTabIndex.cjs");let r=require("react");r=e.__toESM(r,1);var i=`[data-roving-value]`,a=`[role="row"]`;function o(e,t){if(e.disabled===!0)return!1;try{if(e.matches(`:disabled`))return!1}catch{}let n=e.getAttribute(`data-roving-disabled`);if(n!==null&&n!==`false`||!e.hasAttribute(`data-disabled-focusable`)&&e.getAttribute(`aria-disabled`)===`true`)return!1;for(let n=e;n&&n!==t;n=n.parentElement)if(n.hidden||n.hasAttribute(`inert`))return!1;return!0}function s({activeValue:e}){let s=n.useRovingTabIndex({activeValue:e,orientation:`horizontal`,loop:!1,homeEndKeys:!1,tabStop:`last-focused`}),{focusValue:c}=s,l=s.containerProps.onKeyDown,u=t.useEventCallback(e=>{let t=e.currentTarget,n=e.target;if(!n||typeof n.nodeType!=`number`||!t.contains(n))return;let{key:r}=e,s=r===`Home`||r===`End`;if(!(s?!e.metaKey&&!e.altKey:(r===`ArrowUp`||r===`ArrowDown`)&&!e.metaKey&&!e.altKey&&!e.ctrlKey)||e.defaultPrevented){l(e);return}let u=n.closest(i),d=u?.closest(a);if(!u||!d||!t.contains(d))return;let f=Array.from(t.querySelectorAll(a)),p=e=>Array.from(e.querySelectorAll(i)).filter(e=>o(e,t)),m=t=>{e.preventDefault();let n=t?.getAttribute(`data-roving-value`);n!=null&&t!==u&&c(n)};if(s){let t=e.ctrlKey?f.flatMap(p):p(d);m(r===`Home`?t[0]:t[t.length-1]);return}let h=Array.from(d.querySelectorAll(i)).indexOf(u),g=r===`ArrowDown`?1:-1,_=f.indexOf(d);for(let e=_+g;e>=0&&e<f.length;e+=g){let n=Array.from(f[e].querySelectorAll(i)),r,a=1/0;if(n.forEach((e,n)=>{if(!o(e,t))return;let i=Math.abs(n-h);i<a&&(r=e,a=i)}),r){m(r);return}}e.preventDefault()});return{containerProps:r.useMemo(()=>({...s.containerProps,onKeyDown:u}),[s.containerProps,u]),getTabIndex:s.getTabIndex}}exports.useRovingGrid=s;
1
+ "use client";const e=require("./useRovingTabIndex.cjs");function t({activeValue:t}){let{containerProps:n,getTabIndex:r}=e.useRovingTabIndex({activeValue:t,orientation:`grid`,loop:!1,homeEndKeys:!1,tabStop:`last-focused`});return{containerProps:n,getTabIndex:r}}exports.useRovingGrid=t;
2
2
  //# sourceMappingURL=useRovingGrid.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useRovingGrid.cjs","names":["useRovingTabIndex","useEventCallback","React","require_runtime","require_useEventCallback","require_useRovingTabIndex","react","CELL_SELECTOR","ROW_SELECTOR","isEnabledCell","el","marker","node","container","useRovingGrid","roving","baseKeyDown","onKeyDown","event","target","isHomeEnd","key","cell","row","rows","cellsOf","r","goTo","value","focusValue","scope","column","delta","rowIndex","all","best","bestDistance","distance","index"],"sources":["../../src/hooks/useRovingGrid.ts"],"sourcesContent":["import * as React from 'react';\nimport { useEventCallback } from './useEventCallback';\nimport { useRovingTabIndex, type RovingContainerProps } from './useRovingTabIndex';\n\n/** Options of {@link useRovingGrid}. */\nexport interface UseRovingGridOptions {\n /** The selected cell value (the first tab stop), or `null`. */\n activeValue: string | null;\n}\n\n/** Result of {@link useRovingGrid}. */\nexport interface UseRovingGridResult {\n /** Spread onto the grid container; `onKeyDown` already adds the grid keys. */\n containerProps: RovingContainerProps;\n /** `0` for the cell that holds the tab stop (the last focused one, else the selected one, else the first enabled), `-1` for every other cell. */\n getTabIndex: (value: string) => 0 | -1;\n}\n\nconst CELL_SELECTOR = '[data-roving-value]';\nconst ROW_SELECTOR = '[role=\"row\"]';\n\n/** Whether the grid keys may land on `el`: not disabled, not hidden, not inert. */\nfunction isEnabledCell(el: HTMLElement, container: HTMLElement): boolean {\n if ((el as { disabled?: unknown }).disabled === true) return false;\n try {\n if (el.matches(':disabled')) return false; // inside a disabled fieldset\n } catch {\n // A browser without the selector: the other checks apply.\n }\n const marker = el.getAttribute('data-roving-disabled');\n if (marker !== null && marker !== 'false') return false;\n if (!el.hasAttribute('data-disabled-focusable') && el.getAttribute('aria-disabled') === 'true') {\n return false;\n }\n for (let node: HTMLElement | null = el; node && node !== container; node = node.parentElement) {\n if (node.hidden || node.hasAttribute('inert')) return false;\n }\n return true;\n}\n\n/**\n * Internal. Roving focus for a two-dimensional grid of cells (SwatchPicker's grid layout): one\n * `useRovingTabIndex` on the container plus the keys a grid adds. Rows are the `role=\"row\"`\n * elements inside the container and cells their `[data-roving-value]` elements. A cell outside a\n * row gets only the base hook's keys (Left/Right).\n *\n * - Left/Right move through the cells in reading order across rows (mirrored in RTL) and stop at\n * the grid's first and last cell.\n * - Up/Down go to the cell at the same index in the previous/next row, else to the nearest enabled\n * cell of that row (by index; the earlier one on a tie), skipping rows without an enabled cell;\n * they stop at the first/last row.\n * - Home/End go to the first/last enabled cell of the row, Ctrl+Home/End of the grid.\n * - Disabled cells are skipped; Space and Enter are the cell's own. Keys that start outside the\n * container's DOM (a portaled popup) are ignored.\n *\n * @param options - See {@link UseRovingGridOptions}.\n * @returns See {@link UseRovingGridResult}.\n */\nexport function useRovingGrid({ activeValue }: UseRovingGridOptions): UseRovingGridResult {\n const roving = useRovingTabIndex({\n activeValue,\n orientation: 'horizontal',\n loop: false,\n homeEndKeys: false,\n tabStop: 'last-focused',\n });\n const { focusValue } = roving;\n const baseKeyDown = roving.containerProps.onKeyDown;\n\n const onKeyDown = useEventCallback((event: React.KeyboardEvent<HTMLElement>) => {\n const container = event.currentTarget;\n const target = event.target as Node | null;\n // A key from a portaled popup is the popup's: React bubbles it through the container.\n if (!target || typeof target.nodeType !== 'number' || !container.contains(target)) return;\n\n const { key } = event;\n const isHomeEnd = key === 'Home' || key === 'End';\n const isVertical = key === 'ArrowUp' || key === 'ArrowDown';\n const grid = isHomeEnd\n ? !event.metaKey && !event.altKey\n : isVertical && !event.metaKey && !event.altKey && !event.ctrlKey;\n if (!grid || event.defaultPrevented) {\n baseKeyDown(event);\n return;\n }\n\n const cell = (target as Element).closest<HTMLElement>(CELL_SELECTOR);\n const row = cell?.closest<HTMLElement>(ROW_SELECTOR);\n if (!cell || !row || !container.contains(row)) return;\n\n const rows = Array.from(container.querySelectorAll<HTMLElement>(ROW_SELECTOR));\n const cellsOf = (r: HTMLElement) =>\n Array.from(r.querySelectorAll<HTMLElement>(CELL_SELECTOR)).filter((el) =>\n isEnabledCell(el, container),\n );\n const goTo = (el: HTMLElement | undefined) => {\n event.preventDefault();\n const value = el?.getAttribute('data-roving-value');\n if (value != null && el !== cell) focusValue(value);\n };\n\n if (isHomeEnd) {\n const scope = event.ctrlKey ? rows.flatMap(cellsOf) : cellsOf(row);\n goTo(key === 'Home' ? scope[0] : scope[scope.length - 1]);\n return;\n }\n\n // Column distance by index in the row, counting every cell (disabled ones included).\n const column = Array.from(row.querySelectorAll<HTMLElement>(CELL_SELECTOR)).indexOf(cell);\n const delta = key === 'ArrowDown' ? 1 : -1;\n const rowIndex = rows.indexOf(row);\n for (let r = rowIndex + delta; r >= 0 && r < rows.length; r += delta) {\n const all = Array.from(rows[r].querySelectorAll<HTMLElement>(CELL_SELECTOR));\n let best: HTMLElement | undefined;\n let bestDistance = Infinity;\n all.forEach((el, index) => {\n if (!isEnabledCell(el, container)) return;\n const distance = Math.abs(index - column);\n if (distance < bestDistance) {\n best = el;\n bestDistance = distance;\n }\n });\n if (best) {\n goTo(best);\n return;\n }\n }\n // Last (or first) row: the key is the grid's, focus stays.\n event.preventDefault();\n });\n\n const containerProps = React.useMemo<RovingContainerProps>(\n () => ({ ...roving.containerProps, onKeyDown }),\n [roving.containerProps, onKeyDown],\n );\n\n return { containerProps, getTabIndex: roving.getTabIndex };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useRovingGrid.cjs","names":["useRovingTabIndex","require_useRovingTabIndex","useRovingGrid"],"sources":["../../src/hooks/useRovingGrid.ts"],"sourcesContent":["import { useRovingTabIndex, type RovingContainerProps } from './useRovingTabIndex';\n\n/** Options of {@link useRovingGrid}. */\nexport interface UseRovingGridOptions {\n /** The selected cell value (the first tab stop), or `null`. */\n activeValue: string | null;\n}\n\n/** Result of {@link useRovingGrid}. */\nexport interface UseRovingGridResult {\n /** Spread onto the grid container; `onKeyDown` already adds the grid keys. */\n containerProps: RovingContainerProps;\n /** `0` for the cell that holds the tab stop (the last focused one, else the selected one, else the first enabled), `-1` for every other cell. */\n getTabIndex: (value: string) => 0 | -1;\n}\n\n/**\n * Internal. Roving focus for a two-dimensional grid of cells (SwatchPicker's grid layout):\n * `useRovingTabIndex` with `orientation: 'grid'`, which holds the grid keys. Rows are the\n * `role=\"row\"` elements inside the container and cells their `[data-roving-value]` elements; see\n * the hook's `orientation` option for the keys. The tab stop is the last focused cell, else the\n * selected one, else the first enabled.\n *\n * @param options - See {@link UseRovingGridOptions}.\n * @returns See {@link UseRovingGridResult}.\n */\nexport function useRovingGrid({ activeValue }: UseRovingGridOptions): UseRovingGridResult {\n const { containerProps, getTabIndex } = useRovingTabIndex({\n activeValue,\n orientation: 'grid',\n loop: false,\n homeEndKeys: false,\n tabStop: 'last-focused',\n });\n return { containerProps, getTabIndex };\n}\n"],"mappings":"wDA0BA,SAAgBE,EAAc,CAAE,eAA0D,CACxF,GAAM,CAAE,iBAAgB,eAAgBF,EAAAA,kBAAkB,CACxD,cACA,YAAa,OACb,KAAM,GACN,YAAa,GACb,QAAS,cACX,CAAC,EACD,MAAO,CAAE,iBAAgB,aAAY,CACvC"}
@@ -1,69 +1,20 @@
1
1
  "use client";
2
- import { useEventCallback as e } from "./useEventCallback.mjs";
3
- import { useRovingTabIndex as t } from "./useRovingTabIndex.mjs";
4
- import * as n from "react";
2
+ import { useRovingTabIndex as e } from "./useRovingTabIndex.mjs";
5
3
  //#region src/hooks/useRovingGrid.ts
6
- var r = "[data-roving-value]", i = "[role=\"row\"]";
7
- function a(e, t) {
8
- if (e.disabled === !0) return !1;
9
- try {
10
- if (e.matches(":disabled")) return !1;
11
- } catch {}
12
- let n = e.getAttribute("data-roving-disabled");
13
- if (n !== null && n !== "false" || !e.hasAttribute("data-disabled-focusable") && e.getAttribute("aria-disabled") === "true") return !1;
14
- for (let n = e; n && n !== t; n = n.parentElement) if (n.hidden || n.hasAttribute("inert")) return !1;
15
- return !0;
16
- }
17
- function o({ activeValue: o }) {
18
- let s = t({
19
- activeValue: o,
20
- orientation: "horizontal",
4
+ function t({ activeValue: t }) {
5
+ let { containerProps: n, getTabIndex: r } = e({
6
+ activeValue: t,
7
+ orientation: "grid",
21
8
  loop: !1,
22
9
  homeEndKeys: !1,
23
10
  tabStop: "last-focused"
24
- }), { focusValue: c } = s, l = s.containerProps.onKeyDown, u = e((e) => {
25
- let t = e.currentTarget, n = e.target;
26
- if (!n || typeof n.nodeType != "number" || !t.contains(n)) return;
27
- let { key: o } = e, s = o === "Home" || o === "End";
28
- if (!(s ? !e.metaKey && !e.altKey : (o === "ArrowUp" || o === "ArrowDown") && !e.metaKey && !e.altKey && !e.ctrlKey) || e.defaultPrevented) {
29
- l(e);
30
- return;
31
- }
32
- let u = n.closest(r), d = u?.closest(i);
33
- if (!u || !d || !t.contains(d)) return;
34
- let f = Array.from(t.querySelectorAll(i)), p = (e) => Array.from(e.querySelectorAll(r)).filter((e) => a(e, t)), m = (t) => {
35
- e.preventDefault();
36
- let n = t?.getAttribute("data-roving-value");
37
- n != null && t !== u && c(n);
38
- };
39
- if (s) {
40
- let t = e.ctrlKey ? f.flatMap(p) : p(d);
41
- m(o === "Home" ? t[0] : t[t.length - 1]);
42
- return;
43
- }
44
- let h = Array.from(d.querySelectorAll(r)).indexOf(u), g = o === "ArrowDown" ? 1 : -1, _ = f.indexOf(d);
45
- for (let e = _ + g; e >= 0 && e < f.length; e += g) {
46
- let n = Array.from(f[e].querySelectorAll(r)), i, o = Infinity;
47
- if (n.forEach((e, n) => {
48
- if (!a(e, t)) return;
49
- let r = Math.abs(n - h);
50
- r < o && (i = e, o = r);
51
- }), i) {
52
- m(i);
53
- return;
54
- }
55
- }
56
- e.preventDefault();
57
11
  });
58
12
  return {
59
- containerProps: n.useMemo(() => ({
60
- ...s.containerProps,
61
- onKeyDown: u
62
- }), [s.containerProps, u]),
63
- getTabIndex: s.getTabIndex
13
+ containerProps: n,
14
+ getTabIndex: r
64
15
  };
65
16
  }
66
17
  //#endregion
67
- export { o as useRovingGrid };
18
+ export { t as useRovingGrid };
68
19
 
69
20
  //# sourceMappingURL=useRovingGrid.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useRovingGrid.mjs","names":["React","CELL_SELECTOR","ROW_SELECTOR","isEnabledCell","el","marker","node","container","useRovingGrid","roving","useRovingTabIndex","baseKeyDown","onKeyDown","useEventCallback","event","target","isHomeEnd","key","cell","row","rows","cellsOf","r","goTo","value","focusValue","scope","column","delta","rowIndex","all","best","bestDistance","distance","index"],"sources":["../../src/hooks/useRovingGrid.ts"],"sourcesContent":["import * as React from 'react';\nimport { useEventCallback } from './useEventCallback';\nimport { useRovingTabIndex, type RovingContainerProps } from './useRovingTabIndex';\n\n/** Options of {@link useRovingGrid}. */\nexport interface UseRovingGridOptions {\n /** The selected cell value (the first tab stop), or `null`. */\n activeValue: string | null;\n}\n\n/** Result of {@link useRovingGrid}. */\nexport interface UseRovingGridResult {\n /** Spread onto the grid container; `onKeyDown` already adds the grid keys. */\n containerProps: RovingContainerProps;\n /** `0` for the cell that holds the tab stop (the last focused one, else the selected one, else the first enabled), `-1` for every other cell. */\n getTabIndex: (value: string) => 0 | -1;\n}\n\nconst CELL_SELECTOR = '[data-roving-value]';\nconst ROW_SELECTOR = '[role=\"row\"]';\n\n/** Whether the grid keys may land on `el`: not disabled, not hidden, not inert. */\nfunction isEnabledCell(el: HTMLElement, container: HTMLElement): boolean {\n if ((el as { disabled?: unknown }).disabled === true) return false;\n try {\n if (el.matches(':disabled')) return false; // inside a disabled fieldset\n } catch {\n // A browser without the selector: the other checks apply.\n }\n const marker = el.getAttribute('data-roving-disabled');\n if (marker !== null && marker !== 'false') return false;\n if (!el.hasAttribute('data-disabled-focusable') && el.getAttribute('aria-disabled') === 'true') {\n return false;\n }\n for (let node: HTMLElement | null = el; node && node !== container; node = node.parentElement) {\n if (node.hidden || node.hasAttribute('inert')) return false;\n }\n return true;\n}\n\n/**\n * Internal. Roving focus for a two-dimensional grid of cells (SwatchPicker's grid layout): one\n * `useRovingTabIndex` on the container plus the keys a grid adds. Rows are the `role=\"row\"`\n * elements inside the container and cells their `[data-roving-value]` elements. A cell outside a\n * row gets only the base hook's keys (Left/Right).\n *\n * - Left/Right move through the cells in reading order across rows (mirrored in RTL) and stop at\n * the grid's first and last cell.\n * - Up/Down go to the cell at the same index in the previous/next row, else to the nearest enabled\n * cell of that row (by index; the earlier one on a tie), skipping rows without an enabled cell;\n * they stop at the first/last row.\n * - Home/End go to the first/last enabled cell of the row, Ctrl+Home/End of the grid.\n * - Disabled cells are skipped; Space and Enter are the cell's own. Keys that start outside the\n * container's DOM (a portaled popup) are ignored.\n *\n * @param options - See {@link UseRovingGridOptions}.\n * @returns See {@link UseRovingGridResult}.\n */\nexport function useRovingGrid({ activeValue }: UseRovingGridOptions): UseRovingGridResult {\n const roving = useRovingTabIndex({\n activeValue,\n orientation: 'horizontal',\n loop: false,\n homeEndKeys: false,\n tabStop: 'last-focused',\n });\n const { focusValue } = roving;\n const baseKeyDown = roving.containerProps.onKeyDown;\n\n const onKeyDown = useEventCallback((event: React.KeyboardEvent<HTMLElement>) => {\n const container = event.currentTarget;\n const target = event.target as Node | null;\n // A key from a portaled popup is the popup's: React bubbles it through the container.\n if (!target || typeof target.nodeType !== 'number' || !container.contains(target)) return;\n\n const { key } = event;\n const isHomeEnd = key === 'Home' || key === 'End';\n const isVertical = key === 'ArrowUp' || key === 'ArrowDown';\n const grid = isHomeEnd\n ? !event.metaKey && !event.altKey\n : isVertical && !event.metaKey && !event.altKey && !event.ctrlKey;\n if (!grid || event.defaultPrevented) {\n baseKeyDown(event);\n return;\n }\n\n const cell = (target as Element).closest<HTMLElement>(CELL_SELECTOR);\n const row = cell?.closest<HTMLElement>(ROW_SELECTOR);\n if (!cell || !row || !container.contains(row)) return;\n\n const rows = Array.from(container.querySelectorAll<HTMLElement>(ROW_SELECTOR));\n const cellsOf = (r: HTMLElement) =>\n Array.from(r.querySelectorAll<HTMLElement>(CELL_SELECTOR)).filter((el) =>\n isEnabledCell(el, container),\n );\n const goTo = (el: HTMLElement | undefined) => {\n event.preventDefault();\n const value = el?.getAttribute('data-roving-value');\n if (value != null && el !== cell) focusValue(value);\n };\n\n if (isHomeEnd) {\n const scope = event.ctrlKey ? rows.flatMap(cellsOf) : cellsOf(row);\n goTo(key === 'Home' ? scope[0] : scope[scope.length - 1]);\n return;\n }\n\n // Column distance by index in the row, counting every cell (disabled ones included).\n const column = Array.from(row.querySelectorAll<HTMLElement>(CELL_SELECTOR)).indexOf(cell);\n const delta = key === 'ArrowDown' ? 1 : -1;\n const rowIndex = rows.indexOf(row);\n for (let r = rowIndex + delta; r >= 0 && r < rows.length; r += delta) {\n const all = Array.from(rows[r].querySelectorAll<HTMLElement>(CELL_SELECTOR));\n let best: HTMLElement | undefined;\n let bestDistance = Infinity;\n all.forEach((el, index) => {\n if (!isEnabledCell(el, container)) return;\n const distance = Math.abs(index - column);\n if (distance < bestDistance) {\n best = el;\n bestDistance = distance;\n }\n });\n if (best) {\n goTo(best);\n return;\n }\n }\n // Last (or first) row: the key is the grid's, focus stays.\n event.preventDefault();\n });\n\n const containerProps = React.useMemo<RovingContainerProps>(\n () => ({ ...roving.containerProps, onKeyDown }),\n [roving.containerProps, onKeyDown],\n );\n\n return { containerProps, getTabIndex: roving.getTabIndex };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useRovingGrid.mjs","names":["useRovingGrid","useRovingTabIndex"],"sources":["../../src/hooks/useRovingGrid.ts"],"sourcesContent":["import { useRovingTabIndex, type RovingContainerProps } from './useRovingTabIndex';\n\n/** Options of {@link useRovingGrid}. */\nexport interface UseRovingGridOptions {\n /** The selected cell value (the first tab stop), or `null`. */\n activeValue: string | null;\n}\n\n/** Result of {@link useRovingGrid}. */\nexport interface UseRovingGridResult {\n /** Spread onto the grid container; `onKeyDown` already adds the grid keys. */\n containerProps: RovingContainerProps;\n /** `0` for the cell that holds the tab stop (the last focused one, else the selected one, else the first enabled), `-1` for every other cell. */\n getTabIndex: (value: string) => 0 | -1;\n}\n\n/**\n * Internal. Roving focus for a two-dimensional grid of cells (SwatchPicker's grid layout):\n * `useRovingTabIndex` with `orientation: 'grid'`, which holds the grid keys. Rows are the\n * `role=\"row\"` elements inside the container and cells their `[data-roving-value]` elements; see\n * the hook's `orientation` option for the keys. The tab stop is the last focused cell, else the\n * selected one, else the first enabled.\n *\n * @param options - See {@link UseRovingGridOptions}.\n * @returns See {@link UseRovingGridResult}.\n */\nexport function useRovingGrid({ activeValue }: UseRovingGridOptions): UseRovingGridResult {\n const { containerProps, getTabIndex } = useRovingTabIndex({\n activeValue,\n orientation: 'grid',\n loop: false,\n homeEndKeys: false,\n tabStop: 'last-focused',\n });\n return { containerProps, getTabIndex };\n}\n"],"mappings":";;;AA0BA,SAAgBA,EAAc,EAAE,kBAA0D;CACxF,IAAM,EAAE,mBAAgB,mBAAgBC,EAAkB;EACxD;EACA,aAAa;EACb,MAAM;EACN,aAAa;EACb,SAAS;CACX,CAAC;CACD,OAAO;EAAE;EAAgB;CAAY;AACvC"}
@@ -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);function c(e,t,n){let r=t.querySelectorAll(`[role="row"]`);if(r.length>0)return Array.from(r).map(t=>e.filter(e=>t.contains(e.element)));if(n!==void 0&&n>0){let t=[];for(let r=0;r<e.length;r+=n)t.push(e.slice(r,r+n));return t}return null}function l(e,t,n){let r=e.key===`ArrowUp`||e.key===`ArrowDown`,i=e.key===`Home`||e.key===`End`;if(!n||!r&&!i)return;let a=t.findIndex(e=>e.includes(n));if(a<0)return;if(i){let r=(e.ctrlKey?t.flat():t[a]).filter(e=>!e.disabled),i=e.key===`Home`?r[0]:r[r.length-1];return i&&i!==n?[i]:`stay`}let o=t[a].indexOf(n),s=e.key===`ArrowDown`?1:-1;for(let e=a+s;e>=0&&e<t.length;e+=s){let n,r=1/0;if(t[e].forEach((e,t)=>{if(e.disabled)return;let i=Math.abs(t-o);i<r&&(n=e,r=i)}),n)return[n]}return`stay`}var u=`[data-roving-value]`,d=new Set([`radiogroup`,`listbox`,`grid`,`treegrid`,`tablist`,`menu`,`menubar`,`tree`,`spinbutton`]),f=new Set([`slider`,`spinbutton`,`textbox`,`searchbox`]),p=new Set([`button`,`checkbox`,`color`,`file`,`hidden`,`image`,`radio`,`reset`,`submit`]),m=[`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 h(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&&d.has(n)}function g(e,t,n){for(let r=e;r&&r!==t;r=r.parentElement)if(h(r,n))return!0;return!1}var _=`[tabindex], [data-roving-value]`;function v(e,t){try{return e.matches(t)}catch{return!1}}function y(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 b(e){return e.tabIndex>=0&&!e.hasAttribute(`disabled`)}function x(e){let{element:t}=e;if(!e.nested||b(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 S(e){let t=e.getAttribute(`data-roving-disabled`);return t!==null&&t!==`false`||C(e)?!0:!e.hasAttribute(`data-disabled-focusable`)&&e.getAttribute(`aria-disabled`)===`true`}function C(e){if(e.disabled===!0)return!0;try{return e.matches(`:disabled`)}catch{return!1}}function w(e){if(!e||e.nodeType!==1)return!1;let t=e,n=t.getAttribute(`role`);if(n!==null&&f.has(n))return!0;let r=t.localName;if(r===`textarea`||r===`select`)return!0;if(r===`input`)return!p.has(t.type);let i=t.closest(`[contenteditable]`);return i!==null&&i.getAttribute(`contenteditable`)!==`false`}function T(e){return!e.nested&&!w(e.element)}function E(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 ee(e){let t=e.getRootNode().activeElement;return!!t&&e.contains(t)}function te(e){try{return document.createDocumentFragment().querySelector(e),!0}catch{return!1}}function D(e){return[e.value,e.disabled?0:T(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:u,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){T(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)h(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:S(u)};d&&!f.disabled&&!x(f)&&(f.disabled=!0),n&&!f.disabled&&E(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=y(n),{itemSelector:i,roleComposites:a}=this.options;for(let n of Array.from(e.querySelectorAll(_))){let o=this.stamped.get(n);if(!o||t===`stale`&&v(n,i)&&!g(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 y(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:m}),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===x(e):t===n}function I(e,a){let d=a===void 0?void 0:e,{activeValue:f=null,items:p,orientation:m=`horizontal`,columns:h,loop:g=!0,dir:_,itemSelector:v=u,typeahead:y=!1,homeEndKeys:b=!0,tabStop:S=`active`,manageTabIndex:C=!1,onFocusMove:E}=a??e,[D]=s.useState(()=>new A),k=s.useSyncExternalStore(D.subscribe,D.getSnapshot,D.getServerSnapshot),[I,L]=s.useState(null),[R,z]=s.useState(null),B=d===void 0&&p===void 0;s.useLayoutEffect(()=>{D.setOptions({itemSelector:v,manageTabIndex:C,items:p,activeValue:f,tabStop:S,roleComposites:B}),d&&D.setContainer(d.current),D.stamp()}),s.useEffect(()=>{t.isDev&&!te(v)&&t.warnOnce(`useRovingTabIndex:itemSelector:${v}`,`useRovingTabIndex: itemSelector "${v}" is not a valid CSS selector in this browser; no item is navigable or tabbable.`)},[v]),s.useEffect(()=>{if(!t.isDev||m!==`grid`||h)return;let e=D.container;e&&e.querySelector(`[role="row"]`)===null&&t.warnOnce(`useRovingTabIndex:grid-rows`,'useRovingTabIndex: orientation "grid" needs role="row" elements around the items or a `columns` count; without either it acts as "both".')});let V=s.useMemo(()=>j(k),[k]),H=s.useMemo(()=>{if(!p)return V;if(V){let e=new Map(V),t=p.flatMap(t=>{let n=e.get(t);return n===void 0?[]:[[t,n]]});if(t.length>0)return t}return p.map(e=>[e,1])},[p,V]),U=S===`none`?null:H?O(H,f,S,R):f||null,W=s.useCallback(e=>e===U?0:-1,[U]),G=s.useCallback(e=>{D.setContainer(e),d&&n.setRef(d,e)},[D,d]),K=s.useRef([]),q=s.useRef([]),{onTypeahead:ne,isSearching:re}=o.useTypeahead({getItems:()=>K.current,onMatch:(e,t)=>{q.current=t}}),J=e=>{if(!e||e.disabled)return!1;let t=x(e);return!t||(t.focus(),!ee(t))?!1:(D.recordFocus(e),!0)},Y=e=>{for(let t of e)if(J(t))return},X=r.useEventCallback((e,t)=>{let n=m===`grid`&&e.ctrlKey&&!e.altKey&&(e.key===`Home`||e.key===`End`),r=e.metaKey||(e.altKey||e.ctrlKey)&&!o.isAltGraphCharacter(e)&&!n;if(e.defaultPrevented||r||w(e.target))return;let a=D.container??e.currentTarget;if(!N(a,e.target))return;let s=D.resolve(a),u=s.filter(e=>!e.disabled);if(u.length===0)return;let d=P(s,e.target,a),f=!1,p=[],v=m===`grid`&&!t?c(s,a,h):null,x=v?l(e,v,d):void 0,S=t||x?null:i.getArrowIntent(e.key,{orientation:m===`grid`?v?`horizontal`:`both`:m,dir:_??i.getDirection(e.currentTarget)});if(x)f=!0,p=x===`stay`?[]:x;else if(S){if(f=!0,!d)p=S===`next`?u:[...u].reverse();else{let e=S===`next`?1:-1,t=s.length,n=s.indexOf(d);for(let r=1;r<t;r++){if(n+=e,n<0||n>=t){if(!g)break;n=(n+t)%t}s[n].disabled||p.push(s[n])}}}else!t&&b&&(e.key===`Home`||e.key===`End`)?(f=!0,p=e.key===`Home`?u:[...u].reverse()):y&&F(d,e.target,a)&&(K.current=s.map(e=>({value:e.value,text:M(e.element),disabled:e.disabled})),q.current=[],ne(e,d?.value??null)&&(f=!0,p=q.current.flatMap(e=>u.filter(t=>t.value===e))));if(f){e.preventDefault();for(let t of p){if(t===d)return;if(J(t)){E?.(t.value,e);return}}}}),Z=r.useEventCallback(e=>X(e,!1)),Q=r.useEventCallback(e=>{y&&e.key===` `&&re()&&X(e,!0)}),$=r.useEventCallback(e=>{let t=D.container??e.currentTarget;if(!N(t,e.target))return;let n=P(D.resolve(t),e.target,t);n&&(D.recordFocus(n),L(n.value),T(n)&&z(n.value),D.stamp())}),ie=r.useEventCallback(e=>{J(D.resolve().find(t=>t.value===e))}),ae=r.useEventCallback(()=>{Y(D.resolve().filter(e=>!e.disabled))}),oe=r.useEventCallback(()=>{Y(D.resolve().filter(e=>!e.disabled).reverse())});return{containerProps:s.useMemo(()=>({ref:G,"data-roving-container":``,onKeyDown:Z,onKeyDownCapture:Q,onFocus:$}),[G,Z,Q,$]),handleKeyDown:Z,handleKeyDownCapture:Q,handleFocus:$,getTabIndex:W,focusedValue:I,focusValue:ie,focusFirst:ae,focusLast:oe}}exports.isHiddenByCss=E,exports.useRovingTabIndex=I;
2
2
  //# sourceMappingURL=useRovingTabIndex.cjs.map