@astryxdesign/core 0.1.2 → 0.1.3-canary.6d57c8d

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 (567) hide show
  1. package/CHANGELOG.md +165 -0
  2. package/README.md +12 -12
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +11 -5
  5. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +1 -1
  6. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  7. package/dist/Breadcrumbs/BreadcrumbItem.js +24 -5
  8. package/dist/Button/Button.d.ts.map +1 -1
  9. package/dist/Button/Button.js +2 -4
  10. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  11. package/dist/ButtonGroup/ButtonGroup.js +0 -1
  12. package/dist/Calendar/Calendar.d.ts +5 -4
  13. package/dist/Calendar/Calendar.d.ts.map +1 -1
  14. package/dist/Calendar/Calendar.js +82 -43
  15. package/dist/Calendar/hooks/index.d.ts +0 -2
  16. package/dist/Calendar/hooks/index.d.ts.map +1 -1
  17. package/dist/Calendar/hooks/index.js +1 -2
  18. package/dist/Calendar/index.d.ts +3 -3
  19. package/dist/Calendar/index.d.ts.map +1 -1
  20. package/dist/Calendar/index.js +1 -1
  21. package/dist/Calendar/styles.d.ts +3 -9
  22. package/dist/Calendar/styles.d.ts.map +1 -1
  23. package/dist/Calendar/styles.js +3 -11
  24. package/dist/Carousel/Carousel.d.ts.map +1 -1
  25. package/dist/Carousel/Carousel.js +17 -3
  26. package/dist/Center/Center.d.ts +11 -1
  27. package/dist/Center/Center.d.ts.map +1 -1
  28. package/dist/Center/Center.js +9 -3
  29. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  30. package/dist/Chat/ChatComposerInput.js +0 -1
  31. package/dist/Chat/ChatMessageList.d.ts +14 -1
  32. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  33. package/dist/Chat/ChatMessageList.js +2 -0
  34. package/dist/Chat/useTriggerMenu.d.ts +8 -1
  35. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  36. package/dist/Chat/useTriggerMenu.js +15 -3
  37. package/dist/CheckboxInput/CheckboxInput.d.ts +23 -2
  38. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  39. package/dist/CheckboxInput/CheckboxInput.js +51 -9
  40. package/dist/CheckboxList/CheckboxList.d.ts +13 -1
  41. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  42. package/dist/CheckboxList/CheckboxList.js +39 -9
  43. package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
  44. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  45. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  46. package/dist/CheckboxList/CheckboxListItem.js +0 -1
  47. package/dist/Citation/Citation.d.ts.map +1 -1
  48. package/dist/Citation/Citation.js +9 -5
  49. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  50. package/dist/CodeBlock/CodeBlock.js +51 -37
  51. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  52. package/dist/CommandPalette/CommandPalette.js +5 -5
  53. package/dist/ContextMenu/ContextMenu.d.ts +4 -5
  54. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  55. package/dist/ContextMenu/ContextMenu.js +93 -11
  56. package/dist/DateInput/DateInput.d.ts +23 -1
  57. package/dist/DateInput/DateInput.d.ts.map +1 -1
  58. package/dist/DateInput/DateInput.js +167 -99
  59. package/dist/DateRangeInput/DateRangeInput.d.ts +23 -1
  60. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  61. package/dist/DateRangeInput/DateRangeInput.js +42 -9
  62. package/dist/DateTimeInput/DateTimeInput.d.ts +35 -2
  63. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  64. package/dist/DateTimeInput/DateTimeInput.js +78 -15
  65. package/dist/Dialog/Dialog.d.ts.map +1 -1
  66. package/dist/Dialog/Dialog.js +12 -0
  67. package/dist/DropdownMenu/DropdownMenu.d.ts +1 -8
  68. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  69. package/dist/DropdownMenu/DropdownMenu.js +42 -14
  70. package/dist/Field/Field.d.ts +19 -2
  71. package/dist/Field/Field.d.ts.map +1 -1
  72. package/dist/Field/Field.js +4 -0
  73. package/dist/Field/FieldLabel.d.ts +21 -2
  74. package/dist/Field/FieldLabel.d.ts.map +1 -1
  75. package/dist/Field/FieldLabel.js +41 -26
  76. package/dist/FileInput/FileInput.d.ts +22 -1
  77. package/dist/FileInput/FileInput.d.ts.map +1 -1
  78. package/dist/FileInput/FileInput.js +56 -10
  79. package/dist/Grid/Grid.d.ts +11 -1
  80. package/dist/Grid/Grid.d.ts.map +1 -1
  81. package/dist/Grid/Grid.js +32 -6
  82. package/dist/InputGroup/InputGroup.d.ts +4 -3
  83. package/dist/InputGroup/InputGroup.d.ts.map +1 -1
  84. package/dist/InputGroup/InputGroup.js +16 -6
  85. package/dist/InputGroup/InputGroupContext.d.ts +4 -0
  86. package/dist/InputGroup/InputGroupContext.d.ts.map +1 -1
  87. package/dist/InputGroup/InputGroupContext.js +2 -2
  88. package/dist/InputGroup/groupStyles.d.ts +1 -1
  89. package/dist/InputGroup/groupStyles.d.ts.map +1 -1
  90. package/dist/InputGroup/groupStyles.js +3 -3
  91. package/dist/InputGroup/index.d.ts +1 -0
  92. package/dist/InputGroup/index.d.ts.map +1 -1
  93. package/dist/Kbd/Kbd.d.ts.map +1 -1
  94. package/dist/Kbd/Kbd.js +33 -1
  95. package/dist/Layer/layerAnimations.stylex.d.ts +4 -0
  96. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  97. package/dist/Layer/layerAnimations.stylex.js +8 -4
  98. package/dist/Layer/useLayer.d.ts +10 -0
  99. package/dist/Layer/useLayer.d.ts.map +1 -1
  100. package/dist/Layer/useLayer.js +64 -9
  101. package/dist/Layout/Layout.d.ts +4 -11
  102. package/dist/Layout/Layout.d.ts.map +1 -1
  103. package/dist/Layout/Layout.js +10 -11
  104. package/dist/Layout/LayoutFooter.d.ts +2 -2
  105. package/dist/Layout/LayoutFooter.d.ts.map +1 -1
  106. package/dist/Layout/LayoutHeader.d.ts +2 -2
  107. package/dist/Layout/LayoutHeader.d.ts.map +1 -1
  108. package/dist/Layout/LayoutPanel.d.ts +2 -2
  109. package/dist/Layout/LayoutPanel.d.ts.map +1 -1
  110. package/dist/Layout/padding.stylex.d.ts +51 -0
  111. package/dist/Layout/padding.stylex.d.ts.map +1 -1
  112. package/dist/Layout/padding.stylex.js +63 -0
  113. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  114. package/dist/Lightbox/Lightbox.js +4 -2
  115. package/dist/Link/Link.d.ts +16 -1
  116. package/dist/Link/Link.d.ts.map +1 -1
  117. package/dist/Link/Link.js +18 -7
  118. package/dist/Markdown/Markdown.d.ts.map +1 -1
  119. package/dist/Markdown/Markdown.js +7 -0
  120. package/dist/MoreMenu/MoreMenu.d.ts +1 -7
  121. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  122. package/dist/MoreMenu/MoreMenu.js +0 -2
  123. package/dist/MultiSelector/MultiSelector.d.ts +27 -2
  124. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  125. package/dist/MultiSelector/MultiSelector.js +141 -42
  126. package/dist/MultiSelector/hooks.d.ts +12 -1
  127. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  128. package/dist/MultiSelector/hooks.js +14 -1
  129. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  130. package/dist/NavMenu/NavHeadingMenu.js +34 -3
  131. package/dist/NumberInput/NumberInput.d.ts +23 -2
  132. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  133. package/dist/NumberInput/NumberInput.js +62 -12
  134. package/dist/Pagination/Pagination.d.ts +1 -1
  135. package/dist/Pagination/Pagination.d.ts.map +1 -1
  136. package/dist/Pagination/Pagination.js +15 -1
  137. package/dist/Popover/Popover.d.ts +18 -2
  138. package/dist/Popover/Popover.d.ts.map +1 -1
  139. package/dist/Popover/Popover.js +5 -2
  140. package/dist/Popover/usePopover.d.ts +38 -3
  141. package/dist/Popover/usePopover.d.ts.map +1 -1
  142. package/dist/Popover/usePopover.js +24 -9
  143. package/dist/PowerSearch/PowerSearch.d.ts +11 -1
  144. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  145. package/dist/PowerSearch/PowerSearch.js +6 -1
  146. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  147. package/dist/ProgressBar/ProgressBar.js +3 -5
  148. package/dist/RadioList/RadioList.d.ts +20 -2
  149. package/dist/RadioList/RadioList.d.ts.map +1 -1
  150. package/dist/RadioList/RadioList.js +105 -9
  151. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  152. package/dist/RadioList/RadioListItem.js +13 -2
  153. package/dist/SegmentedControl/SegmentedControl.d.ts +13 -2
  154. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  155. package/dist/SegmentedControl/SegmentedControl.js +78 -44
  156. package/dist/SegmentedControl/SegmentedControlContext.d.ts +7 -0
  157. package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  158. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  159. package/dist/SegmentedControl/SegmentedControlItem.js +13 -2
  160. package/dist/Selector/Selector.d.ts +24 -1
  161. package/dist/Selector/Selector.d.ts.map +1 -1
  162. package/dist/Selector/Selector.js +122 -43
  163. package/dist/Selector/hooks.d.ts +8 -1
  164. package/dist/Selector/hooks.d.ts.map +1 -1
  165. package/dist/Selector/hooks.js +14 -1
  166. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  167. package/dist/SideNav/SideNavHeading.js +2 -0
  168. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  169. package/dist/SideNav/SideNavItem.js +1 -1
  170. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  171. package/dist/Skeleton/Skeleton.js +8 -2
  172. package/dist/Slider/Slider.d.ts +21 -0
  173. package/dist/Slider/Slider.d.ts.map +1 -1
  174. package/dist/Slider/Slider.js +36 -9
  175. package/dist/Spinner/Spinner.d.ts.map +1 -1
  176. package/dist/Spinner/Spinner.js +1 -1
  177. package/dist/Stack/Stack.d.ts +38 -23
  178. package/dist/Stack/Stack.d.ts.map +1 -1
  179. package/dist/Stack/Stack.js +25 -1
  180. package/dist/Stack/StackItem.d.ts +12 -23
  181. package/dist/Stack/StackItem.d.ts.map +1 -1
  182. package/dist/Stack/StackItem.js +8 -1
  183. package/dist/Switch/Switch.d.ts +23 -2
  184. package/dist/Switch/Switch.d.ts.map +1 -1
  185. package/dist/Switch/Switch.js +41 -11
  186. package/dist/TabList/Tab.d.ts.map +1 -1
  187. package/dist/TabList/Tab.js +6 -0
  188. package/dist/TabList/TabList.d.ts +16 -4
  189. package/dist/TabList/TabList.d.ts.map +1 -1
  190. package/dist/TabList/TabList.js +87 -10
  191. package/dist/TabList/TabListContext.d.ts +5 -0
  192. package/dist/TabList/TabListContext.d.ts.map +1 -1
  193. package/dist/TabList/TabListContext.js +5 -0
  194. package/dist/TabList/TabMenu.d.ts.map +1 -1
  195. package/dist/TabList/TabMenu.js +6 -1
  196. package/dist/Table/BaseTable.d.ts.map +1 -1
  197. package/dist/Table/BaseTable.js +11 -8
  198. package/dist/Table/Table.d.ts.map +1 -1
  199. package/dist/Table/Table.js +10 -1
  200. package/dist/Table/TableCell.d.ts +8 -1
  201. package/dist/Table/TableCell.d.ts.map +1 -1
  202. package/dist/Table/TableCell.js +10 -10
  203. package/dist/Table/TableHeaderCell.d.ts +8 -1
  204. package/dist/Table/TableHeaderCell.d.ts.map +1 -1
  205. package/dist/Table/TableHeaderCell.js +16 -16
  206. package/dist/Table/index.d.ts +2 -1
  207. package/dist/Table/index.d.ts.map +1 -1
  208. package/dist/Table/index.js +2 -2
  209. package/dist/Table/plugins/pagination/paginateData.d.ts +2 -1
  210. package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
  211. package/dist/Table/plugins/pagination/paginateData.js +8 -3
  212. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  213. package/dist/Table/plugins/pagination/useTablePagination.js +6 -1
  214. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  215. package/dist/Table/plugins/sortable/useTableSortable.js +59 -3
  216. package/dist/Table/tableContextMenu.d.ts +33 -0
  217. package/dist/Table/tableContextMenu.d.ts.map +1 -0
  218. package/dist/Table/tableContextMenu.js +133 -0
  219. package/dist/Table/types.d.ts +66 -0
  220. package/dist/Table/types.d.ts.map +1 -1
  221. package/dist/TextArea/TextArea.d.ts +22 -1
  222. package/dist/TextArea/TextArea.d.ts.map +1 -1
  223. package/dist/TextArea/TextArea.js +41 -9
  224. package/dist/TextInput/TextInput.d.ts +23 -2
  225. package/dist/TextInput/TextInput.d.ts.map +1 -1
  226. package/dist/TextInput/TextInput.js +55 -10
  227. package/dist/TimeInput/TimeInput.d.ts +23 -1
  228. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  229. package/dist/TimeInput/TimeInput.js +147 -76
  230. package/dist/Toast/Toast.d.ts.map +1 -1
  231. package/dist/Toast/Toast.js +14 -0
  232. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  233. package/dist/Toast/ToastViewport.js +131 -3
  234. package/dist/Tokenizer/Tokenizer.d.ts +12 -1
  235. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  236. package/dist/Tokenizer/Tokenizer.js +26 -4
  237. package/dist/Toolbar/Toolbar.d.ts +3 -3
  238. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  239. package/dist/Toolbar/Toolbar.js +51 -7
  240. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  241. package/dist/Tooltip/useTooltip.js +56 -9
  242. package/dist/TopNav/TopNavHeading.d.ts +7 -1
  243. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  244. package/dist/TopNav/TopNavHeading.js +8 -0
  245. package/dist/TreeList/TreeList.d.ts +1 -1
  246. package/dist/TreeList/TreeList.d.ts.map +1 -1
  247. package/dist/TreeList/TreeList.js +72 -2
  248. package/dist/TreeList/TreeListItem.d.ts +11 -1
  249. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  250. package/dist/TreeList/TreeListItem.js +51 -12
  251. package/dist/TreeList/treeListItem.markers.stylex.d.ts +16 -0
  252. package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +1 -0
  253. package/dist/TreeList/treeListItem.markers.stylex.js +22 -0
  254. package/dist/Typeahead/BaseTypeahead.d.ts +13 -0
  255. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  256. package/dist/Typeahead/BaseTypeahead.js +69 -6
  257. package/dist/Typeahead/Typeahead.d.ts +26 -3
  258. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  259. package/dist/Typeahead/Typeahead.js +65 -29
  260. package/dist/VisuallyHidden/VisuallyHidden.d.ts +61 -0
  261. package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
  262. package/dist/VisuallyHidden/VisuallyHidden.js +65 -0
  263. package/dist/VisuallyHidden/index.d.ts +11 -0
  264. package/dist/VisuallyHidden/index.d.ts.map +1 -0
  265. package/dist/VisuallyHidden/index.js +13 -0
  266. package/dist/astryx.css +18 -0
  267. package/dist/astryx.umd.js +55 -55
  268. package/dist/astryx.umd.js.map +4 -4
  269. package/dist/hooks/focusableSelector.d.ts +22 -0
  270. package/dist/hooks/focusableSelector.d.ts.map +1 -0
  271. package/dist/hooks/focusableSelector.js +24 -0
  272. package/dist/hooks/index.d.ts +12 -2
  273. package/dist/hooks/index.d.ts.map +1 -1
  274. package/dist/hooks/index.js +7 -2
  275. package/dist/hooks/useAnnounce.d.ts +40 -0
  276. package/dist/hooks/useAnnounce.d.ts.map +1 -0
  277. package/dist/hooks/useAnnounce.js +142 -0
  278. package/dist/hooks/useClickableContainer.d.ts +16 -1
  279. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  280. package/dist/hooks/useClickableContainer.js +16 -4
  281. package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
  282. package/dist/hooks/useEntryAnimation.js +4 -4
  283. package/dist/hooks/useFocusTrap.d.ts +18 -0
  284. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  285. package/dist/hooks/useFocusTrap.js +87 -3
  286. package/dist/hooks/useGridFocus.d.ts +88 -1
  287. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  288. package/dist/hooks/useGridFocus.js +261 -62
  289. package/dist/hooks/useKeyboardHint.d.ts +82 -0
  290. package/dist/hooks/useKeyboardHint.d.ts.map +1 -0
  291. package/dist/hooks/useKeyboardHint.js +220 -0
  292. package/dist/hooks/useListFocus.d.ts +78 -7
  293. package/dist/hooks/useListFocus.d.ts.map +1 -1
  294. package/dist/hooks/useListFocus.js +305 -52
  295. package/dist/hooks/useLongPress.d.ts +21 -0
  296. package/dist/hooks/useLongPress.d.ts.map +1 -0
  297. package/dist/hooks/useLongPress.js +89 -0
  298. package/dist/hooks/useTreeFocus.d.ts +157 -0
  299. package/dist/hooks/useTreeFocus.d.ts.map +1 -0
  300. package/dist/hooks/useTreeFocus.js +337 -0
  301. package/dist/hooks/useTypeahead.d.ts +63 -0
  302. package/dist/hooks/useTypeahead.d.ts.map +1 -0
  303. package/dist/hooks/useTypeahead.js +117 -0
  304. package/dist/index.d.ts +1 -0
  305. package/dist/index.d.ts.map +1 -1
  306. package/dist/index.js +1 -0
  307. package/dist/utils/dateTypes.d.ts +12 -0
  308. package/dist/utils/dateTypes.d.ts.map +1 -1
  309. package/dist/utils/dateTypes.js +41 -1
  310. package/dist/utils/index.d.ts +4 -0
  311. package/dist/utils/index.d.ts.map +1 -1
  312. package/dist/utils/index.js +3 -1
  313. package/dist/utils/inputAria.d.ts +29 -0
  314. package/dist/utils/inputAria.d.ts.map +1 -0
  315. package/dist/utils/inputAria.js +30 -0
  316. package/dist/utils/timeParser.d.ts.map +1 -1
  317. package/dist/utils/timeParser.js +5 -2
  318. package/docs.mjs +2 -2
  319. package/package.json +12 -8
  320. package/src/Avatar/Avatar.test.tsx +37 -0
  321. package/src/Avatar/Avatar.tsx +10 -5
  322. package/src/Breadcrumbs/BreadcrumbItem.tsx +25 -7
  323. package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -10
  324. package/src/Button/Button.tsx +3 -16
  325. package/src/ButtonGroup/ButtonGroup.test.tsx +9 -9
  326. package/src/ButtonGroup/ButtonGroup.tsx +0 -1
  327. package/src/Calendar/Calendar.doc.mjs +4 -4
  328. package/src/Calendar/Calendar.test.tsx +168 -30
  329. package/src/Calendar/Calendar.tsx +121 -66
  330. package/src/Calendar/hooks/index.ts +0 -6
  331. package/src/Calendar/index.ts +1 -3
  332. package/src/Calendar/styles.ts +4 -9
  333. package/src/Carousel/Carousel.doc.mjs +7 -1
  334. package/src/Carousel/Carousel.test.tsx +20 -0
  335. package/src/Carousel/Carousel.tsx +16 -1
  336. package/src/Center/Center.doc.mjs +12 -2
  337. package/src/Center/Center.tsx +28 -3
  338. package/src/Chat/ChatComposerInput.test.tsx +26 -7
  339. package/src/Chat/ChatComposerInput.tsx +0 -1
  340. package/src/Chat/ChatDictationButton.doc.mjs +1 -1
  341. package/src/Chat/ChatMessageList.doc.mjs +8 -0
  342. package/src/Chat/ChatMessageList.test.tsx +18 -0
  343. package/src/Chat/ChatMessageList.tsx +16 -0
  344. package/src/Chat/useTriggerMenu.tsx +22 -4
  345. package/src/CheckboxInput/CheckboxInput.doc.mjs +10 -0
  346. package/src/CheckboxInput/CheckboxInput.test.tsx +172 -27
  347. package/src/CheckboxInput/CheckboxInput.tsx +78 -6
  348. package/src/CheckboxList/CheckboxList.doc.mjs +9 -0
  349. package/src/CheckboxList/CheckboxList.test.tsx +192 -26
  350. package/src/CheckboxList/CheckboxList.tsx +56 -3
  351. package/src/CheckboxList/CheckboxListContext.tsx +7 -0
  352. package/src/CheckboxList/CheckboxListItem.tsx +0 -3
  353. package/src/Citation/Citation.doc.mjs +10 -0
  354. package/src/Citation/Citation.test.tsx +121 -0
  355. package/src/Citation/Citation.tsx +15 -3
  356. package/src/CodeBlock/CodeBlock.test.tsx +115 -0
  357. package/src/CodeBlock/CodeBlock.tsx +63 -33
  358. package/src/CommandPalette/CommandPalette.test.tsx +47 -1
  359. package/src/CommandPalette/CommandPalette.tsx +9 -21
  360. package/src/ContextMenu/ContextMenu.doc.mjs +4 -4
  361. package/src/ContextMenu/ContextMenu.test.tsx +155 -1
  362. package/src/ContextMenu/ContextMenu.tsx +113 -16
  363. package/src/DateInput/DateInput.doc.mjs +286 -56
  364. package/src/DateInput/DateInput.test.tsx +248 -2
  365. package/src/DateInput/DateInput.tsx +196 -100
  366. package/src/DateRangeInput/DateRangeInput.doc.mjs +251 -46
  367. package/src/DateRangeInput/DateRangeInput.test.tsx +182 -20
  368. package/src/DateRangeInput/DateRangeInput.tsx +63 -5
  369. package/src/DateTimeInput/DateTimeInput.doc.mjs +368 -67
  370. package/src/DateTimeInput/DateTimeInput.test.tsx +279 -11
  371. package/src/DateTimeInput/DateTimeInput.tsx +125 -17
  372. package/src/Dialog/Dialog.doc.mjs +2 -2
  373. package/src/Dialog/Dialog.tsx +12 -0
  374. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -8
  375. package/src/DropdownMenu/DropdownMenu.test.tsx +56 -42
  376. package/src/DropdownMenu/DropdownMenu.tsx +52 -18
  377. package/src/Field/Field.doc.mjs +59 -18
  378. package/src/Field/Field.test.tsx +32 -0
  379. package/src/Field/Field.tsx +23 -5
  380. package/src/Field/FieldLabel.tsx +55 -17
  381. package/src/FileInput/FileInput.doc.mjs +17 -0
  382. package/src/FileInput/FileInput.test.tsx +283 -49
  383. package/src/FileInput/FileInput.tsx +76 -4
  384. package/src/Grid/Grid.doc.mjs +17 -4
  385. package/src/Grid/Grid.test.tsx +43 -30
  386. package/src/Grid/Grid.tsx +38 -3
  387. package/src/Icon/Icon.doc.mjs +3 -3
  388. package/src/InputGroup/InputGroup.doc.mjs +111 -23
  389. package/src/InputGroup/InputGroup.test.tsx +257 -60
  390. package/src/InputGroup/InputGroup.tsx +23 -5
  391. package/src/InputGroup/InputGroupContext.ts +9 -4
  392. package/src/InputGroup/groupStyles.ts +7 -1
  393. package/src/InputGroup/index.ts +1 -0
  394. package/src/Kbd/Kbd.test.tsx +22 -4
  395. package/src/Kbd/Kbd.tsx +35 -3
  396. package/src/Layer/layerAnimations.stylex.ts +20 -4
  397. package/src/Layer/useLayer.doc.mjs +5 -0
  398. package/src/Layer/useLayer.test.tsx +111 -0
  399. package/src/Layer/useLayer.tsx +90 -9
  400. package/src/Layout/Layout.tsx +8 -16
  401. package/src/Layout/LayoutFooter.doc.mjs +4 -4
  402. package/src/Layout/LayoutFooter.tsx +3 -3
  403. package/src/Layout/LayoutHeader.doc.mjs +4 -4
  404. package/src/Layout/LayoutHeader.tsx +3 -3
  405. package/src/Layout/LayoutPanel.tsx +3 -3
  406. package/src/Layout/padding.stylex.ts +52 -0
  407. package/src/Lightbox/Lightbox.doc.mjs +9 -0
  408. package/src/Lightbox/Lightbox.test.tsx +53 -5
  409. package/src/Lightbox/Lightbox.tsx +9 -4
  410. package/src/Link/Link.doc.mjs +14 -0
  411. package/src/Link/Link.test.tsx +50 -0
  412. package/src/Link/Link.tsx +39 -4
  413. package/src/Markdown/Markdown.test.tsx +11 -0
  414. package/src/Markdown/Markdown.tsx +7 -0
  415. package/src/MetadataList/MetadataListItem.doc.mjs +4 -0
  416. package/src/MoreMenu/MoreMenu.doc.mjs +2 -17
  417. package/src/MoreMenu/MoreMenu.tsx +0 -9
  418. package/src/MultiSelector/MultiSelector.doc.mjs +30 -0
  419. package/src/MultiSelector/MultiSelector.test.tsx +375 -8
  420. package/src/MultiSelector/MultiSelector.tsx +194 -48
  421. package/src/MultiSelector/hooks.ts +27 -0
  422. package/src/NavMenu/NavHeadingMenu.test.tsx +77 -1
  423. package/src/NavMenu/NavHeadingMenu.tsx +50 -3
  424. package/src/NumberInput/NumberInput.doc.mjs +17 -0
  425. package/src/NumberInput/NumberInput.test.tsx +242 -40
  426. package/src/NumberInput/NumberInput.tsx +89 -11
  427. package/src/Pagination/Pagination.doc.mjs +3 -3
  428. package/src/Pagination/Pagination.test.tsx +117 -5
  429. package/src/Pagination/Pagination.tsx +21 -1
  430. package/src/Popover/Popover.doc.mjs +26 -0
  431. package/src/Popover/Popover.test.tsx +121 -0
  432. package/src/Popover/Popover.tsx +23 -2
  433. package/src/Popover/usePopover.doc.mjs +9 -3
  434. package/src/Popover/usePopover.tsx +70 -12
  435. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  436. package/src/PowerSearch/PowerSearch.test.tsx +97 -1
  437. package/src/PowerSearch/PowerSearch.tsx +16 -3
  438. package/src/ProgressBar/ProgressBar.test.tsx +34 -26
  439. package/src/ProgressBar/ProgressBar.tsx +3 -4
  440. package/src/RadioList/RadioList.doc.mjs +9 -0
  441. package/src/RadioList/RadioList.test.tsx +252 -16
  442. package/src/RadioList/RadioList.tsx +166 -10
  443. package/src/RadioList/RadioListItem.tsx +14 -2
  444. package/src/SegmentedControl/SegmentedControl.doc.mjs +9 -0
  445. package/src/SegmentedControl/SegmentedControl.test.tsx +214 -42
  446. package/src/SegmentedControl/SegmentedControl.tsx +102 -54
  447. package/src/SegmentedControl/SegmentedControlContext.ts +7 -0
  448. package/src/SegmentedControl/SegmentedControlItem.tsx +14 -1
  449. package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
  450. package/src/Selector/Selector.doc.mjs +26 -0
  451. package/src/Selector/Selector.test.tsx +387 -15
  452. package/src/Selector/Selector.tsx +175 -67
  453. package/src/Selector/hooks.ts +23 -0
  454. package/src/SideNav/SideNav.test.tsx +48 -0
  455. package/src/SideNav/SideNavHeading.tsx +8 -2
  456. package/src/SideNav/SideNavItem.tsx +2 -4
  457. package/src/Skeleton/Skeleton.test.tsx +25 -0
  458. package/src/Skeleton/Skeleton.tsx +11 -1
  459. package/src/Slider/Slider.doc.mjs +15 -0
  460. package/src/Slider/Slider.test.tsx +155 -2
  461. package/src/Slider/Slider.tsx +53 -5
  462. package/src/Spinner/Spinner.tsx +7 -1
  463. package/src/Stack/Stack.doc.mjs +77 -0
  464. package/src/Stack/Stack.test.tsx +55 -5
  465. package/src/Stack/Stack.tsx +73 -24
  466. package/src/Stack/StackItem.test.tsx +23 -0
  467. package/src/Stack/StackItem.tsx +20 -23
  468. package/src/Switch/Switch.doc.mjs +15 -0
  469. package/src/Switch/Switch.test.tsx +162 -27
  470. package/src/Switch/Switch.tsx +61 -21
  471. package/src/TabList/Tab.tsx +6 -0
  472. package/src/TabList/TabList.doc.mjs +6 -0
  473. package/src/TabList/TabList.test.tsx +251 -0
  474. package/src/TabList/TabList.tsx +103 -12
  475. package/src/TabList/TabListContext.ts +7 -3
  476. package/src/TabList/TabMenu.tsx +6 -3
  477. package/src/Table/BaseTable.tsx +21 -17
  478. package/src/Table/Table.doc.mjs +2 -0
  479. package/src/Table/Table.test.tsx +10 -0
  480. package/src/Table/Table.tsx +8 -0
  481. package/src/Table/TableCell.tsx +25 -23
  482. package/src/Table/TableHeaderCell.tsx +28 -29
  483. package/src/Table/index.ts +3 -2
  484. package/src/Table/plugins/pagination/paginateData.ts +10 -3
  485. package/src/Table/plugins/pagination/useTablePagination.test.tsx +16 -0
  486. package/src/Table/plugins/pagination/useTablePagination.tsx +8 -1
  487. package/src/Table/plugins/sortable/useTableSortable.test.tsx +91 -2
  488. package/src/Table/plugins/sortable/useTableSortable.tsx +53 -1
  489. package/src/Table/tableContextMenu.test.tsx +157 -0
  490. package/src/Table/tableContextMenu.tsx +142 -0
  491. package/src/Table/types.ts +74 -0
  492. package/src/Table/useTableColumnResize.doc.mjs +3 -3
  493. package/src/Table/useTableStickyColumns.doc.mjs +2 -2
  494. package/src/Text/Text.doc.mjs +18 -18
  495. package/src/TextArea/TextArea.doc.mjs +17 -0
  496. package/src/TextArea/TextArea.test.tsx +167 -35
  497. package/src/TextArea/TextArea.tsx +62 -15
  498. package/src/TextInput/TextInput.doc.mjs +17 -0
  499. package/src/TextInput/TextInput.test.tsx +168 -34
  500. package/src/TextInput/TextInput.tsx +77 -9
  501. package/src/Thumbnail/Thumbnail.doc.mjs +4 -4
  502. package/src/TimeInput/TimeInput.doc.mjs +340 -60
  503. package/src/TimeInput/TimeInput.test.tsx +255 -7
  504. package/src/TimeInput/TimeInput.tsx +176 -70
  505. package/src/Timestamp/Timestamp.doc.mjs +19 -19
  506. package/src/Toast/Toast.tsx +14 -0
  507. package/src/Toast/ToastViewport.test.tsx +214 -0
  508. package/src/Toast/ToastViewport.tsx +167 -21
  509. package/src/Tokenizer/Tokenizer.doc.mjs +29 -14
  510. package/src/Tokenizer/Tokenizer.test.tsx +105 -1
  511. package/src/Tokenizer/Tokenizer.tsx +37 -1
  512. package/src/Toolbar/Toolbar.test.tsx +109 -6
  513. package/src/Toolbar/Toolbar.tsx +59 -5
  514. package/src/Tooltip/Tooltip.test.tsx +68 -0
  515. package/src/Tooltip/useTooltip.tsx +55 -8
  516. package/src/TopNav/TopNav.test.tsx +50 -0
  517. package/src/TopNav/TopNavHeading.tsx +17 -1
  518. package/src/TreeList/TreeList.test.tsx +295 -6
  519. package/src/TreeList/TreeList.tsx +73 -1
  520. package/src/TreeList/TreeListItem.tsx +51 -5
  521. package/src/TreeList/treeListItem.markers.stylex.ts +20 -0
  522. package/src/Typeahead/BaseTypeahead.tsx +93 -9
  523. package/src/Typeahead/Typeahead.doc.mjs +141 -30
  524. package/src/Typeahead/Typeahead.test.tsx +331 -1
  525. package/src/Typeahead/Typeahead.tsx +105 -33
  526. package/src/VisuallyHidden/VisuallyHidden.doc.mjs +80 -0
  527. package/src/VisuallyHidden/VisuallyHidden.test.tsx +76 -0
  528. package/src/VisuallyHidden/VisuallyHidden.tsx +113 -0
  529. package/src/VisuallyHidden/index.ts +15 -0
  530. package/src/docs-script.test.mjs +50 -0
  531. package/src/hooks/focusableSelector.ts +25 -0
  532. package/src/hooks/index.ts +28 -2
  533. package/src/hooks/useAnnounce.test.tsx +101 -0
  534. package/src/hooks/useAnnounce.ts +168 -0
  535. package/src/hooks/useClickableContainer.ts +16 -4
  536. package/src/hooks/useEntryAnimation.ts +16 -4
  537. package/src/hooks/useFocusTrap.test.tsx +132 -0
  538. package/src/hooks/useFocusTrap.ts +98 -4
  539. package/src/hooks/useGridFocus.doc.mjs +40 -2
  540. package/src/hooks/useGridFocus.test.tsx +132 -0
  541. package/src/hooks/useGridFocus.ts +343 -67
  542. package/src/hooks/useKeyboardHint.doc.mjs +101 -0
  543. package/src/hooks/useKeyboardHint.test.tsx +70 -0
  544. package/src/hooks/useKeyboardHint.tsx +332 -0
  545. package/src/hooks/useListFocus.doc.mjs +50 -10
  546. package/src/hooks/useListFocus.test.tsx +375 -0
  547. package/src/hooks/useListFocus.ts +395 -57
  548. package/src/hooks/useLongPress.test.tsx +134 -0
  549. package/src/hooks/useLongPress.ts +119 -0
  550. package/src/hooks/useTreeFocus.doc.mjs +140 -0
  551. package/src/hooks/useTreeFocus.test.tsx +214 -0
  552. package/src/hooks/useTreeFocus.ts +559 -0
  553. package/src/hooks/useTypeahead.test.tsx +109 -0
  554. package/src/hooks/useTypeahead.ts +183 -0
  555. package/src/index.ts +1 -0
  556. package/src/theme/derivedVarRegistry.test.ts +10 -5
  557. package/src/utils/dateTypes.test.ts +40 -0
  558. package/src/utils/dateTypes.ts +40 -0
  559. package/src/utils/index.ts +9 -0
  560. package/src/utils/inputAria.test.ts +39 -0
  561. package/src/utils/inputAria.ts +58 -0
  562. package/src/utils/timeParser.test.ts +10 -0
  563. package/src/utils/timeParser.ts +5 -2
  564. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts +0 -57
  565. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts.map +0 -1
  566. package/dist/Calendar/hooks/useCalendarRovingTabindex.js +0 -96
  567. package/src/Calendar/hooks/useCalendarRovingTabindex.ts +0 -118
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Configuration for tree focus behavior.
3
+ *
4
+ * The tree keyboard model differs from a linear list (useListFocus): while
5
+ * ArrowUp/ArrowDown/Home/End roam linearly over the *visible* treeitems,
6
+ * ArrowRight/ArrowLeft carry tree semantics (expand/collapse, move to
7
+ * first-child / parent). The hook stays generic by taking callbacks for the
8
+ * tree-specific bits (expansion toggling, activation) — mirroring how
9
+ * useGridFocus takes `isCellFocusable` rather than hardcoding disabled logic.
10
+ */
11
+ export interface UseTreeFocusOptions {
12
+ /**
13
+ * Selector for treeitems within the tree. Matches ALL visible treeitems in
14
+ * DOM order (collapsed subtrees are not rendered, so they are naturally
15
+ * excluded). Disabled treeitems are still matched and then skipped via
16
+ * {@link UseTreeFocusOptions.isItemDisabled}.
17
+ * @default '[role="treeitem"]'
18
+ */
19
+ itemSelector?: string;
20
+ /**
21
+ * Predicate determining whether a treeitem matched by `itemSelector` is
22
+ * disabled and must be skipped during arrow/Home/End navigation. A `.focus()`
23
+ * on a disabled item silently no-ops, so navigation would otherwise stall.
24
+ *
25
+ * @default reads `data-tree-disabled` (present ⇒ disabled)
26
+ */
27
+ isItemDisabled?: (item: HTMLElement) => boolean;
28
+ /**
29
+ * Reads the nesting level (aria-level style, 1-based) of a treeitem. Used to
30
+ * resolve first-child (ArrowRight) and parent (ArrowLeft) targets from the
31
+ * flat visible-item list.
32
+ *
33
+ * @default reads the `aria-level` attribute (falling back to `1`)
34
+ */
35
+ getLevel?: (item: HTMLElement) => number;
36
+ /**
37
+ * Whether a treeitem is an expanded parent. Read DOM-side (aria-expanded)
38
+ * by default so it reflects the rendered tree without prop plumbing.
39
+ *
40
+ * @default `aria-expanded === 'true'`
41
+ */
42
+ isExpanded?: (item: HTMLElement) => boolean;
43
+ /**
44
+ * Whether a treeitem is a collapsed parent (has children but is closed).
45
+ *
46
+ * @default `aria-expanded === 'false'`
47
+ */
48
+ isCollapsed?: (item: HTMLElement) => boolean;
49
+ /**
50
+ * Resolve the stable id for a treeitem, passed to `onToggleExpand` /
51
+ * `onActivate`. Returns `undefined` when the element carries no id.
52
+ *
53
+ * @default reads the `data-tree-id` attribute
54
+ */
55
+ getItemId?: (item: HTMLElement) => string | undefined;
56
+ /**
57
+ * Called to expand/collapse the treeitem with the given id (ArrowRight on a
58
+ * collapsed parent, ArrowLeft on an expanded parent, and Enter/Space on a
59
+ * parent that has no inner action).
60
+ */
61
+ onToggleExpand?: (id: string) => void;
62
+ /**
63
+ * Called when Enter/Space activates a treeitem. Return `true` if the
64
+ * activation was handled (e.g. an inner link/button was clicked); return
65
+ * `false`/`undefined` to let the hook fall back to toggling expansion for a
66
+ * parent. When omitted, the hook falls straight through to expansion
67
+ * toggling for parents.
68
+ *
69
+ * @param item The focused treeitem element.
70
+ * @param id The treeitem's id (per `getItemId`), if any.
71
+ */
72
+ onActivate?: (item: HTMLElement, id: string | undefined) => boolean | undefined;
73
+ /**
74
+ * Whether typeahead (jump to next item whose text starts with the typed
75
+ * characters) is enabled.
76
+ * @default true
77
+ */
78
+ typeahead?: boolean;
79
+ /** Reset delay for the typeahead buffer, in ms. @default 500 */
80
+ typeaheadResetMs?: number;
81
+ /**
82
+ * Notified whenever the hook moves focus to a treeitem, with its id (if any).
83
+ * TreeList uses this to move its single roving tab stop.
84
+ */
85
+ onActiveChange?: (id: string | undefined) => void;
86
+ /**
87
+ * Roving-tabindex ownership. When true, the hook manages a single tab stop
88
+ * across the visible treeitems: exactly one enabled treeitem carries
89
+ * `tabindex="0"` and the rest `tabindex="-1"`. The tab stop is repaired on
90
+ * mount and whenever items mount/unmount or toggle disabled, and moves with
91
+ * keyboard navigation. Attach the returned {@link UseTreeFocusReturn.handleFocus}
92
+ * to the container's `onFocus` to keep the stop in sync after clicks or
93
+ * programmatic focus.
94
+ *
95
+ * On mount the hook preserves an existing `tabindex="0"` treeitem (so a
96
+ * consumer can seed the active item in its render); if none exists it
97
+ * promotes the first enabled treeitem.
98
+ *
99
+ * When false (the default), the hook only *moves* focus (`.focus()`) and
100
+ * never touches `tabindex` — the caller owns tab-stop management.
101
+ * @default false
102
+ */
103
+ hasRovingTabIndex?: boolean;
104
+ }
105
+ /**
106
+ * Return type for useTreeFocus hook.
107
+ */
108
+ export interface UseTreeFocusReturn<T extends HTMLElement = HTMLElement> {
109
+ /** Ref to attach to the tree container element (role="tree"). */
110
+ treeRef: React.RefObject<T | null>;
111
+ /** Key down handler to attach to the tree container. */
112
+ handleKeyDown: (e: React.KeyboardEvent) => void;
113
+ /**
114
+ * Focus handler to attach to the container's `onFocus`. Keeps the roving tab
115
+ * stop in sync when `hasRovingTabIndex` is enabled; a no-op otherwise, so it
116
+ * is always safe to attach.
117
+ */
118
+ handleFocus: (e: React.FocusEvent) => void;
119
+ /** Focus the first enabled visible treeitem. */
120
+ focusFirst: () => void;
121
+ /** Focus the last enabled visible treeitem. */
122
+ focusLast: () => void;
123
+ }
124
+ /**
125
+ * Hook for managing roving-tabindex focus + the WAI-ARIA tree keyboard model.
126
+ *
127
+ * A tree is not a linear list: ArrowUp/ArrowDown/Home/End roam linearly over
128
+ * the *visible* treeitems (skipping disabled ones), but ArrowRight/ArrowLeft
129
+ * carry tree semantics. This is why a tree needs its own hook rather than
130
+ * reusing `useListFocus` — the same reason 2D grids get `useGridFocus`.
131
+ *
132
+ * - ArrowDown / ArrowUp: move to next/previous visible treeitem (skip disabled)
133
+ * - ArrowRight: collapsed parent → expand; expanded parent → first child;
134
+ * leaf → no-op
135
+ * - ArrowLeft: expanded parent → collapse; otherwise → move to parent treeitem
136
+ * - Home / End: first / last visible treeitem
137
+ * - Enter / Space: activate (`onActivate`), falling back to expansion toggle
138
+ * - Printable characters: typeahead to the next matching treeitem
139
+ *
140
+ * The hook is DOM-query based (reads aria-expanded/aria-level and data-*
141
+ * attributes) and stays generic via option callbacks for the tree-specific
142
+ * bits (`onToggleExpand`, `onActivate`, `getLevel`, `isItemDisabled`, …).
143
+ *
144
+ * @example
145
+ * ```
146
+ * const {treeRef, handleKeyDown, handleFocus} = useTreeFocus<HTMLUListElement>({
147
+ * onToggleExpand: id => toggle(id),
148
+ * hasRovingTabIndex: true,
149
+ * });
150
+ *
151
+ * <ul ref={treeRef} role="tree" onKeyDown={handleKeyDown} onFocus={handleFocus}>
152
+ * {items.map(item => <li role="treeitem" tabIndex={-1}>{item.label}</li>)}
153
+ * </ul>
154
+ * ```
155
+ */
156
+ export declare function useTreeFocus<T extends HTMLElement = HTMLElement>(options?: UseTreeFocusOptions): UseTreeFocusReturn<T>;
157
+ //# sourceMappingURL=useTreeFocus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTreeFocus.d.ts","sourceRoot":"","sources":["../../src/hooks/useTreeFocus.ts"],"names":[],"mappings":"AAkCA;;;;;;;;;GASG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC;IAEhD;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,MAAM,CAAC;IAEzC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC;IAE5C;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC;IAE7C;;;;;OAKG;IACH,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,MAAM,GAAG,SAAS,CAAC;IAEtD;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAEtC;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,CACX,IAAI,EAAE,WAAW,EACjB,EAAE,EAAE,MAAM,GAAG,SAAS,KACnB,OAAO,GAAG,SAAS,CAAC;IAEzB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,gEAAgE;IAChE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IAElD;;;;;;;;;;;;;;;;OAgBG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW;IACrE,iEAAiE;IACjE,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAEnC,wDAAwD;IACxD,aAAa,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,aAAa,KAAK,IAAI,CAAC;IAEhD;;;;OAIG;IACH,WAAW,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAE3C,gDAAgD;IAChD,UAAU,EAAE,MAAM,IAAI,CAAC;IAEvB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAC9D,OAAO,GAAE,mBAAwB,GAChC,kBAAkB,CAAC,CAAC,CAAC,CA2VvB"}
@@ -0,0 +1,337 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useTreeFocus.ts
7
+ * @input Uses React useCallback, useRef, useIsomorphicLayoutEffect
8
+ * @output Exports useTreeFocus hook for WAI-ARIA tree keyboard navigation
9
+ * @position Core hook; used by TreeList for roving tabindex + APG tree keyboard model
10
+ *
11
+ * SYNC: When modified, update:
12
+ * - /packages/core/src/hooks/index.ts
13
+ * - /packages/core/src/hooks/useTreeFocus.doc.mjs
14
+ * - /packages/core/src/hooks/useTreeFocus.test.tsx
15
+ */
16
+ import { useCallback, useRef } from 'react';
17
+ import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect.js";
18
+
19
+ /** Keys handled by the tree keyboard model (used to gate typeahead). */
20
+ const NAVIGATION_KEYS = new Set(['ArrowDown', 'ArrowUp', 'ArrowRight', 'ArrowLeft', 'Home', 'End', 'Enter', ' ']);
21
+
22
+ /** Default reset delay for the typeahead buffer. */
23
+ const DEFAULT_TYPEAHEAD_RESET_MS = 500;
24
+
25
+ /**
26
+ * Configuration for tree focus behavior.
27
+ *
28
+ * The tree keyboard model differs from a linear list (useListFocus): while
29
+ * ArrowUp/ArrowDown/Home/End roam linearly over the *visible* treeitems,
30
+ * ArrowRight/ArrowLeft carry tree semantics (expand/collapse, move to
31
+ * first-child / parent). The hook stays generic by taking callbacks for the
32
+ * tree-specific bits (expansion toggling, activation) — mirroring how
33
+ * useGridFocus takes `isCellFocusable` rather than hardcoding disabled logic.
34
+ */
35
+
36
+ /**
37
+ * Return type for useTreeFocus hook.
38
+ */
39
+
40
+ /**
41
+ * Hook for managing roving-tabindex focus + the WAI-ARIA tree keyboard model.
42
+ *
43
+ * A tree is not a linear list: ArrowUp/ArrowDown/Home/End roam linearly over
44
+ * the *visible* treeitems (skipping disabled ones), but ArrowRight/ArrowLeft
45
+ * carry tree semantics. This is why a tree needs its own hook rather than
46
+ * reusing `useListFocus` — the same reason 2D grids get `useGridFocus`.
47
+ *
48
+ * - ArrowDown / ArrowUp: move to next/previous visible treeitem (skip disabled)
49
+ * - ArrowRight: collapsed parent → expand; expanded parent → first child;
50
+ * leaf → no-op
51
+ * - ArrowLeft: expanded parent → collapse; otherwise → move to parent treeitem
52
+ * - Home / End: first / last visible treeitem
53
+ * - Enter / Space: activate (`onActivate`), falling back to expansion toggle
54
+ * - Printable characters: typeahead to the next matching treeitem
55
+ *
56
+ * The hook is DOM-query based (reads aria-expanded/aria-level and data-*
57
+ * attributes) and stays generic via option callbacks for the tree-specific
58
+ * bits (`onToggleExpand`, `onActivate`, `getLevel`, `isItemDisabled`, …).
59
+ *
60
+ * @example
61
+ * ```
62
+ * const {treeRef, handleKeyDown, handleFocus} = useTreeFocus<HTMLUListElement>({
63
+ * onToggleExpand: id => toggle(id),
64
+ * hasRovingTabIndex: true,
65
+ * });
66
+ *
67
+ * <ul ref={treeRef} role="tree" onKeyDown={handleKeyDown} onFocus={handleFocus}>
68
+ * {items.map(item => <li role="treeitem" tabIndex={-1}>{item.label}</li>)}
69
+ * </ul>
70
+ * ```
71
+ */
72
+ export function useTreeFocus(options = {}) {
73
+ const {
74
+ itemSelector = '[role="treeitem"]',
75
+ isItemDisabled,
76
+ getLevel,
77
+ isExpanded,
78
+ isCollapsed,
79
+ getItemId,
80
+ onToggleExpand,
81
+ onActivate,
82
+ typeahead = true,
83
+ typeaheadResetMs = DEFAULT_TYPEAHEAD_RESET_MS,
84
+ onActiveChange,
85
+ hasRovingTabIndex = false
86
+ } = options;
87
+ const treeRef = useRef(null);
88
+ const typeaheadRef = useRef({
89
+ buffer: '',
90
+ timer: null
91
+ });
92
+
93
+ /** Visible treeitems in DOM order (collapsed subtrees are not rendered). */
94
+ const getItems = useCallback(() => {
95
+ const root = treeRef.current;
96
+ if (root == null) {
97
+ return [];
98
+ }
99
+ return Array.from(root.querySelectorAll(itemSelector));
100
+ }, [itemSelector]);
101
+ const itemDisabled = useCallback(el => isItemDisabled ? isItemDisabled(el) : el.dataset.treeDisabled != null || el.getAttribute('aria-disabled') === 'true', [isItemDisabled]);
102
+ const levelOf = useCallback(el => getLevel ? getLevel(el) : Number(el.getAttribute('aria-level') ?? '1'), [getLevel]);
103
+ const expandedOf = useCallback(el => isExpanded ? isExpanded(el) : el.getAttribute('aria-expanded') === 'true', [isExpanded]);
104
+ const collapsedOf = useCallback(el => isCollapsed ? isCollapsed(el) : el.getAttribute('aria-expanded') === 'false', [isCollapsed]);
105
+ const idOf = useCallback(el => getItemId ? getItemId(el) : el.dataset.treeId, [getItemId]);
106
+
107
+ // --- Roving tabindex ownership (opt-in via `hasRovingTabIndex`) -------------
108
+
109
+ /**
110
+ * Set `tabindex` on a treeitem, but only when it differs (avoids redundant
111
+ * DOM writes).
112
+ */
113
+ const setTabIndex = useCallback((el, value) => {
114
+ if (el.getAttribute('tabindex') !== String(value)) {
115
+ el.setAttribute('tabindex', String(value));
116
+ }
117
+ }, []);
118
+
119
+ /**
120
+ * Make `target` the sole tabbable treeitem: 0 on it, -1 on every other
121
+ * visible treeitem.
122
+ */
123
+ const moveTabStop = useCallback((items, target) => {
124
+ for (const el of items) {
125
+ setTabIndex(el, el === target ? 0 : -1);
126
+ }
127
+ }, [setTabIndex]);
128
+
129
+ /**
130
+ * Repair the roving tab stop: exactly one enabled treeitem is tabbable (0),
131
+ * the rest are -1. Prefer an existing `tabindex="0"` treeitem (so a consumer
132
+ * can seed the active item in its render); otherwise promote the first
133
+ * enabled treeitem.
134
+ */
135
+ const syncTabStops = useCallback(() => {
136
+ const items = getItems();
137
+ const enabled = items.filter(el => !itemDisabled(el));
138
+ if (enabled.length === 0) {
139
+ return;
140
+ }
141
+ const current = enabled.find(el => el.getAttribute('tabindex') === '0');
142
+ moveTabStop(items, current ?? enabled[0]);
143
+ }, [getItems, itemDisabled, moveTabStop]);
144
+
145
+ // Keep the tab stop valid across renders (items added/removed, disabled
146
+ // toggled). Runs after every commit but only when roving tabindex is on.
147
+ useIsomorphicLayoutEffect(() => {
148
+ if (hasRovingTabIndex) {
149
+ syncTabStops();
150
+ }
151
+ });
152
+
153
+ /** Move focus to a treeitem and notify the active-change listener. */
154
+ const focusItem = useCallback(el => {
155
+ if (el == null) {
156
+ return;
157
+ }
158
+ if (hasRovingTabIndex) {
159
+ moveTabStop(getItems(), el);
160
+ }
161
+ onActiveChange?.(idOf(el));
162
+ el.focus();
163
+ }, [idOf, onActiveChange, hasRovingTabIndex, moveTabStop, getItems]);
164
+
165
+ /**
166
+ * Focus the first enabled treeitem from `start`, moving by `dir`. No wrap —
167
+ * a tree's linear roam clamps at the ends.
168
+ */
169
+ const focusEnabledFrom = useCallback((items, start, dir) => {
170
+ for (let i = start; i >= 0 && i < items.length; i += dir) {
171
+ const candidate = items[i];
172
+ if (candidate != null && !itemDisabled(candidate)) {
173
+ focusItem(candidate);
174
+ return;
175
+ }
176
+ }
177
+ }, [itemDisabled, focusItem]);
178
+ const focusFirst = useCallback(() => {
179
+ focusEnabledFrom(getItems(), 0, 1);
180
+ }, [getItems, focusEnabledFrom]);
181
+ const focusLast = useCallback(() => {
182
+ const items = getItems();
183
+ focusEnabledFrom(items, items.length - 1, -1);
184
+ }, [getItems, focusEnabledFrom]);
185
+ const runTypeahead = useCallback((e, items, currentIndex) => {
186
+ const state = typeaheadRef.current;
187
+ if (state.timer != null) {
188
+ clearTimeout(state.timer);
189
+ }
190
+ state.buffer += e.key.toLowerCase();
191
+ state.timer = setTimeout(() => {
192
+ typeaheadRef.current.buffer = '';
193
+ }, typeaheadResetMs);
194
+ const query = state.buffer;
195
+ const start = currentIndex < 0 ? 0 : currentIndex;
196
+ const ordered = [...items.slice(start + 1), ...items.slice(0, start + 1)];
197
+ const match = ordered.find(item => !itemDisabled(item) && (item.textContent ?? '').trim().toLowerCase().startsWith(query));
198
+ if (match != null) {
199
+ e.preventDefault();
200
+ focusItem(match);
201
+ }
202
+ }, [typeaheadResetMs, itemDisabled, focusItem]);
203
+ const handleKeyDown = useCallback(e => {
204
+ const items = getItems();
205
+ if (items.length === 0) {
206
+ return;
207
+ }
208
+ const active = document.activeElement;
209
+ // Resolve the treeitem that owns focus: the nearest treeitem ancestor of
210
+ // the active element (never an outer treeitem that merely contains it).
211
+ const activeItem = active instanceof Element ? active.closest('[role="treeitem"]') : null;
212
+ const currentIndex = items.findIndex(item => item === activeItem);
213
+ const current = currentIndex >= 0 ? items[currentIndex] : undefined;
214
+
215
+ // Typeahead: printable single characters jump to the next matching item.
216
+ if (typeahead && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey && NAVIGATION_KEYS.has(e.key) === false) {
217
+ runTypeahead(e, items, currentIndex);
218
+ return;
219
+ }
220
+ if (!NAVIGATION_KEYS.has(e.key)) {
221
+ return;
222
+ }
223
+ switch (e.key) {
224
+ case 'ArrowDown':
225
+ {
226
+ e.preventDefault();
227
+ focusEnabledFrom(items, currentIndex < 0 ? 0 : currentIndex + 1, 1);
228
+ break;
229
+ }
230
+ case 'ArrowUp':
231
+ {
232
+ e.preventDefault();
233
+ focusEnabledFrom(items, currentIndex < 0 ? items.length - 1 : currentIndex - 1, -1);
234
+ break;
235
+ }
236
+ case 'ArrowRight':
237
+ {
238
+ if (current == null) {
239
+ break;
240
+ }
241
+ e.preventDefault();
242
+ if (collapsedOf(current)) {
243
+ // Collapsed parent → expand.
244
+ const id = idOf(current);
245
+ if (id != null) {
246
+ onToggleExpand?.(id);
247
+ }
248
+ } else if (expandedOf(current)) {
249
+ // Expanded parent → move to first child.
250
+ const next = items[currentIndex + 1];
251
+ if (next != null && levelOf(next) > levelOf(current)) {
252
+ focusItem(next);
253
+ }
254
+ }
255
+ // Leaf → no-op.
256
+ break;
257
+ }
258
+ case 'ArrowLeft':
259
+ {
260
+ if (current == null) {
261
+ break;
262
+ }
263
+ e.preventDefault();
264
+ if (expandedOf(current)) {
265
+ // Expanded parent → collapse.
266
+ const id = idOf(current);
267
+ if (id != null) {
268
+ onToggleExpand?.(id);
269
+ }
270
+ } else {
271
+ // Otherwise → move to parent treeitem (nearest shallower item
272
+ // scanning upward in visible order).
273
+ const currentLevel = levelOf(current);
274
+ for (let i = currentIndex - 1; i >= 0; i--) {
275
+ const candidate = items[i];
276
+ if (candidate != null && levelOf(candidate) < currentLevel) {
277
+ focusItem(candidate);
278
+ break;
279
+ }
280
+ }
281
+ }
282
+ break;
283
+ }
284
+ case 'Home':
285
+ {
286
+ e.preventDefault();
287
+ focusEnabledFrom(items, 0, 1);
288
+ break;
289
+ }
290
+ case 'End':
291
+ {
292
+ e.preventDefault();
293
+ focusEnabledFrom(items, items.length - 1, -1);
294
+ break;
295
+ }
296
+ case 'Enter':
297
+ case ' ':
298
+ {
299
+ if (current == null || itemDisabled(current)) {
300
+ break;
301
+ }
302
+ e.preventDefault();
303
+ const id = idOf(current);
304
+ // Activate the item: prefer the consumer's onActivate (e.g. click an
305
+ // inner link/button). Fall back to toggling expansion for a parent
306
+ // without its own action.
307
+ const handled = onActivate ? onActivate(current, id) === true : false;
308
+ if (!handled && current.getAttribute('aria-expanded') != null) {
309
+ if (id != null) {
310
+ onToggleExpand?.(id);
311
+ }
312
+ }
313
+ break;
314
+ }
315
+ default:
316
+ break;
317
+ }
318
+ }, [getItems, typeahead, runTypeahead, focusEnabledFrom, focusItem, collapsedOf, expandedOf, idOf, levelOf, itemDisabled, onToggleExpand, onActivate]);
319
+
320
+ /**
321
+ * Keep the roving stop pointing at whatever ended up focused (e.g. a click
322
+ * or programmatic focus) so the next Tab behaves correctly. No-op unless
323
+ * roving tabindex is enabled.
324
+ */
325
+ const handleFocus = useCallback(() => {
326
+ if (hasRovingTabIndex) {
327
+ syncTabStops();
328
+ }
329
+ }, [hasRovingTabIndex, syncTabStops]);
330
+ return {
331
+ treeRef,
332
+ handleKeyDown,
333
+ handleFocus,
334
+ focusFirst,
335
+ focusLast
336
+ };
337
+ }
@@ -0,0 +1,63 @@
1
+ export interface UseTypeaheadOptions {
2
+ /**
3
+ * Returns the current, in-DOM-order list of item labels to match against.
4
+ * An entry may be `null`/empty to mark a non-matchable slot (its index is
5
+ * preserved so it maps 1:1 to the caller's items).
6
+ */
7
+ getItemLabels: () => ReadonlyArray<string | null | undefined>;
8
+ /**
9
+ * Called with the index of the matched item so the caller can focus/select
10
+ * it (e.g. `useListFocus`'s `focusItem`).
11
+ */
12
+ onMatch: (index: number) => void;
13
+ /**
14
+ * The index to start searching from — typically the currently focused item,
15
+ * so repeated presses of the same letter cycle through matches. Defaults to
16
+ * -1 (search from the top).
17
+ */
18
+ getCurrentIndex?: () => number;
19
+ /**
20
+ * Milliseconds of inactivity after which the typed buffer resets.
21
+ * @default 750
22
+ */
23
+ resetMs?: number;
24
+ /**
25
+ * Whether an index should be skipped (e.g. disabled items).
26
+ */
27
+ isDisabled?: (index: number) => boolean;
28
+ }
29
+ export interface UseTypeaheadReturn {
30
+ /**
31
+ * Keydown handler. Call it (or let it fall through) from the collection's
32
+ * own key handler. Returns `true` when it handled a printable character (so
33
+ * the caller can `preventDefault`/stop further handling), `false` otherwise.
34
+ */
35
+ onKeyDown: (e: React.KeyboardEvent | KeyboardEvent) => boolean;
36
+ /** Clear the pending buffer (e.g. on close). */
37
+ reset: () => void;
38
+ }
39
+ /**
40
+ * First-character ("type-ahead") search for a collection.
41
+ *
42
+ * Buffers printable keystrokes (resetting after `resetMs` of inactivity),
43
+ * matches items whose label starts with the buffer, and cycles from the
44
+ * current index — so pressing "s" repeatedly walks through the "s" items
45
+ * instead of sticking on the first. Space is reserved for activation and is
46
+ * only treated as typeahead while a buffer is already active.
47
+ *
48
+ * Additive and collection-agnostic: it does not move focus itself; the caller
49
+ * wires `onMatch` to its own focus/selection (e.g. `useListFocus.focusItem`).
50
+ *
51
+ * @example
52
+ * ```
53
+ * const {listRef, handleKeyDown, focusItem, getItems} = useListFocus(...);
54
+ * const typeahead = useTypeahead({
55
+ * getItemLabels: () => getItems().map(el => el.textContent),
56
+ * onMatch: focusItem,
57
+ * getCurrentIndex: () => getItems().findIndex(el => el === document.activeElement),
58
+ * });
59
+ * const onKeyDown = (e) => { if (!typeahead.onKeyDown(e)) handleKeyDown(e); };
60
+ * ```
61
+ */
62
+ export declare function useTypeahead(options: UseTypeaheadOptions): UseTypeaheadReturn;
63
+ //# sourceMappingURL=useTypeahead.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTypeahead.d.ts","sourceRoot":"","sources":["../../src/hooks/useTypeahead.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,aAAa,EAAE,MAAM,aAAa,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IAE9D;;;OAGG;IACH,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAEjC;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,MAAM,CAAC;IAE/B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;CACzC;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,SAAS,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,aAAa,GAAG,aAAa,KAAK,OAAO,CAAC;IAC/D,gDAAgD;IAChD,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAiBD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,kBAAkB,CAkF7E"}
@@ -0,0 +1,117 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useTypeahead.ts
7
+ * @input Uses React useCallback/useRef
8
+ * @output Exports useTypeahead hook for first-character (type-to-focus) search
9
+ * @position Core hook; adds APG typeahead to menus, listboxes, and other
10
+ * collections. Composes with useListFocus/useGridFocus (or any collection)
11
+ * via a caller-supplied getItemLabels + onMatch.
12
+ *
13
+ * SYNC: When modified, update:
14
+ * - /packages/core/src/hooks/index.ts
15
+ */
16
+ import { useCallback, useRef } from 'react';
17
+ /**
18
+ * Whether a key event represents a single printable character (a type-to-focus
19
+ * candidate) rather than a control/navigation key or a shortcut chord.
20
+ */
21
+ function isPrintableCharacter(e) {
22
+ return e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey &&
23
+ // A lone space is used for activation in menus, not typeahead-from-empty.
24
+ e.key !== ' ';
25
+ }
26
+
27
+ /**
28
+ * First-character ("type-ahead") search for a collection.
29
+ *
30
+ * Buffers printable keystrokes (resetting after `resetMs` of inactivity),
31
+ * matches items whose label starts with the buffer, and cycles from the
32
+ * current index — so pressing "s" repeatedly walks through the "s" items
33
+ * instead of sticking on the first. Space is reserved for activation and is
34
+ * only treated as typeahead while a buffer is already active.
35
+ *
36
+ * Additive and collection-agnostic: it does not move focus itself; the caller
37
+ * wires `onMatch` to its own focus/selection (e.g. `useListFocus.focusItem`).
38
+ *
39
+ * @example
40
+ * ```
41
+ * const {listRef, handleKeyDown, focusItem, getItems} = useListFocus(...);
42
+ * const typeahead = useTypeahead({
43
+ * getItemLabels: () => getItems().map(el => el.textContent),
44
+ * onMatch: focusItem,
45
+ * getCurrentIndex: () => getItems().findIndex(el => el === document.activeElement),
46
+ * });
47
+ * const onKeyDown = (e) => { if (!typeahead.onKeyDown(e)) handleKeyDown(e); };
48
+ * ```
49
+ */
50
+ export function useTypeahead(options) {
51
+ const {
52
+ getItemLabels,
53
+ onMatch,
54
+ getCurrentIndex,
55
+ resetMs = 750,
56
+ isDisabled
57
+ } = options;
58
+ const bufferRef = useRef('');
59
+ const timeoutRef = useRef(undefined);
60
+ const reset = useCallback(() => {
61
+ bufferRef.current = '';
62
+ if (timeoutRef.current) {
63
+ clearTimeout(timeoutRef.current);
64
+ timeoutRef.current = undefined;
65
+ }
66
+ }, []);
67
+ const scheduleReset = useCallback(() => {
68
+ if (timeoutRef.current) {
69
+ clearTimeout(timeoutRef.current);
70
+ }
71
+ timeoutRef.current = setTimeout(() => {
72
+ bufferRef.current = '';
73
+ timeoutRef.current = undefined;
74
+ }, resetMs);
75
+ }, [resetMs]);
76
+ const onKeyDown = useCallback(e => {
77
+ // A bare Space with no active buffer is not typeahead (menus activate on
78
+ // Space); once the user is mid-typing, Space extends the query.
79
+ const isSpaceMidType = e.key === ' ' && bufferRef.current.length > 0;
80
+ if (!isPrintableCharacter(e) && !isSpaceMidType) {
81
+ return false;
82
+ }
83
+ const labels = getItemLabels();
84
+ if (labels.length === 0) {
85
+ return false;
86
+ }
87
+
88
+ // Pressing the SAME single character repeatedly cycles to the next match
89
+ // rather than filtering deeper (APG behavior).
90
+ const char = e.key.toLowerCase();
91
+ const isRepeatSameChar = bufferRef.current.length > 0 && bufferRef.current.split('').every(c => c === char);
92
+ const nextBuffer = isRepeatSameChar ? char : bufferRef.current + char;
93
+ bufferRef.current = nextBuffer;
94
+ scheduleReset();
95
+ const start = getCurrentIndex?.() ?? -1;
96
+ const count = labels.length;
97
+ // When repeating a single char, start the search AFTER the current item
98
+ // so we advance; otherwise include the current item.
99
+ const offset = isRepeatSameChar ? 1 : 0;
100
+ for (let i = 0; i < count; i++) {
101
+ const index = (start + offset + i + count) % count;
102
+ if (isDisabled?.(index)) {
103
+ continue;
104
+ }
105
+ const label = labels[index];
106
+ if (label != null && label.trim().toLowerCase().startsWith(nextBuffer)) {
107
+ onMatch(index);
108
+ return true;
109
+ }
110
+ }
111
+ return true;
112
+ }, [getItemLabels, onMatch, getCurrentIndex, scheduleReset, isDisabled]);
113
+ return {
114
+ onKeyDown,
115
+ reset
116
+ };
117
+ }
package/dist/index.d.ts CHANGED
@@ -34,6 +34,7 @@ export * from './Collapsible';
34
34
  export * from './RadioList';
35
35
  export * from './Resizable';
36
36
  export * from './Divider';
37
+ export * from './VisuallyHidden';
37
38
  export * from './EmptyState';
38
39
  export * from './Lightbox';
39
40
  export * from './Link';