@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
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
  import { getArrowIntent as e, getDirection as t } from "../../lib/direction.mjs";
3
- import { FOCUSABLE_SELECTOR as n, getFirstTabbable as r, isFocusable as i } from "../../lib/focus.mjs";
4
- import * as a from "react";
3
+ import { FOCUSABLE_SELECTOR as n, getFirstTabbable as r, isFocusable as i, isFocused as a } from "../../lib/focus.mjs";
4
+ import * as o from "react";
5
5
  //#region src/components/table/useGridNavigation.ts
6
- var o = /* @__PURE__ */ new Set([
6
+ var s = /* @__PURE__ */ new Set([
7
7
  "checkbox",
8
8
  "radio",
9
9
  "button",
@@ -12,13 +12,13 @@ var o = /* @__PURE__ */ new Set([
12
12
  "image",
13
13
  "file",
14
14
  "color"
15
- ]), s = /* @__PURE__ */ new Set([
15
+ ]), c = /* @__PURE__ */ new Set([
16
16
  "textbox",
17
17
  "searchbox",
18
18
  "combobox",
19
19
  "spinbutton",
20
20
  "slider"
21
- ]), c = ["[data-roving-container]", ...[
21
+ ]), l = ["[data-roving-container]", ...[
22
22
  "toolbar",
23
23
  "radiogroup",
24
24
  "listbox",
@@ -28,7 +28,7 @@ var o = /* @__PURE__ */ new Set([
28
28
  "menu",
29
29
  "menubar",
30
30
  "tree"
31
- ].map((e) => `[role="${e}"]`)].join(", "), l = `${n}, ${c}`, u = [
31
+ ].map((e) => `[role="${e}"]`)].join(", "), u = `${n}, ${l}`, d = [
32
32
  "disabled",
33
33
  "tabindex",
34
34
  "hidden",
@@ -39,31 +39,31 @@ var o = /* @__PURE__ */ new Set([
39
39
  "data-roving-container",
40
40
  "aria-selected"
41
41
  ];
42
- function d(e) {
42
+ function f(e) {
43
43
  let t = e.localName;
44
44
  if (t === "textarea" || t === "select") return !0;
45
- if (t === "input") return !o.has(e.type);
45
+ if (t === "input") return !s.has(e.type);
46
46
  let n = e.getAttribute("contenteditable");
47
47
  if (n !== null && n !== "false") return !0;
48
48
  let r = e.getAttribute("role");
49
- return r !== null && s.has(r);
49
+ return r !== null && c.has(r);
50
50
  }
51
- function f(e) {
52
- return e.matches(c);
51
+ function p(e) {
52
+ return e.matches(l);
53
53
  }
54
- function p(e, t) {
54
+ function m(e, t) {
55
55
  if (e === t) return !1;
56
- for (let n = e.parentElement; n && n !== t; n = n.parentElement) if (f(n)) return !0;
56
+ for (let n = e.parentElement; n && n !== t; n = n.parentElement) if (p(n)) return !0;
57
57
  return !1;
58
58
  }
59
- function m(e) {
59
+ function h(e) {
60
60
  let t = e.querySelector("[tabindex=\"0\"]");
61
61
  return t && i(t) ? t : r(e, { includeContainer: !0 });
62
62
  }
63
- function h(e) {
63
+ function g(e) {
64
64
  return e.localName === "input" && e.type === "hidden" || e.matches(":disabled") ? !1 : e.closest("[hidden], [inert]") === null;
65
65
  }
66
- function g(e) {
66
+ function _(e) {
67
67
  if (!e.name) return !1;
68
68
  let t = e.getRootNode();
69
69
  if (typeof t.querySelectorAll != "function") return !1;
@@ -71,16 +71,13 @@ function g(e) {
71
71
  for (let t of Array.from(n)) if (t !== e && t.name === e.name && t.form === e.form) return !0;
72
72
  return !1;
73
73
  }
74
- function _(e) {
75
- return e.localName === "td" || e.localName === "th";
76
- }
77
74
  function v(e) {
78
- return e.getRootNode().activeElement === e;
75
+ return e.localName === "td" || e.localName === "th";
79
76
  }
80
77
  function y(e, t) {
81
78
  let n = t instanceof Element ? t : null;
82
79
  for (; n && n !== e;) {
83
- if (_(n) && n.closest("table") === e) return n;
80
+ if (v(n) && n.closest("table") === e) return n;
84
81
  n = n.parentElement;
85
82
  }
86
83
  return null;
@@ -119,52 +116,52 @@ function C(e, t) {
119
116
  }
120
117
  function w(e, t, n) {
121
118
  if (t instanceof Element) {
122
- _(t) && t.closest("table") === e && n.add(t);
119
+ v(t) && t.closest("table") === e && n.add(t);
123
120
  for (let r of Array.from(t.querySelectorAll("td, th"))) r.closest("table") === e && n.add(r);
124
121
  }
125
122
  }
126
123
  function T(n = {}) {
127
- let r = Math.max(1, n.pageSize ?? 10), [o, s] = a.useState(null), c = a.useRef(null), x = a.useRef(null), [T] = a.useState(() => /* @__PURE__ */ new WeakSet()), [E] = a.useState(() => /* @__PURE__ */ new WeakMap()), [D] = a.useState(() => /* @__PURE__ */ new WeakSet()), O = a.useRef(r);
128
- a.useLayoutEffect(() => {
124
+ let r = Math.max(1, n.pageSize ?? 10), [s, c] = o.useState(null), l = o.useRef(null), x = o.useRef(null), [T] = o.useState(() => /* @__PURE__ */ new WeakSet()), [E] = o.useState(() => /* @__PURE__ */ new WeakMap()), [D] = o.useState(() => /* @__PURE__ */ new WeakSet()), O = o.useRef(r);
125
+ o.useLayoutEffect(() => {
129
126
  O.current = r;
130
127
  }, [r]);
131
- let k = a.useCallback((e, t) => {
128
+ let k = o.useCallback((e, t) => {
132
129
  let n = [], r = null;
133
- for (let a of Array.from(e.querySelectorAll(l))) {
130
+ for (let a of Array.from(e.querySelectorAll(u))) {
134
131
  if (r && r.contains(a)) continue;
135
- if (f(a)) {
136
- r = a, (t ? m(a) !== null : h(a)) && n.push({
132
+ if (p(a)) {
133
+ r = a, (t ? h(a) !== null : g(a)) && n.push({
137
134
  element: a,
138
135
  composite: !0
139
136
  });
140
137
  continue;
141
138
  }
142
139
  let e = a.getAttribute("tabindex");
143
- e !== null && Number(e) < 0 && !T.has(a) || (t ? i(a) : h(a)) && n.push({
140
+ e !== null && Number(e) < 0 && !T.has(a) || (t ? i(a) : g(a)) && n.push({
144
141
  element: a,
145
142
  composite: !1
146
143
  });
147
144
  }
148
145
  return n;
149
- }, [T]), A = a.useCallback((e, t, n) => {
146
+ }, [T]), A = o.useCallback((e, t, n) => {
150
147
  if (t.length !== 1) return e;
151
148
  let [{ element: r, composite: i }] = t;
152
- return i || d(r) || n && r instanceof HTMLInputElement && r.type === "radio" && !r.checked && g(r) ? e : r;
153
- }, []), j = a.useCallback((e, t) => {
149
+ return i || f(r) || n && r instanceof HTMLInputElement && r.type === "radio" && !r.checked && _(r) ? e : r;
150
+ }, []), j = o.useCallback((e, t) => {
154
151
  let n = String(t);
155
152
  e.getAttribute("tabindex") !== n && (E.set(e, n), e.setAttribute("tabindex", n)), T.add(e);
156
- }, [T, E]), M = a.useCallback((e, t, n) => {
153
+ }, [T, E]), M = o.useCallback((e, t, n) => {
157
154
  let r = k(e, t);
158
155
  if (n) {
159
156
  D.delete(e), j(e, -1);
160
157
  for (let e of r) e.composite || j(e.element, 0);
161
158
  return;
162
159
  }
163
- let i = A(e, r, t), a = !1;
164
- i === e ? (D.delete(e), j(e, t ? 0 : -1)) : T.has(e) && v(e) ? (a = !0, D.add(e), j(e, t ? 0 : -1)) : T.has(e) && (D.delete(e), T.delete(e), e.hasAttribute("tabindex") && (E.set(e, null), e.removeAttribute("tabindex")));
160
+ let i = A(e, r, t), o = !1;
161
+ i === e ? (D.delete(e), j(e, t ? 0 : -1)) : T.has(e) && a(e) ? (o = !0, D.add(e), j(e, t ? 0 : -1)) : T.has(e) && (D.delete(e), T.delete(e), e.hasAttribute("tabindex") && (E.set(e, null), e.removeAttribute("tabindex")));
165
162
  for (let e of r) {
166
163
  if (e.composite) continue;
167
- let n = e.element === i && t && !a;
164
+ let n = e.element === i && t && !o;
168
165
  j(e.element, n ? 0 : -1);
169
166
  }
170
167
  }, [
@@ -174,9 +171,9 @@ function T(n = {}) {
174
171
  j,
175
172
  T,
176
173
  E
177
- ]), N = a.useCallback((e, t) => {
178
- let n = b(e), r = c.current, i = /* @__PURE__ */ new Set();
179
- if ((!r || !r.isConnected || !n.includes(r.parentElement)) && (r = n[0]?.cells[0] ?? null, c.current = r, r && i.add(r)), x.current !== null && x.current !== r) {
174
+ ]), N = o.useCallback((e, t) => {
175
+ let n = b(e), r = l.current, i = /* @__PURE__ */ new Set();
176
+ if ((!r || !r.isConnected || !n.includes(r.parentElement)) && (r = n[0]?.cells[0] ?? null, l.current = r, r && i.add(r)), x.current !== null && x.current !== r) {
180
177
  let e = x.current;
181
178
  x.current = null, i.add(e);
182
179
  }
@@ -185,55 +182,55 @@ function T(n = {}) {
185
182
  else for (let e of t) e && i.add(e);
186
183
  for (let t of i) t.isConnected && y(e, t) === t && M(t, t === r, t === a);
187
184
  }, [M]);
188
- a.useLayoutEffect(() => {
189
- if (!o || (N(o), typeof MutationObserver > "u")) return;
185
+ o.useLayoutEffect(() => {
186
+ if (!s || (N(s), typeof MutationObserver > "u")) return;
190
187
  let e = new MutationObserver((e) => {
191
188
  let t = !1, n = !1, r = /* @__PURE__ */ new Set();
192
189
  for (let i of e) {
193
190
  let e = i.target;
194
- if (!(e instanceof Element) || p(e, o)) continue;
191
+ if (!(e instanceof Element) || m(e, s)) continue;
195
192
  if (i.type === "attributes") {
196
193
  if (i.attributeName === "tabindex" && E.has(e) && E.get(e) === e.getAttribute("tabindex")) continue;
197
194
  t = !0;
198
- let a = y(o, e);
199
- a ? r.add(a) : e.localName === "tr" ? w(o, e, r) : n = !0;
195
+ let a = y(s, e);
196
+ a ? r.add(a) : e.localName === "tr" ? w(s, e, r) : n = !0;
200
197
  continue;
201
198
  }
202
199
  t = !0;
203
- let a = y(o, e);
200
+ let a = y(s, e);
204
201
  if (a) {
205
202
  r.add(a);
206
203
  continue;
207
204
  }
208
- for (let e of Array.from(i.addedNodes)) w(o, e, r);
205
+ for (let e of Array.from(i.addedNodes)) w(s, e, r);
209
206
  }
210
- t && (c.current && r.add(c.current), N(o, n ? void 0 : r));
207
+ t && (l.current && r.add(l.current), N(s, n ? void 0 : r));
211
208
  });
212
- return e.observe(o, {
209
+ return e.observe(s, {
213
210
  childList: !0,
214
211
  subtree: !0,
215
212
  attributes: !0,
216
- attributeFilter: u
213
+ attributeFilter: d
217
214
  }), () => e.disconnect();
218
215
  }, [
219
- o,
216
+ s,
220
217
  N,
221
218
  E
222
219
  ]);
223
- let P = a.useCallback((e, t, n) => {
224
- let r = c.current, i = x.current;
225
- c.current = t, x.current = n ? t : null, N(e, [
220
+ let P = o.useCallback((e, t, n) => {
221
+ let r = l.current, i = x.current;
222
+ l.current = t, x.current = n ? t : null, N(e, [
226
223
  r,
227
224
  i,
228
225
  t
229
226
  ]);
230
- }, [N]), F = a.useCallback((e, t) => {
227
+ }, [N]), F = o.useCallback((e, t) => {
231
228
  P(e, t, !1), A(t, k(t, !0), !0).focus();
232
229
  }, [
233
230
  P,
234
231
  A,
235
232
  k
236
- ]), I = a.useCallback((n) => {
233
+ ]), I = o.useCallback((n) => {
237
234
  let r = n.currentTarget;
238
235
  if (n.defaultPrevented) return;
239
236
  let i = y(r, n.target);
@@ -246,14 +243,14 @@ function T(n = {}) {
246
243
  if (n.altKey || n.metaKey) return;
247
244
  if ((n.key === "Enter" || n.key === "F2") && s) {
248
245
  let e = null;
249
- for (let t of a) if (e = t.composite ? m(t.element) : t.element, e) break;
246
+ for (let t of a) if (e = t.composite ? h(t.element) : t.element, e) break;
250
247
  if (!e) return;
251
248
  n.preventDefault(), P(r, i, !0), e.focus();
252
249
  return;
253
250
  }
254
251
  let c = b(r), l = c.findIndex((e) => e === i.parentElement);
255
252
  if (l < 0) return;
256
- let u = Array.from(c[l].cells).indexOf(i), d = c.length - 1, f = n.ctrlKey, p = null, h = null;
253
+ let u = Array.from(c[l].cells).indexOf(i), d = c.length - 1, f = n.ctrlKey, p = null, m = null;
257
254
  switch (n.key) {
258
255
  case "ArrowLeft":
259
256
  case "ArrowRight":
@@ -264,10 +261,10 @@ function T(n = {}) {
264
261
  }) === "next" ? 1 : -1)];
265
262
  break;
266
263
  case "ArrowDown":
267
- f || (h = [l + 1, 1]);
264
+ f || (m = [l + 1, 1]);
268
265
  break;
269
266
  case "ArrowUp":
270
- f || (h = [l - 1, -1]);
267
+ f || (m = [l - 1, -1]);
271
268
  break;
272
269
  case "Home":
273
270
  p = f ? [0, 0] : [l, 0];
@@ -276,16 +273,16 @@ function T(n = {}) {
276
273
  p = f ? [d, Infinity] : [l, Infinity];
277
274
  break;
278
275
  case "PageDown":
279
- f || (h = [Math.min(l + O.current, d), 1]);
276
+ f || (m = [Math.min(l + O.current, d), 1]);
280
277
  break;
281
- case "PageUp": f || (h = [Math.max(l - O.current, 0), -1]);
278
+ case "PageUp": f || (m = [Math.max(l - O.current, 0), -1]);
282
279
  }
283
- if (!p && !h) return;
280
+ if (!p && !m) return;
284
281
  n.preventDefault();
285
282
  let g = null;
286
- if (h) {
283
+ if (m) {
287
284
  let e = S(c), t = Math.max(e[l].indexOf(i), 0);
288
- for (let n = h[0]; n >= 0 && n <= d; n += h[1]) {
285
+ for (let n = m[0]; n >= 0 && n <= d; n += m[1]) {
289
286
  let r = C(e[n], t);
290
287
  if (r && r !== i) {
291
288
  g = r;
@@ -302,7 +299,7 @@ function T(n = {}) {
302
299
  F,
303
300
  A,
304
301
  k
305
- ]), L = a.useCallback((e) => {
302
+ ]), L = o.useCallback((e) => {
306
303
  let t = e.currentTarget, n = e.target, r = y(t, n);
307
304
  if (!r) return;
308
305
  let i = n !== r && n !== A(r, k(r, !0), !1);
@@ -311,15 +308,15 @@ function T(n = {}) {
311
308
  P,
312
309
  A,
313
310
  k
314
- ]), R = a.useCallback((e) => {
311
+ ]), R = o.useCallback((e) => {
315
312
  let t = e.currentTarget, n = e.target, r = [];
316
- n instanceof Element && _(n) && D.has(n) && r.push(n);
313
+ n instanceof Element && v(n) && D.has(n) && r.push(n);
317
314
  let i = e.relatedTarget, a = x.current;
318
315
  a !== null && !(i instanceof Node && t.contains(i)) && (x.current = null, r.push(a)), r.length > 0 && N(t, r);
319
316
  }, [D, N]);
320
317
  return {
321
- gridRef: s,
322
- gridProps: a.useMemo(() => ({
318
+ gridRef: c,
319
+ gridProps: o.useMemo(() => ({
323
320
  onKeyDown: I,
324
321
  onFocus: L,
325
322
  onBlur: R
@@ -331,6 +328,6 @@ function T(n = {}) {
331
328
  };
332
329
  }
333
330
  //#endregion
334
- export { d as isTextEntryElement, T as useGridNavigation };
331
+ export { f as isTextEntryElement, T as useGridNavigation };
335
332
 
336
333
  //# sourceMappingURL=useGridNavigation.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useGridNavigation.mjs","names":["React","NON_TEXT_INPUT_TYPES","KEY_CONSUMING_ROLES","COMPOSITE_SELECTOR","role","WIDGET_CANDIDATE_SELECTOR","FOCUSABLE_SELECTOR","OBSERVED_ATTRIBUTES","isTextEntryElement","tag","el","editable","isNestedComposite","isInsideNestedComposite","node","grid","getCompositeEntry","stop","composite","isFocusable","getFirstTabbable","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","getArrowIntent","getDirection","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.mjs","names":["React","NON_TEXT_INPUT_TYPES","KEY_CONSUMING_ROLES","COMPOSITE_SELECTOR","role","WIDGET_CANDIDATE_SELECTOR","FOCUSABLE_SELECTOR","OBSERVED_ATTRIBUTES","isTextEntryElement","tag","el","editable","isNestedComposite","isInsideNestedComposite","node","grid","getCompositeEntry","stop","composite","isFocusable","getFirstTabbable","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","isFocused","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","getArrowIntent","getDirection","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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"}
@@ -0,0 +1,2 @@
1
+ "use client";const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./useEventCallback.cjs");let n=require("react");n=e.__toESM(n,1);function r(e,t){return e===t||e.length===t.length&&e.every((e,n)=>e===t[n])}function i(e,t,n,r){let i=e.length;if(i===0)return null;let a=t===null?-1:e.indexOf(t);if(a===-1)return n>0?e[0]:e[i-1];let o=a+n;return(o<0||o>=i)&&(o=r&&Math.abs(n)===1?(o+i)%i:Math.max(0,Math.min(i-1,o))),e[o]}function a(e){let{items:a,getId:o,enabled:s=!0,fallback:c=null,loop:l=!1,activateFirstOnChange:u=!1,getElement:d,onActiveValueChange:f}=e,p=n.useMemo(()=>new Set(a),[a]),[m,h]=n.useState(null),g=s?m:null,[_,v]=n.useState(()=>({enabled:s,items:a}));if(u){let e=!r(_.items,a);(e||_.enabled!==s)&&(v({enabled:s,items:a}),s&&e&&(g=a[0]??null))}g!==null&&!p.has(g)&&(g=null),g!==m&&h(g);let y=s?g??(c!==null&&p.has(c)?c:null):null,b=y===null?void 0:o(y),x=n.useRef(null),S=t.useEventCallback((e,t)=>{x.current=t?.scroll===!1?e:null,h(e)}),C=t.useEventCallback(e=>{s&&p.has(e)&&e!==y&&(x.current=e,h(e))}),w=t.useEventCallback(()=>S(a[0]??null)),T=t.useEventCallback(()=>S(a[a.length-1]??null)),E=t.useEventCallback(()=>S(i(a,y,1,l))),D=t.useEventCallback(()=>S(i(a,y,-1,l))),O=t.useEventCallback(e=>S(i(a,y,e,!1))),k=t.useEventCallback(e=>d?.(e)||(typeof document>`u`?null:document.getElementById(o(e))));n.useLayoutEffect(()=>{let e=y!==null&&y===x.current;x.current=null,!(y===null||e)&&k(y)?.scrollIntoView?.({block:`nearest`})},[y,k]);let A=n.useRef(null),j=t.useEventCallback(e=>f?.(e));return n.useEffect(()=>{A.current!==y&&(A.current=y,j(y))},[y,j]),{activeValue:y,activeDescendantId:b,setActiveValue:S,highlight:C,first:w,last:T,next:E,prev:D,move:O}}exports.useActiveDescendant=a;
2
+ //# sourceMappingURL=useActiveDescendant.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useActiveDescendant.cjs","names":["React","useEventCallback","require_runtime","require_useEventCallback","react","sameValues","a","b","value","index","step","count","values","current","delta","next","loop","useActiveDescendant","options","itemSet","items","movedRaw","setMovedRaw","moved","enabled","track","setTrack","activateFirstOnChange","itemsChanged","activeValue","fallback","activeDescendantId","getId","noScrollRef","setActiveValue","moveOptions","highlight","first","last","prev","move","resolveElement","getElement","suppressed","reportedRef","report","onActiveValueChange"],"sources":["../../src/hooks/useActiveDescendant.ts"],"sourcesContent":["import * as React from 'react';\nimport { useEventCallback } from './useEventCallback';\n\n/** Options of {@link useActiveDescendant}. */\nexport interface UseActiveDescendantOptions {\n /** The values that can become active, in navigation order. */\n items: readonly string[];\n /** The DOM id of an item's element: the `aria-activedescendant` target. */\n getId: (value: string) => string;\n /**\n * While `false`, no item is active and the moved-to item is forgotten (a closed popup, an\n * unfocused list). A move made in the same update that enables the hook is kept.\n * @default true\n */\n enabled?: boolean;\n /**\n * The active item while none was moved to, or after the moved-to item left `items` (the\n * selected option, for example). Ignored when it is not in `items`.\n * @default null\n */\n fallback?: string | null;\n /** `next()` and `prev()` wrap at the ends. @default false */\n loop?: boolean;\n /**\n * Whenever `items` changes while the hook is enabled (compared by content), its first item\n * becomes active — also when the change comes with enabling (the keystroke that opens a\n * filtered list). Enabling with unchanged items keeps the fallback. It wins over a move made in\n * the same update.\n * @default false\n */\n activateFirstOnChange?: boolean;\n /** The element of an item, for scrolling it into view. @default document.getElementById(getId(value)) */\n getElement?: (value: string) => HTMLElement | null;\n /** Called after the active item changed (from an effect), `null` included. */\n onActiveValueChange?: (value: string | null) => void;\n}\n\n/** Result of {@link useActiveDescendant}. The methods have stable identities. */\nexport interface UseActiveDescendantResult {\n /** The moved-to item, else the fallback; `null` while disabled. Derived during render. */\n activeValue: string | null;\n /** `getId(activeValue)`, or `undefined`: spread as `aria-activedescendant`. */\n activeDescendantId: string | undefined;\n /**\n * Moves to `value`, or back to the fallback with `null`. The value is kept as it is; the next\n * render drops it when it is not in that render's `items` or the hook is disabled, clearing any\n * earlier move. Scrolled into view unless `scroll` is `false`.\n */\n setActiveValue: (value: string | null, options?: { scroll?: boolean }) => void;\n /**\n * Moves to `value` for pointer movement, not scrolled into view; ignored while disabled, when\n * `value` is not in `items` or is already active.\n */\n highlight: (value: string) => void;\n /** Moves to the first item. */\n first: () => void;\n /** Moves to the last item. */\n last: () => void;\n /** The item after the active one (the first when none is active); wraps with `loop`. */\n next: () => void;\n /** The item before the active one (the last when none is active); wraps with `loop`. */\n prev: () => void;\n /**\n * Moves `delta` items (PageDown: `move(10)`), clamped at the ends, never wrapping; from no active\n * item, a positive delta goes to the first item and a negative one to the last.\n */\n move: (delta: number) => void;\n}\n\nfunction sameValues(a: readonly string[], b: readonly string[]): boolean {\n if (a === b) return true;\n if (a.length !== b.length) return false;\n return a.every((value, index) => value === b[index]);\n}\n\nfunction step(values: readonly string[], current: string | null, delta: number, loop: boolean) {\n const count = values.length;\n if (count === 0) return null;\n const index = current === null ? -1 : values.indexOf(current);\n if (index === -1) return delta > 0 ? values[0] : values[count - 1];\n let next = index + delta;\n if (next < 0 || next >= count) {\n next =\n loop && Math.abs(delta) === 1\n ? (next + count) % count\n : Math.max(0, Math.min(count - 1, next));\n }\n return values[next];\n}\n\n/**\n * The active (virtually focused) item of a composite that keeps DOM focus on one element and points\n * `aria-activedescendant` at the active item: a combobox, a listbox that holds focus, a command\n * palette. The state is React state, so the active id is derived during render (server HTML\n * included) and nothing reads the DOM during render.\n *\n * - The moved-to item is dropped when it leaves `items` or while `enabled` is `false`, and does not\n * come back without a user action; `fallback` is active while nothing was moved to.\n * - Keyboard and programmatic moves are scrolled into view (`{ block: 'nearest' }`); pointer moves\n * (`highlight`) and `setActiveValue(value, { scroll: false })` are not.\n * - `onActiveValueChange` reports every change after the commit, once (StrictMode included).\n *\n * `useListbox` is built on it; use it directly for composites that are not listboxes.\n *\n * @example\n * const ad = useActiveDescendant({ items: results.map((r) => r.id), getId: (id) => `cmd-${id}`, enabled: open });\n * <input aria-activedescendant={ad.activeDescendantId} onKeyDown={(e) => { if (e.key === 'ArrowDown') ad.next(); }} />\n */\nexport function useActiveDescendant(\n options: UseActiveDescendantOptions,\n): UseActiveDescendantResult {\n const {\n items,\n getId,\n enabled = true,\n fallback = null,\n loop = false,\n activateFirstOnChange = false,\n getElement,\n onActiveValueChange,\n } = options;\n\n const itemSet = React.useMemo(() => new Set(items), [items]);\n const [movedRaw, setMovedRaw] = React.useState<string | null>(null);\n let moved = enabled ? movedRaw : null;\n\n // activateFirstOnChange: tracked (and updated) only while the option is on, so a render without\n // it adds no render-phase update (C-HOOKS: adjust-during-render).\n const [track, setTrack] = React.useState(() => ({ enabled, items }));\n if (activateFirstOnChange) {\n const itemsChanged = !sameValues(track.items, items);\n if (itemsChanged || track.enabled !== enabled) {\n setTrack({ enabled, items });\n if (enabled && itemsChanged) moved = items[0] ?? null;\n }\n }\n if (moved !== null && !itemSet.has(moved)) moved = null;\n if (moved !== movedRaw) setMovedRaw(moved);\n\n const activeValue = enabled\n ? (moved ?? (fallback !== null && itemSet.has(fallback) ? fallback : null))\n : null;\n const activeDescendantId = activeValue === null ? undefined : getId(activeValue);\n\n // The value the pointer (or a `scroll: false` move) activated, until the scroll effect saw it.\n const noScrollRef = React.useRef<string | null>(null);\n\n const setActiveValue = useEventCallback(\n (value: string | null, moveOptions?: { scroll?: boolean }) => {\n noScrollRef.current = moveOptions?.scroll === false ? value : null;\n setMovedRaw(value);\n },\n );\n const highlight = useEventCallback((value: string) => {\n if (!enabled || !itemSet.has(value) || value === activeValue) return;\n noScrollRef.current = value;\n setMovedRaw(value);\n });\n const first = useEventCallback(() => setActiveValue(items[0] ?? null));\n const last = useEventCallback(() => setActiveValue(items[items.length - 1] ?? null));\n const next = useEventCallback(() => setActiveValue(step(items, activeValue, 1, loop)));\n const prev = useEventCallback(() => setActiveValue(step(items, activeValue, -1, loop)));\n // Never wraps, whatever `loop` is: `loop` is next()/prev()'s wrap at the ends only (fix round 1).\n const move = useEventCallback((delta: number) =>\n setActiveValue(step(items, activeValue, delta, false)),\n );\n\n const resolveElement = useEventCallback((value: string): HTMLElement | null => {\n const custom = getElement?.(value);\n if (custom) return custom;\n return typeof document === 'undefined' ? null : document.getElementById(getId(value));\n });\n React.useLayoutEffect(() => {\n const suppressed = activeValue !== null && activeValue === noScrollRef.current;\n noScrollRef.current = null;\n if (activeValue === null || suppressed) return;\n resolveElement(activeValue)?.scrollIntoView?.({ block: 'nearest' });\n }, [activeValue, resolveElement]);\n\n const reportedRef = React.useRef<string | null>(null);\n const report = useEventCallback((value: string | null) => onActiveValueChange?.(value));\n React.useEffect(() => {\n if (reportedRef.current === activeValue) return;\n reportedRef.current = activeValue;\n report(activeValue);\n }, [activeValue, report]);\n\n return {\n activeValue,\n activeDescendantId,\n setActiveValue,\n highlight,\n first,\n last,\n next,\n prev,\n move,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ "use client";
2
+ import { useEventCallback as e } from "./useEventCallback.mjs";
3
+ import * as t from "react";
4
+ //#region src/hooks/useActiveDescendant.ts
5
+ function n(e, t) {
6
+ return e === t || e.length === t.length && e.every((e, n) => e === t[n]);
7
+ }
8
+ function r(e, t, n, r) {
9
+ let i = e.length;
10
+ if (i === 0) return null;
11
+ let a = t === null ? -1 : e.indexOf(t);
12
+ if (a === -1) return n > 0 ? e[0] : e[i - 1];
13
+ let o = a + n;
14
+ return (o < 0 || o >= i) && (o = r && Math.abs(n) === 1 ? (o + i) % i : Math.max(0, Math.min(i - 1, o))), e[o];
15
+ }
16
+ function i(i) {
17
+ let { items: a, getId: o, enabled: s = !0, fallback: c = null, loop: l = !1, activateFirstOnChange: u = !1, getElement: d, onActiveValueChange: f } = i, p = t.useMemo(() => new Set(a), [a]), [m, h] = t.useState(null), g = s ? m : null, [_, v] = t.useState(() => ({
18
+ enabled: s,
19
+ items: a
20
+ }));
21
+ if (u) {
22
+ let e = !n(_.items, a);
23
+ (e || _.enabled !== s) && (v({
24
+ enabled: s,
25
+ items: a
26
+ }), s && e && (g = a[0] ?? null));
27
+ }
28
+ g !== null && !p.has(g) && (g = null), g !== m && h(g);
29
+ let y = s ? g ?? (c !== null && p.has(c) ? c : null) : null, b = y === null ? void 0 : o(y), x = t.useRef(null), S = e((e, t) => {
30
+ x.current = t?.scroll === !1 ? e : null, h(e);
31
+ }), C = e((e) => {
32
+ s && p.has(e) && e !== y && (x.current = e, h(e));
33
+ }), w = e(() => S(a[0] ?? null)), T = e(() => S(a[a.length - 1] ?? null)), E = e(() => S(r(a, y, 1, l))), D = e(() => S(r(a, y, -1, l))), O = e((e) => S(r(a, y, e, !1))), k = e((e) => d?.(e) || (typeof document > "u" ? null : document.getElementById(o(e))));
34
+ t.useLayoutEffect(() => {
35
+ let e = y !== null && y === x.current;
36
+ x.current = null, !(y === null || e) && k(y)?.scrollIntoView?.({ block: "nearest" });
37
+ }, [y, k]);
38
+ let A = t.useRef(null), j = e((e) => f?.(e));
39
+ return t.useEffect(() => {
40
+ A.current !== y && (A.current = y, j(y));
41
+ }, [y, j]), {
42
+ activeValue: y,
43
+ activeDescendantId: b,
44
+ setActiveValue: S,
45
+ highlight: C,
46
+ first: w,
47
+ last: T,
48
+ next: E,
49
+ prev: D,
50
+ move: O
51
+ };
52
+ }
53
+ //#endregion
54
+ export { i as useActiveDescendant };
55
+
56
+ //# sourceMappingURL=useActiveDescendant.mjs.map