@mortenbrudvik/waveui 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/README.md +758 -152
  2. package/dist/components/button/Toolbar.cjs +1 -1
  3. package/dist/components/button/Toolbar.cjs.map +1 -1
  4. package/dist/components/button/Toolbar.mjs +25 -30
  5. package/dist/components/button/Toolbar.mjs.map +1 -1
  6. package/dist/components/button/Toolbar.navigation.cjs +2 -0
  7. package/dist/components/button/Toolbar.navigation.cjs.map +1 -0
  8. package/dist/components/button/Toolbar.navigation.mjs +17 -0
  9. package/dist/components/button/Toolbar.navigation.mjs.map +1 -0
  10. package/dist/components/data-display/Avatar.cjs +1 -1
  11. package/dist/components/data-display/Avatar.cjs.map +1 -1
  12. package/dist/components/data-display/Avatar.context.cjs +2 -0
  13. package/dist/components/data-display/Avatar.context.cjs.map +1 -0
  14. package/dist/components/data-display/Avatar.context.mjs +9 -0
  15. package/dist/components/data-display/Avatar.context.mjs.map +1 -0
  16. package/dist/components/data-display/Avatar.mjs +66 -61
  17. package/dist/components/data-display/Avatar.mjs.map +1 -1
  18. package/dist/components/data-display/InteractionTag.cjs +2 -0
  19. package/dist/components/data-display/InteractionTag.cjs.map +1 -0
  20. package/dist/components/data-display/InteractionTag.mjs +234 -0
  21. package/dist/components/data-display/InteractionTag.mjs.map +1 -0
  22. package/dist/components/data-display/Tag.cjs +1 -1
  23. package/dist/components/data-display/Tag.cjs.map +1 -1
  24. package/dist/components/data-display/Tag.mjs +222 -90
  25. package/dist/components/data-display/Tag.mjs.map +1 -1
  26. package/dist/components/data-display/TagGroup.cjs +2 -0
  27. package/dist/components/data-display/TagGroup.cjs.map +1 -0
  28. package/dist/components/data-display/TagGroup.context.cjs +2 -0
  29. package/dist/components/data-display/TagGroup.context.cjs.map +1 -0
  30. package/dist/components/data-display/TagGroup.context.mjs +113 -0
  31. package/dist/components/data-display/TagGroup.context.mjs.map +1 -0
  32. package/dist/components/data-display/TagGroup.mjs +149 -0
  33. package/dist/components/data-display/TagGroup.mjs.map +1 -0
  34. package/dist/components/data-display/tagStyles.cjs +2 -0
  35. package/dist/components/data-display/tagStyles.cjs.map +1 -0
  36. package/dist/components/data-display/tagStyles.mjs +108 -0
  37. package/dist/components/data-display/tagStyles.mjs.map +1 -0
  38. package/dist/components/feedback/Toaster.cjs +1 -1
  39. package/dist/components/feedback/Toaster.cjs.map +1 -1
  40. package/dist/components/feedback/Toaster.mjs +213 -181
  41. package/dist/components/feedback/Toaster.mjs.map +1 -1
  42. package/dist/components/input/Calendar.cjs +2 -0
  43. package/dist/components/input/Calendar.cjs.map +1 -0
  44. package/dist/components/input/Calendar.mjs +352 -0
  45. package/dist/components/input/Calendar.mjs.map +1 -0
  46. package/dist/components/input/Calendar.views.cjs +2 -0
  47. package/dist/components/input/Calendar.views.cjs.map +1 -0
  48. package/dist/components/input/Calendar.views.mjs +194 -0
  49. package/dist/components/input/Calendar.views.mjs.map +1 -0
  50. package/dist/components/input/ColorArea.cjs +1 -1
  51. package/dist/components/input/ColorArea.cjs.map +1 -1
  52. package/dist/components/input/ColorArea.mjs +18 -18
  53. package/dist/components/input/ColorArea.mjs.map +1 -1
  54. package/dist/components/input/Combobox.cjs +1 -1
  55. package/dist/components/input/Combobox.cjs.map +1 -1
  56. package/dist/components/input/Combobox.expand.cjs +1 -1
  57. package/dist/components/input/Combobox.expand.cjs.map +1 -1
  58. package/dist/components/input/Combobox.expand.mjs +7 -7
  59. package/dist/components/input/Combobox.expand.mjs.map +1 -1
  60. package/dist/components/input/Combobox.mjs +232 -156
  61. package/dist/components/input/Combobox.mjs.map +1 -1
  62. package/dist/components/input/Combobox.text.cjs +2 -0
  63. package/dist/components/input/Combobox.text.cjs.map +1 -0
  64. package/dist/components/input/Combobox.text.mjs +24 -0
  65. package/dist/components/input/Combobox.text.mjs.map +1 -0
  66. package/dist/components/input/DatePicker.cjs +1 -1
  67. package/dist/components/input/DatePicker.cjs.map +1 -1
  68. package/dist/components/input/DatePicker.mjs +203 -288
  69. package/dist/components/input/DatePicker.mjs.map +1 -1
  70. package/dist/components/input/Dropdown.cjs +1 -1
  71. package/dist/components/input/Dropdown.cjs.map +1 -1
  72. package/dist/components/input/Dropdown.mjs +135 -109
  73. package/dist/components/input/Dropdown.mjs.map +1 -1
  74. package/dist/components/input/Listbox.cjs +2 -0
  75. package/dist/components/input/Listbox.cjs.map +1 -0
  76. package/dist/components/input/Listbox.mjs +134 -0
  77. package/dist/components/input/Listbox.mjs.map +1 -0
  78. package/dist/components/input/Option.cjs +1 -1
  79. package/dist/components/input/Option.cjs.map +1 -1
  80. package/dist/components/input/Option.mjs +163 -97
  81. package/dist/components/input/Option.mjs.map +1 -1
  82. package/dist/components/input/SwatchPicker.cjs +1 -1
  83. package/dist/components/input/SwatchPicker.cjs.map +1 -1
  84. package/dist/components/input/SwatchPicker.context.cjs +2 -0
  85. package/dist/components/input/SwatchPicker.context.cjs.map +1 -0
  86. package/dist/components/input/SwatchPicker.context.mjs +35 -0
  87. package/dist/components/input/SwatchPicker.context.mjs.map +1 -0
  88. package/dist/components/input/SwatchPicker.mjs +131 -103
  89. package/dist/components/input/SwatchPicker.mjs.map +1 -1
  90. package/dist/components/input/SwatchPicker.swatches.cjs +2 -0
  91. package/dist/components/input/SwatchPicker.swatches.cjs.map +1 -0
  92. package/dist/components/input/SwatchPicker.swatches.mjs +207 -0
  93. package/dist/components/input/SwatchPicker.swatches.mjs.map +1 -0
  94. package/dist/components/input/TagPicker.cjs +1 -1
  95. package/dist/components/input/TagPicker.cjs.map +1 -1
  96. package/dist/components/input/TagPicker.context.cjs +2 -0
  97. package/dist/components/input/TagPicker.context.cjs.map +1 -0
  98. package/dist/components/input/TagPicker.context.mjs +20 -0
  99. package/dist/components/input/TagPicker.context.mjs.map +1 -0
  100. package/dist/components/input/TagPicker.create.cjs +2 -0
  101. package/dist/components/input/TagPicker.create.cjs.map +1 -0
  102. package/dist/components/input/TagPicker.create.mjs +24 -0
  103. package/dist/components/input/TagPicker.create.mjs.map +1 -0
  104. package/dist/components/input/TagPicker.mjs +13 -274
  105. package/dist/components/input/TagPicker.mjs.map +1 -1
  106. package/dist/components/input/TagPicker.option.cjs +2 -0
  107. package/dist/components/input/TagPicker.option.cjs.map +1 -0
  108. package/dist/components/input/TagPicker.option.mjs +45 -0
  109. package/dist/components/input/TagPicker.option.mjs.map +1 -0
  110. package/dist/components/input/TagPicker.overflow.cjs +2 -0
  111. package/dist/components/input/TagPicker.overflow.cjs.map +1 -0
  112. package/dist/components/input/TagPicker.overflow.mjs +153 -0
  113. package/dist/components/input/TagPicker.overflow.mjs.map +1 -0
  114. package/dist/components/input/TagPicker.parts.cjs +2 -0
  115. package/dist/components/input/TagPicker.parts.cjs.map +1 -0
  116. package/dist/components/input/TagPicker.parts.mjs +425 -0
  117. package/dist/components/input/TagPicker.parts.mjs.map +1 -0
  118. package/dist/components/input/TagPicker.root.cjs +2 -0
  119. package/dist/components/input/TagPicker.root.cjs.map +1 -0
  120. package/dist/components/input/TagPicker.root.mjs +416 -0
  121. package/dist/components/input/TagPicker.root.mjs.map +1 -0
  122. package/dist/components/input/TimePicker.cjs +1 -1
  123. package/dist/components/input/TimePicker.cjs.map +1 -1
  124. package/dist/components/input/TimePicker.mjs +77 -77
  125. package/dist/components/input/TimePicker.mjs.map +1 -1
  126. package/dist/components/input/dateUtils.cjs +1 -1
  127. package/dist/components/input/dateUtils.cjs.map +1 -1
  128. package/dist/components/input/dateUtils.mjs +114 -96
  129. package/dist/components/input/dateUtils.mjs.map +1 -1
  130. package/dist/components/input/pickerLabels.cjs +2 -0
  131. package/dist/components/input/pickerLabels.cjs.map +1 -0
  132. package/dist/components/input/pickerLabels.mjs +16 -0
  133. package/dist/components/input/pickerLabels.mjs.map +1 -0
  134. package/dist/components/input/pickerValues.cjs +2 -0
  135. package/dist/components/input/pickerValues.cjs.map +1 -0
  136. package/dist/components/input/pickerValues.mjs +24 -0
  137. package/dist/components/input/pickerValues.mjs.map +1 -0
  138. package/dist/components/layout/Accordion.cjs.map +1 -1
  139. package/dist/components/layout/Accordion.mjs.map +1 -1
  140. package/dist/components/navigation/Hamburger.cjs +2 -0
  141. package/dist/components/navigation/Hamburger.cjs.map +1 -0
  142. package/dist/components/navigation/Hamburger.mjs +54 -0
  143. package/dist/components/navigation/Hamburger.mjs.map +1 -0
  144. package/dist/components/navigation/Nav.cjs +1 -1
  145. package/dist/components/navigation/Nav.cjs.map +1 -1
  146. package/dist/components/navigation/Nav.context.cjs +2 -0
  147. package/dist/components/navigation/Nav.context.cjs.map +1 -0
  148. package/dist/components/navigation/Nav.context.mjs +137 -0
  149. package/dist/components/navigation/Nav.context.mjs.map +1 -0
  150. package/dist/components/navigation/Nav.mjs +151 -258
  151. package/dist/components/navigation/Nav.mjs.map +1 -1
  152. package/dist/components/navigation/Nav.parts.cjs +2 -0
  153. package/dist/components/navigation/Nav.parts.cjs.map +1 -0
  154. package/dist/components/navigation/Nav.parts.mjs +77 -0
  155. package/dist/components/navigation/Nav.parts.mjs.map +1 -0
  156. package/dist/components/navigation/Nav.root.cjs +2 -0
  157. package/dist/components/navigation/Nav.root.cjs.map +1 -0
  158. package/dist/components/navigation/Nav.root.mjs +78 -0
  159. package/dist/components/navigation/Nav.root.mjs.map +1 -0
  160. package/dist/components/navigation/NavDrawer.cjs +2 -0
  161. package/dist/components/navigation/NavDrawer.cjs.map +1 -0
  162. package/dist/components/navigation/NavDrawer.context.cjs +2 -0
  163. package/dist/components/navigation/NavDrawer.context.cjs.map +1 -0
  164. package/dist/components/navigation/NavDrawer.context.mjs +11 -0
  165. package/dist/components/navigation/NavDrawer.context.mjs.map +1 -0
  166. package/dist/components/navigation/NavDrawer.mjs +179 -0
  167. package/dist/components/navigation/NavDrawer.mjs.map +1 -0
  168. package/dist/components/overlays/Dialog.cjs +1 -1
  169. package/dist/components/overlays/Dialog.cjs.map +1 -1
  170. package/dist/components/overlays/Dialog.mjs +190 -125
  171. package/dist/components/overlays/Dialog.mjs.map +1 -1
  172. package/dist/components/overlays/Dialog.shared.cjs +1 -1
  173. package/dist/components/overlays/Dialog.shared.cjs.map +1 -1
  174. package/dist/components/overlays/Dialog.shared.mjs +157 -81
  175. package/dist/components/overlays/Dialog.shared.mjs.map +1 -1
  176. package/dist/components/overlays/Dialog.slots.cjs +2 -0
  177. package/dist/components/overlays/Dialog.slots.cjs.map +1 -0
  178. package/dist/components/overlays/Dialog.slots.mjs +129 -0
  179. package/dist/components/overlays/Dialog.slots.mjs.map +1 -0
  180. package/dist/components/overlays/Drawer.cjs +1 -1
  181. package/dist/components/overlays/Drawer.cjs.map +1 -1
  182. package/dist/components/overlays/Drawer.context.cjs +2 -0
  183. package/dist/components/overlays/Drawer.context.cjs.map +1 -0
  184. package/dist/components/overlays/Drawer.context.mjs +29 -0
  185. package/dist/components/overlays/Drawer.context.mjs.map +1 -0
  186. package/dist/components/overlays/Drawer.mjs +12 -187
  187. package/dist/components/overlays/Drawer.mjs.map +1 -1
  188. package/dist/components/overlays/Drawer.panel.cjs +2 -0
  189. package/dist/components/overlays/Drawer.panel.cjs.map +1 -0
  190. package/dist/components/overlays/Drawer.panel.mjs +159 -0
  191. package/dist/components/overlays/Drawer.panel.mjs.map +1 -0
  192. package/dist/components/overlays/Drawer.parts.cjs +2 -0
  193. package/dist/components/overlays/Drawer.parts.cjs.map +1 -0
  194. package/dist/components/overlays/Drawer.parts.mjs +152 -0
  195. package/dist/components/overlays/Drawer.parts.mjs.map +1 -0
  196. package/dist/components/overlays/Drawer.root.cjs +2 -0
  197. package/dist/components/overlays/Drawer.root.cjs.map +1 -0
  198. package/dist/components/overlays/Drawer.root.mjs +207 -0
  199. package/dist/components/overlays/Drawer.root.mjs.map +1 -0
  200. package/dist/components/overlays/Drawer.scroll.cjs +2 -0
  201. package/dist/components/overlays/Drawer.scroll.cjs.map +1 -0
  202. package/dist/components/overlays/Drawer.scroll.mjs +42 -0
  203. package/dist/components/overlays/Drawer.scroll.mjs.map +1 -0
  204. package/dist/components/table/useGridNavigation.cjs +1 -1
  205. package/dist/components/table/useGridNavigation.cjs.map +1 -1
  206. package/dist/components/table/useGridNavigation.mjs +67 -70
  207. package/dist/components/table/useGridNavigation.mjs.map +1 -1
  208. package/dist/hooks/useActiveDescendant.cjs +2 -0
  209. package/dist/hooks/useActiveDescendant.cjs.map +1 -0
  210. package/dist/hooks/useActiveDescendant.mjs +56 -0
  211. package/dist/hooks/useActiveDescendant.mjs.map +1 -0
  212. package/dist/hooks/useDismiss.cjs.map +1 -1
  213. package/dist/hooks/useDismiss.mjs.map +1 -1
  214. package/dist/hooks/useFocusTrap.cjs +1 -1
  215. package/dist/hooks/useFocusTrap.cjs.map +1 -1
  216. package/dist/hooks/useFocusTrap.mjs +115 -98
  217. package/dist/hooks/useFocusTrap.mjs.map +1 -1
  218. package/dist/hooks/useListbox.cjs +1 -1
  219. package/dist/hooks/useListbox.cjs.map +1 -1
  220. package/dist/hooks/useListbox.mjs +250 -210
  221. package/dist/hooks/useListbox.mjs.map +1 -1
  222. package/dist/hooks/useModalLayer.cjs +1 -1
  223. package/dist/hooks/useModalLayer.cjs.map +1 -1
  224. package/dist/hooks/useModalLayer.mjs +35 -22
  225. package/dist/hooks/useModalLayer.mjs.map +1 -1
  226. package/dist/hooks/useRestoreFocus.cjs +1 -1
  227. package/dist/hooks/useRestoreFocus.cjs.map +1 -1
  228. package/dist/hooks/useRestoreFocus.mjs +91 -78
  229. package/dist/hooks/useRestoreFocus.mjs.map +1 -1
  230. package/dist/hooks/useRovingGrid.cjs +2 -0
  231. package/dist/hooks/useRovingGrid.cjs.map +1 -0
  232. package/dist/hooks/useRovingGrid.mjs +69 -0
  233. package/dist/hooks/useRovingGrid.mjs.map +1 -0
  234. package/dist/hooks/useRovingTabIndex.cjs +1 -1
  235. package/dist/hooks/useRovingTabIndex.cjs.map +1 -1
  236. package/dist/hooks/useRovingTabIndex.mjs +102 -78
  237. package/dist/hooks/useRovingTabIndex.mjs.map +1 -1
  238. package/dist/index.cjs +1 -1
  239. package/dist/index.d.cts +4391 -394
  240. package/dist/index.d.ts +4391 -394
  241. package/dist/index.mjs +115 -101
  242. package/dist/lib/classNames.cjs +1 -1
  243. package/dist/lib/classNames.cjs.map +1 -1
  244. package/dist/lib/classNames.mjs +170 -74
  245. package/dist/lib/classNames.mjs.map +1 -1
  246. package/dist/lib/date.cjs +2 -0
  247. package/dist/lib/date.cjs.map +1 -0
  248. package/dist/lib/date.mjs +44 -0
  249. package/dist/lib/date.mjs.map +1 -0
  250. package/dist/lib/focus.cjs +1 -1
  251. package/dist/lib/focus.cjs.map +1 -1
  252. package/dist/lib/focus.mjs +5 -2
  253. package/dist/lib/focus.mjs.map +1 -1
  254. package/dist/lib/icons.cjs +1 -1
  255. package/dist/lib/icons.cjs.map +1 -1
  256. package/dist/lib/icons.mjs +13 -6
  257. package/dist/lib/icons.mjs.map +1 -1
  258. package/dist/lib/layers.cjs +1 -1
  259. package/dist/lib/layers.cjs.map +1 -1
  260. package/dist/lib/layers.mjs +53 -47
  261. package/dist/lib/layers.mjs.map +1 -1
  262. package/dist/styles.css +1 -1
  263. package/package.json +1 -1
  264. package/src/styles/tokens.css +25 -0
