@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":"useFocusTrap.mjs","names":["getState","getGlobalRegistry","compareTraps","idA","a","idB","b","isDescendantLayer","layerA","getLayer","layerB","compareLayers","getActiveTrap","active","trap","state","FOLLOWING","PRECEDING","getAllowRegions","doc","all","region","other","getCycle","entries","getTabbableElements","regions","el","index","follows","reference","precedes","findFromPosition","backward","sameSegment","cycle","entry","segment","from","i","isInsideDescendantLayers","layerId","getLayerTreeElements","node","isInsideTrap","findDescendantLayer","found","layer","getOpenLayers","anchor","findPortalWrapper","containing","focusFirstPossible","candidate","candidates","focusElement","isAtRegionEdge","tabbables","focusCycleEdge","handlePortalWrapperTab","wrapper","event","handleDescendantLayerTab","depth","isFocusable","neighbour","outer","next","findNextTo","handleTab","container","inContainer","regionIndex","nextIndex","handleKeyDown","handleFocusIn","target","reclaiming","reclaimFocus","isFocusLost","lost","last","lastIsValid","getFirstTabbable","ensureListeners","pushTrap","resolveInitialFocus","first","initialFocus","useFocusTrap","options","latestRef","useRef","useInsertionEffect","useLayoutEffect","enabled","latest","release","now"],"sources":["../../src/hooks/useFocusTrap.ts"],"sourcesContent":["import { useInsertionEffect, useLayoutEffect, useRef } from 'react';\nimport type * as React from 'react';\nimport { getGlobalRegistry } from '../lib/globalRegistry';\nimport { focusElement, getFirstTabbable, getTabbableElements, isFocusable } from '../lib/focus';\nimport {\n ALLOW_OUTSIDE_SELECTOR,\n compareLayers,\n getLayer,\n getLayerTreeElements,\n getOpenLayers,\n isDescendantLayer,\n type LayerRecord,\n} from '../lib/layers';\n\n/** Options of {@link useFocusTrap}. */\nexport interface UseFocusTrapOptions {\n /** Whether the trap is active. */\n enabled: boolean;\n /**\n * The surface's dismiss layer; focus inside its descendant layers (portaled popovers opened from\n * inside) is allowed. The layer's own `refs` outside the container (its trigger) are not.\n */\n layerId?: string;\n /**\n * What to focus when the trap starts, unless focus is already inside (an `autoFocus` element\n * wins). @default 'first' (the first tabbable element, else the container)\n */\n initialFocus?:\n React.RefObject<HTMLElement | null> | 'first' | 'container' | (() => HTMLElement | null);\n /** Regions outside the container whose tabbables join the Tab cycle. @default ALLOW_OUTSIDE_SELECTOR */\n allowOutsideSelector?: string;\n}\n\ninterface TrapEntry {\n container: HTMLElement;\n seq: number;\n readonly layerId: string | undefined;\n readonly allowOutsideSelector: string;\n lastFocused: HTMLElement | null;\n}\n\ninterface TrapState {\n traps: TrapEntry[];\n seq: number;\n listeners: { remove(): void } | null;\n}\n\nfunction getState(): TrapState {\n return getGlobalRegistry<TrapState>('traps', () => ({ traps: [], seq: 0, listeners: null }));\n}\n\n// ---------------------------------------------------------------------------\n// Trap stack\n// ---------------------------------------------------------------------------\n\n/** `> 0` when trap `a` is above trap `b`: layer parentage/stacking first, then activation order. */\nfunction compareTraps(a: TrapEntry, b: TrapEntry): number {\n const idA = a.layerId;\n const idB = b.layerId;\n if (idA !== undefined && idB !== undefined && idA !== idB) {\n if (isDescendantLayer(idA, idB)) return 1;\n if (isDescendantLayer(idB, idA)) return -1;\n const layerA = getLayer(idA);\n const layerB = getLayer(idB);\n if (layerA && layerB) return compareLayers(layerA, layerB);\n }\n return a.seq - b.seq;\n}\n\nfunction getActiveTrap(state: TrapState): TrapEntry | null {\n let active: TrapEntry | null = null;\n for (const trap of state.traps) {\n if (!active || compareTraps(trap, active) > 0) active = trap;\n }\n return active;\n}\n\n// ---------------------------------------------------------------------------\n// Tab cycle\n// ---------------------------------------------------------------------------\n\ninterface CycleEntry {\n el: HTMLElement;\n /** 0 = the container, 1… = allow-listed regions in document order. */\n segment: number;\n}\n\nconst FOLLOWING = 4; // Node.DOCUMENT_POSITION_FOLLOWING\nconst PRECEDING = 2; // Node.DOCUMENT_POSITION_PRECEDING\n\n/** Top-level allow-listed regions outside the container. */\nfunction getAllowRegions(trap: TrapEntry): HTMLElement[] {\n const doc = trap.container.ownerDocument;\n const all = Array.from(doc.querySelectorAll<HTMLElement>(trap.allowOutsideSelector));\n return all.filter(\n (region) =>\n !trap.container.contains(region) &&\n !region.contains(trap.container) &&\n !all.some((other) => other !== region && other.contains(region)),\n );\n}\n\nfunction getCycle(trap: TrapEntry, regions: HTMLElement[]): CycleEntry[] {\n const entries: CycleEntry[] = getTabbableElements(trap.container).map((el) => ({\n el,\n segment: 0,\n }));\n regions.forEach((region, index) => {\n for (const el of getTabbableElements(region)) entries.push({ el, segment: index + 1 });\n });\n return entries;\n}\n\nfunction follows(reference: Node, el: Node): boolean {\n return (reference.compareDocumentPosition(el) & FOLLOWING) !== 0;\n}\n\nfunction precedes(reference: Node, el: Node): boolean {\n // An ancestor also \"precedes\"; it is not a place Tab goes back to.\n return (reference.compareDocumentPosition(el) & PRECEDING) !== 0 && !el.contains(reference);\n}\n\n/** The cycle entry Tab (or Shift+Tab) reaches from `from`, an element not in the cycle. */\nfunction findFromPosition(\n cycle: CycleEntry[],\n from: Element,\n segment: number,\n backward: boolean,\n): HTMLElement {\n if (!backward) {\n const sameSegment = cycle.find((entry) => entry.segment === segment && follows(from, entry.el));\n if (sameSegment) return sameSegment.el;\n const later = cycle.find((entry) => entry.segment > segment);\n return (later ?? cycle[0]).el;\n }\n for (let i = cycle.length - 1; i >= 0; i--) {\n const entry = cycle[i];\n if (entry.segment === segment && precedes(from, entry.el)) return entry.el;\n }\n for (let i = cycle.length - 1; i >= 0; i--) {\n if (cycle[i].segment < segment) return cycle[i].el;\n }\n return cycle[cycle.length - 1].el;\n}\n\n/**\n * Whether `node` is inside a **descendant** of the layer `layerId`: the portal wrappers registered\n * with it (overlays rendered inside the trapped surface) and the trees of its descendant layers.\n * The layer's own `getElements()` are left out — they usually include its trigger, which lives\n * outside the container (a DatePicker toggle) and must not count as inside the trap.\n */\nfunction isInsideDescendantLayers(layerId: string | undefined, node: Node): boolean {\n if (layerId === undefined) return false;\n return getLayerTreeElements(layerId, { includeOwnElements: false }).some((el) =>\n el.contains(node),\n );\n}\n\nfunction isInsideTrap(trap: TrapEntry, node: Node): boolean {\n if (trap.container.contains(node)) return true;\n const el = node.nodeType === 1 ? (node as Element) : node.parentElement;\n if (el && el.closest(trap.allowOutsideSelector)) return true;\n return isInsideDescendantLayers(trap.layerId, node);\n}\n\n/**\n * The open descendant layer of the trap's layer that `node` is in (the topmost when several\n * contain it), with the element of that layer containing `node` (its portaled surface). A layer's\n * trigger is not its surface: an element that is, or contains, the layer's anchor (an info\n * button, a tooltip's wrapper) belongs to the region around it — the container, the surface of\n * an enclosing descendant layer or a plain portal.\n */\nfunction findDescendantLayer(\n trap: TrapEntry,\n node: Node,\n): { layer: LayerRecord; region: HTMLElement } | null {\n const layerId = trap.layerId;\n if (layerId === undefined) return null;\n let found: { layer: LayerRecord; region: HTMLElement } | null = null;\n for (const layer of getOpenLayers()) {\n if (layer.id === layerId || !isDescendantLayer(layer.id, layerId)) continue;\n const anchor = layer.getAnchor();\n const region = layer\n .getElements()\n .find(\n (el): el is HTMLElement =>\n !!el &&\n el.contains(node) &&\n !trap.container.contains(el) &&\n !(anchor && el.contains(anchor)),\n );\n if (region && (!found || compareLayers(layer, found.layer) > 0)) found = { layer, region };\n }\n return found;\n}\n\n/**\n * The outermost element of the trap's layer tree (its own layer's elements left out) that contains\n * `node`. Asked once no descendant layer's surface contains `node`, this is the wrapper of a plain\n * `<Portal>` rendered inside the trapped surface (or inside one of its descendant layers).\n */\nfunction findPortalWrapper(trap: TrapEntry, node: Node): HTMLElement | null {\n if (trap.layerId === undefined) return null;\n const containing = getLayerTreeElements(trap.layerId, { includeOwnElements: false }).filter(\n (el) => el.contains(node),\n );\n return (\n containing.find((el) => !containing.some((other) => other !== el && other.contains(el))) ?? null\n );\n}\n\nfunction focusFirstPossible(candidates: Array<HTMLElement | null | undefined>): void {\n for (const candidate of candidates) {\n if (candidate && focusElement(candidate)) return;\n }\n}\n\n/** Whether Tab (Shift+Tab) from `from` leaves `region`: no tabbable of it follows (precedes). */\nfunction isAtRegionEdge(region: HTMLElement, from: HTMLElement, backward: boolean): boolean {\n const tabbables = getTabbableElements(region);\n const index = tabbables.indexOf(from);\n if (index === -1) {\n return backward\n ? !tabbables.some((el) => precedes(from, el))\n : !tabbables.some((el) => follows(from, el));\n }\n return backward ? index === 0 : index === tabbables.length - 1;\n}\n\n/** Leaving a region without an anchor: the first (Shift+Tab: last) element of the Tab cycle. */\nfunction focusCycleEdge(trap: TrapEntry, regions: HTMLElement[], backward: boolean): void {\n const cycle = getCycle(trap, regions);\n focusFirstPossible([backward ? cycle[cycle.length - 1]?.el : cycle[0]?.el, trap.container]);\n}\n\n/**\n * Tab inside a plain portal wrapper of the trap's layer tree (see {@link findPortalWrapper}):\n * native inside it; it has no anchor, so leaving it continues at the edge of the Tab cycle.\n * Returns `true` when it handled the event.\n */\nfunction handlePortalWrapperTab(\n trap: TrapEntry,\n event: KeyboardEvent,\n active: HTMLElement,\n regions: HTMLElement[],\n): boolean {\n const wrapper = findPortalWrapper(trap, active);\n if (!wrapper) return false;\n if (!isAtRegionEdge(wrapper, active, event.shiftKey)) return true; // native Tab inside it\n event.preventDefault();\n focusCycleEdge(trap, regions, event.shiftKey);\n return true;\n}\n\n/**\n * Tab inside a descendant layer: native inside it; leaving it forward goes to the tabbable after\n * the layer's anchor, backward to the anchor itself. Returns `true` when it handled the event.\n */\nfunction handleDescendantLayerTab(\n trap: TrapEntry,\n event: KeyboardEvent,\n active: HTMLElement,\n regions: HTMLElement[],\n): boolean {\n let from: HTMLElement = active;\n const backward = event.shiftKey;\n // Nested descendant layers: leave each one whose edge is reached, up to the container.\n for (let depth = 0; depth < 10; depth++) {\n const found = findDescendantLayer(trap, from);\n if (!found) return false;\n const { layer, region } = found;\n // Native Tab inside the layer.\n if (from === active && !isAtRegionEdge(region, from, backward)) return true;\n\n const anchor = layer.getAnchor();\n event.preventDefault();\n if (!anchor || !anchor.isConnected) {\n focusCycleEdge(trap, regions, backward);\n return true;\n }\n if (backward && isFocusable(anchor)) {\n focusElement(anchor);\n return true;\n }\n if (trap.container.contains(anchor)) {\n const cycle = getCycle(trap, regions);\n if (cycle.length === 0) {\n focusElement(trap.container);\n return true;\n }\n const target = cycle.some((entry) => entry.el === anchor)\n ? neighbour(cycle, anchor, backward)\n : findFromPosition(cycle, anchor, 0, backward);\n focusFirstPossible([target, trap.container]);\n return true;\n }\n // The anchor lives in another descendant layer: continue from there.\n const outer = findDescendantLayer(trap, anchor);\n if (!outer) {\n // Or in a plain portal of the trap's tree: the element next to it there, else the edge of\n // the Tab cycle (as when Tab leaves that portal).\n const wrapper = findPortalWrapper(trap, anchor);\n if (!wrapper) {\n focusFirstPossible([anchor, trap.container]);\n return true;\n }\n const next = findNextTo(getTabbableElements(wrapper), anchor, backward);\n if (next) focusElement(next);\n else focusCycleEdge(trap, regions, backward);\n return true;\n }\n const next = findNextTo(getTabbableElements(outer.region), anchor, backward);\n if (next) {\n focusElement(next);\n return true;\n }\n from = anchor;\n }\n return true;\n}\n\n/** The tabbable after `anchor` (backward: before it) in `tabbables`, never one inside it. */\nfunction findNextTo(\n tabbables: HTMLElement[],\n anchor: HTMLElement,\n backward: boolean,\n): HTMLElement | undefined {\n return backward\n ? [...tabbables].reverse().find((el) => precedes(anchor, el))\n : tabbables.find((el) => follows(anchor, el) && !anchor.contains(el));\n}\n\nfunction neighbour(cycle: CycleEntry[], el: HTMLElement, backward: boolean): HTMLElement {\n const index = cycle.findIndex((entry) => entry.el === el);\n const next = backward ? index - 1 : index + 1;\n return cycle[(next + cycle.length) % cycle.length].el;\n}\n\nfunction handleTab(trap: TrapEntry, event: KeyboardEvent): void {\n const { container } = trap;\n if (!container.isConnected) return;\n const doc = container.ownerDocument;\n const active = doc.activeElement as HTMLElement | null;\n const backward = event.shiftKey;\n const regions = getAllowRegions(trap);\n\n const inContainer = !!active && container.contains(active);\n const regionIndex =\n active && !inContainer ? regions.findIndex((region) => region.contains(active)) : -1;\n\n if (active && !inContainer && regionIndex === -1 && active !== doc.body) {\n if (isInsideDescendantLayers(trap.layerId, active)) {\n if (handleDescendantLayerTab(trap, event, active, regions)) return;\n if (handlePortalWrapperTab(trap, event, active, regions)) return;\n }\n }\n\n const cycle = getCycle(trap, regions);\n if (cycle.length === 0) {\n event.preventDefault();\n focusElement(container);\n return;\n }\n\n const index = active ? cycle.findIndex((entry) => entry.el === active) : -1;\n if (index !== -1) {\n const nextIndex = backward ? index - 1 : index + 1;\n if (nextIndex < 0 || nextIndex >= cycle.length) {\n event.preventDefault();\n focusElement(cycle[backward ? cycle.length - 1 : 0].el);\n return;\n }\n // Inside one segment the browser's own Tab order is used; crossing segments is explicit.\n const next = cycle[nextIndex];\n if (next.segment !== cycle[index].segment) {\n event.preventDefault();\n focusElement(next.el);\n }\n return;\n }\n\n event.preventDefault();\n if (active && (inContainer || regionIndex !== -1)) {\n const segment = inContainer ? 0 : regionIndex + 1;\n focusElement(findFromPosition(cycle, active, segment, backward));\n return;\n }\n focusElement(cycle[backward ? cycle.length - 1 : 0].el);\n}\n\n// ---------------------------------------------------------------------------\n// Document listeners\n// ---------------------------------------------------------------------------\n\nfunction handleKeyDown(event: KeyboardEvent): void {\n if (event.key !== 'Tab' || event.defaultPrevented) return;\n if (event.altKey || event.ctrlKey || event.metaKey || event.isComposing) return;\n const trap = getActiveTrap(getState());\n if (trap) handleTab(trap, event);\n}\n\nfunction handleFocusIn(event: FocusEvent): void {\n const trap = getActiveTrap(getState());\n if (!trap) return;\n const target = event.target as { nodeType?: unknown } | null;\n if (!target || typeof target.nodeType !== 'number') return;\n const node = target as Node;\n if (node.nodeType !== 1) return;\n const el = node as HTMLElement;\n if (isInsideTrap(trap, el)) {\n trap.lastFocused = el;\n return;\n }\n // Pulled out again by the focus being returned (another focus trap on the page): leave it there\n // rather than fight forever.\n if (reclaiming) return;\n // Not in this dispatch: React applies `autoFocus` in the layout phase of the commit that mounts\n // a surface, before the surface's portal wrapper and dismiss layer register and before its own\n // trap starts (a nested dialog, a confirm dialog over a drawer, a popover opened from a dialog).\n // Once that commit has run, the element is inside the trap that is active then.\n queueMicrotask(() => reclaimFocus(el));\n}\n\n/** Whether {@link reclaimFocus} is moving focus (its `focusin` events are dispatched meanwhile). */\nlet reclaiming = false;\n\n/** Whether focus is on no element: `<body>`, the document element or nothing. */\nfunction isFocusLost(doc: Document): boolean {\n const active = doc.activeElement;\n return !active || active === doc.body || active === doc.documentElement;\n}\n\n/**\n * Returns focus that landed on `el`, outside the trap, to the last focused element inside — also\n * when `el` lost it again before this ran (removed or blurred: focus is on `<body>` then, and no\n * later `focusin` would bring it back). Focus that moved on to another element is left to that\n * element's own check.\n */\nfunction reclaimFocus(el: HTMLElement): void {\n const doc = el.ownerDocument;\n const lost = doc.activeElement !== el;\n if (lost && !isFocusLost(doc)) return; // focus has moved on since\n const trap = getActiveTrap(getState());\n if (!trap || !trap.container.isConnected) return;\n if (!lost && isInsideTrap(trap, el)) {\n trap.lastFocused = el;\n return;\n }\n const last = trap.lastFocused;\n const lastIsValid = !!last && last.isConnected && isInsideTrap(trap, last) && isFocusable(last);\n reclaiming = true;\n try {\n focusFirstPossible([\n lastIsValid ? last : null,\n getFirstTabbable(trap.container),\n trap.container,\n ]);\n } finally {\n reclaiming = false;\n }\n}\n\nfunction ensureListeners(state: TrapState): void {\n if (state.listeners || typeof document === 'undefined') return;\n const doc = document;\n doc.addEventListener('keydown', handleKeyDown);\n doc.addEventListener('focusin', handleFocusIn, true);\n state.listeners = {\n remove() {\n doc.removeEventListener('keydown', handleKeyDown);\n doc.removeEventListener('focusin', handleFocusIn, true);\n },\n };\n}\n\nfunction pushTrap(entry: TrapEntry): () => void {\n const state = getState();\n state.traps.push(entry);\n ensureListeners(state);\n return () => {\n const index = state.traps.indexOf(entry);\n if (index !== -1) state.traps.splice(index, 1);\n if (state.traps.length === 0) {\n state.listeners?.remove();\n state.listeners = null;\n }\n };\n}\n\nfunction resolveInitialFocus(\n container: HTMLElement,\n initialFocus: UseFocusTrapOptions['initialFocus'],\n): Array<HTMLElement | null> {\n const first = getFirstTabbable(container);\n if (initialFocus === 'container') return [container];\n if (initialFocus === undefined || initialFocus === 'first') return [first, container];\n const chosen = typeof initialFocus === 'function' ? initialFocus() : initialFocus.current;\n return [chosen, first, container];\n}\n\n/**\n * Keeps keyboard focus inside `container` while `enabled` (modal dialogs, drawers, the DatePicker\n * calendar). Element-based: pass the surface held in state via a callback ref, so the trap starts\n * when the element appears.\n *\n * - **Initial focus** runs synchronously in the commit in which the container appears (no\n * `requestAnimationFrame`), and does nothing when focus is already inside (an `autoFocus`\n * element wins).\n * - **Tab / Shift+Tab** are handled by a document bubble-phase `keydown` listener that ignores\n * events whose default is already prevented, so a React handler inside (a menu closing on Tab, a\n * grid) can take the key first. Focus wraps at the edges, and from the container itself or a\n * non-tabbable element it continues in document order. Tabbable elements are computed on every\n * keydown (content may change; radio-group stops depend on focus), never cached.\n * - Tabbables inside `[data-wave-focus-trap-allow]` regions (the Toaster viewport) join the cycle.\n * - **Descendant layers** (a popover or menu opened from inside, identified through `layerId`):\n * Tab moves natively inside them; leaving one moves focus to the element after its anchor\n * (Shift+Tab: the anchor itself). Inside a plain `<Portal>` rendered in the surface (no layer,\n * no anchor) Tab is native too; leaving it wraps to the first (Shift+Tab: last) element. A\n * layer's trigger (its anchor, or an element around it such as a tooltip's wrapper) belongs to\n * the region it sits in, so Tab from an open info button or a button showing its tooltip moves\n * on as from any other element of that portal or popover.\n * - Focus that lands outside (not in the container, an allowed region or a descendant layer)\n * returns to the last focused element inside, also when that outside element loses it again\n * (removed or blurred, leaving focus on `<body>`). That is decided in a microtask, once the\n * current commit has run: an `autoFocus` element of a surface opened above the trap (a nested or\n * stacked dialog, a popover opened from the surface) gets focus before that surface's layer and\n * trap exist, and keeps it. The layer's own `refs` (its trigger, outside the container) are\n * outside: opening from the focused trigger still moves focus in, and focus moving back onto\n * the trigger is returned. Focus pulled out again while it is being returned (another focus\n * trap on the page) is left there.\n * - Traps form a stack (shared through the global registry): only the topmost acts.\n */\nexport function useFocusTrap(container: HTMLElement | null, options: UseFocusTrapOptions): void {\n const { enabled } = options;\n const latestRef = useRef(options);\n\n useInsertionEffect(() => {\n latestRef.current = options;\n });\n\n useLayoutEffect(() => {\n if (!enabled || !container) return;\n const latest = latestRef;\n const state = getState();\n state.seq += 1;\n const entry: TrapEntry = {\n container,\n seq: state.seq,\n get layerId() {\n return latest.current.layerId;\n },\n get allowOutsideSelector() {\n return latest.current.allowOutsideSelector ?? ALLOW_OUTSIDE_SELECTOR;\n },\n lastFocused: null,\n };\n const release = pushTrap(entry);\n\n // Initial focus is skipped only when focus is already inside the container or one of its\n // descendant layers (never for the layer's own trigger or an allow-listed toast).\n const active = container.ownerDocument.activeElement as HTMLElement | null;\n const focusInside =\n !!active &&\n (container.contains(active) ||\n (!active.closest(entry.allowOutsideSelector) &&\n isInsideDescendantLayers(latest.current.layerId, active)));\n if (focusInside) {\n entry.lastFocused = active;\n } else {\n focusFirstPossible(resolveInitialFocus(container, latest.current.initialFocus));\n const now = container.ownerDocument.activeElement as HTMLElement | null;\n if (now && container.contains(now)) entry.lastFocused = now;\n }\n return release;\n }, [enabled, container]);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useFocusTrap.mjs","names":["getState","getGlobalRegistry","compareTraps","idA","a","idB","b","isDescendantLayer","layerA","getLayer","layerB","compareLayers","getActiveTrap","active","trap","state","FOLLOWING","PRECEDING","getAllowRegions","doc","all","region","other","getCycle","entries","getTabbableElements","regions","el","index","follows","reference","precedes","findFromPosition","backward","sameSegment","cycle","entry","segment","from","i","isInsideDescendantLayers","layerId","getLayerTreeElements","node","isInsideTrap","findDescendantLayer","found","layer","getOpenLayers","anchor","findPortalWrapper","containing","focusFirstPossible","candidate","candidates","focusElement","isAtRegionEdge","tabbables","focusCycleEdge","handlePortalWrapperTab","wrapper","event","handleDescendantLayerTab","depth","isFocusable","neighbour","outer","next","findNextTo","handleTab","container","inContainer","regionIndex","nextIndex","handleKeyDown","handleFocusIn","target","reclaiming","reclaimFocus","isFocusLost","lost","last","lastIsValid","getFirstTabbable","ensureListeners","pushTrap","resolveInitialFocus","first","initialFocus","useFocusTrap","options","latestRef","useRef","useInsertionEffect","useLayoutEffect","enabled","latest","release","focused","applyInitialFocus","now","useInitialFocus","ALLOW_OUTSIDE_SELECTOR"],"sources":["../../src/hooks/useFocusTrap.ts"],"sourcesContent":["import { useInsertionEffect, useLayoutEffect, useRef } from 'react';\nimport type * as React from 'react';\nimport { getGlobalRegistry } from '../lib/globalRegistry';\nimport { focusElement, getFirstTabbable, getTabbableElements, isFocusable } from '../lib/focus';\nimport {\n ALLOW_OUTSIDE_SELECTOR,\n compareLayers,\n getLayer,\n getLayerTreeElements,\n getOpenLayers,\n isDescendantLayer,\n type LayerRecord,\n} from '../lib/layers';\n\n/** Options of {@link useFocusTrap}. */\nexport interface UseFocusTrapOptions {\n /** Whether the trap is active. */\n enabled: boolean;\n /**\n * The surface's dismiss layer; focus inside its descendant layers (portaled popovers opened from\n * inside) is allowed. The layer's own `refs` outside the container (its trigger) are not.\n */\n layerId?: string;\n /**\n * What to focus when the trap starts, unless focus is already inside (an `autoFocus` element\n * wins). @default 'first' (the first tabbable element, else the container)\n */\n initialFocus?:\n React.RefObject<HTMLElement | null> | 'first' | 'container' | (() => HTMLElement | null);\n /** Regions outside the container whose tabbables join the Tab cycle. @default '[data-wave-focus-trap-allow]' (the regions marked with `modalAllowProps`) */\n allowOutsideSelector?: string;\n}\n\ninterface TrapEntry {\n container: HTMLElement;\n seq: number;\n readonly layerId: string | undefined;\n readonly allowOutsideSelector: string;\n lastFocused: HTMLElement | null;\n}\n\ninterface TrapState {\n traps: TrapEntry[];\n seq: number;\n listeners: { remove(): void } | null;\n}\n\nfunction getState(): TrapState {\n return getGlobalRegistry<TrapState>('traps', () => ({ traps: [], seq: 0, listeners: null }));\n}\n\n// ---------------------------------------------------------------------------\n// Trap stack\n// ---------------------------------------------------------------------------\n\n/** `> 0` when trap `a` is above trap `b`: layer parentage/stacking first, then activation order. */\nfunction compareTraps(a: TrapEntry, b: TrapEntry): number {\n const idA = a.layerId;\n const idB = b.layerId;\n if (idA !== undefined && idB !== undefined && idA !== idB) {\n if (isDescendantLayer(idA, idB)) return 1;\n if (isDescendantLayer(idB, idA)) return -1;\n const layerA = getLayer(idA);\n const layerB = getLayer(idB);\n if (layerA && layerB) return compareLayers(layerA, layerB);\n }\n return a.seq - b.seq;\n}\n\nfunction getActiveTrap(state: TrapState): TrapEntry | null {\n let active: TrapEntry | null = null;\n for (const trap of state.traps) {\n if (!active || compareTraps(trap, active) > 0) active = trap;\n }\n return active;\n}\n\n// ---------------------------------------------------------------------------\n// Tab cycle\n// ---------------------------------------------------------------------------\n\ninterface CycleEntry {\n el: HTMLElement;\n /** 0 = the container, 1… = allow-listed regions in document order. */\n segment: number;\n}\n\nconst FOLLOWING = 4; // Node.DOCUMENT_POSITION_FOLLOWING\nconst PRECEDING = 2; // Node.DOCUMENT_POSITION_PRECEDING\n\n/** Top-level allow-listed regions outside the container. */\nfunction getAllowRegions(trap: TrapEntry): HTMLElement[] {\n const doc = trap.container.ownerDocument;\n const all = Array.from(doc.querySelectorAll<HTMLElement>(trap.allowOutsideSelector));\n return all.filter(\n (region) =>\n !trap.container.contains(region) &&\n !region.contains(trap.container) &&\n !all.some((other) => other !== region && other.contains(region)),\n );\n}\n\nfunction getCycle(trap: TrapEntry, regions: HTMLElement[]): CycleEntry[] {\n const entries: CycleEntry[] = getTabbableElements(trap.container).map((el) => ({\n el,\n segment: 0,\n }));\n regions.forEach((region, index) => {\n for (const el of getTabbableElements(region)) entries.push({ el, segment: index + 1 });\n });\n return entries;\n}\n\nfunction follows(reference: Node, el: Node): boolean {\n return (reference.compareDocumentPosition(el) & FOLLOWING) !== 0;\n}\n\nfunction precedes(reference: Node, el: Node): boolean {\n // An ancestor also \"precedes\"; it is not a place Tab goes back to.\n return (reference.compareDocumentPosition(el) & PRECEDING) !== 0 && !el.contains(reference);\n}\n\n/** The cycle entry Tab (or Shift+Tab) reaches from `from`, an element not in the cycle. */\nfunction findFromPosition(\n cycle: CycleEntry[],\n from: Element,\n segment: number,\n backward: boolean,\n): HTMLElement {\n if (!backward) {\n const sameSegment = cycle.find((entry) => entry.segment === segment && follows(from, entry.el));\n if (sameSegment) return sameSegment.el;\n const later = cycle.find((entry) => entry.segment > segment);\n return (later ?? cycle[0]).el;\n }\n for (let i = cycle.length - 1; i >= 0; i--) {\n const entry = cycle[i];\n if (entry.segment === segment && precedes(from, entry.el)) return entry.el;\n }\n for (let i = cycle.length - 1; i >= 0; i--) {\n if (cycle[i].segment < segment) return cycle[i].el;\n }\n return cycle[cycle.length - 1].el;\n}\n\n/**\n * Whether `node` is inside a **descendant** of the layer `layerId`: the portal wrappers registered\n * with it (overlays rendered inside the trapped surface) and the trees of its descendant layers.\n * The layer's own `getElements()` are left out — they usually include its trigger, which lives\n * outside the container (a DatePicker toggle) and must not count as inside the trap.\n */\nfunction isInsideDescendantLayers(layerId: string | undefined, node: Node): boolean {\n if (layerId === undefined) return false;\n return getLayerTreeElements(layerId, { includeOwnElements: false }).some((el) =>\n el.contains(node),\n );\n}\n\nfunction isInsideTrap(trap: TrapEntry, node: Node): boolean {\n if (trap.container.contains(node)) return true;\n const el = node.nodeType === 1 ? (node as Element) : node.parentElement;\n if (el && el.closest(trap.allowOutsideSelector)) return true;\n return isInsideDescendantLayers(trap.layerId, node);\n}\n\n/**\n * The open descendant layer of the trap's layer that `node` is in (the topmost when several\n * contain it), with the element of that layer containing `node` (its portaled surface). A layer's\n * trigger is not its surface: an element that is, or contains, the layer's anchor (an info\n * button, a tooltip's wrapper) belongs to the region around it — the container, the surface of\n * an enclosing descendant layer or a plain portal.\n */\nfunction findDescendantLayer(\n trap: TrapEntry,\n node: Node,\n): { layer: LayerRecord; region: HTMLElement } | null {\n const layerId = trap.layerId;\n if (layerId === undefined) return null;\n let found: { layer: LayerRecord; region: HTMLElement } | null = null;\n for (const layer of getOpenLayers()) {\n if (layer.id === layerId || !isDescendantLayer(layer.id, layerId)) continue;\n const anchor = layer.getAnchor();\n const region = layer\n .getElements()\n .find(\n (el): el is HTMLElement =>\n !!el &&\n el.contains(node) &&\n !trap.container.contains(el) &&\n !(anchor && el.contains(anchor)),\n );\n if (region && (!found || compareLayers(layer, found.layer) > 0)) found = { layer, region };\n }\n return found;\n}\n\n/**\n * The outermost element of the trap's layer tree (its own layer's elements left out) that contains\n * `node`. Asked once no descendant layer's surface contains `node`, this is the wrapper of a plain\n * `<Portal>` rendered inside the trapped surface (or inside one of its descendant layers).\n */\nfunction findPortalWrapper(trap: TrapEntry, node: Node): HTMLElement | null {\n if (trap.layerId === undefined) return null;\n const containing = getLayerTreeElements(trap.layerId, { includeOwnElements: false }).filter(\n (el) => el.contains(node),\n );\n return (\n containing.find((el) => !containing.some((other) => other !== el && other.contains(el))) ?? null\n );\n}\n\nfunction focusFirstPossible(candidates: Array<HTMLElement | null | undefined>): void {\n for (const candidate of candidates) {\n if (candidate && focusElement(candidate)) return;\n }\n}\n\n/** Whether Tab (Shift+Tab) from `from` leaves `region`: no tabbable of it follows (precedes). */\nfunction isAtRegionEdge(region: HTMLElement, from: HTMLElement, backward: boolean): boolean {\n const tabbables = getTabbableElements(region);\n const index = tabbables.indexOf(from);\n if (index === -1) {\n return backward\n ? !tabbables.some((el) => precedes(from, el))\n : !tabbables.some((el) => follows(from, el));\n }\n return backward ? index === 0 : index === tabbables.length - 1;\n}\n\n/** Leaving a region without an anchor: the first (Shift+Tab: last) element of the Tab cycle. */\nfunction focusCycleEdge(trap: TrapEntry, regions: HTMLElement[], backward: boolean): void {\n const cycle = getCycle(trap, regions);\n focusFirstPossible([backward ? cycle[cycle.length - 1]?.el : cycle[0]?.el, trap.container]);\n}\n\n/**\n * Tab inside a plain portal wrapper of the trap's layer tree (see {@link findPortalWrapper}):\n * native inside it; it has no anchor, so leaving it continues at the edge of the Tab cycle.\n * Returns `true` when it handled the event.\n */\nfunction handlePortalWrapperTab(\n trap: TrapEntry,\n event: KeyboardEvent,\n active: HTMLElement,\n regions: HTMLElement[],\n): boolean {\n const wrapper = findPortalWrapper(trap, active);\n if (!wrapper) return false;\n if (!isAtRegionEdge(wrapper, active, event.shiftKey)) return true; // native Tab inside it\n event.preventDefault();\n focusCycleEdge(trap, regions, event.shiftKey);\n return true;\n}\n\n/**\n * Tab inside a descendant layer: native inside it; leaving it forward goes to the tabbable after\n * the layer's anchor, backward to the anchor itself. Returns `true` when it handled the event.\n */\nfunction handleDescendantLayerTab(\n trap: TrapEntry,\n event: KeyboardEvent,\n active: HTMLElement,\n regions: HTMLElement[],\n): boolean {\n let from: HTMLElement = active;\n const backward = event.shiftKey;\n // Nested descendant layers: leave each one whose edge is reached, up to the container.\n for (let depth = 0; depth < 10; depth++) {\n const found = findDescendantLayer(trap, from);\n if (!found) return false;\n const { layer, region } = found;\n // Native Tab inside the layer.\n if (from === active && !isAtRegionEdge(region, from, backward)) return true;\n\n const anchor = layer.getAnchor();\n event.preventDefault();\n if (!anchor || !anchor.isConnected) {\n focusCycleEdge(trap, regions, backward);\n return true;\n }\n if (backward && isFocusable(anchor)) {\n focusElement(anchor);\n return true;\n }\n if (trap.container.contains(anchor)) {\n const cycle = getCycle(trap, regions);\n if (cycle.length === 0) {\n focusElement(trap.container);\n return true;\n }\n const target = cycle.some((entry) => entry.el === anchor)\n ? neighbour(cycle, anchor, backward)\n : findFromPosition(cycle, anchor, 0, backward);\n focusFirstPossible([target, trap.container]);\n return true;\n }\n // The anchor lives in another descendant layer: continue from there.\n const outer = findDescendantLayer(trap, anchor);\n if (!outer) {\n // Or in a plain portal of the trap's tree: the element next to it there, else the edge of\n // the Tab cycle (as when Tab leaves that portal).\n const wrapper = findPortalWrapper(trap, anchor);\n if (!wrapper) {\n focusFirstPossible([anchor, trap.container]);\n return true;\n }\n const next = findNextTo(getTabbableElements(wrapper), anchor, backward);\n if (next) focusElement(next);\n else focusCycleEdge(trap, regions, backward);\n return true;\n }\n const next = findNextTo(getTabbableElements(outer.region), anchor, backward);\n if (next) {\n focusElement(next);\n return true;\n }\n from = anchor;\n }\n return true;\n}\n\n/** The tabbable after `anchor` (backward: before it) in `tabbables`, never one inside it. */\nfunction findNextTo(\n tabbables: HTMLElement[],\n anchor: HTMLElement,\n backward: boolean,\n): HTMLElement | undefined {\n return backward\n ? [...tabbables].reverse().find((el) => precedes(anchor, el))\n : tabbables.find((el) => follows(anchor, el) && !anchor.contains(el));\n}\n\nfunction neighbour(cycle: CycleEntry[], el: HTMLElement, backward: boolean): HTMLElement {\n const index = cycle.findIndex((entry) => entry.el === el);\n const next = backward ? index - 1 : index + 1;\n return cycle[(next + cycle.length) % cycle.length].el;\n}\n\nfunction handleTab(trap: TrapEntry, event: KeyboardEvent): void {\n const { container } = trap;\n if (!container.isConnected) return;\n const doc = container.ownerDocument;\n const active = doc.activeElement as HTMLElement | null;\n const backward = event.shiftKey;\n const regions = getAllowRegions(trap);\n\n const inContainer = !!active && container.contains(active);\n const regionIndex =\n active && !inContainer ? regions.findIndex((region) => region.contains(active)) : -1;\n\n if (active && !inContainer && regionIndex === -1 && active !== doc.body) {\n if (isInsideDescendantLayers(trap.layerId, active)) {\n if (handleDescendantLayerTab(trap, event, active, regions)) return;\n if (handlePortalWrapperTab(trap, event, active, regions)) return;\n }\n }\n\n const cycle = getCycle(trap, regions);\n if (cycle.length === 0) {\n event.preventDefault();\n focusElement(container);\n return;\n }\n\n const index = active ? cycle.findIndex((entry) => entry.el === active) : -1;\n if (index !== -1) {\n const nextIndex = backward ? index - 1 : index + 1;\n if (nextIndex < 0 || nextIndex >= cycle.length) {\n event.preventDefault();\n focusElement(cycle[backward ? cycle.length - 1 : 0].el);\n return;\n }\n // Inside one segment the browser's own Tab order is used; crossing segments is explicit.\n const next = cycle[nextIndex];\n if (next.segment !== cycle[index].segment) {\n event.preventDefault();\n focusElement(next.el);\n }\n return;\n }\n\n event.preventDefault();\n if (active && (inContainer || regionIndex !== -1)) {\n const segment = inContainer ? 0 : regionIndex + 1;\n focusElement(findFromPosition(cycle, active, segment, backward));\n return;\n }\n focusElement(cycle[backward ? cycle.length - 1 : 0].el);\n}\n\n// ---------------------------------------------------------------------------\n// Document listeners\n// ---------------------------------------------------------------------------\n\nfunction handleKeyDown(event: KeyboardEvent): void {\n if (event.key !== 'Tab' || event.defaultPrevented) return;\n if (event.altKey || event.ctrlKey || event.metaKey || event.isComposing) return;\n const trap = getActiveTrap(getState());\n if (trap) handleTab(trap, event);\n}\n\nfunction handleFocusIn(event: FocusEvent): void {\n const trap = getActiveTrap(getState());\n if (!trap) return;\n const target = event.target as { nodeType?: unknown } | null;\n if (!target || typeof target.nodeType !== 'number') return;\n const node = target as Node;\n if (node.nodeType !== 1) return;\n const el = node as HTMLElement;\n if (isInsideTrap(trap, el)) {\n trap.lastFocused = el;\n return;\n }\n // Pulled out again by the focus being returned (another focus trap on the page): leave it there\n // rather than fight forever.\n if (reclaiming) return;\n // Not in this dispatch: React applies `autoFocus` in the layout phase of the commit that mounts\n // a surface, before the surface's portal wrapper and dismiss layer register and before its own\n // trap starts (a nested dialog, a confirm dialog over a drawer, a popover opened from a dialog).\n // Once that commit has run, the element is inside the trap that is active then.\n queueMicrotask(() => reclaimFocus(el));\n}\n\n/** Whether {@link reclaimFocus} is moving focus (its `focusin` events are dispatched meanwhile). */\nlet reclaiming = false;\n\n/** Whether focus is on no element: `<body>`, the document element or nothing. */\nfunction isFocusLost(doc: Document): boolean {\n const active = doc.activeElement;\n return !active || active === doc.body || active === doc.documentElement;\n}\n\n/**\n * Returns focus that landed on `el`, outside the trap, to the last focused element inside — also\n * when `el` lost it again before this ran (removed or blurred: focus is on `<body>` then, and no\n * later `focusin` would bring it back). Focus that moved on to another element is left to that\n * element's own check.\n */\nfunction reclaimFocus(el: HTMLElement): void {\n const doc = el.ownerDocument;\n const lost = doc.activeElement !== el;\n if (lost && !isFocusLost(doc)) return; // focus has moved on since\n const trap = getActiveTrap(getState());\n if (!trap || !trap.container.isConnected) return;\n if (!lost && isInsideTrap(trap, el)) {\n trap.lastFocused = el;\n return;\n }\n const last = trap.lastFocused;\n const lastIsValid = !!last && last.isConnected && isInsideTrap(trap, last) && isFocusable(last);\n reclaiming = true;\n try {\n focusFirstPossible([\n lastIsValid ? last : null,\n getFirstTabbable(trap.container),\n trap.container,\n ]);\n } finally {\n reclaiming = false;\n }\n}\n\nfunction ensureListeners(state: TrapState): void {\n if (state.listeners || typeof document === 'undefined') return;\n const doc = document;\n doc.addEventListener('keydown', handleKeyDown);\n doc.addEventListener('focusin', handleFocusIn, true);\n state.listeners = {\n remove() {\n doc.removeEventListener('keydown', handleKeyDown);\n doc.removeEventListener('focusin', handleFocusIn, true);\n },\n };\n}\n\nfunction pushTrap(entry: TrapEntry): () => void {\n const state = getState();\n state.traps.push(entry);\n ensureListeners(state);\n return () => {\n const index = state.traps.indexOf(entry);\n if (index !== -1) state.traps.splice(index, 1);\n if (state.traps.length === 0) {\n state.listeners?.remove();\n state.listeners = null;\n }\n };\n}\n\nfunction resolveInitialFocus(\n container: HTMLElement,\n initialFocus: UseFocusTrapOptions['initialFocus'],\n): Array<HTMLElement | null> {\n const first = getFirstTabbable(container);\n if (initialFocus === 'container') return [container];\n if (initialFocus === undefined || initialFocus === 'first') return [first, container];\n const chosen = typeof initialFocus === 'function' ? initialFocus() : initialFocus.current;\n return [chosen, first, container];\n}\n\n/**\n * Keeps keyboard focus inside `container` while `enabled` (modal dialogs, drawers, the DatePicker\n * calendar). Element-based: pass the surface held in state via a callback ref, so the trap starts\n * when the element appears.\n *\n * - **Initial focus** runs synchronously in the commit in which the container appears (no\n * `requestAnimationFrame`), and does nothing when focus is already inside (an `autoFocus`\n * element wins).\n * - **Tab / Shift+Tab** are handled by a document bubble-phase `keydown` listener that ignores\n * events whose default is already prevented, so a React handler inside (a menu closing on Tab, a\n * grid) can take the key first. Focus wraps at the edges, and from the container itself or a\n * non-tabbable element it continues in document order. Tabbable elements are computed on every\n * keydown (content may change; radio-group stops depend on focus), never cached.\n * - Tabbables inside `[data-wave-focus-trap-allow]` regions (the Toaster viewport) join the cycle.\n * - **Descendant layers** (a popover or menu opened from inside, identified through `layerId`):\n * Tab moves natively inside them; leaving one moves focus to the element after its anchor\n * (Shift+Tab: the anchor itself). Inside a plain `<Portal>` rendered in the surface (no layer,\n * no anchor) Tab is native too; leaving it wraps to the first (Shift+Tab: last) element. A\n * layer's trigger (its anchor, or an element around it such as a tooltip's wrapper) belongs to\n * the region it sits in, so Tab from an open info button or a button showing its tooltip moves\n * on as from any other element of that portal or popover.\n * - Focus that lands outside (not in the container, an allowed region or a descendant layer)\n * returns to the last focused element inside, also when that outside element loses it again\n * (removed or blurred, leaving focus on `<body>`). That is decided in a microtask, once the\n * current commit has run: an `autoFocus` element of a surface opened above the trap (a nested or\n * stacked dialog, a popover opened from the surface) gets focus before that surface's layer and\n * trap exist, and keeps it. The layer's own `refs` (its trigger, outside the container) are\n * outside: opening from the focused trigger still moves focus in, and focus moving back onto\n * the trigger is returned. Focus pulled out again while it is being returned (another focus\n * trap on the page) is left there.\n * - Traps form a stack (shared through the global registry): only the topmost acts.\n */\nexport function useFocusTrap(container: HTMLElement | null, options: UseFocusTrapOptions): void {\n const { enabled } = options;\n const latestRef = useRef(options);\n\n useInsertionEffect(() => {\n latestRef.current = options;\n });\n\n useLayoutEffect(() => {\n if (!enabled || !container) return;\n const latest = latestRef;\n const state = getState();\n state.seq += 1;\n const entry: TrapEntry = {\n container,\n seq: state.seq,\n get layerId() {\n return latest.current.layerId;\n },\n get allowOutsideSelector() {\n return latest.current.allowOutsideSelector ?? ALLOW_OUTSIDE_SELECTOR;\n },\n lastFocused: null,\n };\n const release = pushTrap(entry);\n const focused = applyInitialFocus(container, {\n layerId: latest.current.layerId,\n initialFocus: latest.current.initialFocus,\n allowOutsideSelector: entry.allowOutsideSelector,\n });\n if (focused) entry.lastFocused = focused;\n return release;\n }, [enabled, container]);\n}\n\n/**\n * The initial focus of a surface (the trap's, and {@link useInitialFocus}'s): skipped when focus is\n * already inside the container or one of the descendant layers of `layerId` (never for the\n * layer's own trigger or an allow-listed toast), else `initialFocus` (default: the first tabbable\n * element), else the container. Returns the focused element when it is inside the container or\n * those layers (the trap's `lastFocused`), else `null`.\n */\nfunction applyInitialFocus(\n container: HTMLElement,\n options: {\n layerId: string | undefined;\n initialFocus: UseFocusTrapOptions['initialFocus'];\n allowOutsideSelector: string;\n },\n): HTMLElement | null {\n const active = container.ownerDocument.activeElement as HTMLElement | null;\n const focusInside =\n !!active &&\n (container.contains(active) ||\n (!active.closest(options.allowOutsideSelector) &&\n isInsideDescendantLayers(options.layerId, active)));\n if (focusInside) return active;\n focusFirstPossible(resolveInitialFocus(container, options.initialFocus));\n const now = container.ownerDocument.activeElement as HTMLElement | null;\n return now && container.contains(now) ? now : null;\n}\n\n/** Options of {@link useInitialFocus}. */\nexport interface UseInitialFocusOptions {\n /** Whether the surface is open: focus moves in when it becomes `true` with a container. */\n enabled: boolean;\n /** The surface's dismiss layer: focus already in one of its descendant layers stays there. */\n layerId?: string;\n /** What to focus first. @default 'first' (the first tabbable element, else the container) */\n initialFocus?: UseFocusTrapOptions['initialFocus'];\n}\n\n/**\n * The trap's initial focus without the trap (a non-modal Dialog or Drawer, which moves focus in\n * when it opens but does not keep it there): in the layout phase of the commit in which\n * `enabled && container` becomes true, does nothing when focus is already inside the container or\n * one of the layer's descendant layers (an `autoFocus` element wins), else focuses\n * `initialFocus` (default: the first tabbable element), else the container. `useFocusTrap` runs\n * the same code when it starts. It adds no listener.\n */\nexport function useInitialFocus(\n container: HTMLElement | null,\n options: UseInitialFocusOptions,\n): void {\n const { enabled } = options;\n const latestRef = useRef(options);\n\n useInsertionEffect(() => {\n latestRef.current = options;\n });\n\n useLayoutEffect(() => {\n if (!enabled || !container) return;\n applyInitialFocus(container, {\n layerId: latestRef.current.layerId,\n initialFocus: latestRef.current.initialFocus,\n allowOutsideSelector: ALLOW_OUTSIDE_SELECTOR,\n });\n }, [enabled, container]);\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use client";let e=require("react");var t=new Set([`Shift`,`Control`,`Alt`,`Meta`,`AltGraph`]),n=[`pointerdown`,`mousedown`,`touchstart`],r=!1,i=new Set;function a(e){e!==r&&(r=e,i.forEach(e=>e()))}function o(e){t.has(e.key)||a(!0)}function s(){a(!1)}function c(e){if(i.size===0){document.addEventListener(`keydown`,o,!0);for(let e of n)document.addEventListener(e,s,!0)}return i.add(e),()=>{if(i.delete(e),i.size===0){document.removeEventListener(`keydown`,o,!0);for(let e of n)document.removeEventListener(e,s,!0);r=!1}}}function l(){return r}function u(){return!1}function d(){return(0,e.useSyncExternalStore)(c,l,u)}exports.useFocusVisible=d;
2
+ //# sourceMappingURL=useFocusVisible.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFocusVisible.cjs","names":["useSyncExternalStore","react","MODIFIER_KEYS","POINTER_EVENTS","keyboardNavigation","listeners","setKeyboardNavigation","next","listener","onKeyDown","event","onPointer","subscribe","type","getSnapshot","getServerSnapshot","useFocusVisible"],"sources":["../../src/hooks/useFocusVisible.ts"],"sourcesContent":["import { useSyncExternalStore } from 'react';\n\n/** Keys that do not start keyboard navigation on their own. */\nconst MODIFIER_KEYS: ReadonlySet<string> = new Set(['Shift', 'Control', 'Alt', 'Meta', 'AltGraph']);\n\nconst POINTER_EVENTS = ['pointerdown', 'mousedown', 'touchstart'] as const;\n\nlet keyboardNavigation = false;\nconst listeners = new Set<() => void>();\n\nfunction setKeyboardNavigation(next: boolean): void {\n if (next === keyboardNavigation) return;\n keyboardNavigation = next;\n listeners.forEach((listener) => listener());\n}\n\nfunction onKeyDown(event: KeyboardEvent): void {\n if (!MODIFIER_KEYS.has(event.key)) setKeyboardNavigation(true);\n}\n\nfunction onPointer(): void {\n setKeyboardNavigation(false);\n}\n\nfunction subscribe(listener: () => void): () => void {\n if (listeners.size === 0) {\n document.addEventListener('keydown', onKeyDown, true);\n for (const type of POINTER_EVENTS) document.addEventListener(type, onPointer, true);\n }\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n if (listeners.size === 0) {\n document.removeEventListener('keydown', onKeyDown, true);\n for (const type of POINTER_EVENTS) document.removeEventListener(type, onPointer, true);\n // Without listeners the last input is unknown: the next subscriber starts from the pointer.\n keyboardNavigation = false;\n }\n };\n}\n\nfunction getSnapshot(): boolean {\n return keyboardNavigation;\n}\n\nfunction getServerSnapshot(): boolean {\n return false;\n}\n\n/**\n * Whether the page is in keyboard-navigation mode: `true` after the last input was a key press\n * (any key but a lone modifier), `false` after a pointer or touch press and before any input. For\n * logic that needs the mode in script, such as showing a focus cue in a widget that draws its own;\n * plain focus rings should keep using the `focus-visible` variant. One set of document listeners\n * serves every caller and goes away with the last one. `false` on the server and during\n * hydration.\n *\n * @example\n * const keyboard = useFocusVisible();\n * return <div data-keyboard={keyboard ? '' : undefined}>…</div>;\n */\nexport function useFocusVisible(): boolean {\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n"],"mappings":"oCAGA,IAAME,EAAqC,IAAI,IAAI,CAAC,QAAS,UAAW,MAAO,OAAQ,UAAU,CAAC,EAE5FC,EAAiB,CAAC,cAAe,YAAa,YAAY,EAE5DC,EAAqB,GACnBC,EAAY,IAAI,IAEtB,SAASC,EAAsB,EAAqB,CAC9CC,IAASH,IACb,EAAqBG,EACrBF,EAAU,QAAS,GAAaG,EAAS,CAAC,EAC5C,CAEA,SAASC,EAAU,EAA4B,CACxCP,EAAc,IAAIQ,EAAM,GAAG,GAAGJ,EAAsB,EAAI,CAC/D,CAEA,SAASK,GAAkB,CACzBL,EAAsB,EAAK,CAC7B,CAEA,SAASM,EAAU,EAAkC,CACnD,GAAIP,EAAU,OAAS,EAAG,CACxB,SAAS,iBAAiB,UAAWI,EAAW,EAAI,EACpD,IAAK,IAAMI,KAAQV,EAAgB,SAAS,iBAAiBU,EAAMF,EAAW,EAAI,CACpF,CAEA,OADAN,EAAU,IAAIG,CAAQ,MACT,CAEX,GADAH,EAAU,OAAOG,CAAQ,EACrBH,EAAU,OAAS,EAAG,CACxB,SAAS,oBAAoB,UAAWI,EAAW,EAAI,EACvD,IAAK,IAAMI,KAAQV,EAAgB,SAAS,oBAAoBU,EAAMF,EAAW,EAAI,EAErF,EAAqB,EACvB,CACF,CACF,CAEA,SAASG,GAAuB,CAC9B,OAAOV,CACT,CAEA,SAASW,GAA6B,CACpC,MAAO,EACT,CAcA,SAAgBC,GAA2B,CACzC,OAAA,EAAOhB,EAAAA,qBAAAA,CAAqBY,EAAWE,EAAaC,CAAiB,CACvE"}
@@ -0,0 +1,49 @@
1
+ "use client";
2
+ import { useSyncExternalStore as e } from "react";
3
+ //#region src/hooks/useFocusVisible.ts
4
+ var t = /* @__PURE__ */ new Set([
5
+ "Shift",
6
+ "Control",
7
+ "Alt",
8
+ "Meta",
9
+ "AltGraph"
10
+ ]), n = [
11
+ "pointerdown",
12
+ "mousedown",
13
+ "touchstart"
14
+ ], r = !1, i = /* @__PURE__ */ new Set();
15
+ function a(e) {
16
+ e !== r && (r = e, i.forEach((e) => e()));
17
+ }
18
+ function o(e) {
19
+ t.has(e.key) || a(!0);
20
+ }
21
+ function s() {
22
+ a(!1);
23
+ }
24
+ function c(e) {
25
+ if (i.size === 0) {
26
+ document.addEventListener("keydown", o, !0);
27
+ for (let e of n) document.addEventListener(e, s, !0);
28
+ }
29
+ return i.add(e), () => {
30
+ if (i.delete(e), i.size === 0) {
31
+ document.removeEventListener("keydown", o, !0);
32
+ for (let e of n) document.removeEventListener(e, s, !0);
33
+ r = !1;
34
+ }
35
+ };
36
+ }
37
+ function l() {
38
+ return r;
39
+ }
40
+ function u() {
41
+ return !1;
42
+ }
43
+ function d() {
44
+ return e(c, l, u);
45
+ }
46
+ //#endregion
47
+ export { d as useFocusVisible };
48
+
49
+ //# sourceMappingURL=useFocusVisible.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFocusVisible.mjs","names":["MODIFIER_KEYS","POINTER_EVENTS","keyboardNavigation","listeners","setKeyboardNavigation","next","listener","onKeyDown","event","onPointer","subscribe","type","getSnapshot","getServerSnapshot","useFocusVisible","useSyncExternalStore"],"sources":["../../src/hooks/useFocusVisible.ts"],"sourcesContent":["import { useSyncExternalStore } from 'react';\n\n/** Keys that do not start keyboard navigation on their own. */\nconst MODIFIER_KEYS: ReadonlySet<string> = new Set(['Shift', 'Control', 'Alt', 'Meta', 'AltGraph']);\n\nconst POINTER_EVENTS = ['pointerdown', 'mousedown', 'touchstart'] as const;\n\nlet keyboardNavigation = false;\nconst listeners = new Set<() => void>();\n\nfunction setKeyboardNavigation(next: boolean): void {\n if (next === keyboardNavigation) return;\n keyboardNavigation = next;\n listeners.forEach((listener) => listener());\n}\n\nfunction onKeyDown(event: KeyboardEvent): void {\n if (!MODIFIER_KEYS.has(event.key)) setKeyboardNavigation(true);\n}\n\nfunction onPointer(): void {\n setKeyboardNavigation(false);\n}\n\nfunction subscribe(listener: () => void): () => void {\n if (listeners.size === 0) {\n document.addEventListener('keydown', onKeyDown, true);\n for (const type of POINTER_EVENTS) document.addEventListener(type, onPointer, true);\n }\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n if (listeners.size === 0) {\n document.removeEventListener('keydown', onKeyDown, true);\n for (const type of POINTER_EVENTS) document.removeEventListener(type, onPointer, true);\n // Without listeners the last input is unknown: the next subscriber starts from the pointer.\n keyboardNavigation = false;\n }\n };\n}\n\nfunction getSnapshot(): boolean {\n return keyboardNavigation;\n}\n\nfunction getServerSnapshot(): boolean {\n return false;\n}\n\n/**\n * Whether the page is in keyboard-navigation mode: `true` after the last input was a key press\n * (any key but a lone modifier), `false` after a pointer or touch press and before any input. For\n * logic that needs the mode in script, such as showing a focus cue in a widget that draws its own;\n * plain focus rings should keep using the `focus-visible` variant. One set of document listeners\n * serves every caller and goes away with the last one. `false` on the server and during\n * hydration.\n *\n * @example\n * const keyboard = useFocusVisible();\n * return <div data-keyboard={keyboard ? '' : undefined}>…</div>;\n */\nexport function useFocusVisible(): boolean {\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n"],"mappings":";;;AAGA,IAAMA,oBAAqC,IAAI,IAAI;CAAC;CAAS;CAAW;CAAO;CAAQ;AAAU,CAAC,GAE5FC,IAAiB;CAAC;CAAe;CAAa;AAAY,GAE5DC,IAAqB,IACnBC,oBAAY,IAAI,IAAgB;AAEtC,SAASC,EAAsB,GAAqB;CAClD,AAAIC,MAASH,MACb,IAAqBG,GACrBF,EAAU,SAAS,MAAaG,EAAS,CAAC;AAC5C;AAEA,SAASC,EAAU,GAA4B;CAC7C,AAAKP,EAAc,IAAIQ,EAAM,GAAG,KAAGJ,EAAsB,EAAI;AAC/D;AAEA,SAASK,IAAkB;CACzB,EAAsB,EAAK;AAC7B;AAEA,SAASC,EAAU,GAAkC;CACnD,IAAIP,EAAU,SAAS,GAAG;EACxB,SAAS,iBAAiB,WAAWI,GAAW,EAAI;EACpD,KAAK,IAAMI,KAAQV,GAAgB,SAAS,iBAAiBU,GAAMF,GAAW,EAAI;CACpF;CAEA,OADAN,EAAU,IAAIG,CAAQ,SACT;EAEX,IADAH,EAAU,OAAOG,CAAQ,GACrBH,EAAU,SAAS,GAAG;GACxB,SAAS,oBAAoB,WAAWI,GAAW,EAAI;GACvD,KAAK,IAAMI,KAAQV,GAAgB,SAAS,oBAAoBU,GAAMF,GAAW,EAAI;GAErF,IAAqB;EACvB;CACF;AACF;AAEA,SAASG,IAAuB;CAC9B,OAAOV;AACT;AAEA,SAASW,IAA6B;CACpC,OAAO;AACT;AAcA,SAAgBC,IAA2B;CACzC,OAAOC,EAAqBL,GAAWE,GAAaC,CAAiB;AACvE"}
@@ -0,0 +1,2 @@
1
+ "use client";const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./useEventCallback.cjs"),n=require("../lib/focus.cjs");let r=require("react");r=e.__toESM(r,1);var i=`data-focusable-group`,a=new WeakSet;function o(e){return e.closest(`[${i}]`)}function s(e){return n.getTabbableElements(e.ownerDocument.body).find(t=>!e.contains(t)&&(e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING)!==0)??null}function c(e={}){let{tabBehavior:c=`unlimited`}=e,l=c!==`unlimited`,u=r.useRef(!1),d=r.useRef(!1);r.useEffect(()=>{if(!l)return;let e=e=>{u.current=e.key===`Tab`&&e.shiftKey},t=()=>{d.current=u.current,u.current=!1};return document.addEventListener(`keydown`,e,!0),document.addEventListener(`focusin`,t,!0),()=>{document.removeEventListener(`keydown`,e,!0),document.removeEventListener(`focusin`,t,!0)}},[l]);let f=t.useEventCallback(e=>{if(!l||e.defaultPrevented)return;let t=e.currentTarget,r=e.target;if(!(r instanceof Element)||!t.contains(r)||o(r)!==t)return;let i=e.altKey||e.ctrlKey||e.metaKey;if(r===t){if(i)return;if(!e.shiftKey&&(e.key===`Enter`||e.key===`F2`)){let r=n.getFirstTabbable(t);r&&n.focusElement(r)&&e.preventDefault()}else if(!e.shiftKey&&e.key===`Tab`){let r=s(t);r&&n.focusElement(r)&&e.preventDefault()}else if(!e.shiftKey&&e.key===`Escape`){let n=t.parentElement?o(t.parentElement):null;n instanceof HTMLElement&&(e.preventDefault(),n.focus())}return}if(!i){if(!e.shiftKey&&e.key===`Escape`){e.preventDefault(),t.focus();return}if(c===`limited-trap-focus`&&e.key===`Tab`){let i=n.getTabbableElements(t),a=i[0],o=i[i.length-1];!e.shiftKey&&r===o&&a?(e.preventDefault(),n.focusElement(a)):e.shiftKey&&r===a&&o&&(e.preventDefault(),n.focusElement(o))}}}),p=t.useEventCallback(e=>{if(!l||a.has(e.nativeEvent))return;let t=e.currentTarget,n=e.target,r=e.relatedTarget;n!==t&&n instanceof Node&&t.contains(n)&&d.current&&r instanceof Node&&!t.contains(r)&&(a.add(e.nativeEvent),t.focus())});return{groupProps:r.useMemo(()=>({tabIndex:0,onKeyDown:f,onFocusCapture:p,[i]:``}),[f,p])}}exports.useFocusableGroup=c;
2
+ //# sourceMappingURL=useFocusableGroup.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFocusableGroup.cjs","names":["getTabbableElements","React","useEventCallback","getFirstTabbable","focusElement","require_runtime","require_useEventCallback","require_focus","react","GROUP_ATTRIBUTE","redirected","owningGroup","element","firstTabbableAfter","group","useFocusableGroup","options","limited","tabBehavior","pendingShiftTab","arrivedByShiftTab","onKeyDown","event","onFocusIn","target","modified","first","next","parent","tabbables","last","onFocusCapture","from"],"sources":["../../src/hooks/useFocusableGroup.ts"],"sourcesContent":["import * as React from 'react';\nimport { focusElement, getFirstTabbable, getTabbableElements } from '../lib/focus';\nimport { useEventCallback } from './useEventCallback';\n\n/** How Tab and Enter treat the elements inside a focusable group. */\nexport type FocusableGroupTabBehavior = 'unlimited' | 'limited' | 'limited-trap-focus';\n\n/** Options of {@link useFocusableGroup}. */\nexport interface UseFocusableGroupOptions {\n /**\n * How the group treats the elements inside it (Fluent's `tabBehavior`):\n *\n * - `'unlimited'`: the group is one more tab stop and the elements inside it stay reachable by\n * Tab; no key is intercepted.\n * - `'limited'`: the group is one tab stop, in both directions. Enter or F2 on it moves focus to\n * its first tabbable element, Tab and Shift+Tab then move between the elements inside, Tab\n * past the last one leaves the group, and Escape returns focus to the group.\n * - `'limited-trap-focus'`: as `'limited'`, but Tab past the last element wraps to the first and\n * Shift+Tab before the first wraps to the last, until Escape.\n *\n * @default 'unlimited'\n */\n tabBehavior?: FocusableGroupTabBehavior;\n}\n\n/** Props to spread onto the group element (compose its handlers with your own). */\nexport interface FocusableGroupProps {\n /** The group is itself focusable. */\n tabIndex: 0;\n onKeyDown: React.KeyboardEventHandler<HTMLElement>;\n onFocusCapture: React.FocusEventHandler<HTMLElement>;\n /** Marks the element as a group: nested groups own their own contents. */\n 'data-focusable-group': '';\n}\n\n/** Result of {@link useFocusableGroup}. */\nexport interface UseFocusableGroupResult {\n /** Spread onto the group element. */\n groupProps: FocusableGroupProps;\n}\n\nconst GROUP_ATTRIBUTE = 'data-focusable-group';\n\n/** Focus events a group has already redirected, so a group nested in it does not redirect again. */\nconst redirected = new WeakSet<Event>();\n\n/** The group that owns `element`: the nearest group element above it. */\nfunction owningGroup(element: Element): Element | null {\n return element.closest(`[${GROUP_ATTRIBUTE}]`);\n}\n\n/** The first tabbable element after the group's own subtree in document order, or `null`. */\nfunction firstTabbableAfter(group: HTMLElement): HTMLElement | null {\n const candidates = getTabbableElements(group.ownerDocument.body);\n return (\n candidates.find(\n (element) =>\n !group.contains(element) &&\n (group.compareDocumentPosition(element) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0,\n ) ?? null\n );\n}\n\n/**\n * A focusable group (Fluent's `useFocusableGroup`, tabster's groupper): one tab stop that holds\n * several focusable elements, so a long list of rich rows does not cost a Tab press per control.\n * With `tabBehavior: 'limited'` or `'limited-trap-focus'`, Tab skips the elements inside while focus\n * is on the group element itself, and Shift+Tab from outside lands on the group element instead of\n * its last element; Enter or F2 enters the group, and Escape leaves it. A group is entered while\n * focus is on an element inside it, however it got there (a click, a popup returning focus, a\n * script). No tabindex is rewritten: components with their own tab stops (Toolbar, TabList) work\n * inside a group. A key from a popup portaled out of the group, a key that belongs to a nested\n * group and a key an inner element already handled (`preventDefault()`, as an input consuming\n * Escape does) are left alone.\n *\n * @example\n * const { groupProps } = useFocusableGroup({ tabBehavior: 'limited' });\n * <li role=\"group\" aria-label=\"Message\" {...groupProps}>\n * <button>Reply</button> <button>Forward</button>\n * </li>\n */\nexport function useFocusableGroup(options: UseFocusableGroupOptions = {}): UseFocusableGroupResult {\n const { tabBehavior = 'unlimited' } = options;\n const limited = tabBehavior !== 'unlimited';\n // Whether the focus that is arriving was caused by Shift+Tab (written by document listeners).\n const pendingShiftTab = React.useRef(false);\n const arrivedByShiftTab = React.useRef(false);\n\n React.useEffect(() => {\n if (!limited) return;\n const onKeyDown = (event: KeyboardEvent) => {\n pendingShiftTab.current = event.key === 'Tab' && event.shiftKey;\n };\n const onFocusIn = () => {\n arrivedByShiftTab.current = pendingShiftTab.current;\n pendingShiftTab.current = false;\n };\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('focusin', onFocusIn, true);\n return () => {\n document.removeEventListener('keydown', onKeyDown, true);\n document.removeEventListener('focusin', onFocusIn, true);\n };\n }, [limited]);\n\n const onKeyDown = useEventCallback((event: React.KeyboardEvent<HTMLElement>) => {\n if (!limited || event.defaultPrevented) return;\n const group = event.currentTarget;\n const target = event.target;\n // A key from a portaled popup, or from a nested group, is not this group's.\n if (!(target instanceof Element) || !group.contains(target)) return;\n if (owningGroup(target) !== group) return;\n const modified = event.altKey || event.ctrlKey || event.metaKey;\n\n if (target === group) {\n if (modified) return;\n if (!event.shiftKey && (event.key === 'Enter' || event.key === 'F2')) {\n const first = getFirstTabbable(group);\n if (first && focusElement(first)) event.preventDefault();\n } else if (!event.shiftKey && event.key === 'Tab') {\n // One tab stop: skip the elements inside.\n const next = firstTabbableAfter(group);\n if (next && focusElement(next)) event.preventDefault();\n } else if (!event.shiftKey && event.key === 'Escape') {\n // A nested group's Escape goes to the group around it.\n const parent = group.parentElement ? owningGroup(group.parentElement) : null;\n if (parent instanceof HTMLElement) {\n event.preventDefault();\n parent.focus();\n }\n }\n return;\n }\n\n if (modified) return;\n if (!event.shiftKey && event.key === 'Escape') {\n event.preventDefault();\n group.focus();\n return;\n }\n\n if (tabBehavior === 'limited-trap-focus' && event.key === 'Tab') {\n const tabbables = getTabbableElements(group);\n const first = tabbables[0];\n const last = tabbables[tabbables.length - 1];\n if (!event.shiftKey && target === last && first) {\n event.preventDefault();\n focusElement(first);\n } else if (event.shiftKey && target === first && last) {\n event.preventDefault();\n focusElement(last);\n }\n }\n });\n\n // Capture phase: the outermost group sees the focus first, and a group inside it stands down.\n const onFocusCapture = useEventCallback((event: React.FocusEvent<HTMLElement>) => {\n if (!limited || redirected.has(event.nativeEvent)) return;\n const group = event.currentTarget;\n const target = event.target;\n const from = event.relatedTarget;\n if (target === group || !(target instanceof Node) || !group.contains(target)) return;\n // Focus that arrives from outside by Shift+Tab lands on the group element, not on its last\n // element. Any other arrival (a click, a script, a popup returning focus) enters the group.\n if (!arrivedByShiftTab.current || !(from instanceof Node) || group.contains(from)) return;\n redirected.add(event.nativeEvent);\n group.focus();\n });\n\n const groupProps = React.useMemo<FocusableGroupProps>(\n () => ({\n tabIndex: 0,\n onKeyDown,\n onFocusCapture,\n [GROUP_ATTRIBUTE]: '',\n }),\n [onKeyDown, onFocusCapture],\n );\n\n return { groupProps };\n}\n"],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ "use client";
2
+ import { useEventCallback as e } from "./useEventCallback.mjs";
3
+ import { focusElement as t, getFirstTabbable as n, getTabbableElements as r } from "../lib/focus.mjs";
4
+ import * as i from "react";
5
+ //#region src/hooks/useFocusableGroup.ts
6
+ var a = "data-focusable-group", o = /* @__PURE__ */ new WeakSet();
7
+ function s(e) {
8
+ return e.closest(`[${a}]`);
9
+ }
10
+ function c(e) {
11
+ return r(e.ownerDocument.body).find((t) => !e.contains(t) && (e.compareDocumentPosition(t) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0) ?? null;
12
+ }
13
+ function l(l = {}) {
14
+ let { tabBehavior: u = "unlimited" } = l, d = u !== "unlimited", f = i.useRef(!1), p = i.useRef(!1);
15
+ i.useEffect(() => {
16
+ if (!d) return;
17
+ let e = (e) => {
18
+ f.current = e.key === "Tab" && e.shiftKey;
19
+ }, t = () => {
20
+ p.current = f.current, f.current = !1;
21
+ };
22
+ return document.addEventListener("keydown", e, !0), document.addEventListener("focusin", t, !0), () => {
23
+ document.removeEventListener("keydown", e, !0), document.removeEventListener("focusin", t, !0);
24
+ };
25
+ }, [d]);
26
+ let m = e((e) => {
27
+ if (!d || e.defaultPrevented) return;
28
+ let i = e.currentTarget, a = e.target;
29
+ if (!(a instanceof Element) || !i.contains(a) || s(a) !== i) return;
30
+ let o = e.altKey || e.ctrlKey || e.metaKey;
31
+ if (a === i) {
32
+ if (o) return;
33
+ if (!e.shiftKey && (e.key === "Enter" || e.key === "F2")) {
34
+ let r = n(i);
35
+ r && t(r) && e.preventDefault();
36
+ } else if (!e.shiftKey && e.key === "Tab") {
37
+ let n = c(i);
38
+ n && t(n) && e.preventDefault();
39
+ } else if (!e.shiftKey && e.key === "Escape") {
40
+ let t = i.parentElement ? s(i.parentElement) : null;
41
+ t instanceof HTMLElement && (e.preventDefault(), t.focus());
42
+ }
43
+ return;
44
+ }
45
+ if (!o) {
46
+ if (!e.shiftKey && e.key === "Escape") {
47
+ e.preventDefault(), i.focus();
48
+ return;
49
+ }
50
+ if (u === "limited-trap-focus" && e.key === "Tab") {
51
+ let n = r(i), o = n[0], s = n[n.length - 1];
52
+ !e.shiftKey && a === s && o ? (e.preventDefault(), t(o)) : e.shiftKey && a === o && s && (e.preventDefault(), t(s));
53
+ }
54
+ }
55
+ }), h = e((e) => {
56
+ if (!d || o.has(e.nativeEvent)) return;
57
+ let t = e.currentTarget, n = e.target, r = e.relatedTarget;
58
+ n !== t && n instanceof Node && t.contains(n) && p.current && r instanceof Node && !t.contains(r) && (o.add(e.nativeEvent), t.focus());
59
+ });
60
+ return { groupProps: i.useMemo(() => ({
61
+ tabIndex: 0,
62
+ onKeyDown: m,
63
+ onFocusCapture: h,
64
+ [a]: ""
65
+ }), [m, h]) };
66
+ }
67
+ //#endregion
68
+ export { l as useFocusableGroup };
69
+
70
+ //# sourceMappingURL=useFocusableGroup.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFocusableGroup.mjs","names":["React","GROUP_ATTRIBUTE","redirected","owningGroup","element","firstTabbableAfter","getTabbableElements","group","useFocusableGroup","options","limited","tabBehavior","pendingShiftTab","arrivedByShiftTab","onKeyDown","event","onFocusIn","useEventCallback","target","modified","first","getFirstTabbable","focusElement","next","parent","tabbables","last","onFocusCapture","from"],"sources":["../../src/hooks/useFocusableGroup.ts"],"sourcesContent":["import * as React from 'react';\nimport { focusElement, getFirstTabbable, getTabbableElements } from '../lib/focus';\nimport { useEventCallback } from './useEventCallback';\n\n/** How Tab and Enter treat the elements inside a focusable group. */\nexport type FocusableGroupTabBehavior = 'unlimited' | 'limited' | 'limited-trap-focus';\n\n/** Options of {@link useFocusableGroup}. */\nexport interface UseFocusableGroupOptions {\n /**\n * How the group treats the elements inside it (Fluent's `tabBehavior`):\n *\n * - `'unlimited'`: the group is one more tab stop and the elements inside it stay reachable by\n * Tab; no key is intercepted.\n * - `'limited'`: the group is one tab stop, in both directions. Enter or F2 on it moves focus to\n * its first tabbable element, Tab and Shift+Tab then move between the elements inside, Tab\n * past the last one leaves the group, and Escape returns focus to the group.\n * - `'limited-trap-focus'`: as `'limited'`, but Tab past the last element wraps to the first and\n * Shift+Tab before the first wraps to the last, until Escape.\n *\n * @default 'unlimited'\n */\n tabBehavior?: FocusableGroupTabBehavior;\n}\n\n/** Props to spread onto the group element (compose its handlers with your own). */\nexport interface FocusableGroupProps {\n /** The group is itself focusable. */\n tabIndex: 0;\n onKeyDown: React.KeyboardEventHandler<HTMLElement>;\n onFocusCapture: React.FocusEventHandler<HTMLElement>;\n /** Marks the element as a group: nested groups own their own contents. */\n 'data-focusable-group': '';\n}\n\n/** Result of {@link useFocusableGroup}. */\nexport interface UseFocusableGroupResult {\n /** Spread onto the group element. */\n groupProps: FocusableGroupProps;\n}\n\nconst GROUP_ATTRIBUTE = 'data-focusable-group';\n\n/** Focus events a group has already redirected, so a group nested in it does not redirect again. */\nconst redirected = new WeakSet<Event>();\n\n/** The group that owns `element`: the nearest group element above it. */\nfunction owningGroup(element: Element): Element | null {\n return element.closest(`[${GROUP_ATTRIBUTE}]`);\n}\n\n/** The first tabbable element after the group's own subtree in document order, or `null`. */\nfunction firstTabbableAfter(group: HTMLElement): HTMLElement | null {\n const candidates = getTabbableElements(group.ownerDocument.body);\n return (\n candidates.find(\n (element) =>\n !group.contains(element) &&\n (group.compareDocumentPosition(element) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0,\n ) ?? null\n );\n}\n\n/**\n * A focusable group (Fluent's `useFocusableGroup`, tabster's groupper): one tab stop that holds\n * several focusable elements, so a long list of rich rows does not cost a Tab press per control.\n * With `tabBehavior: 'limited'` or `'limited-trap-focus'`, Tab skips the elements inside while focus\n * is on the group element itself, and Shift+Tab from outside lands on the group element instead of\n * its last element; Enter or F2 enters the group, and Escape leaves it. A group is entered while\n * focus is on an element inside it, however it got there (a click, a popup returning focus, a\n * script). No tabindex is rewritten: components with their own tab stops (Toolbar, TabList) work\n * inside a group. A key from a popup portaled out of the group, a key that belongs to a nested\n * group and a key an inner element already handled (`preventDefault()`, as an input consuming\n * Escape does) are left alone.\n *\n * @example\n * const { groupProps } = useFocusableGroup({ tabBehavior: 'limited' });\n * <li role=\"group\" aria-label=\"Message\" {...groupProps}>\n * <button>Reply</button> <button>Forward</button>\n * </li>\n */\nexport function useFocusableGroup(options: UseFocusableGroupOptions = {}): UseFocusableGroupResult {\n const { tabBehavior = 'unlimited' } = options;\n const limited = tabBehavior !== 'unlimited';\n // Whether the focus that is arriving was caused by Shift+Tab (written by document listeners).\n const pendingShiftTab = React.useRef(false);\n const arrivedByShiftTab = React.useRef(false);\n\n React.useEffect(() => {\n if (!limited) return;\n const onKeyDown = (event: KeyboardEvent) => {\n pendingShiftTab.current = event.key === 'Tab' && event.shiftKey;\n };\n const onFocusIn = () => {\n arrivedByShiftTab.current = pendingShiftTab.current;\n pendingShiftTab.current = false;\n };\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('focusin', onFocusIn, true);\n return () => {\n document.removeEventListener('keydown', onKeyDown, true);\n document.removeEventListener('focusin', onFocusIn, true);\n };\n }, [limited]);\n\n const onKeyDown = useEventCallback((event: React.KeyboardEvent<HTMLElement>) => {\n if (!limited || event.defaultPrevented) return;\n const group = event.currentTarget;\n const target = event.target;\n // A key from a portaled popup, or from a nested group, is not this group's.\n if (!(target instanceof Element) || !group.contains(target)) return;\n if (owningGroup(target) !== group) return;\n const modified = event.altKey || event.ctrlKey || event.metaKey;\n\n if (target === group) {\n if (modified) return;\n if (!event.shiftKey && (event.key === 'Enter' || event.key === 'F2')) {\n const first = getFirstTabbable(group);\n if (first && focusElement(first)) event.preventDefault();\n } else if (!event.shiftKey && event.key === 'Tab') {\n // One tab stop: skip the elements inside.\n const next = firstTabbableAfter(group);\n if (next && focusElement(next)) event.preventDefault();\n } else if (!event.shiftKey && event.key === 'Escape') {\n // A nested group's Escape goes to the group around it.\n const parent = group.parentElement ? owningGroup(group.parentElement) : null;\n if (parent instanceof HTMLElement) {\n event.preventDefault();\n parent.focus();\n }\n }\n return;\n }\n\n if (modified) return;\n if (!event.shiftKey && event.key === 'Escape') {\n event.preventDefault();\n group.focus();\n return;\n }\n\n if (tabBehavior === 'limited-trap-focus' && event.key === 'Tab') {\n const tabbables = getTabbableElements(group);\n const first = tabbables[0];\n const last = tabbables[tabbables.length - 1];\n if (!event.shiftKey && target === last && first) {\n event.preventDefault();\n focusElement(first);\n } else if (event.shiftKey && target === first && last) {\n event.preventDefault();\n focusElement(last);\n }\n }\n });\n\n // Capture phase: the outermost group sees the focus first, and a group inside it stands down.\n const onFocusCapture = useEventCallback((event: React.FocusEvent<HTMLElement>) => {\n if (!limited || redirected.has(event.nativeEvent)) return;\n const group = event.currentTarget;\n const target = event.target;\n const from = event.relatedTarget;\n if (target === group || !(target instanceof Node) || !group.contains(target)) return;\n // Focus that arrives from outside by Shift+Tab lands on the group element, not on its last\n // element. Any other arrival (a click, a script, a popup returning focus) enters the group.\n if (!arrivedByShiftTab.current || !(from instanceof Node) || group.contains(from)) return;\n redirected.add(event.nativeEvent);\n group.focus();\n });\n\n const groupProps = React.useMemo<FocusableGroupProps>(\n () => ({\n tabIndex: 0,\n onKeyDown,\n onFocusCapture,\n [GROUP_ATTRIBUTE]: '',\n }),\n [onKeyDown, onFocusCapture],\n );\n\n return { groupProps };\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use client";const e=require("./useDismiss.cjs"),t=require("./useFocusTrap.cjs"),n=require("./useRestoreFocus.cjs"),r=require("./useModalIsolation.cjs"),i=require("./useScrollLock.cjs");function a(a){let{container:o,initialFocus:s,triggerRef:c,finalFocusRef:l,...u}=a,{open:d}=a,f=e.useDismiss({...u,kind:`modal`});return t.useFocusTrap(o,{enabled:d,layerId:f.layerId,initialFocus:s}),r.useModalIsolation(d,{layerId:f.layerId,container:o}),i.useScrollLock(d),n.useRestoreFocus({enabled:d,container:o,triggerRef:c,finalFocusRef:l}),f}exports.useModalLayer=a;
1
+ "use client";const e=require("./useDismiss.cjs"),t=require("./useFocusTrap.cjs"),n=require("./useModalIsolation.cjs"),r=require("./useRestoreFocus.cjs"),i=require("./useScrollLock.cjs");function a(a){let{container:o,modalType:s=`modal`,initialFocus:c,triggerRef:l,finalFocusRef:u,...d}=a,{open:f}=a,p=s===`non-modal`,m=f&&!p,h=e.useDismiss(p?{...d,kind:`dialog`,escape:`inside`,outsidePress:!1,focusOutside:!1}:{...d,kind:`modal`,...s===`alert`&&{outsidePress:!1}});return t.useFocusTrap(o,{enabled:m,layerId:h.layerId,initialFocus:c}),t.useInitialFocus(o,{enabled:f&&p,layerId:h.layerId,initialFocus:c}),n.useModalIsolation(m,{layerId:h.layerId,container:o}),i.useScrollLock(m),r.useRestoreFocus({enabled:f,container:o,triggerRef:l,finalFocusRef:u,requireFocusInside:p,layerId:h.layerId}),h}exports.useModalLayer=a;
2
2
  //# sourceMappingURL=useModalLayer.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useModalLayer.cjs","names":["useDismiss","require_useDismiss","require_useFocusTrap","require_useRestoreFocus","require_useModalIsolation","require_useScrollLock","useModalLayer","dismissOptions","options","layer","container","open"],"sources":["../../src/hooks/useModalLayer.ts"],"sourcesContent":["import type * as React from 'react';\nimport { useDismiss, type DismissLayer, type UseDismissOptions } from './useDismiss';\nimport { useFocusTrap, type UseFocusTrapOptions } from './useFocusTrap';\nimport { useModalIsolation } from './useModalIsolation';\nimport { useRestoreFocus } from './useRestoreFocus';\nimport { useScrollLock } from './useScrollLock';\n\n/** Options of {@link useModalLayer}: `useDismiss` options (kind is always `'modal'`) plus focus. */\nexport interface UseModalLayerOptions extends Omit<UseDismissOptions, 'kind'> {\n /** The modal surface, held in state via a callback ref. */\n container: HTMLElement | null;\n /** What the focus trap focuses first. @default 'first' */\n initialFocus?: UseFocusTrapOptions['initialFocus'];\n /** The trigger: preferred focus-restore target. */\n triggerRef?: React.RefObject<HTMLElement | null>;\n /** Consumer override for the focus-restore target. */\n finalFocusRef?: React.RefObject<HTMLElement | null>;\n}\n\n/**\n * Everything a modal surface (Dialog, Drawer) needs, composed in the order that works:\n *\n * 1. `useDismiss({ kind: 'modal' })` — Escape and outside presses (the backdrop is outside the\n * content surface, so a click on it dismisses; a drag from the content does not).\n * 2. `useFocusTrap` — initial focus, Tab cycle incl. toasts and descendant layers.\n * 3. `useModalIsolation` — the rest of the page is `inert` (instead of `aria-modal`).\n * 4. `useScrollLock` — ref-counted page scroll lock.\n * 5. `useRestoreFocus` — the opener (or `triggerRef`/`finalFocusRef`) gets focus back.\n *\n * On close the trap is released before the isolation is lifted, and the isolation is lifted\n * before focus is restored (an inert opener cannot take focus). Wrap the surface in\n * `Portal layerId={layerId}` so layers opened inside the modal become its descendants.\n */\nexport function useModalLayer(options: UseModalLayerOptions): DismissLayer {\n const { container, initialFocus, triggerRef, finalFocusRef, ...dismissOptions } = options;\n const { open } = options;\n\n const layer = useDismiss({ ...dismissOptions, kind: 'modal' });\n useFocusTrap(container, { enabled: open, layerId: layer.layerId, initialFocus });\n useModalIsolation(open, { layerId: layer.layerId, container });\n useScrollLock(open);\n useRestoreFocus({ enabled: open, container, triggerRef, finalFocusRef });\n\n return layer;\n}\n"],"mappings":"0LAiCA,SAAgBM,EAAc,EAA6C,CACzE,GAAM,CAAE,YAAW,eAAc,aAAY,gBAAe,GAAGC,GAAmBC,EAC5E,CAAE,QAASA,EAEXC,EAAQT,EAAAA,WAAW,CAAE,GAAGO,EAAgB,KAAM,OAAQ,CAAC,EAM7D,OALAL,EAAA,aAAaQ,EAAW,CAAE,QAASC,EAAM,QAASF,EAAM,QAAS,cAAa,CAAC,EAC/EL,EAAA,kBAAkBO,EAAM,CAAE,QAASF,EAAM,QAAS,WAAU,CAAC,EAC7DJ,EAAA,cAAcM,CAAI,EAClBR,EAAA,gBAAgB,CAAE,QAASQ,EAAM,YAAW,aAAY,eAAc,CAAC,EAEhEF,CACT"}
1
+ {"version":3,"file":"useModalLayer.cjs","names":["useDismiss","require_useDismiss","require_useFocusTrap","require_useModalIsolation","require_useRestoreFocus","require_useScrollLock","useModalLayer","dismissOptions","options","nonModal","modalType","modal","open","layer","container"],"sources":["../../src/hooks/useModalLayer.ts"],"sourcesContent":["import type * as React from 'react';\nimport type { ModalType } from '../lib/types';\nimport { useDismiss, type DismissLayer, type UseDismissOptions } from './useDismiss';\nimport { useFocusTrap, useInitialFocus, type UseFocusTrapOptions } from './useFocusTrap';\nimport { useModalIsolation } from './useModalIsolation';\nimport { useRestoreFocus } from './useRestoreFocus';\nimport { useScrollLock } from './useScrollLock';\n\n/**\n * Options of {@link useModalLayer}: `useDismiss` options (the kind follows `modalType`) plus focus.\n */\nexport interface UseModalLayerOptions extends Omit<UseDismissOptions, 'kind'> {\n /** The modal surface, held in state via a callback ref. */\n container: HTMLElement | null;\n /**\n * `modal` (default) and `alert` register a `modal` layer, trap focus, isolate the page, lock its\n * scroll and restore focus (an `alert` layer ignores outside presses); `non-modal` registers a\n * `dialog` layer (`escape: 'inside'`, no outside press, no focus-outside), traps, isolates and\n * locks nothing, moves focus in on open with the trap's initial-focus rule and restores focus only\n * from inside (`requireFocusInside`).\n * @default 'modal'\n */\n modalType?: ModalType;\n /** What the focus trap (or a non-modal surface's initial focus) focuses first. @default 'first' */\n initialFocus?: UseFocusTrapOptions['initialFocus'];\n /** The trigger: preferred focus-restore target. */\n triggerRef?: React.RefObject<HTMLElement | null>;\n /** Consumer override for the focus-restore target. */\n finalFocusRef?: React.RefObject<HTMLElement | null>;\n}\n\n/**\n * Everything a modal surface (Dialog, Drawer) needs, composed in the order that works:\n *\n * 1. `useDismiss({ kind: 'modal' })` — Escape and outside presses (the backdrop is outside the\n * content surface, so a click on it dismisses; a drag from the content does not).\n * 2. `useFocusTrap` — initial focus, Tab cycle incl. toasts and descendant layers.\n * 3. `useModalIsolation` — the rest of the page is `inert` (instead of `aria-modal`).\n * 4. `useScrollLock` — ref-counted page scroll lock.\n * 5. `useRestoreFocus` — the opener (or `triggerRef`/`finalFocusRef`) gets focus back.\n *\n * On close the trap is released before the isolation is lifted, and the isolation is lifted\n * before focus is restored (an inert opener cannot take focus). Wrap the surface in\n * `Portal layerId={layerId}` so layers opened inside the modal become its descendants.\n *\n * `modalType: 'alert'` composes the same with `outsidePress: false`. `modalType: 'non-modal'`\n * registers a `dialog` layer (Escape only from inside its tree, no outside press, no focus-outside\n * dismissal), keeps the trap, the isolation and the scroll lock off, moves focus in on open with\n * the trap's initial-focus rule (`useInitialFocus`) and restores focus only when it is inside the\n * surface (or in a layer opened from it) at close, or was lost to `<body>` from there. Every hook\n * is always called and keys on `open`: a `modalType` that changes while open applies at once (the\n * trap, isolation and lock follow). It leaves focus inside the surface where it is; focus outside\n * it moves in, as on open: a switch to `modal` or `alert` starts the trap (the page turns inert),\n * and a switch to `non-modal` runs the initial focus (focus in the toast region, which a trap lets\n * in, moves into the surface). The Tab order of a non-modal surface (its place after the opener)\n * is the component's.\n *\n * A surface that mounts through the presence core (Dialog, Drawer) passes its open state as\n * `open`, never its mount (`isMounted`), and its element as `container` also while it is mounted\n * closed: the layer, the trap, the isolation and the scroll lock end and focus is restored in the\n * commit that closes it, while the element still runs its exit motion (`inert`) or stays mounted\n * (`hidden`, with `unmountOnClose={false}`); the next open starts them again with the same\n * element.\n */\nexport function useModalLayer(options: UseModalLayerOptions): DismissLayer {\n const {\n container,\n modalType = 'modal',\n initialFocus,\n triggerRef,\n finalFocusRef,\n ...dismissOptions\n } = options;\n const { open } = options;\n const nonModal = modalType === 'non-modal';\n const modal = open && !nonModal;\n\n const layer = useDismiss(\n nonModal\n ? {\n ...dismissOptions,\n kind: 'dialog',\n escape: 'inside',\n outsidePress: false,\n focusOutside: false,\n }\n : {\n ...dismissOptions,\n kind: 'modal',\n ...(modalType === 'alert' && { outsidePress: false }),\n },\n );\n useFocusTrap(container, { enabled: modal, layerId: layer.layerId, initialFocus });\n useInitialFocus(container, { enabled: open && nonModal, layerId: layer.layerId, initialFocus });\n useModalIsolation(modal, { layerId: layer.layerId, container });\n useScrollLock(modal);\n useRestoreFocus({\n enabled: open,\n container,\n triggerRef,\n finalFocusRef,\n requireFocusInside: nonModal,\n layerId: layer.layerId,\n });\n\n return layer;\n}\n"],"mappings":"0LAgEA,SAAgBM,EAAc,EAA6C,CACzE,GAAM,CACJ,YACA,YAAY,QACZ,eACA,aACA,gBACA,GAAGC,GACDC,EACE,CAAE,QAASA,EACXC,EAAWC,IAAc,YACzBC,EAAQC,GAAQ,CAACH,EAEjBI,EAAQb,EAAAA,WACZS,EACI,CACE,GAAGF,EACH,KAAM,SACN,OAAQ,SACR,aAAc,GACd,aAAc,EAChB,EACA,CACE,GAAGA,EACH,KAAM,QACN,GAAIG,IAAc,SAAW,CAAE,aAAc,EAAM,CACrD,CACN,EAcA,OAbAR,EAAA,aAAaY,EAAW,CAAE,QAASH,EAAO,QAASE,EAAM,QAAS,cAAa,CAAC,EAChFX,EAAA,gBAAgBY,EAAW,CAAE,QAASF,GAAQH,EAAU,QAASI,EAAM,QAAS,cAAa,CAAC,EAC9FV,EAAA,kBAAkBQ,EAAO,CAAE,QAASE,EAAM,QAAS,WAAU,CAAC,EAC9DR,EAAA,cAAcM,CAAK,EACnBP,EAAA,gBAAgB,CACd,QAASQ,EACT,YACA,aACA,gBACA,mBAAoBH,EACpB,QAASI,EAAM,OACjB,CAAC,EAEMA,CACT"}
@@ -1,30 +1,43 @@
1
1
  "use client";
2
2
  import { useDismiss as e } from "./useDismiss.mjs";
3
- import { useFocusTrap as t } from "./useFocusTrap.mjs";
4
- import { useRestoreFocus as n } from "./useRestoreFocus.mjs";
3
+ import { useFocusTrap as t, useInitialFocus as n } from "./useFocusTrap.mjs";
5
4
  import { useModalIsolation as r } from "./useModalIsolation.mjs";
6
- import { useScrollLock as i } from "./useScrollLock.mjs";
5
+ import { useRestoreFocus as i } from "./useRestoreFocus.mjs";
6
+ import { useScrollLock as a } from "./useScrollLock.mjs";
7
7
  //#region src/hooks/useModalLayer.ts
8
- function a(a) {
9
- let { container: o, initialFocus: s, triggerRef: c, finalFocusRef: l, ...u } = a, { open: d } = a, f = e({
10
- ...u,
11
- kind: "modal"
8
+ function o(o) {
9
+ let { container: s, modalType: c = "modal", initialFocus: l, triggerRef: u, finalFocusRef: d, ...f } = o, { open: p } = o, m = c === "non-modal", h = p && !m, g = e(m ? {
10
+ ...f,
11
+ kind: "dialog",
12
+ escape: "inside",
13
+ outsidePress: !1,
14
+ focusOutside: !1
15
+ } : {
16
+ ...f,
17
+ kind: "modal",
18
+ ...c === "alert" && { outsidePress: !1 }
12
19
  });
13
- return t(o, {
14
- enabled: d,
15
- layerId: f.layerId,
16
- initialFocus: s
17
- }), r(d, {
18
- layerId: f.layerId,
19
- container: o
20
- }), i(d), n({
21
- enabled: d,
22
- container: o,
23
- triggerRef: c,
24
- finalFocusRef: l
25
- }), f;
20
+ return t(s, {
21
+ enabled: h,
22
+ layerId: g.layerId,
23
+ initialFocus: l
24
+ }), n(s, {
25
+ enabled: p && m,
26
+ layerId: g.layerId,
27
+ initialFocus: l
28
+ }), r(h, {
29
+ layerId: g.layerId,
30
+ container: s
31
+ }), a(h), i({
32
+ enabled: p,
33
+ container: s,
34
+ triggerRef: u,
35
+ finalFocusRef: d,
36
+ requireFocusInside: m,
37
+ layerId: g.layerId
38
+ }), g;
26
39
  }
27
40
  //#endregion
28
- export { a as useModalLayer };
41
+ export { o as useModalLayer };
29
42
 
30
43
  //# sourceMappingURL=useModalLayer.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useModalLayer.mjs","names":["useModalLayer","dismissOptions","options","layer","useDismiss","useFocusTrap","container","open","useModalIsolation","useScrollLock","useRestoreFocus"],"sources":["../../src/hooks/useModalLayer.ts"],"sourcesContent":["import type * as React from 'react';\nimport { useDismiss, type DismissLayer, type UseDismissOptions } from './useDismiss';\nimport { useFocusTrap, type UseFocusTrapOptions } from './useFocusTrap';\nimport { useModalIsolation } from './useModalIsolation';\nimport { useRestoreFocus } from './useRestoreFocus';\nimport { useScrollLock } from './useScrollLock';\n\n/** Options of {@link useModalLayer}: `useDismiss` options (kind is always `'modal'`) plus focus. */\nexport interface UseModalLayerOptions extends Omit<UseDismissOptions, 'kind'> {\n /** The modal surface, held in state via a callback ref. */\n container: HTMLElement | null;\n /** What the focus trap focuses first. @default 'first' */\n initialFocus?: UseFocusTrapOptions['initialFocus'];\n /** The trigger: preferred focus-restore target. */\n triggerRef?: React.RefObject<HTMLElement | null>;\n /** Consumer override for the focus-restore target. */\n finalFocusRef?: React.RefObject<HTMLElement | null>;\n}\n\n/**\n * Everything a modal surface (Dialog, Drawer) needs, composed in the order that works:\n *\n * 1. `useDismiss({ kind: 'modal' })` — Escape and outside presses (the backdrop is outside the\n * content surface, so a click on it dismisses; a drag from the content does not).\n * 2. `useFocusTrap` — initial focus, Tab cycle incl. toasts and descendant layers.\n * 3. `useModalIsolation` — the rest of the page is `inert` (instead of `aria-modal`).\n * 4. `useScrollLock` — ref-counted page scroll lock.\n * 5. `useRestoreFocus` — the opener (or `triggerRef`/`finalFocusRef`) gets focus back.\n *\n * On close the trap is released before the isolation is lifted, and the isolation is lifted\n * before focus is restored (an inert opener cannot take focus). Wrap the surface in\n * `Portal layerId={layerId}` so layers opened inside the modal become its descendants.\n */\nexport function useModalLayer(options: UseModalLayerOptions): DismissLayer {\n const { container, initialFocus, triggerRef, finalFocusRef, ...dismissOptions } = options;\n const { open } = options;\n\n const layer = useDismiss({ ...dismissOptions, kind: 'modal' });\n useFocusTrap(container, { enabled: open, layerId: layer.layerId, initialFocus });\n useModalIsolation(open, { layerId: layer.layerId, container });\n useScrollLock(open);\n useRestoreFocus({ enabled: open, container, triggerRef, finalFocusRef });\n\n return layer;\n}\n"],"mappings":";;;;;;;AAiCA,SAAgBA,EAAc,GAA6C;CACzE,IAAM,EAAE,cAAW,iBAAc,eAAY,kBAAe,GAAGC,MAAmBC,GAC5E,EAAE,YAASA,GAEXC,IAAQC,EAAW;EAAE,GAAGH;EAAgB,MAAM;CAAQ,CAAC;CAM7D,OALAI,EAAaC,GAAW;EAAE,SAASC;EAAM,SAASJ,EAAM;EAAS;CAAa,CAAC,GAC/EK,EAAkBD,GAAM;EAAE,SAASJ,EAAM;EAAS;CAAU,CAAC,GAC7DM,EAAcF,CAAI,GAClBG,EAAgB;EAAE,SAASH;EAAM;EAAW;EAAY;CAAc,CAAC,GAEhEJ;AACT"}
1
+ {"version":3,"file":"useModalLayer.mjs","names":["useModalLayer","dismissOptions","options","nonModal","modalType","modal","open","layer","useDismiss","useFocusTrap","container","useInitialFocus","useModalIsolation","useScrollLock","useRestoreFocus"],"sources":["../../src/hooks/useModalLayer.ts"],"sourcesContent":["import type * as React from 'react';\nimport type { ModalType } from '../lib/types';\nimport { useDismiss, type DismissLayer, type UseDismissOptions } from './useDismiss';\nimport { useFocusTrap, useInitialFocus, type UseFocusTrapOptions } from './useFocusTrap';\nimport { useModalIsolation } from './useModalIsolation';\nimport { useRestoreFocus } from './useRestoreFocus';\nimport { useScrollLock } from './useScrollLock';\n\n/**\n * Options of {@link useModalLayer}: `useDismiss` options (the kind follows `modalType`) plus focus.\n */\nexport interface UseModalLayerOptions extends Omit<UseDismissOptions, 'kind'> {\n /** The modal surface, held in state via a callback ref. */\n container: HTMLElement | null;\n /**\n * `modal` (default) and `alert` register a `modal` layer, trap focus, isolate the page, lock its\n * scroll and restore focus (an `alert` layer ignores outside presses); `non-modal` registers a\n * `dialog` layer (`escape: 'inside'`, no outside press, no focus-outside), traps, isolates and\n * locks nothing, moves focus in on open with the trap's initial-focus rule and restores focus only\n * from inside (`requireFocusInside`).\n * @default 'modal'\n */\n modalType?: ModalType;\n /** What the focus trap (or a non-modal surface's initial focus) focuses first. @default 'first' */\n initialFocus?: UseFocusTrapOptions['initialFocus'];\n /** The trigger: preferred focus-restore target. */\n triggerRef?: React.RefObject<HTMLElement | null>;\n /** Consumer override for the focus-restore target. */\n finalFocusRef?: React.RefObject<HTMLElement | null>;\n}\n\n/**\n * Everything a modal surface (Dialog, Drawer) needs, composed in the order that works:\n *\n * 1. `useDismiss({ kind: 'modal' })` — Escape and outside presses (the backdrop is outside the\n * content surface, so a click on it dismisses; a drag from the content does not).\n * 2. `useFocusTrap` — initial focus, Tab cycle incl. toasts and descendant layers.\n * 3. `useModalIsolation` — the rest of the page is `inert` (instead of `aria-modal`).\n * 4. `useScrollLock` — ref-counted page scroll lock.\n * 5. `useRestoreFocus` — the opener (or `triggerRef`/`finalFocusRef`) gets focus back.\n *\n * On close the trap is released before the isolation is lifted, and the isolation is lifted\n * before focus is restored (an inert opener cannot take focus). Wrap the surface in\n * `Portal layerId={layerId}` so layers opened inside the modal become its descendants.\n *\n * `modalType: 'alert'` composes the same with `outsidePress: false`. `modalType: 'non-modal'`\n * registers a `dialog` layer (Escape only from inside its tree, no outside press, no focus-outside\n * dismissal), keeps the trap, the isolation and the scroll lock off, moves focus in on open with\n * the trap's initial-focus rule (`useInitialFocus`) and restores focus only when it is inside the\n * surface (or in a layer opened from it) at close, or was lost to `<body>` from there. Every hook\n * is always called and keys on `open`: a `modalType` that changes while open applies at once (the\n * trap, isolation and lock follow). It leaves focus inside the surface where it is; focus outside\n * it moves in, as on open: a switch to `modal` or `alert` starts the trap (the page turns inert),\n * and a switch to `non-modal` runs the initial focus (focus in the toast region, which a trap lets\n * in, moves into the surface). The Tab order of a non-modal surface (its place after the opener)\n * is the component's.\n *\n * A surface that mounts through the presence core (Dialog, Drawer) passes its open state as\n * `open`, never its mount (`isMounted`), and its element as `container` also while it is mounted\n * closed: the layer, the trap, the isolation and the scroll lock end and focus is restored in the\n * commit that closes it, while the element still runs its exit motion (`inert`) or stays mounted\n * (`hidden`, with `unmountOnClose={false}`); the next open starts them again with the same\n * element.\n */\nexport function useModalLayer(options: UseModalLayerOptions): DismissLayer {\n const {\n container,\n modalType = 'modal',\n initialFocus,\n triggerRef,\n finalFocusRef,\n ...dismissOptions\n } = options;\n const { open } = options;\n const nonModal = modalType === 'non-modal';\n const modal = open && !nonModal;\n\n const layer = useDismiss(\n nonModal\n ? {\n ...dismissOptions,\n kind: 'dialog',\n escape: 'inside',\n outsidePress: false,\n focusOutside: false,\n }\n : {\n ...dismissOptions,\n kind: 'modal',\n ...(modalType === 'alert' && { outsidePress: false }),\n },\n );\n useFocusTrap(container, { enabled: modal, layerId: layer.layerId, initialFocus });\n useInitialFocus(container, { enabled: open && nonModal, layerId: layer.layerId, initialFocus });\n useModalIsolation(modal, { layerId: layer.layerId, container });\n useScrollLock(modal);\n useRestoreFocus({\n enabled: open,\n container,\n triggerRef,\n finalFocusRef,\n requireFocusInside: nonModal,\n layerId: layer.layerId,\n });\n\n return layer;\n}\n"],"mappings":";;;;;;;AAgEA,SAAgBA,EAAc,GAA6C;CACzE,IAAM,EACJ,cACA,eAAY,SACZ,iBACA,eACA,kBACA,GAAGC,MACDC,GACE,EAAE,YAASA,GACXC,IAAWC,MAAc,aACzBC,IAAQC,KAAQ,CAACH,GAEjBI,IAAQC,EACZL,IACI;EACE,GAAGF;EACH,MAAM;EACN,QAAQ;EACR,cAAc;EACd,cAAc;CAChB,IACA;EACE,GAAGA;EACH,MAAM;EACN,GAAIG,MAAc,WAAW,EAAE,cAAc,GAAM;CACrD,CACN;CAcA,OAbAK,EAAaC,GAAW;EAAE,SAASL;EAAO,SAASE,EAAM;EAAS;CAAa,CAAC,GAChFI,EAAgBD,GAAW;EAAE,SAASJ,KAAQH;EAAU,SAASI,EAAM;EAAS;CAAa,CAAC,GAC9FK,EAAkBP,GAAO;EAAE,SAASE,EAAM;EAAS;CAAU,CAAC,GAC9DM,EAAcR,CAAK,GACnBS,EAAgB;EACd,SAASR;EACT;EACA;EACA;EACA,oBAAoBH;EACpB,SAASI,EAAM;CACjB,CAAC,GAEMA;AACT"}
@@ -0,0 +1,2 @@
1
+ "use client";const e=require("../lib/layers.cjs");let t=require("react");function n(){return!1}function r(){return(0,t.useSyncExternalStore)(e.subscribeLayers,e.hasOpenModal,n)}exports.useModalOpen=r;
2
+ //# sourceMappingURL=useModalOpen.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useModalOpen.cjs","names":["useSyncExternalStore","subscribeLayers","hasOpenModal","require_layers","react","getServerSnapshot","useModalOpen"],"sources":["../../src/hooks/useModalOpen.ts"],"sourcesContent":["import { useSyncExternalStore } from 'react';\nimport { hasOpenModal, subscribeLayers } from '../lib/layers';\n\nfunction getServerSnapshot(): boolean {\n return false;\n}\n\n/**\n * Whether a modal surface is open (a Dialog, an overlay Drawer, an alert, the DatePicker's calendar, or your\n * own `useDismiss({ kind: 'modal' })` layer): `true` from the\n * commit that opens it to the commit that closes it, not until its exit motion ends. A non-modal\n * Dialog or Drawer, a Popover or a Menu is no modal. `false` on the server and during hydration.\n *\n * Use it to hold back app behavior the modal should own, such as global keyboard shortcuts, instead\n * of guessing from the page's `inert` attributes.\n *\n * @example\n * const modalOpen = useModalOpen();\n * useEffect(() => {\n * if (modalOpen) return;\n * const onKey = (event: KeyboardEvent) => { if (event.key === '/') focusSearch(); };\n * document.addEventListener('keydown', onKey);\n * return () => document.removeEventListener('keydown', onKey);\n * }, [modalOpen]);\n */\nexport function useModalOpen(): boolean {\n return useSyncExternalStore(subscribeLayers, hasOpenModal, getServerSnapshot);\n}\n"],"mappings":"yEAGA,SAASK,GAA6B,CACpC,MAAO,EACT,CAoBA,SAAgBC,GAAwB,CACtC,OAAA,EAAON,EAAAA,qBAAAA,CAAqBC,EAAAA,gBAAiBC,EAAAA,aAAcG,CAAiB,CAC9E"}
@@ -0,0 +1,14 @@
1
+ "use client";
2
+ import { hasOpenModal as e, subscribeLayers as t } from "../lib/layers.mjs";
3
+ import { useSyncExternalStore as n } from "react";
4
+ //#region src/hooks/useModalOpen.ts
5
+ function r() {
6
+ return !1;
7
+ }
8
+ function i() {
9
+ return n(t, e, r);
10
+ }
11
+ //#endregion
12
+ export { i as useModalOpen };
13
+
14
+ //# sourceMappingURL=useModalOpen.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useModalOpen.mjs","names":["getServerSnapshot","useModalOpen","useSyncExternalStore","subscribeLayers","hasOpenModal"],"sources":["../../src/hooks/useModalOpen.ts"],"sourcesContent":["import { useSyncExternalStore } from 'react';\nimport { hasOpenModal, subscribeLayers } from '../lib/layers';\n\nfunction getServerSnapshot(): boolean {\n return false;\n}\n\n/**\n * Whether a modal surface is open (a Dialog, an overlay Drawer, an alert, the DatePicker's calendar, or your\n * own `useDismiss({ kind: 'modal' })` layer): `true` from the\n * commit that opens it to the commit that closes it, not until its exit motion ends. A non-modal\n * Dialog or Drawer, a Popover or a Menu is no modal. `false` on the server and during hydration.\n *\n * Use it to hold back app behavior the modal should own, such as global keyboard shortcuts, instead\n * of guessing from the page's `inert` attributes.\n *\n * @example\n * const modalOpen = useModalOpen();\n * useEffect(() => {\n * if (modalOpen) return;\n * const onKey = (event: KeyboardEvent) => { if (event.key === '/') focusSearch(); };\n * document.addEventListener('keydown', onKey);\n * return () => document.removeEventListener('keydown', onKey);\n * }, [modalOpen]);\n */\nexport function useModalOpen(): boolean {\n return useSyncExternalStore(subscribeLayers, hasOpenModal, getServerSnapshot);\n}\n"],"mappings":";;;;AAGA,SAASA,IAA6B;CACpC,OAAO;AACT;AAoBA,SAAgBC,IAAwB;CACtC,OAAOC,EAAqBC,GAAiBC,GAAcJ,CAAiB;AAC9E"}
@@ -1,2 +1,2 @@
1
- "use client";const e=require("./useEventCallback.cjs"),t=require("./useDirection.cjs");let n=require("react"),r=require("@floating-ui/react-dom");var i={top:`bottom`,bottom:`top`,left:`right`,right:`left`},a=`calc(var(--wave-popup-arrow-size, 8px) / -2)`;function o(e,t){return e===`start`?t===`rtl`?`right`:`left`:e===`end`?t===`rtl`?`left`:`right`:e}var s={x:0,y:0,width:0,height:0,top:0,right:0,bottom:0,left:0};function c(e){return typeof e.nodeType==`number`}function l(e,t){return{getBoundingClientRect:()=>e.current?.getBoundingClientRect()??s,contextElement:t}}function u(e,t,n){return(0,r.autoUpdate)(e,t,n,{elementResize:typeof ResizeObserver<`u`,layoutShift:typeof IntersectionObserver<`u`})}var d=({availableWidth:e,availableHeight:t,rects:n,elements:r})=>{let i=r.floating.style;i.setProperty(`--wave-popup-available-width`,`${Math.max(0,Math.floor(e))}px`),i.setProperty(`--wave-popup-available-height`,`${Math.max(0,Math.floor(t))}px`),i.setProperty(`--wave-popup-reference-width`,`${Math.round(n.reference.width)}px`)};function f(s){let{open:f,side:p=`bottom`,align:m=`start`,offset:h=4,flip:g=!0,shift:_={padding:8},matchReferenceWidth:v=!1,fitViewport:y=!1,arrowRef:b,strategy:x=`fixed`}=s,S=t.useDirection(),C=o(p,S),w=m===`center`?C:`${C}-${m}`,T=_===!1?0:_===!0?8:_.padding,E=typeof _==`object`&&_.crossAxis===!0,D=[(0,r.offset)(h)];g&&D.push((0,r.flip)({padding:T})),_!==!1&&D.push((0,r.shift)({padding:T,crossAxis:E})),(v||y)&&D.push((0,r.size)({padding:T,apply:d})),b&&D.push((0,r.arrow)({element:b}));let O=(0,n.useMemo)(()=>({...r.platform,isRTL:()=>S===`rtl`}),[S]),{refs:k,floatingStyles:A,placement:j,middlewareData:M,isPositioned:N,update:P}=(0,r.useFloating)({open:f,placement:w,strategy:x,middleware:D,platform:O,whileElementsMounted:f?u:void 0}),F=(0,n.useRef)(null),I=(0,n.useRef)(null),L=e.useEventCallback(P),{setReference:R,reference:z}=k,B=(0,n.useCallback)(e=>{if(e===null||c(e)){F.current=null,R(e);return}F.current=e;let t=I.current;(!t||t.contextElement!==e.contextElement)&&(t=l(F,e.contextElement),I.current=t),z.current===t?L():R(t)},[R,z,L]),[V,H=`center`]=j.split(`-`),U=(0,n.useMemo)(()=>{let e={...A};return v&&(e.width=`var(--wave-popup-reference-width)`),y&&(e.maxWidth=`var(--wave-popup-available-width)`,e.maxHeight=`var(--wave-popup-available-height)`),e},[A,v,y]),W=M.arrow?.x,G=M.arrow?.y,K=(0,n.useMemo)(()=>{let e={position:`absolute`,left:W==null?``:`${W}px`,top:G==null?``:`${G}px`};return e[i[V]]=a,e},[W,G,V]),q=(0,n.useMemo)(()=>({"data-side":V,"data-align":H,style:U}),[V,H,U]);return{setReference:B,setFloating:k.setFloating,floatingStyles:U,arrowStyles:K,placement:j,side:V,isPositioned:N,floatingProps:q}}exports.usePopupPosition=f;
1
+ "use client";const e=require("./useEventCallback.cjs"),t=require("./useDirection.cjs");let n=require("react"),r=require("@floating-ui/react-dom");function i(e,t){return{offset:e?.offset??t.offset,matchReferenceWidth:e?.matchReferenceWidth??t.matchReferenceWidth,fitViewport:e?.fitViewport??t.fitViewport,fallbackPlacements:e?.fallbackPlacements}}var a={top:`bottom`,bottom:`top`,left:`right`,right:`left`},o=`calc(var(--wave-popup-arrow-size, 8px) / -2)`;function s(e,t){return e===`start`?t===`rtl`?`right`:`left`:e===`end`?t===`rtl`?`left`:`right`:e}var c={x:0,y:0,width:0,height:0,top:0,right:0,bottom:0,left:0};function l(e){return typeof e.nodeType==`number`}function u(e,t){return{getBoundingClientRect:()=>e.current?.getBoundingClientRect()??c,contextElement:t}}function d(e,t,n){return(0,r.autoUpdate)(e,t,n,{elementResize:typeof ResizeObserver<`u`,layoutShift:typeof IntersectionObserver<`u`})}var f=({availableWidth:e,availableHeight:t,rects:n,elements:r})=>{let i=r.floating.style;i.setProperty(`--wave-popup-available-width`,`${Math.max(0,Math.floor(e))}px`),i.setProperty(`--wave-popup-available-height`,`${Math.max(0,Math.floor(t))}px`),i.setProperty(`--wave-popup-reference-width`,`${Math.round(n.reference.width)}px`)};function p(i){let{open:c,side:p=`bottom`,align:m=`start`,offset:h=4,flip:g=!0,shift:_={padding:8},matchReferenceWidth:v=!1,fitViewport:y=!1,arrowRef:b,strategy:x=`fixed`,fallbackPlacements:S,hideWhenDetached:C=!1,onPositioned:w}=i,T=t.useDirection(),E=s(p,T),D=(e,t)=>t===`center`?e:`${e}-${t}`,O=D(E,m),k=_===!1?0:_===!0?8:_.padding,A=typeof _==`object`&&_.crossAxis===!0,j=[(0,r.offset)(h)];g&&j.push((0,r.flip)({padding:k,...S&&{fallbackPlacements:S.map(e=>D(s(e.side,T),e.align??m))}})),_!==!1&&j.push((0,r.shift)({padding:k,crossAxis:A})),(v||y)&&j.push((0,r.size)({padding:k,apply:f})),C&&j.push((0,r.hide)({strategy:`referenceHidden`})),b&&j.push((0,r.arrow)({element:b}));let M=(0,n.useMemo)(()=>({...r.platform,isRTL:()=>T===`rtl`}),[T]),{refs:N,floatingStyles:P,placement:F,middlewareData:I,isPositioned:L,update:R}=(0,r.useFloating)({open:c,placement:O,strategy:x,middleware:j,platform:M,whileElementsMounted:c?d:void 0}),z=(0,n.useRef)(null),B=(0,n.useRef)(null),V=e.useEventCallback(R),H=e.useEventCallback(()=>{c&&R()}),{setReference:U,reference:W}=N,G=(0,n.useCallback)(e=>{if(e===null||l(e)){z.current=null,U(e);return}z.current=e;let t=B.current;(!t||t.contextElement!==e.contextElement)&&(t=u(z,e.contextElement),B.current=t),W.current===t?V():U(t)},[U,W,V]),[K,q=`center`]=F.split(`-`),J=e.useEventCallback(e=>w?.(e));(0,n.useEffect)(()=>{c&&L&&J({placement:F,side:K,isPositioned:!0})},[c,L,F,K,J]);let Y=I.hide?.referenceHidden===!0,X=(0,n.useMemo)(()=>{let e={...P};return v&&(e.width=`var(--wave-popup-reference-width)`),y&&(e.maxWidth=`var(--wave-popup-available-width)`,e.maxHeight=`var(--wave-popup-available-height)`),Y&&(e.visibility=`hidden`),e},[P,v,y,Y]),Z=I.arrow?.x,Q=I.arrow?.y,$=(0,n.useMemo)(()=>{let e={position:`absolute`,left:Z==null?``:`${Z}px`,top:Q==null?``:`${Q}px`};return e[a[K]]=o,e},[Z,Q,K]),ee=(0,n.useMemo)(()=>({"data-side":K,"data-align":q,style:X}),[K,q,X]);return{setReference:G,setFloating:N.setFloating,floatingStyles:X,arrowStyles:$,placement:F,side:K,isPositioned:L,update:H,floatingProps:ee}}exports.resolvePositioning=i,exports.usePopupPosition=p;
2
2
  //# sourceMappingURL=usePopupPosition.cjs.map