@@ -0,0 +1,2 @@
1
+ "use client";const e=require("../../_virtual/_rolldown/runtime.cjs");let t=require("react");t=e.__toESM(t,1);var n=1;function r(e){let{scrollTop:t,scrollHeight:r,clientHeight:i}=e;return r-i<n?`none`:t<n?`top`:t+i>=r-n?`bottom`:`middle`}function i(e,t){e.addEventListener(`scroll`,t,{passive:!0});let n=typeof ResizeObserver>`u`?null:new ResizeObserver(()=>t()),r=()=>{if(n){n.disconnect(),n.observe(e);for(let t of Array.from(e.children))n.observe(t)}};r();let i=typeof MutationObserver>`u`?null:new MutationObserver(()=>{r(),t()});return i?.observe(e,{childList:!0,subtree:!0,characterData:!0,attributes:!0}),()=>{e.removeEventListener(`scroll`,t),n?.disconnect(),i?.disconnect()}}var a=()=>`none`;function o(e){let n=t.useCallback(t=>e?i(e,t):()=>{},[e]),o=t.useCallback(()=>e?r(e):`none`,[e]);return t.useSyncExternalStore(n,o,a)}var s={header:`border-b border-transparent data-[scroll-state=middle]:border-border data-[scroll-state=bottom]:border-border forced-colors:border-[CanvasText]`,footer:`border-t border-transparent data-[scroll-state=top]:border-border data-[scroll-state=middle]:border-border forced-colors:border-[CanvasText]`};exports.scrollSeparatorClasses=s,exports.useScrollState=o;
2
+ //# sourceMappingURL=Drawer.scroll.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Drawer.scroll.cjs","names":["React","require_runtime","react","SCROLL_TOLERANCE","getScrollState","element","scrollHeight","clientHeight","scrollTop","subscribeScrollState","onChange","resizeObserver","observeBoxes","child","mutationObserver","getServerSnapshot","useScrollState","subscribe","getSnapshot","scrollSeparatorClasses"],"sources":["../../../src/components/overlays/Drawer.scroll.ts"],"sourcesContent":["import * as React from 'react';\n\n/*\n * The scroll state of a drawer's scrolling body, which draws the separators of its header and\n * footer (the scroll store of D108's contract, module-only). Imports no component; the root, the\n * parts and NavDrawer read it.\n */\n\n/**\n * How far a scrolling body is scrolled: `none` (the content fits), `top` (at the top, more below),\n * `middle`, or `bottom` (at the end). Rendered as `data-scroll-state`.\n */\nexport type DrawerScrollState = 'none' | 'top' | 'middle' | 'bottom';\n\n/**\n * Below this distance (px) an overflow or a scroll offset counts as none: zoom and high-density\n * screens give fractional offsets, so the end of the content can sit a fraction of a pixel away.\n */\nconst SCROLL_TOLERANCE = 1;\n\n/** The scroll state of `element`, with a 1px tolerance on each threshold. */\nfunction getScrollState(element: HTMLElement): DrawerScrollState {\n const { scrollTop, scrollHeight, clientHeight } = element;\n if (scrollHeight - clientHeight < SCROLL_TOLERANCE) return 'none';\n if (scrollTop < SCROLL_TOLERANCE) return 'top';\n if (scrollTop + clientHeight >= scrollHeight - SCROLL_TOLERANCE) return 'bottom';\n return 'middle';\n}\n\n/**\n * Calls `onChange` whenever the scroll state of `element` may have changed: a scroll (a passive\n * listener on the element), a size change of the element or of one of its element children (a\n * `ResizeObserver`, where it exists), and a change of its content (a `MutationObserver`, which\n * also observes the children it adds). Content can grow without a render of its own (an\n * Accordion expanding, an image loading) while the element's box stays the same, which observing\n * only the element would miss. Adds no `document` listener. Returns the unsubscribe function.\n */\nfunction subscribeScrollState(element: HTMLElement, onChange: () => void): () => void {\n element.addEventListener('scroll', onChange, { passive: true });\n const resizeObserver =\n typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(() => onChange());\n const observeBoxes = () => {\n if (!resizeObserver) return;\n resizeObserver.disconnect();\n resizeObserver.observe(element);\n for (const child of Array.from(element.children)) resizeObserver.observe(child);\n };\n observeBoxes();\n const mutationObserver =\n typeof MutationObserver === 'undefined'\n ? null\n : new MutationObserver(() => {\n observeBoxes();\n onChange();\n });\n mutationObserver?.observe(element, {\n childList: true,\n subtree: true,\n characterData: true,\n attributes: true,\n });\n return () => {\n element.removeEventListener('scroll', onChange);\n resizeObserver?.disconnect();\n mutationObserver?.disconnect();\n };\n}\n\n/** The server snapshot: nothing is measured on the server. */\nconst getServerSnapshot = (): DrawerScrollState => 'none';\n\n/**\n * The scroll state of `element` (held in state through a callback ref), read through\n * `useSyncExternalStore`: the component re-renders only when the state changes. `'none'` for\n * `null` and on the server.\n */\nexport function useScrollState(element: HTMLElement | null): DrawerScrollState {\n const subscribe = React.useCallback(\n (onChange: () => void) => (element ? subscribeScrollState(element, onChange) : () => {}),\n [element],\n );\n const getSnapshot = React.useCallback(\n (): DrawerScrollState => (element ? getScrollState(element) : 'none'),\n [element],\n );\n return React.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n\n/**\n * The separators of a drawer's header (`header`: a line under it while the body is scrolled away\n * from its top) and footer (`footer`: a line above it while content continues below). Each is a\n * border that always exists and only changes colour with the element's `data-scroll-state`, so it\n * never shifts the layout; forced colors draw both lines at all times. Literal strings, so the\n * module makes no call at load and the CSS build sees every class.\n */\nexport const scrollSeparatorClasses: { readonly header: string; readonly footer: string } = {\n header:\n 'border-b border-transparent data-[scroll-state=middle]:border-border data-[scroll-state=bottom]:border-border forced-colors:border-[CanvasText]',\n footer:\n 'border-t border-transparent data-[scroll-state=top]:border-border data-[scroll-state=middle]:border-border forced-colors:border-[CanvasText]',\n};\n"],"mappings":"6GAkBA,IAAMG,EAAmB,EAGzB,SAASC,EAAe,EAAyC,CAC/D,GAAM,CAAE,YAAW,eAAc,gBAAiBC,EAIlD,OAHIC,EAAeC,EAAeJ,EAAyB,OACvDK,EAAYL,EAAyB,MACrCK,EAAYD,GAAgBD,EAAeH,EAAyB,SACjE,QACT,CAUA,SAASM,EAAqB,EAAsB,EAAkC,CACpFJ,EAAQ,iBAAiB,SAAUK,EAAU,CAAE,QAAS,EAAK,CAAC,EAC9D,IAAMC,EACJ,OAAO,eAAmB,IAAc,KAAO,IAAI,mBAAqBD,EAAS,CAAC,EAC9EE,MAAqB,CACzB,GAAKD,EAEL,CADAA,EAAe,WAAW,EAC1BA,EAAe,QAAQN,CAAO,EAC9B,IAAK,IAAMQ,KAAS,MAAM,KAAKR,EAAQ,QAAQ,EAAGM,EAAe,QAAQE,CAAK,CADhD,CAEhC,EACAD,EAAa,EACb,IAAME,EACJ,OAAO,iBAAqB,IACxB,KACA,IAAI,qBAAuB,CACzBF,EAAa,EACbF,EAAS,CACX,CAAC,EAOP,OANAI,GAAkB,QAAQT,EAAS,CACjC,UAAW,GACX,QAAS,GACT,cAAe,GACf,WAAY,EACd,CAAC,MACY,CACXA,EAAQ,oBAAoB,SAAUK,CAAQ,EAC9CC,GAAgB,WAAW,EAC3BG,GAAkB,WAAW,CAC/B,CACF,CAGA,IAAMC,MAA6C,OAOnD,SAAgBC,EAAe,EAAgD,CAC7E,IAAMC,EAAYjB,EAAM,YACrB,GAA0BK,EAAUI,EAAqBJ,EAASK,CAAQ,MAAU,CAAC,EACtF,CAACL,CAAO,CACV,EACMa,EAAclB,EAAM,gBACEK,EAAUD,EAAeC,CAAO,EAAI,OAC9D,CAACA,CAAO,CACV,EACA,OAAOL,EAAM,qBAAqBiB,EAAWC,EAAaH,CAAiB,CAC7E,CASA,IAAaI,EAA+E,CAC1F,OACE,kJACF,OACE,8IACJ"}
@@ -0,0 +1,42 @@
1
+ "use client";
2
+ import * as e from "react";
3
+ //#region src/components/overlays/Drawer.scroll.ts
4
+ var t = 1;
5
+ function n(e) {
6
+ let { scrollTop: n, scrollHeight: r, clientHeight: i } = e;
7
+ return r - i < t ? "none" : n < t ? "top" : n + i >= r - t ? "bottom" : "middle";
8
+ }
9
+ function r(e, t) {
10
+ e.addEventListener("scroll", t, { passive: !0 });
11
+ let n = typeof ResizeObserver > "u" ? null : new ResizeObserver(() => t()), r = () => {
12
+ if (n) {
13
+ n.disconnect(), n.observe(e);
14
+ for (let t of Array.from(e.children)) n.observe(t);
15
+ }
16
+ };
17
+ r();
18
+ let i = typeof MutationObserver > "u" ? null : new MutationObserver(() => {
19
+ r(), t();
20
+ });
21
+ return i?.observe(e, {
22
+ childList: !0,
23
+ subtree: !0,
24
+ characterData: !0,
25
+ attributes: !0
26
+ }), () => {
27
+ e.removeEventListener("scroll", t), n?.disconnect(), i?.disconnect();
28
+ };
29
+ }
30
+ var i = () => "none";
31
+ function a(t) {
32
+ let a = e.useCallback((e) => t ? r(t, e) : () => {}, [t]), o = e.useCallback(() => t ? n(t) : "none", [t]);
33
+ return e.useSyncExternalStore(a, o, i);
34
+ }
35
+ var o = {
36
+ header: "border-b border-transparent data-[scroll-state=middle]:border-border data-[scroll-state=bottom]:border-border forced-colors:border-[CanvasText]",
37
+ footer: "border-t border-transparent data-[scroll-state=top]:border-border data-[scroll-state=middle]:border-border forced-colors:border-[CanvasText]"
38
+ };
39
+ //#endregion
40
+ export { o as scrollSeparatorClasses, a as useScrollState };
41
+
42
+ //# sourceMappingURL=Drawer.scroll.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Drawer.scroll.mjs","names":["React","SCROLL_TOLERANCE","getScrollState","element","scrollHeight","clientHeight","scrollTop","subscribeScrollState","onChange","resizeObserver","observeBoxes","child","mutationObserver","getServerSnapshot","useScrollState","subscribe","getSnapshot","scrollSeparatorClasses"],"sources":["../../../src/components/overlays/Drawer.scroll.ts"],"sourcesContent":["import * as React from 'react';\n\n/*\n * The scroll state of a drawer's scrolling body, which draws the separators of its header and\n * footer (the scroll store of D108's contract, module-only). Imports no component; the root, the\n * parts and NavDrawer read it.\n */\n\n/**\n * How far a scrolling body is scrolled: `none` (the content fits), `top` (at the top, more below),\n * `middle`, or `bottom` (at the end). Rendered as `data-scroll-state`.\n */\nexport type DrawerScrollState = 'none' | 'top' | 'middle' | 'bottom';\n\n/**\n * Below this distance (px) an overflow or a scroll offset counts as none: zoom and high-density\n * screens give fractional offsets, so the end of the content can sit a fraction of a pixel away.\n */\nconst SCROLL_TOLERANCE = 1;\n\n/** The scroll state of `element`, with a 1px tolerance on each threshold. */\nfunction getScrollState(element: HTMLElement): DrawerScrollState {\n const { scrollTop, scrollHeight, clientHeight } = element;\n if (scrollHeight - clientHeight < SCROLL_TOLERANCE) return 'none';\n if (scrollTop < SCROLL_TOLERANCE) return 'top';\n if (scrollTop + clientHeight >= scrollHeight - SCROLL_TOLERANCE) return 'bottom';\n return 'middle';\n}\n\n/**\n * Calls `onChange` whenever the scroll state of `element` may have changed: a scroll (a passive\n * listener on the element), a size change of the element or of one of its element children (a\n * `ResizeObserver`, where it exists), and a change of its content (a `MutationObserver`, which\n * also observes the children it adds). Content can grow without a render of its own (an\n * Accordion expanding, an image loading) while the element's box stays the same, which observing\n * only the element would miss. Adds no `document` listener. Returns the unsubscribe function.\n */\nfunction subscribeScrollState(element: HTMLElement, onChange: () => void): () => void {\n element.addEventListener('scroll', onChange, { passive: true });\n const resizeObserver =\n typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(() => onChange());\n const observeBoxes = () => {\n if (!resizeObserver) return;\n resizeObserver.disconnect();\n resizeObserver.observe(element);\n for (const child of Array.from(element.children)) resizeObserver.observe(child);\n };\n observeBoxes();\n const mutationObserver =\n typeof MutationObserver === 'undefined'\n ? null\n : new MutationObserver(() => {\n observeBoxes();\n onChange();\n });\n mutationObserver?.observe(element, {\n childList: true,\n subtree: true,\n characterData: true,\n attributes: true,\n });\n return () => {\n element.removeEventListener('scroll', onChange);\n resizeObserver?.disconnect();\n mutationObserver?.disconnect();\n };\n}\n\n/** The server snapshot: nothing is measured on the server. */\nconst getServerSnapshot = (): DrawerScrollState => 'none';\n\n/**\n * The scroll state of `element` (held in state through a callback ref), read through\n * `useSyncExternalStore`: the component re-renders only when the state changes. `'none'` for\n * `null` and on the server.\n */\nexport function useScrollState(element: HTMLElement | null): DrawerScrollState {\n const subscribe = React.useCallback(\n (onChange: () => void) => (element ? subscribeScrollState(element, onChange) : () => {}),\n [element],\n );\n const getSnapshot = React.useCallback(\n (): DrawerScrollState => (element ? getScrollState(element) : 'none'),\n [element],\n );\n return React.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n\n/**\n * The separators of a drawer's header (`header`: a line under it while the body is scrolled away\n * from its top) and footer (`footer`: a line above it while content continues below). Each is a\n * border that always exists and only changes colour with the element's `data-scroll-state`, so it\n * never shifts the layout; forced colors draw both lines at all times. Literal strings, so the\n * module makes no call at load and the CSS build sees every class.\n */\nexport const scrollSeparatorClasses: { readonly header: string; readonly footer: string } = {\n header:\n 'border-b border-transparent data-[scroll-state=middle]:border-border data-[scroll-state=bottom]:border-border forced-colors:border-[CanvasText]',\n footer:\n 'border-t border-transparent data-[scroll-state=top]:border-border data-[scroll-state=middle]:border-border forced-colors:border-[CanvasText]',\n};\n"],"mappings":";;;AAkBA,IAAMC,IAAmB;AAGzB,SAASC,EAAe,GAAyC;CAC/D,IAAM,EAAE,cAAW,iBAAc,oBAAiBC;CAIlD,OAHIC,IAAeC,IAAeJ,IAAyB,SACvDK,IAAYL,IAAyB,QACrCK,IAAYD,KAAgBD,IAAeH,IAAyB,WACjE;AACT;AAUA,SAASM,EAAqB,GAAsB,GAAkC;CACpF,EAAQ,iBAAiB,UAAUC,GAAU,EAAE,SAAS,GAAK,CAAC;CAC9D,IAAMC,IACJ,OAAO,iBAAmB,MAAc,OAAO,IAAI,qBAAqBD,EAAS,CAAC,GAC9EE,UAAqB;EACzB,IAAKD,GAEL;GADAA,EAAe,WAAW,GAC1BA,EAAe,QAAQN,CAAO;GAC9B,KAAK,IAAMQ,KAAS,MAAM,KAAKR,EAAQ,QAAQ,GAAG,EAAe,QAAQQ,CAAK;EADhD;CAEhC;CACA,EAAa;CACb,IAAMC,IACJ,OAAO,mBAAqB,MACxB,OACA,IAAI,uBAAuB;EAEzB,AADAF,EAAa,GACbF,EAAS;CACX,CAAC;CAOP,OANAI,GAAkB,QAAQT,GAAS;EACjC,WAAW;EACX,SAAS;EACT,eAAe;EACf,YAAY;CACd,CAAC,SACY;EAGX,AAFAA,EAAQ,oBAAoB,UAAUK,CAAQ,GAC9CC,GAAgB,WAAW,GAC3BG,GAAkB,WAAW;CAC/B;AACF;AAGA,IAAMC,UAA6C;AAOnD,SAAgBC,EAAe,GAAgD;CAC7E,IAAMC,IAAYf,EAAM,aACrB,MAA0BG,IAAUI,EAAqBJ,GAASK,CAAQ,UAAU,CAAC,GACtF,CAACL,CAAO,CACV,GACMa,IAAchB,EAAM,kBACEG,IAAUD,EAAeC,CAAO,IAAI,QAC9D,CAACA,CAAO,CACV;CACA,OAAOH,EAAM,qBAAqBe,GAAWC,GAAaH,CAAiB;AAC7E;AASA,IAAaI,IAA+E;CAC1F,QACE;CACF,QACE;AACJ"}
@@ -1,2 +1,2 @@
1
- "use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/direction.cjs"),n=require("../../lib/focus.cjs");let r=require("react");r=e.__toESM(r,1);var i=new Set([`checkbox`,`radio`,`button`,`submit`,`reset`,`image`,`file`,`color`]),a=new Set([`textbox`,`searchbox`,`combobox`,`spinbutton`,`slider`]),o=[`[data-roving-container]`,...[`toolbar`,`radiogroup`,`listbox`,`grid`,`treegrid`,`tablist`,`menu`,`menubar`,`tree`].map(e=>`[role="${e}"]`)].join(`, `),s=`${n.FOCUSABLE_SELECTOR}, ${o}`,c=[`disabled`,`tabindex`,`hidden`,`inert`,`type`,`contenteditable`,`role`,`data-roving-container`,`aria-selected`];function l(e){let t=e.localName;if(t===`textarea`||t===`select`)return!0;if(t===`input`)return!i.has(e.type);let n=e.getAttribute(`contenteditable`);if(n!==null&&n!==`false`)return!0;let r=e.getAttribute(`role`);return r!==null&&a.has(r)}function u(e){return e.matches(o)}function d(e,t){if(e===t)return!1;for(let n=e.parentElement;n&&n!==t;n=n.parentElement)if(u(n))return!0;return!1}function f(e){let t=e.querySelector(`[tabindex="0"]`);return t&&n.isFocusable(t)?t:n.getFirstTabbable(e,{includeContainer:!0})}function p(e){return e.localName===`input`&&e.type===`hidden`||e.matches(`:disabled`)?!1:e.closest(`[hidden], [inert]`)===null}function m(e){if(!e.name)return!1;let t=e.getRootNode();if(typeof t.querySelectorAll!=`function`)return!1;let n=t.querySelectorAll(`input[type="radio"]:checked`);for(let t of Array.from(n))if(t!==e&&t.name===e.name&&t.form===e.form)return!0;return!1}function h(e){return e.localName===`td`||e.localName===`th`}function g(e){return e.getRootNode().activeElement===e}function _(e,t){let n=t instanceof Element?t:null;for(;n&&n!==e;){if(h(n)&&n.closest(`table`)===e)return n;n=n.parentElement}return null}function v(e){return Array.from(e.rows).filter(e=>e.cells.length>0&&!e.hidden)}var y=1e3;function b(e){let t=new Map(e.map((e,t)=>[e,t])),n=e.map(()=>[]),r=(e,t,r,i)=>{for(let a=0;a<r;a+=1)n[e][t+a]=i};return e.forEach((e,i)=>{let a=n[i],o=0;for(let n of Array.from(e.cells)){for(;a[o];)o+=1;let s=Math.min(Math.max(n.colSpan,1),y);r(i,o,s,n);let c=n.rowSpan===0?1/0:n.rowSpan-1;for(let i=e.nextElementSibling;i&&c>0;i=i.nextElementSibling){if(i.localName!==`tr`)continue;--c;let e=t.get(i);e!==void 0&&r(e,o,s,n)}o+=s}}),n}function x(e,t){for(let n=Math.min(t,e.length-1);n>=0;--n){let t=e[n];if(t)return t}return null}function S(e,t,n){if(t instanceof Element){h(t)&&t.closest(`table`)===e&&n.add(t);for(let r of Array.from(t.querySelectorAll(`td, th`)))r.closest(`table`)===e&&n.add(r)}}function C(e={}){let i=Math.max(1,e.pageSize??10),[a,o]=r.useState(null),y=r.useRef(null),C=r.useRef(null),[w]=r.useState(()=>new WeakSet),[T]=r.useState(()=>new WeakMap),[E]=r.useState(()=>new WeakSet),D=r.useRef(i);r.useLayoutEffect(()=>{D.current=i},[i]);let O=r.useCallback((e,t)=>{let r=[],i=null;for(let a of Array.from(e.querySelectorAll(s))){if(i&&i.contains(a))continue;if(u(a)){i=a,(t?f(a)!==null:p(a))&&r.push({element:a,composite:!0});continue}let e=a.getAttribute(`tabindex`);e!==null&&Number(e)<0&&!w.has(a)||(t?n.isFocusable(a):p(a))&&r.push({element:a,composite:!1})}return r},[w]),k=r.useCallback((e,t,n)=>{if(t.length!==1)return e;let[{element:r,composite:i}]=t;return i||l(r)||n&&r instanceof HTMLInputElement&&r.type===`radio`&&!r.checked&&m(r)?e:r},[]),A=r.useCallback((e,t)=>{let n=String(t);e.getAttribute(`tabindex`)!==n&&(T.set(e,n),e.setAttribute(`tabindex`,n)),w.add(e)},[w,T]),j=r.useCallback((e,t,n)=>{let r=O(e,t);if(n){E.delete(e),A(e,-1);for(let e of r)e.composite||A(e.element,0);return}let i=k(e,r,t),a=!1;i===e?(E.delete(e),A(e,t?0:-1)):w.has(e)&&g(e)?(a=!0,E.add(e),A(e,t?0:-1)):w.has(e)&&(E.delete(e),w.delete(e),e.hasAttribute(`tabindex`)&&(T.set(e,null),e.removeAttribute(`tabindex`)));for(let e of r){if(e.composite)continue;let n=e.element===i&&t&&!a;A(e.element,n?0:-1)}},[k,O,E,A,w,T]),M=r.useCallback((e,t)=>{let n=v(e),r=y.current,i=new Set;if((!r||!r.isConnected||!n.includes(r.parentElement))&&(r=n[0]?.cells[0]??null,y.current=r,r&&i.add(r)),C.current!==null&&C.current!==r){let e=C.current;C.current=null,i.add(e)}let a=C.current;if(t===void 0)for(let e of n)for(let t of Array.from(e.cells))i.add(t);else for(let e of t)e&&i.add(e);for(let t of i)t.isConnected&&_(e,t)===t&&j(t,t===r,t===a)},[j]);r.useLayoutEffect(()=>{if(!a||(M(a),typeof MutationObserver>`u`))return;let e=new MutationObserver(e=>{let t=!1,n=!1,r=new Set;for(let i of e){let e=i.target;if(!(e instanceof Element)||d(e,a))continue;if(i.type===`attributes`){if(i.attributeName===`tabindex`&&T.has(e)&&T.get(e)===e.getAttribute(`tabindex`))continue;t=!0;let o=_(a,e);o?r.add(o):e.localName===`tr`?S(a,e,r):n=!0;continue}t=!0;let o=_(a,e);if(o){r.add(o);continue}for(let e of Array.from(i.addedNodes))S(a,e,r)}t&&(y.current&&r.add(y.current),M(a,n?void 0:r))});return e.observe(a,{childList:!0,subtree:!0,attributes:!0,attributeFilter:c}),()=>e.disconnect()},[a,M,T]);let N=r.useCallback((e,t,n)=>{let r=y.current,i=C.current;y.current=t,C.current=n?t:null,M(e,[r,i,t])},[M]),P=r.useCallback((e,t)=>{N(e,t,!1),k(t,O(t,!0),!0).focus()},[N,k,O]),F=r.useCallback(e=>{let n=e.currentTarget;if(e.defaultPrevented)return;let r=_(n,e.target);if(!r)return;let i=O(r,!0),a=k(r,i,!1),o=e.target===r;if(!o&&e.target!==a){e.key===`Escape`&&(e.preventDefault(),P(n,r));return}if(e.altKey||e.metaKey)return;if((e.key===`Enter`||e.key===`F2`)&&o){let t=null;for(let e of i)if(t=e.composite?f(e.element):e.element,t)break;if(!t)return;e.preventDefault(),N(n,r,!0),t.focus();return}let s=v(n),c=s.findIndex(e=>e===r.parentElement);if(c<0)return;let l=Array.from(s[c].cells).indexOf(r),u=s.length-1,d=e.ctrlKey,p=null,m=null;switch(e.key){case`ArrowLeft`:case`ArrowRight`:if(d)break;p=[c,l+(t.getArrowIntent(e.key,{orientation:`horizontal`,dir:t.getDirection(n)})===`next`?1:-1)];break;case`ArrowDown`:d||(m=[c+1,1]);break;case`ArrowUp`:d||(m=[c-1,-1]);break;case`Home`:p=d?[0,0]:[c,0];break;case`End`:p=d?[u,1/0]:[c,1/0];break;case`PageDown`:d||(m=[Math.min(c+D.current,u),1]);break;case`PageUp`:d||(m=[Math.max(c-D.current,0),-1])}if(!p&&!m)return;e.preventDefault();let h=null;if(m){let e=b(s),t=Math.max(e[c].indexOf(r),0);for(let n=m[0];n>=0&&n<=u;n+=m[1]){let i=x(e[n],t);if(i&&i!==r){h=i;break}}}else if(p){let[e,t]=p,n=s[e].cells;h=t===1/0?n[n.length-1]:n.item(t)}h&&h!==r&&P(n,h)},[N,P,k,O]),I=r.useCallback(e=>{let t=e.currentTarget,n=e.target,r=_(t,n);if(!r)return;let i=n!==r&&n!==k(r,O(r,!0),!1);N(t,r,i)},[N,k,O]),L=r.useCallback(e=>{let t=e.currentTarget,n=e.target,r=[];n instanceof Element&&h(n)&&E.has(n)&&r.push(n);let i=e.relatedTarget,a=C.current;a!==null&&!(i instanceof Node&&t.contains(i))&&(C.current=null,r.push(a)),r.length>0&&M(t,r)},[E,M]);return{gridRef:o,gridProps:r.useMemo(()=>({onKeyDown:F,onFocus:I,onBlur:L}),[L,I,F])}}exports.isTextEntryElement=l,exports.useGridNavigation=C;
1
+ "use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/direction.cjs"),n=require("../../lib/focus.cjs");let r=require("react");r=e.__toESM(r,1);var i=new Set([`checkbox`,`radio`,`button`,`submit`,`reset`,`image`,`file`,`color`]),a=new Set([`textbox`,`searchbox`,`combobox`,`spinbutton`,`slider`]),o=[`[data-roving-container]`,...[`toolbar`,`radiogroup`,`listbox`,`grid`,`treegrid`,`tablist`,`menu`,`menubar`,`tree`].map(e=>`[role="${e}"]`)].join(`, `),s=`${n.FOCUSABLE_SELECTOR}, ${o}`,c=[`disabled`,`tabindex`,`hidden`,`inert`,`type`,`contenteditable`,`role`,`data-roving-container`,`aria-selected`];function l(e){let t=e.localName;if(t===`textarea`||t===`select`)return!0;if(t===`input`)return!i.has(e.type);let n=e.getAttribute(`contenteditable`);if(n!==null&&n!==`false`)return!0;let r=e.getAttribute(`role`);return r!==null&&a.has(r)}function u(e){return e.matches(o)}function d(e,t){if(e===t)return!1;for(let n=e.parentElement;n&&n!==t;n=n.parentElement)if(u(n))return!0;return!1}function f(e){let t=e.querySelector(`[tabindex="0"]`);return t&&n.isFocusable(t)?t:n.getFirstTabbable(e,{includeContainer:!0})}function p(e){return e.localName===`input`&&e.type===`hidden`||e.matches(`:disabled`)?!1:e.closest(`[hidden], [inert]`)===null}function m(e){if(!e.name)return!1;let t=e.getRootNode();if(typeof t.querySelectorAll!=`function`)return!1;let n=t.querySelectorAll(`input[type="radio"]:checked`);for(let t of Array.from(n))if(t!==e&&t.name===e.name&&t.form===e.form)return!0;return!1}function h(e){return e.localName===`td`||e.localName===`th`}function g(e,t){let n=t instanceof Element?t:null;for(;n&&n!==e;){if(h(n)&&n.closest(`table`)===e)return n;n=n.parentElement}return null}function _(e){return Array.from(e.rows).filter(e=>e.cells.length>0&&!e.hidden)}var v=1e3;function y(e){let t=new Map(e.map((e,t)=>[e,t])),n=e.map(()=>[]),r=(e,t,r,i)=>{for(let a=0;a<r;a+=1)n[e][t+a]=i};return e.forEach((e,i)=>{let a=n[i],o=0;for(let n of Array.from(e.cells)){for(;a[o];)o+=1;let s=Math.min(Math.max(n.colSpan,1),v);r(i,o,s,n);let c=n.rowSpan===0?1/0:n.rowSpan-1;for(let i=e.nextElementSibling;i&&c>0;i=i.nextElementSibling){if(i.localName!==`tr`)continue;--c;let e=t.get(i);e!==void 0&&r(e,o,s,n)}o+=s}}),n}function b(e,t){for(let n=Math.min(t,e.length-1);n>=0;--n){let t=e[n];if(t)return t}return null}function x(e,t,n){if(t instanceof Element){h(t)&&t.closest(`table`)===e&&n.add(t);for(let r of Array.from(t.querySelectorAll(`td, th`)))r.closest(`table`)===e&&n.add(r)}}function S(e={}){let i=Math.max(1,e.pageSize??10),[a,o]=r.useState(null),v=r.useRef(null),S=r.useRef(null),[C]=r.useState(()=>new WeakSet),[w]=r.useState(()=>new WeakMap),[T]=r.useState(()=>new WeakSet),E=r.useRef(i);r.useLayoutEffect(()=>{E.current=i},[i]);let D=r.useCallback((e,t)=>{let r=[],i=null;for(let a of Array.from(e.querySelectorAll(s))){if(i&&i.contains(a))continue;if(u(a)){i=a,(t?f(a)!==null:p(a))&&r.push({element:a,composite:!0});continue}let e=a.getAttribute(`tabindex`);e!==null&&Number(e)<0&&!C.has(a)||(t?n.isFocusable(a):p(a))&&r.push({element:a,composite:!1})}return r},[C]),O=r.useCallback((e,t,n)=>{if(t.length!==1)return e;let[{element:r,composite:i}]=t;return i||l(r)||n&&r instanceof HTMLInputElement&&r.type===`radio`&&!r.checked&&m(r)?e:r},[]),k=r.useCallback((e,t)=>{let n=String(t);e.getAttribute(`tabindex`)!==n&&(w.set(e,n),e.setAttribute(`tabindex`,n)),C.add(e)},[C,w]),A=r.useCallback((e,t,r)=>{let i=D(e,t);if(r){T.delete(e),k(e,-1);for(let e of i)e.composite||k(e.element,0);return}let a=O(e,i,t),o=!1;a===e?(T.delete(e),k(e,t?0:-1)):C.has(e)&&n.isFocused(e)?(o=!0,T.add(e),k(e,t?0:-1)):C.has(e)&&(T.delete(e),C.delete(e),e.hasAttribute(`tabindex`)&&(w.set(e,null),e.removeAttribute(`tabindex`)));for(let e of i){if(e.composite)continue;let n=e.element===a&&t&&!o;k(e.element,n?0:-1)}},[O,D,T,k,C,w]),j=r.useCallback((e,t)=>{let n=_(e),r=v.current,i=new Set;if((!r||!r.isConnected||!n.includes(r.parentElement))&&(r=n[0]?.cells[0]??null,v.current=r,r&&i.add(r)),S.current!==null&&S.current!==r){let e=S.current;S.current=null,i.add(e)}let a=S.current;if(t===void 0)for(let e of n)for(let t of Array.from(e.cells))i.add(t);else for(let e of t)e&&i.add(e);for(let t of i)t.isConnected&&g(e,t)===t&&A(t,t===r,t===a)},[A]);r.useLayoutEffect(()=>{if(!a||(j(a),typeof MutationObserver>`u`))return;let e=new MutationObserver(e=>{let t=!1,n=!1,r=new Set;for(let i of e){let e=i.target;if(!(e instanceof Element)||d(e,a))continue;if(i.type===`attributes`){if(i.attributeName===`tabindex`&&w.has(e)&&w.get(e)===e.getAttribute(`tabindex`))continue;t=!0;let o=g(a,e);o?r.add(o):e.localName===`tr`?x(a,e,r):n=!0;continue}t=!0;let o=g(a,e);if(o){r.add(o);continue}for(let e of Array.from(i.addedNodes))x(a,e,r)}t&&(v.current&&r.add(v.current),j(a,n?void 0:r))});return e.observe(a,{childList:!0,subtree:!0,attributes:!0,attributeFilter:c}),()=>e.disconnect()},[a,j,w]);let M=r.useCallback((e,t,n)=>{let r=v.current,i=S.current;v.current=t,S.current=n?t:null,j(e,[r,i,t])},[j]),N=r.useCallback((e,t)=>{M(e,t,!1),O(t,D(t,!0),!0).focus()},[M,O,D]),P=r.useCallback(e=>{let n=e.currentTarget;if(e.defaultPrevented)return;let r=g(n,e.target);if(!r)return;let i=D(r,!0),a=O(r,i,!1),o=e.target===r;if(!o&&e.target!==a){e.key===`Escape`&&(e.preventDefault(),N(n,r));return}if(e.altKey||e.metaKey)return;if((e.key===`Enter`||e.key===`F2`)&&o){let t=null;for(let e of i)if(t=e.composite?f(e.element):e.element,t)break;if(!t)return;e.preventDefault(),M(n,r,!0),t.focus();return}let s=_(n),c=s.findIndex(e=>e===r.parentElement);if(c<0)return;let l=Array.from(s[c].cells).indexOf(r),u=s.length-1,d=e.ctrlKey,p=null,m=null;switch(e.key){case`ArrowLeft`:case`ArrowRight`:if(d)break;p=[c,l+(t.getArrowIntent(e.key,{orientation:`horizontal`,dir:t.getDirection(n)})===`next`?1:-1)];break;case`ArrowDown`:d||(m=[c+1,1]);break;case`ArrowUp`:d||(m=[c-1,-1]);break;case`Home`:p=d?[0,0]:[c,0];break;case`End`:p=d?[u,1/0]:[c,1/0];break;case`PageDown`:d||(m=[Math.min(c+E.current,u),1]);break;case`PageUp`:d||(m=[Math.max(c-E.current,0),-1])}if(!p&&!m)return;e.preventDefault();let h=null;if(m){let e=y(s),t=Math.max(e[c].indexOf(r),0);for(let n=m[0];n>=0&&n<=u;n+=m[1]){let i=b(e[n],t);if(i&&i!==r){h=i;break}}}else if(p){let[e,t]=p,n=s[e].cells;h=t===1/0?n[n.length-1]:n.item(t)}h&&h!==r&&N(n,h)},[M,N,O,D]),F=r.useCallback(e=>{let t=e.currentTarget,n=e.target,r=g(t,n);if(!r)return;let i=n!==r&&n!==O(r,D(r,!0),!1);M(t,r,i)},[M,O,D]),I=r.useCallback(e=>{let t=e.currentTarget,n=e.target,r=[];n instanceof Element&&h(n)&&T.has(n)&&r.push(n);let i=e.relatedTarget,a=S.current;a!==null&&!(i instanceof Node&&t.contains(i))&&(S.current=null,r.push(a)),r.length>0&&j(t,r)},[T,j]);return{gridRef:o,gridProps:r.useMemo(()=>({onKeyDown:P,onFocus:F,onBlur:I}),[I,F,P])}}exports.isTextEntryElement=l,exports.useGridNavigation=S;
2
2
  //# sourceMappingURL=useGridNavigation.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useGridNavigation.cjs","names":["FOCUSABLE_SELECTOR","isFocusable","getFirstTabbable","React","getArrowIntent","getDirection","require_runtime","require_direction","require_focus","react","NON_TEXT_INPUT_TYPES","KEY_CONSUMING_ROLES","COMPOSITE_SELECTOR","role","WIDGET_CANDIDATE_SELECTOR","OBSERVED_ATTRIBUTES","isTextEntryElement","tag","el","editable","isNestedComposite","isInsideNestedComposite","node","grid","getCompositeEntry","stop","composite","isLikelyFocusable","hasCheckedPeer","radio","root","checked","other","isCellElement","isFocused","getCell","getRows","row","MAX_COL_SPAN","getColumnMap","indexOf","rows","index","map","cover","offset","colSpan","rowIndex","column","cell","own","remaining","next","covered","getCellAtColumn","rowMap","collectCells","into","useGridNavigation","pageSize","options","setGrid","activeCellRef","interactionCellRef","stamped","written","retained","pageSizeRef","getWidgets","widgets","precise","tabIndex","getTarget","element","checkRadioGroup","setTabIndex","value","syncCell","isActive","isInteraction","widget","target","keepCell","isStop","sync","table","active","targets","previous","interaction","cells","observer","relevant","all","dirty","record","records","activate","previousActive","previousInteraction","focusCell","onKeyDown","event","widgetTarget","onCell","entry","columnIndex","lastRow","ctrl","vertical","destination","columns","candidate","nextRow","nextColumn","rowCells","onFocus","onBlur","left"],"sources":["../../../src/components/table/useGridNavigation.ts"],"sourcesContent":["import * as React from 'react';\nimport { getArrowIntent, getDirection } from '../../lib/direction';\nimport { FOCUSABLE_SELECTOR, getFirstTabbable, isFocusable } from '../../lib/focus';\n\n/** Options of {@link useGridNavigation}. */\nexport interface UseGridNavigationOptions {\n /**\n * How many rows PageUp and PageDown move focus.\n * @default 10\n */\n pageSize?: number;\n}\n\n/** Handlers {@link useGridNavigation} needs on the grid element (compose them with the consumer's). */\nexport interface GridNavigationHandlers {\n onKeyDown: React.KeyboardEventHandler<HTMLTableElement>;\n onFocus: React.FocusEventHandler<HTMLTableElement>;\n onBlur: React.FocusEventHandler<HTMLTableElement>;\n}\n\n/** Result of {@link useGridNavigation}. */\nexport interface UseGridNavigationResult {\n /** Callback ref for the `<table role=\"grid\">` element. Stable identity. */\n gridRef: React.RefCallback<HTMLTableElement>;\n /** Keyboard and focus handlers for the grid element. Stable identities. */\n gridProps: GridNavigationHandlers;\n}\n\n/** Input types that do not take arrow or character keys, so a lone one can be a cell's focus target. */\nconst NON_TEXT_INPUT_TYPES: ReadonlySet<string> = new Set([\n 'checkbox',\n 'radio',\n 'button',\n 'submit',\n 'reset',\n 'image',\n 'file',\n 'color',\n]);\n\n/** Widget roles that use arrow or character keys themselves (treated like text entry). */\nconst KEY_CONSUMING_ROLES: ReadonlySet<string> = new Set([\n 'textbox',\n 'searchbox',\n 'combobox',\n 'spinbutton',\n 'slider',\n]);\n\n/**\n * Roles of composite widgets that manage their own children (and usually their tab indexes): an\n * element with one of them, or with `data-roving-container`, is a nested composite. Close to the\n * list of `useRovingTabIndex`, which is kept separately and differs on purpose: here a `toolbar`\n * is a composite (a toolbar in a cell is one widget), while `spinbutton` is not (it is one\n * focusable control that uses the arrow keys, handled by `KEY_CONSUMING_ROLES` like a text\n * field). Keep both lists in mind when adding a role.\n */\nconst COMPOSITE_ROLES: readonly string[] = [\n 'toolbar',\n 'radiogroup',\n 'listbox',\n 'grid',\n 'treegrid',\n 'tablist',\n 'menu',\n 'menubar',\n 'tree',\n];\n\nconst COMPOSITE_SELECTOR = [\n '[data-roving-container]',\n ...COMPOSITE_ROLES.map((role) => `[role=\"${role}\"]`),\n].join(', ');\n\n/** Every element that can be a cell widget: a focusable element or a nested composite root. */\nconst WIDGET_CANDIDATE_SELECTOR = `${FOCUSABLE_SELECTOR}, ${COMPOSITE_SELECTOR}`;\n\n/**\n * Attributes whose changes can change a cell's widgets or the grid's rows. `aria-selected` on a row\n * signals a selection change, which can change the active cell's target (radio groups).\n */\nconst OBSERVED_ATTRIBUTES = [\n 'disabled',\n 'tabindex',\n 'hidden',\n 'inert',\n 'type',\n 'contenteditable',\n 'role',\n 'data-roving-container',\n 'aria-selected',\n];\n\n/**\n * Whether `el` is a text-entry control (or another widget that uses arrow keys itself): a text-like\n * `input`, `textarea`, `select`, a `contenteditable` element, or an element with role textbox,\n * searchbox, combobox, spinbutton or slider. Such widgets are never a cell's navigation target: the\n * cell takes focus and Enter/F2 moves into the widget, so the arrow keys keep moving the caret.\n */\nexport function isTextEntryElement(el: Element): boolean {\n const tag = el.localName;\n if (tag === 'textarea' || tag === 'select') return true;\n if (tag === 'input') return !NON_TEXT_INPUT_TYPES.has((el as HTMLInputElement).type);\n const editable = el.getAttribute('contenteditable');\n if (editable !== null && editable !== 'false') return true;\n const role = el.getAttribute('role');\n return role !== null && KEY_CONSUMING_ROLES.has(role);\n}\n\n/** Whether `el` is a nested composite widget (own roving container or a composite role). */\nfunction isNestedComposite(el: Element): boolean {\n return el.matches(COMPOSITE_SELECTOR);\n}\n\n/** Whether `node` is strictly inside a nested composite of `grid` (the composite root excluded). */\nfunction isInsideNestedComposite(node: Element, grid: HTMLTableElement): boolean {\n if (node === grid) return false;\n for (let el = node.parentElement; el && el !== grid; el = el.parentElement) {\n if (isNestedComposite(el)) return true;\n }\n return false;\n}\n\n/**\n * The element a nested composite is entered at: its own tab stop (`tabindex=\"0\"`), else its first\n * tabbable element (the root itself when it is the focusable element).\n */\nfunction getCompositeEntry(composite: HTMLElement): HTMLElement | null {\n const stop = composite.querySelector<HTMLElement>('[tabindex=\"0\"]');\n if (stop && isFocusable(stop)) return stop;\n return getFirstTabbable(composite, { includeContainer: true });\n}\n\n/**\n * A cheap focusability check for cells that do not hold the tab stop: no style reads, only the\n * attributes that take an element out of the focus order. The cell that holds (or receives) the tab\n * stop is checked precisely with `isFocusable`.\n */\nfunction isLikelyFocusable(el: HTMLElement): boolean {\n if (el.localName === 'input' && (el as HTMLInputElement).type === 'hidden') return false;\n if (el.matches(':disabled')) return false;\n return el.closest('[hidden], [inert]') === null;\n}\n\n/** Whether another radio of `radio`'s named group is checked (the browser's tab stop of the group). */\nfunction hasCheckedPeer(radio: HTMLInputElement): boolean {\n if (!radio.name) return false;\n const root = radio.getRootNode() as Document | ShadowRoot;\n if (typeof root.querySelectorAll !== 'function') return false;\n const checked = root.querySelectorAll<HTMLInputElement>('input[type=\"radio\"]:checked');\n for (const other of Array.from(checked)) {\n if (other !== radio && other.name === radio.name && other.form === radio.form) return true;\n }\n return false;\n}\n\nfunction isCellElement(el: Element): el is HTMLTableCellElement {\n return el.localName === 'td' || el.localName === 'th';\n}\n\n/** Whether `el` is the focused element of its document (or shadow root). */\nfunction isFocused(el: Element): boolean {\n return (el.getRootNode() as Partial<DocumentOrShadowRoot>).activeElement === el;\n}\n\n/** The cell of `grid` that contains `node` (a nested table's cells belong to the outer cell). */\nfunction getCell(grid: HTMLTableElement, node: EventTarget | null): HTMLTableCellElement | null {\n let el: Element | null = node instanceof Element ? node : null;\n while (el && el !== grid) {\n if (isCellElement(el) && el.closest('table') === grid) return el;\n el = el.parentElement;\n }\n return null;\n}\n\n/** The rows of `grid` (header, body and footer rows; a nested table's rows are not included). */\nfunction getRows(grid: HTMLTableElement): HTMLTableRowElement[] {\n return Array.from(grid.rows).filter((row) => row.cells.length > 0 && !row.hidden);\n}\n\n/** Browsers clamp `colSpan` to 1..1000 (`rowSpan` 0 covers the rest of its row group). */\nconst MAX_COL_SPAN = 1000;\n\n/** Per row, the cell covering each visual column (see {@link getColumnMap}). */\ntype ColumnMap = ReadonlyArray<ReadonlyArray<HTMLTableCellElement | undefined>>;\n\n/**\n * The HTML table model of `rows`: per row, the cell covering each visual column, with `colSpan` and\n * `rowSpan` counted. A row span covers the next rows of its row group in the DOM (a hidden row\n * counts, but gets no entry). A column no cell covers is empty. O(number of cells).\n */\nfunction getColumnMap(rows: readonly HTMLTableRowElement[]): ColumnMap {\n const indexOf = new Map<Element, number>(rows.map((row, index) => [row, index]));\n const map: Array<Array<HTMLTableCellElement | undefined>> = rows.map(() => []);\n const cover = (rowIndex: number, column: number, colSpan: number, cell: HTMLTableCellElement) => {\n for (let offset = 0; offset < colSpan; offset += 1) map[rowIndex][column + offset] = cell;\n };\n rows.forEach((row, rowIndex) => {\n const own = map[rowIndex];\n let column = 0;\n for (const cell of Array.from(row.cells)) {\n while (own[column]) column += 1;\n const colSpan = Math.min(Math.max(cell.colSpan, 1), MAX_COL_SPAN);\n cover(rowIndex, column, colSpan, cell);\n let remaining = cell.rowSpan === 0 ? Infinity : cell.rowSpan - 1;\n for (\n let next = row.nextElementSibling;\n next && remaining > 0;\n next = next.nextElementSibling\n ) {\n if (next.localName !== 'tr') continue;\n remaining -= 1;\n const covered = indexOf.get(next);\n if (covered !== undefined) cover(covered, column, colSpan, cell);\n }\n column += colSpan;\n }\n });\n return map;\n}\n\n/** The cell of a row (as {@link getColumnMap} maps it) at `column`, else the last one before it. */\nfunction getCellAtColumn(\n rowMap: ReadonlyArray<HTMLTableCellElement | undefined>,\n column: number,\n): HTMLTableCellElement | null {\n for (let index = Math.min(column, rowMap.length - 1); index >= 0; index -= 1) {\n const cell = rowMap[index];\n if (cell) return cell;\n }\n return null;\n}\n\n/** The grid cells in `node` (itself, or its descendants that belong to `grid`). */\nfunction collectCells(grid: HTMLTableElement, node: Node, into: Set<HTMLTableCellElement>): void {\n if (!(node instanceof Element)) return;\n if (isCellElement(node) && node.closest('table') === grid) into.add(node);\n for (const el of Array.from(node.querySelectorAll<HTMLTableCellElement>('td, th'))) {\n if (el.closest('table') === grid) into.add(el);\n }\n}\n\n/** A widget of a cell: a focusable element, or a nested composite counted as one widget. */\ninterface CellWidget {\n /** The focusable element, or the nested composite's root. */\n element: HTMLElement;\n /** A nested composite: the grid never writes `tabindex` on or inside it. */\n composite: boolean;\n}\n\n/**\n * Grid keyboard model of the APG Grid pattern for a `<table role=\"grid\">` (DataGrid).\n *\n * - **One tab stop.** Exactly one cell of the grid is in the tab order (initially the first cell).\n * Its focus target is its only focusable widget when that widget is not a text-entry control\n * (a checkbox, a sort button, a link); otherwise — no widget, several widgets, a text-entry\n * control or a nested composite — the cell itself. An unchecked radio whose named group has a\n * checked radio is not a target either (browsers skip it in the Tab order), so its cell is.\n * Every other cell and every widget inside a cell gets `tabindex=\"-1\"`. Rows are never focusable.\n * When a widget becomes the target of the focused cell (Space on the cell checks its radio), the\n * cell keeps focus and the tab stop (removing its `tabindex` would blur it) while its target stays\n * at `tabindex=\"-1\"`; the target takes the tab stop once the cell is blurred.\n * - **Navigation mode** (focus on a cell or on its target widget): ArrowLeft/ArrowRight (the\n * previous/next cell of the row, mirrored in RTL), ArrowUp/ArrowDown, Home/End (first/last cell\n * of the row), Ctrl+Home/Ctrl+End (first cell of the grid / last cell of the last row) and\n * PageUp/PageDown ({@link UseGridNavigationOptions.pageSize} rows) move focus between cells.\n * ArrowUp/ArrowDown and PageUp/PageDown keep the visual column (`colSpan` and `rowSpan` counted,\n * so a grouped header works), clamped to a shorter row. Space and Enter on a target widget reach\n * the widget.\n * - **Interaction mode**: Enter or F2 on a cell that is its own target moves focus into its first\n * widget; clicking a widget that is not the cell's target (a text input) enters it too. The\n * grid keys are then ignored, so the arrow keys move the caret, and Tab moves between the cell's\n * widgets. Escape returns focus to the cell. Leaving the grid ends interaction mode.\n * - **Nested composites** (an element with `data-roving-container` or role toolbar, radiogroup,\n * listbox, grid, treegrid, tablist, menu, menubar or tree) count as one widget that uses the arrow\n * keys: its cell takes focus, and Enter/F2 enters the composite at its own tab stop. The grid never\n * writes `tabindex` on or inside a composite (the composite manages its own), so a composite\n * keeps its own Tab stop — the same documented limitation as `useRovingTabIndex`.\n *\n * Keys are ignored when the event's default was prevented (a widget that handled the key, a\n * consumer handler), or with Alt/Meta held. The tab indexes are written to the DOM (layout effect\n * plus a `MutationObserver`), so rows and widgets added later are covered without a re-render. Focus\n * moves re-sync only the cells involved, the observer only the cells that changed, and the grid's\n * own `tabindex` writes do not trigger a re-sync.\n *\n * Internal to the table package.\n */\nexport function useGridNavigation(options: UseGridNavigationOptions = {}): UseGridNavigationResult {\n const pageSize = Math.max(1, options.pageSize ?? 10);\n const [grid, setGrid] = React.useState<HTMLTableElement | null>(null);\n /** The cell that holds the tab stop. */\n const activeCellRef = React.useRef<HTMLTableCellElement | null>(null);\n /** The cell whose widgets are being interacted with, or `null` (navigation mode). */\n const interactionCellRef = React.useRef<HTMLTableCellElement | null>(null);\n /** Elements whose `tabindex` this hook wrote. */\n const [stamped] = React.useState(() => new WeakSet<Element>());\n /** The `tabindex` value this hook last wrote per element (`null`: removed), to skip own records. */\n const [written] = React.useState(() => new WeakMap<Element, string | null>());\n /**\n * Cells kept focusable (holding the tab stop, their target at -1) because they were focused when\n * a widget became their target (a radio that got checked, a widget added): they lose the\n * attribute once blurred.\n */\n const [retained] = React.useState(() => new WeakSet<Element>());\n const pageSizeRef = React.useRef(pageSize);\n\n React.useLayoutEffect(() => {\n pageSizeRef.current = pageSize;\n }, [pageSize]);\n\n /**\n * The widgets of `cell` in DOM order. `precise` checks focusability with `isFocusable` (style\n * reads); otherwise only attributes are checked (cells that do not hold the tab stop).\n */\n const getWidgets = React.useCallback(\n (cell: HTMLTableCellElement, precise: boolean): CellWidget[] => {\n const widgets: CellWidget[] = [];\n let composite: HTMLElement | null = null;\n for (const el of Array.from(cell.querySelectorAll<HTMLElement>(WIDGET_CANDIDATE_SELECTOR))) {\n // Descendants of a nested composite belong to it (candidates come in document order).\n if (composite && composite.contains(el)) continue;\n if (isNestedComposite(el)) {\n composite = el;\n const available = precise ? getCompositeEntry(el) !== null : isLikelyFocusable(el);\n if (available) widgets.push({ element: el, composite: true });\n continue;\n }\n const tabIndex = el.getAttribute('tabindex');\n // An author tabindex=\"-1\" (not written by this hook) opts the element out.\n if (tabIndex !== null && Number(tabIndex) < 0 && !stamped.has(el)) continue;\n if (precise ? isFocusable(el) : isLikelyFocusable(el)) {\n widgets.push({ element: el, composite: false });\n }\n }\n return widgets;\n },\n [stamped],\n );\n\n /**\n * The element of `cell` that takes focus in navigation mode. `checkRadioGroup` applies the radio\n * group rule, which only decides where the tab stop goes (the cell that holds it); a focused\n * lone radio is in navigation mode either way.\n */\n const getTarget = React.useCallback(\n (cell: HTMLTableCellElement, widgets: CellWidget[], checkRadioGroup: boolean): HTMLElement => {\n if (widgets.length !== 1) return cell;\n const [{ element, composite }] = widgets;\n if (composite || isTextEntryElement(element)) return cell;\n if (\n checkRadioGroup &&\n element instanceof HTMLInputElement &&\n element.type === 'radio' &&\n !element.checked &&\n hasCheckedPeer(element)\n ) {\n return cell;\n }\n return element;\n },\n [],\n );\n\n const setTabIndex = React.useCallback(\n (el: HTMLElement, value: 0 | -1) => {\n const next = String(value);\n if (el.getAttribute('tabindex') !== next) {\n written.set(el, next);\n el.setAttribute('tabindex', next);\n }\n stamped.add(el);\n },\n [stamped, written],\n );\n\n /** Writes the tab indexes of one cell and its widgets (only attributes that change). */\n const syncCell = React.useCallback(\n (cell: HTMLTableCellElement, isActive: boolean, isInteraction: boolean) => {\n const widgets = getWidgets(cell, isActive);\n if (isInteraction) {\n retained.delete(cell);\n setTabIndex(cell, -1);\n for (const widget of widgets) {\n if (!widget.composite) setTabIndex(widget.element, 0);\n }\n return;\n }\n const target = getTarget(cell, widgets, isActive);\n /** The focused cell stands in for its target until it is blurred (see below). */\n let keepCell = false;\n if (target === cell) {\n retained.delete(cell);\n setTabIndex(cell, isActive ? 0 : -1);\n } else if (stamped.has(cell) && isFocused(cell)) {\n // A widget became the focused cell's target (its radio got checked, a widget was added).\n // Removing the tabindex of the focused element blurs it (browsers move focus to the body,\n // which ends the grid session), so the cell stays focusable and stays the grid's position.\n // It keeps the tab stop (`tabindex=\"0\"`, when active) and its target stays at -1: the grid\n // still has exactly one Tab stop even if focus is lost without a focusout, and Tab and\n // Shift+Tab from the cell leave the grid (nothing after or before it inside is tabbable).\n // Once the cell is blurred, onBlur re-syncs it and the target takes the tab stop.\n keepCell = true;\n retained.add(cell);\n setTabIndex(cell, isActive ? 0 : -1);\n } else if (stamped.has(cell)) {\n // The cell's widget is its target: the cell itself is not focusable.\n retained.delete(cell);\n stamped.delete(cell);\n if (cell.hasAttribute('tabindex')) {\n written.set(cell, null);\n cell.removeAttribute('tabindex');\n }\n }\n for (const widget of widgets) {\n if (widget.composite) continue;\n const isStop = widget.element === target && isActive && !keepCell;\n setTabIndex(widget.element, isStop ? 0 : -1);\n }\n },\n [getTarget, getWidgets, retained, setTabIndex, stamped, written],\n );\n\n /**\n * Re-resolves the active cell (the first cell when it is gone) and syncs `cells` — every cell\n * when omitted. A new active cell is always synced.\n */\n const sync = React.useCallback(\n (table: HTMLTableElement, cells?: Iterable<HTMLTableCellElement | null>) => {\n const rows = getRows(table);\n let active = activeCellRef.current;\n const targets = new Set<HTMLTableCellElement>();\n if (\n !active ||\n !active.isConnected ||\n !rows.includes(active.parentElement as HTMLTableRowElement)\n ) {\n active = rows[0]?.cells[0] ?? null;\n activeCellRef.current = active;\n if (active) targets.add(active);\n }\n if (interactionCellRef.current !== null && interactionCellRef.current !== active) {\n const previous = interactionCellRef.current;\n interactionCellRef.current = null;\n targets.add(previous);\n }\n const interaction = interactionCellRef.current;\n\n if (cells === undefined) {\n for (const row of rows) for (const cell of Array.from(row.cells)) targets.add(cell);\n } else {\n for (const cell of cells) if (cell) targets.add(cell);\n }\n for (const cell of targets) {\n if (!cell.isConnected || getCell(table, cell) !== cell) continue;\n syncCell(cell, cell === active, cell === interaction);\n }\n },\n [syncCell],\n );\n\n React.useLayoutEffect(() => {\n if (!grid) return;\n sync(grid);\n if (typeof MutationObserver === 'undefined') return;\n const observer = new MutationObserver((records) => {\n let relevant = false;\n let all = false;\n const dirty = new Set<HTMLTableCellElement>();\n for (const record of records) {\n const target = record.target;\n if (!(target instanceof Element)) continue;\n // A nested composite manages its own items (and their tab indexes).\n if (isInsideNestedComposite(target, grid)) continue;\n if (record.type === 'attributes') {\n // The record of one of our own writes.\n if (\n record.attributeName === 'tabindex' &&\n written.has(target) &&\n written.get(target) === target.getAttribute('tabindex')\n ) {\n continue;\n }\n relevant = true;\n const cell = getCell(grid, target);\n if (cell) dirty.add(cell);\n else if (target.localName === 'tr') collectCells(grid, target, dirty);\n else all = true;\n continue;\n }\n relevant = true;\n const cell = getCell(grid, target);\n if (cell) {\n dirty.add(cell);\n continue;\n }\n // Rows, sections or cells added (removed ones need no sync; `sync` re-resolves the\n // active cell when it went away).\n for (const node of Array.from(record.addedNodes)) collectCells(grid, node, dirty);\n }\n if (!relevant) return;\n // The active cell's target can depend on other cells (a radio group whose checked radio\n // changed, reflected by the rows' `aria-selected`), so it is always re-checked.\n if (activeCellRef.current) dirty.add(activeCellRef.current);\n sync(grid, all ? undefined : dirty);\n });\n observer.observe(grid, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: OBSERVED_ATTRIBUTES,\n });\n return () => observer.disconnect();\n }, [grid, sync, written]);\n\n /**\n * Makes `cell` the active cell (in interaction mode when `interaction`) and re-syncs only the\n * cells involved: the previous active and interaction cells and `cell`.\n */\n const activate = React.useCallback(\n (table: HTMLTableElement, cell: HTMLTableCellElement, interaction: boolean) => {\n const previousActive = activeCellRef.current;\n const previousInteraction = interactionCellRef.current;\n activeCellRef.current = cell;\n interactionCellRef.current = interaction ? cell : null;\n sync(table, [previousActive, previousInteraction, cell]);\n },\n [sync],\n );\n\n const focusCell = React.useCallback(\n (table: HTMLTableElement, cell: HTMLTableCellElement) => {\n activate(table, cell, false);\n getTarget(cell, getWidgets(cell, true), true).focus();\n },\n [activate, getTarget, getWidgets],\n );\n\n const onKeyDown = React.useCallback<React.KeyboardEventHandler<HTMLTableElement>>(\n (event) => {\n const table = event.currentTarget;\n if (event.defaultPrevented) return;\n const cell = getCell(table, event.target);\n if (!cell) return;\n const widgets = getWidgets(cell, true);\n // A lone non-text widget is in navigation mode when focused, even an unchecked radio that\n // the radio group rule keeps from holding the tab stop.\n const widgetTarget = getTarget(cell, widgets, false);\n const onCell = event.target === cell;\n\n if (!onCell && event.target !== widgetTarget) {\n // Interaction mode: the widget keeps its keys; Escape returns to the cell.\n if (event.key === 'Escape') {\n event.preventDefault();\n focusCell(table, cell);\n }\n return;\n }\n if (event.altKey || event.metaKey) return;\n\n if ((event.key === 'Enter' || event.key === 'F2') && onCell) {\n let entry: HTMLElement | null = null;\n for (const widget of widgets) {\n entry = widget.composite ? getCompositeEntry(widget.element) : widget.element;\n if (entry) break;\n }\n if (!entry) return;\n event.preventDefault();\n activate(table, cell, true);\n entry.focus();\n return;\n }\n\n const rows = getRows(table);\n const rowIndex = rows.findIndex((row) => row === cell.parentElement);\n if (rowIndex < 0) return;\n const columnIndex = Array.from(rows[rowIndex].cells).indexOf(cell);\n const lastRow = rows.length - 1;\n const ctrl = event.ctrlKey;\n /** Left/Right, Home/End: target row and cell index in it (`Infinity`: the row's last cell). */\n let next: [row: number, column: number] | null = null;\n /** Up/Down, PageUp/PageDown: the first row to try and the direction to keep going in. */\n let vertical: [row: number, step: 1 | -1] | null = null;\n\n switch (event.key) {\n case 'ArrowLeft':\n case 'ArrowRight': {\n if (ctrl) break;\n const intent = getArrowIntent(event.key, {\n orientation: 'horizontal',\n dir: getDirection(table),\n });\n next = [rowIndex, columnIndex + (intent === 'next' ? 1 : -1)];\n break;\n }\n case 'ArrowDown':\n if (!ctrl) vertical = [rowIndex + 1, 1];\n break;\n case 'ArrowUp':\n if (!ctrl) vertical = [rowIndex - 1, -1];\n break;\n case 'Home':\n next = ctrl ? [0, 0] : [rowIndex, 0];\n break;\n case 'End':\n next = ctrl ? [lastRow, Infinity] : [rowIndex, Infinity];\n break;\n case 'PageDown':\n if (!ctrl) vertical = [Math.min(rowIndex + pageSizeRef.current, lastRow), 1];\n break;\n case 'PageUp':\n if (!ctrl) vertical = [Math.max(rowIndex - pageSizeRef.current, 0), -1];\n break;\n default:\n break;\n }\n if (!next && !vertical) return;\n // A handled key never scrolls the page or reaches the widget, even at the grid's edge.\n event.preventDefault();\n let destination: HTMLTableCellElement | null = null;\n if (vertical) {\n // Up/Down keep the visual column (spans counted), clamped to a shorter row; the rows a\n // row-spanning cell covers are skipped when moving away from it.\n const columns = getColumnMap(rows);\n const column = Math.max(columns[rowIndex].indexOf(cell), 0);\n for (let row = vertical[0]; row >= 0 && row <= lastRow; row += vertical[1]) {\n const candidate = getCellAtColumn(columns[row], column);\n if (candidate && candidate !== cell) {\n destination = candidate;\n break;\n }\n }\n } else if (next) {\n // Left/Right stop at the row's edges (no cell there).\n const [nextRow, nextColumn] = next;\n const rowCells = rows[nextRow].cells;\n destination =\n nextColumn === Infinity ? rowCells[rowCells.length - 1] : rowCells.item(nextColumn);\n }\n if (destination && destination !== cell) focusCell(table, destination);\n },\n [activate, focusCell, getTarget, getWidgets],\n );\n\n const onFocus = React.useCallback<React.FocusEventHandler<HTMLTableElement>>(\n (event) => {\n const table = event.currentTarget;\n // React types `target` as the handler's element type; it is the focused descendant.\n const target: EventTarget = event.target;\n const cell = getCell(table, target);\n if (!cell) return;\n const interaction =\n target !== cell && target !== getTarget(cell, getWidgets(cell, true), false);\n activate(table, cell, interaction);\n },\n [activate, getTarget, getWidgets],\n );\n\n const onBlur = React.useCallback<React.FocusEventHandler<HTMLTableElement>>(\n (event) => {\n const table = event.currentTarget;\n // React types `target` as the handler's element type; it is the blurred descendant.\n const left: EventTarget = event.target;\n const cells: HTMLTableCellElement[] = [];\n // A cell kept focusable while focused (see syncCell) is re-synced now that it is blurred\n // (during focusout the blurred element is no longer the active element).\n if (left instanceof Element && isCellElement(left) && retained.has(left)) cells.push(left);\n const next = event.relatedTarget;\n const interaction = interactionCellRef.current;\n if (interaction !== null && !(next instanceof Node && table.contains(next))) {\n interactionCellRef.current = null;\n cells.push(interaction);\n }\n if (cells.length > 0) sync(table, cells);\n },\n [retained, sync],\n );\n\n const gridProps = React.useMemo<GridNavigationHandlers>(\n () => ({ onKeyDown, onFocus, onBlur }),\n [onBlur, onFocus, onKeyDown],\n );\n\n return { gridRef: setGrid, gridProps };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useGridNavigation.cjs","names":["FOCUSABLE_SELECTOR","isFocusable","getFirstTabbable","React","isFocused","getArrowIntent","getDirection","require_runtime","require_direction","require_focus","react","NON_TEXT_INPUT_TYPES","KEY_CONSUMING_ROLES","COMPOSITE_SELECTOR","role","WIDGET_CANDIDATE_SELECTOR","OBSERVED_ATTRIBUTES","isTextEntryElement","tag","el","editable","isNestedComposite","isInsideNestedComposite","node","grid","getCompositeEntry","stop","composite","isLikelyFocusable","hasCheckedPeer","radio","root","checked","other","isCellElement","getCell","getRows","row","MAX_COL_SPAN","getColumnMap","indexOf","rows","index","map","cover","offset","colSpan","rowIndex","column","cell","own","remaining","next","covered","getCellAtColumn","rowMap","collectCells","into","useGridNavigation","pageSize","options","setGrid","activeCellRef","interactionCellRef","stamped","written","retained","pageSizeRef","getWidgets","widgets","precise","tabIndex","getTarget","element","checkRadioGroup","setTabIndex","value","syncCell","isActive","isInteraction","widget","target","keepCell","isStop","sync","table","active","targets","previous","interaction","cells","observer","relevant","all","dirty","record","records","activate","previousActive","previousInteraction","focusCell","onKeyDown","event","widgetTarget","onCell","entry","columnIndex","lastRow","ctrl","vertical","destination","columns","candidate","nextRow","nextColumn","rowCells","onFocus","onBlur","left"],"sources":["../../../src/components/table/useGridNavigation.ts"],"sourcesContent":["import * as React from 'react';\nimport { getArrowIntent, getDirection } from '../../lib/direction';\nimport { FOCUSABLE_SELECTOR, getFirstTabbable, isFocusable, isFocused } from '../../lib/focus';\n\n/** Options of {@link useGridNavigation}. */\nexport interface UseGridNavigationOptions {\n /**\n * How many rows PageUp and PageDown move focus.\n * @default 10\n */\n pageSize?: number;\n}\n\n/** Handlers {@link useGridNavigation} needs on the grid element (compose them with the consumer's). */\nexport interface GridNavigationHandlers {\n onKeyDown: React.KeyboardEventHandler<HTMLTableElement>;\n onFocus: React.FocusEventHandler<HTMLTableElement>;\n onBlur: React.FocusEventHandler<HTMLTableElement>;\n}\n\n/** Result of {@link useGridNavigation}. */\nexport interface UseGridNavigationResult {\n /** Callback ref for the `<table role=\"grid\">` element. Stable identity. */\n gridRef: React.RefCallback<HTMLTableElement>;\n /** Keyboard and focus handlers for the grid element. Stable identities. */\n gridProps: GridNavigationHandlers;\n}\n\n/** Input types that do not take arrow or character keys, so a lone one can be a cell's focus target. */\nconst NON_TEXT_INPUT_TYPES: ReadonlySet<string> = new Set([\n 'checkbox',\n 'radio',\n 'button',\n 'submit',\n 'reset',\n 'image',\n 'file',\n 'color',\n]);\n\n/** Widget roles that use arrow or character keys themselves (treated like text entry). */\nconst KEY_CONSUMING_ROLES: ReadonlySet<string> = new Set([\n 'textbox',\n 'searchbox',\n 'combobox',\n 'spinbutton',\n 'slider',\n]);\n\n/**\n * Roles of composite widgets that manage their own children (and usually their tab indexes): an\n * element with one of them, or with `data-roving-container`, is a nested composite. Close to the\n * list of `useRovingTabIndex`, which is kept separately and differs on purpose: here a `toolbar`\n * is a composite (a toolbar in a cell is one widget), while `spinbutton` is not (it is one\n * focusable control that uses the arrow keys, handled by `KEY_CONSUMING_ROLES` like a text\n * field). Keep both lists in mind when adding a role.\n */\nconst COMPOSITE_ROLES: readonly string[] = [\n 'toolbar',\n 'radiogroup',\n 'listbox',\n 'grid',\n 'treegrid',\n 'tablist',\n 'menu',\n 'menubar',\n 'tree',\n];\n\nconst COMPOSITE_SELECTOR = [\n '[data-roving-container]',\n ...COMPOSITE_ROLES.map((role) => `[role=\"${role}\"]`),\n].join(', ');\n\n/** Every element that can be a cell widget: a focusable element or a nested composite root. */\nconst WIDGET_CANDIDATE_SELECTOR = `${FOCUSABLE_SELECTOR}, ${COMPOSITE_SELECTOR}`;\n\n/**\n * Attributes whose changes can change a cell's widgets or the grid's rows. `aria-selected` on a row\n * signals a selection change, which can change the active cell's target (radio groups).\n */\nconst OBSERVED_ATTRIBUTES = [\n 'disabled',\n 'tabindex',\n 'hidden',\n 'inert',\n 'type',\n 'contenteditable',\n 'role',\n 'data-roving-container',\n 'aria-selected',\n];\n\n/**\n * Whether `el` is a text-entry control (or another widget that uses arrow keys itself): a text-like\n * `input`, `textarea`, `select`, a `contenteditable` element, or an element with role textbox,\n * searchbox, combobox, spinbutton or slider. Such widgets are never a cell's navigation target: the\n * cell takes focus and Enter/F2 moves into the widget, so the arrow keys keep moving the caret.\n */\nexport function isTextEntryElement(el: Element): boolean {\n const tag = el.localName;\n if (tag === 'textarea' || tag === 'select') return true;\n if (tag === 'input') return !NON_TEXT_INPUT_TYPES.has((el as HTMLInputElement).type);\n const editable = el.getAttribute('contenteditable');\n if (editable !== null && editable !== 'false') return true;\n const role = el.getAttribute('role');\n return role !== null && KEY_CONSUMING_ROLES.has(role);\n}\n\n/** Whether `el` is a nested composite widget (own roving container or a composite role). */\nfunction isNestedComposite(el: Element): boolean {\n return el.matches(COMPOSITE_SELECTOR);\n}\n\n/** Whether `node` is strictly inside a nested composite of `grid` (the composite root excluded). */\nfunction isInsideNestedComposite(node: Element, grid: HTMLTableElement): boolean {\n if (node === grid) return false;\n for (let el = node.parentElement; el && el !== grid; el = el.parentElement) {\n if (isNestedComposite(el)) return true;\n }\n return false;\n}\n\n/**\n * The element a nested composite is entered at: its own tab stop (`tabindex=\"0\"`), else its first\n * tabbable element (the root itself when it is the focusable element).\n */\nfunction getCompositeEntry(composite: HTMLElement): HTMLElement | null {\n const stop = composite.querySelector<HTMLElement>('[tabindex=\"0\"]');\n if (stop && isFocusable(stop)) return stop;\n return getFirstTabbable(composite, { includeContainer: true });\n}\n\n/**\n * A cheap focusability check for cells that do not hold the tab stop: no style reads, only the\n * attributes that take an element out of the focus order. The cell that holds (or receives) the tab\n * stop is checked precisely with `isFocusable`.\n */\nfunction isLikelyFocusable(el: HTMLElement): boolean {\n if (el.localName === 'input' && (el as HTMLInputElement).type === 'hidden') return false;\n if (el.matches(':disabled')) return false;\n return el.closest('[hidden], [inert]') === null;\n}\n\n/** Whether another radio of `radio`'s named group is checked (the browser's tab stop of the group). */\nfunction hasCheckedPeer(radio: HTMLInputElement): boolean {\n if (!radio.name) return false;\n const root = radio.getRootNode() as Document | ShadowRoot;\n if (typeof root.querySelectorAll !== 'function') return false;\n const checked = root.querySelectorAll<HTMLInputElement>('input[type=\"radio\"]:checked');\n for (const other of Array.from(checked)) {\n if (other !== radio && other.name === radio.name && other.form === radio.form) return true;\n }\n return false;\n}\n\nfunction isCellElement(el: Element): el is HTMLTableCellElement {\n return el.localName === 'td' || el.localName === 'th';\n}\n\n/** The cell of `grid` that contains `node` (a nested table's cells belong to the outer cell). */\nfunction getCell(grid: HTMLTableElement, node: EventTarget | null): HTMLTableCellElement | null {\n let el: Element | null = node instanceof Element ? node : null;\n while (el && el !== grid) {\n if (isCellElement(el) && el.closest('table') === grid) return el;\n el = el.parentElement;\n }\n return null;\n}\n\n/** The rows of `grid` (header, body and footer rows; a nested table's rows are not included). */\nfunction getRows(grid: HTMLTableElement): HTMLTableRowElement[] {\n return Array.from(grid.rows).filter((row) => row.cells.length > 0 && !row.hidden);\n}\n\n/** Browsers clamp `colSpan` to 1..1000 (`rowSpan` 0 covers the rest of its row group). */\nconst MAX_COL_SPAN = 1000;\n\n/** Per row, the cell covering each visual column (see {@link getColumnMap}). */\ntype ColumnMap = ReadonlyArray<ReadonlyArray<HTMLTableCellElement | undefined>>;\n\n/**\n * The HTML table model of `rows`: per row, the cell covering each visual column, with `colSpan` and\n * `rowSpan` counted. A row span covers the next rows of its row group in the DOM (a hidden row\n * counts, but gets no entry). A column no cell covers is empty. O(number of cells).\n */\nfunction getColumnMap(rows: readonly HTMLTableRowElement[]): ColumnMap {\n const indexOf = new Map<Element, number>(rows.map((row, index) => [row, index]));\n const map: Array<Array<HTMLTableCellElement | undefined>> = rows.map(() => []);\n const cover = (rowIndex: number, column: number, colSpan: number, cell: HTMLTableCellElement) => {\n for (let offset = 0; offset < colSpan; offset += 1) map[rowIndex][column + offset] = cell;\n };\n rows.forEach((row, rowIndex) => {\n const own = map[rowIndex];\n let column = 0;\n for (const cell of Array.from(row.cells)) {\n while (own[column]) column += 1;\n const colSpan = Math.min(Math.max(cell.colSpan, 1), MAX_COL_SPAN);\n cover(rowIndex, column, colSpan, cell);\n let remaining = cell.rowSpan === 0 ? Infinity : cell.rowSpan - 1;\n for (\n let next = row.nextElementSibling;\n next && remaining > 0;\n next = next.nextElementSibling\n ) {\n if (next.localName !== 'tr') continue;\n remaining -= 1;\n const covered = indexOf.get(next);\n if (covered !== undefined) cover(covered, column, colSpan, cell);\n }\n column += colSpan;\n }\n });\n return map;\n}\n\n/** The cell of a row (as {@link getColumnMap} maps it) at `column`, else the last one before it. */\nfunction getCellAtColumn(\n rowMap: ReadonlyArray<HTMLTableCellElement | undefined>,\n column: number,\n): HTMLTableCellElement | null {\n for (let index = Math.min(column, rowMap.length - 1); index >= 0; index -= 1) {\n const cell = rowMap[index];\n if (cell) return cell;\n }\n return null;\n}\n\n/** The grid cells in `node` (itself, or its descendants that belong to `grid`). */\nfunction collectCells(grid: HTMLTableElement, node: Node, into: Set<HTMLTableCellElement>): void {\n if (!(node instanceof Element)) return;\n if (isCellElement(node) && node.closest('table') === grid) into.add(node);\n for (const el of Array.from(node.querySelectorAll<HTMLTableCellElement>('td, th'))) {\n if (el.closest('table') === grid) into.add(el);\n }\n}\n\n/** A widget of a cell: a focusable element, or a nested composite counted as one widget. */\ninterface CellWidget {\n /** The focusable element, or the nested composite's root. */\n element: HTMLElement;\n /** A nested composite: the grid never writes `tabindex` on or inside it. */\n composite: boolean;\n}\n\n/**\n * Grid keyboard model of the APG Grid pattern for a `<table role=\"grid\">` (DataGrid).\n *\n * - **One tab stop.** Exactly one cell of the grid is in the tab order (initially the first cell).\n * Its focus target is its only focusable widget when that widget is not a text-entry control\n * (a checkbox, a sort button, a link); otherwise — no widget, several widgets, a text-entry\n * control or a nested composite — the cell itself. An unchecked radio whose named group has a\n * checked radio is not a target either (browsers skip it in the Tab order), so its cell is.\n * Every other cell and every widget inside a cell gets `tabindex=\"-1\"`. Rows are never focusable.\n * When a widget becomes the target of the focused cell (Space on the cell checks its radio), the\n * cell keeps focus and the tab stop (removing its `tabindex` would blur it) while its target stays\n * at `tabindex=\"-1\"`; the target takes the tab stop once the cell is blurred.\n * - **Navigation mode** (focus on a cell or on its target widget): ArrowLeft/ArrowRight (the\n * previous/next cell of the row, mirrored in RTL), ArrowUp/ArrowDown, Home/End (first/last cell\n * of the row), Ctrl+Home/Ctrl+End (first cell of the grid / last cell of the last row) and\n * PageUp/PageDown ({@link UseGridNavigationOptions.pageSize} rows) move focus between cells.\n * ArrowUp/ArrowDown and PageUp/PageDown keep the visual column (`colSpan` and `rowSpan` counted,\n * so a grouped header works), clamped to a shorter row. Space and Enter on a target widget reach\n * the widget.\n * - **Interaction mode**: Enter or F2 on a cell that is its own target moves focus into its first\n * widget; clicking a widget that is not the cell's target (a text input) enters it too. The\n * grid keys are then ignored, so the arrow keys move the caret, and Tab moves between the cell's\n * widgets. Escape returns focus to the cell. Leaving the grid ends interaction mode.\n * - **Nested composites** (an element with `data-roving-container` or role toolbar, radiogroup,\n * listbox, grid, treegrid, tablist, menu, menubar or tree) count as one widget that uses the arrow\n * keys: its cell takes focus, and Enter/F2 enters the composite at its own tab stop. The grid never\n * writes `tabindex` on or inside a composite (the composite manages its own), so a composite\n * keeps its own Tab stop — the same documented limitation as `useRovingTabIndex`.\n *\n * Keys are ignored when the event's default was prevented (a widget that handled the key, a\n * consumer handler), or with Alt/Meta held. The tab indexes are written to the DOM (layout effect\n * plus a `MutationObserver`), so rows and widgets added later are covered without a re-render. Focus\n * moves re-sync only the cells involved, the observer only the cells that changed, and the grid's\n * own `tabindex` writes do not trigger a re-sync.\n *\n * Internal to the table package.\n */\nexport function useGridNavigation(options: UseGridNavigationOptions = {}): UseGridNavigationResult {\n const pageSize = Math.max(1, options.pageSize ?? 10);\n const [grid, setGrid] = React.useState<HTMLTableElement | null>(null);\n /** The cell that holds the tab stop. */\n const activeCellRef = React.useRef<HTMLTableCellElement | null>(null);\n /** The cell whose widgets are being interacted with, or `null` (navigation mode). */\n const interactionCellRef = React.useRef<HTMLTableCellElement | null>(null);\n /** Elements whose `tabindex` this hook wrote. */\n const [stamped] = React.useState(() => new WeakSet<Element>());\n /** The `tabindex` value this hook last wrote per element (`null`: removed), to skip own records. */\n const [written] = React.useState(() => new WeakMap<Element, string | null>());\n /**\n * Cells kept focusable (holding the tab stop, their target at -1) because they were focused when\n * a widget became their target (a radio that got checked, a widget added): they lose the\n * attribute once blurred.\n */\n const [retained] = React.useState(() => new WeakSet<Element>());\n const pageSizeRef = React.useRef(pageSize);\n\n React.useLayoutEffect(() => {\n pageSizeRef.current = pageSize;\n }, [pageSize]);\n\n /**\n * The widgets of `cell` in DOM order. `precise` checks focusability with `isFocusable` (style\n * reads); otherwise only attributes are checked (cells that do not hold the tab stop).\n */\n const getWidgets = React.useCallback(\n (cell: HTMLTableCellElement, precise: boolean): CellWidget[] => {\n const widgets: CellWidget[] = [];\n let composite: HTMLElement | null = null;\n for (const el of Array.from(cell.querySelectorAll<HTMLElement>(WIDGET_CANDIDATE_SELECTOR))) {\n // Descendants of a nested composite belong to it (candidates come in document order).\n if (composite && composite.contains(el)) continue;\n if (isNestedComposite(el)) {\n composite = el;\n const available = precise ? getCompositeEntry(el) !== null : isLikelyFocusable(el);\n if (available) widgets.push({ element: el, composite: true });\n continue;\n }\n const tabIndex = el.getAttribute('tabindex');\n // An author tabindex=\"-1\" (not written by this hook) opts the element out.\n if (tabIndex !== null && Number(tabIndex) < 0 && !stamped.has(el)) continue;\n if (precise ? isFocusable(el) : isLikelyFocusable(el)) {\n widgets.push({ element: el, composite: false });\n }\n }\n return widgets;\n },\n [stamped],\n );\n\n /**\n * The element of `cell` that takes focus in navigation mode. `checkRadioGroup` applies the radio\n * group rule, which only decides where the tab stop goes (the cell that holds it); a focused\n * lone radio is in navigation mode either way.\n */\n const getTarget = React.useCallback(\n (cell: HTMLTableCellElement, widgets: CellWidget[], checkRadioGroup: boolean): HTMLElement => {\n if (widgets.length !== 1) return cell;\n const [{ element, composite }] = widgets;\n if (composite || isTextEntryElement(element)) return cell;\n if (\n checkRadioGroup &&\n element instanceof HTMLInputElement &&\n element.type === 'radio' &&\n !element.checked &&\n hasCheckedPeer(element)\n ) {\n return cell;\n }\n return element;\n },\n [],\n );\n\n const setTabIndex = React.useCallback(\n (el: HTMLElement, value: 0 | -1) => {\n const next = String(value);\n if (el.getAttribute('tabindex') !== next) {\n written.set(el, next);\n el.setAttribute('tabindex', next);\n }\n stamped.add(el);\n },\n [stamped, written],\n );\n\n /** Writes the tab indexes of one cell and its widgets (only attributes that change). */\n const syncCell = React.useCallback(\n (cell: HTMLTableCellElement, isActive: boolean, isInteraction: boolean) => {\n const widgets = getWidgets(cell, isActive);\n if (isInteraction) {\n retained.delete(cell);\n setTabIndex(cell, -1);\n for (const widget of widgets) {\n if (!widget.composite) setTabIndex(widget.element, 0);\n }\n return;\n }\n const target = getTarget(cell, widgets, isActive);\n /** The focused cell stands in for its target until it is blurred (see below). */\n let keepCell = false;\n if (target === cell) {\n retained.delete(cell);\n setTabIndex(cell, isActive ? 0 : -1);\n } else if (stamped.has(cell) && isFocused(cell)) {\n // A widget became the focused cell's target (its radio got checked, a widget was added).\n // Removing the tabindex of the focused element blurs it (browsers move focus to the body,\n // which ends the grid session), so the cell stays focusable and stays the grid's position.\n // It keeps the tab stop (`tabindex=\"0\"`, when active) and its target stays at -1: the grid\n // still has exactly one Tab stop even if focus is lost without a focusout, and Tab and\n // Shift+Tab from the cell leave the grid (nothing after or before it inside is tabbable).\n // Once the cell is blurred, onBlur re-syncs it and the target takes the tab stop.\n keepCell = true;\n retained.add(cell);\n setTabIndex(cell, isActive ? 0 : -1);\n } else if (stamped.has(cell)) {\n // The cell's widget is its target: the cell itself is not focusable.\n retained.delete(cell);\n stamped.delete(cell);\n if (cell.hasAttribute('tabindex')) {\n written.set(cell, null);\n cell.removeAttribute('tabindex');\n }\n }\n for (const widget of widgets) {\n if (widget.composite) continue;\n const isStop = widget.element === target && isActive && !keepCell;\n setTabIndex(widget.element, isStop ? 0 : -1);\n }\n },\n [getTarget, getWidgets, retained, setTabIndex, stamped, written],\n );\n\n /**\n * Re-resolves the active cell (the first cell when it is gone) and syncs `cells` — every cell\n * when omitted. A new active cell is always synced.\n */\n const sync = React.useCallback(\n (table: HTMLTableElement, cells?: Iterable<HTMLTableCellElement | null>) => {\n const rows = getRows(table);\n let active = activeCellRef.current;\n const targets = new Set<HTMLTableCellElement>();\n if (\n !active ||\n !active.isConnected ||\n !rows.includes(active.parentElement as HTMLTableRowElement)\n ) {\n active = rows[0]?.cells[0] ?? null;\n activeCellRef.current = active;\n if (active) targets.add(active);\n }\n if (interactionCellRef.current !== null && interactionCellRef.current !== active) {\n const previous = interactionCellRef.current;\n interactionCellRef.current = null;\n targets.add(previous);\n }\n const interaction = interactionCellRef.current;\n\n if (cells === undefined) {\n for (const row of rows) for (const cell of Array.from(row.cells)) targets.add(cell);\n } else {\n for (const cell of cells) if (cell) targets.add(cell);\n }\n for (const cell of targets) {\n if (!cell.isConnected || getCell(table, cell) !== cell) continue;\n syncCell(cell, cell === active, cell === interaction);\n }\n },\n [syncCell],\n );\n\n React.useLayoutEffect(() => {\n if (!grid) return;\n sync(grid);\n if (typeof MutationObserver === 'undefined') return;\n const observer = new MutationObserver((records) => {\n let relevant = false;\n let all = false;\n const dirty = new Set<HTMLTableCellElement>();\n for (const record of records) {\n const target = record.target;\n if (!(target instanceof Element)) continue;\n // A nested composite manages its own items (and their tab indexes).\n if (isInsideNestedComposite(target, grid)) continue;\n if (record.type === 'attributes') {\n // The record of one of our own writes.\n if (\n record.attributeName === 'tabindex' &&\n written.has(target) &&\n written.get(target) === target.getAttribute('tabindex')\n ) {\n continue;\n }\n relevant = true;\n const cell = getCell(grid, target);\n if (cell) dirty.add(cell);\n else if (target.localName === 'tr') collectCells(grid, target, dirty);\n else all = true;\n continue;\n }\n relevant = true;\n const cell = getCell(grid, target);\n if (cell) {\n dirty.add(cell);\n continue;\n }\n // Rows, sections or cells added (removed ones need no sync; `sync` re-resolves the\n // active cell when it went away).\n for (const node of Array.from(record.addedNodes)) collectCells(grid, node, dirty);\n }\n if (!relevant) return;\n // The active cell's target can depend on other cells (a radio group whose checked radio\n // changed, reflected by the rows' `aria-selected`), so it is always re-checked.\n if (activeCellRef.current) dirty.add(activeCellRef.current);\n sync(grid, all ? undefined : dirty);\n });\n observer.observe(grid, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: OBSERVED_ATTRIBUTES,\n });\n return () => observer.disconnect();\n }, [grid, sync, written]);\n\n /**\n * Makes `cell` the active cell (in interaction mode when `interaction`) and re-syncs only the\n * cells involved: the previous active and interaction cells and `cell`.\n */\n const activate = React.useCallback(\n (table: HTMLTableElement, cell: HTMLTableCellElement, interaction: boolean) => {\n const previousActive = activeCellRef.current;\n const previousInteraction = interactionCellRef.current;\n activeCellRef.current = cell;\n interactionCellRef.current = interaction ? cell : null;\n sync(table, [previousActive, previousInteraction, cell]);\n },\n [sync],\n );\n\n const focusCell = React.useCallback(\n (table: HTMLTableElement, cell: HTMLTableCellElement) => {\n activate(table, cell, false);\n getTarget(cell, getWidgets(cell, true), true).focus();\n },\n [activate, getTarget, getWidgets],\n );\n\n const onKeyDown = React.useCallback<React.KeyboardEventHandler<HTMLTableElement>>(\n (event) => {\n const table = event.currentTarget;\n if (event.defaultPrevented) return;\n const cell = getCell(table, event.target);\n if (!cell) return;\n const widgets = getWidgets(cell, true);\n // A lone non-text widget is in navigation mode when focused, even an unchecked radio that\n // the radio group rule keeps from holding the tab stop.\n const widgetTarget = getTarget(cell, widgets, false);\n const onCell = event.target === cell;\n\n if (!onCell && event.target !== widgetTarget) {\n // Interaction mode: the widget keeps its keys; Escape returns to the cell.\n if (event.key === 'Escape') {\n event.preventDefault();\n focusCell(table, cell);\n }\n return;\n }\n if (event.altKey || event.metaKey) return;\n\n if ((event.key === 'Enter' || event.key === 'F2') && onCell) {\n let entry: HTMLElement | null = null;\n for (const widget of widgets) {\n entry = widget.composite ? getCompositeEntry(widget.element) : widget.element;\n if (entry) break;\n }\n if (!entry) return;\n event.preventDefault();\n activate(table, cell, true);\n entry.focus();\n return;\n }\n\n const rows = getRows(table);\n const rowIndex = rows.findIndex((row) => row === cell.parentElement);\n if (rowIndex < 0) return;\n const columnIndex = Array.from(rows[rowIndex].cells).indexOf(cell);\n const lastRow = rows.length - 1;\n const ctrl = event.ctrlKey;\n /** Left/Right, Home/End: target row and cell index in it (`Infinity`: the row's last cell). */\n let next: [row: number, column: number] | null = null;\n /** Up/Down, PageUp/PageDown: the first row to try and the direction to keep going in. */\n let vertical: [row: number, step: 1 | -1] | null = null;\n\n switch (event.key) {\n case 'ArrowLeft':\n case 'ArrowRight': {\n if (ctrl) break;\n const intent = getArrowIntent(event.key, {\n orientation: 'horizontal',\n dir: getDirection(table),\n });\n next = [rowIndex, columnIndex + (intent === 'next' ? 1 : -1)];\n break;\n }\n case 'ArrowDown':\n if (!ctrl) vertical = [rowIndex + 1, 1];\n break;\n case 'ArrowUp':\n if (!ctrl) vertical = [rowIndex - 1, -1];\n break;\n case 'Home':\n next = ctrl ? [0, 0] : [rowIndex, 0];\n break;\n case 'End':\n next = ctrl ? [lastRow, Infinity] : [rowIndex, Infinity];\n break;\n case 'PageDown':\n if (!ctrl) vertical = [Math.min(rowIndex + pageSizeRef.current, lastRow), 1];\n break;\n case 'PageUp':\n if (!ctrl) vertical = [Math.max(rowIndex - pageSizeRef.current, 0), -1];\n break;\n default:\n break;\n }\n if (!next && !vertical) return;\n // A handled key never scrolls the page or reaches the widget, even at the grid's edge.\n event.preventDefault();\n let destination: HTMLTableCellElement | null = null;\n if (vertical) {\n // Up/Down keep the visual column (spans counted), clamped to a shorter row; the rows a\n // row-spanning cell covers are skipped when moving away from it.\n const columns = getColumnMap(rows);\n const column = Math.max(columns[rowIndex].indexOf(cell), 0);\n for (let row = vertical[0]; row >= 0 && row <= lastRow; row += vertical[1]) {\n const candidate = getCellAtColumn(columns[row], column);\n if (candidate && candidate !== cell) {\n destination = candidate;\n break;\n }\n }\n } else if (next) {\n // Left/Right stop at the row's edges (no cell there).\n const [nextRow, nextColumn] = next;\n const rowCells = rows[nextRow].cells;\n destination =\n nextColumn === Infinity ? rowCells[rowCells.length - 1] : rowCells.item(nextColumn);\n }\n if (destination && destination !== cell) focusCell(table, destination);\n },\n [activate, focusCell, getTarget, getWidgets],\n );\n\n const onFocus = React.useCallback<React.FocusEventHandler<HTMLTableElement>>(\n (event) => {\n const table = event.currentTarget;\n // React types `target` as the handler's element type; it is the focused descendant.\n const target: EventTarget = event.target;\n const cell = getCell(table, target);\n if (!cell) return;\n const interaction =\n target !== cell && target !== getTarget(cell, getWidgets(cell, true), false);\n activate(table, cell, interaction);\n },\n [activate, getTarget, getWidgets],\n );\n\n const onBlur = React.useCallback<React.FocusEventHandler<HTMLTableElement>>(\n (event) => {\n const table = event.currentTarget;\n // React types `target` as the handler's element type; it is the blurred descendant.\n const left: EventTarget = event.target;\n const cells: HTMLTableCellElement[] = [];\n // A cell kept focusable while focused (see syncCell) is re-synced now that it is blurred\n // (during focusout the blurred element is no longer the active element).\n if (left instanceof Element && isCellElement(left) && retained.has(left)) cells.push(left);\n const next = event.relatedTarget;\n const interaction = interactionCellRef.current;\n if (interaction !== null && !(next instanceof Node && table.contains(next))) {\n interactionCellRef.current = null;\n cells.push(interaction);\n }\n if (cells.length > 0) sync(table, cells);\n },\n [retained, sync],\n );\n\n const gridProps = React.useMemo<GridNavigationHandlers>(\n () => ({ onKeyDown, onFocus, onBlur }),\n [onBlur, onFocus, onKeyDown],\n );\n\n return { gridRef: setGrid, gridProps };\n}\n"],"mappings":"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"}