@astryxdesign/core 0.1.1 → 0.1.2-canary.04e71b2

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 (631) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/README.md +131 -13
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +11 -5
  5. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  6. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  7. package/dist/AvatarGroup/AvatarGroupOverflow.js +4 -1
  8. package/dist/Banner/Banner.d.ts +7 -0
  9. package/dist/Banner/Banner.d.ts.map +1 -1
  10. package/dist/Banner/Banner.js +9 -2
  11. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +1 -1
  12. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  13. package/dist/Breadcrumbs/BreadcrumbItem.js +24 -5
  14. package/dist/Button/Button.d.ts +12 -2
  15. package/dist/Button/Button.d.ts.map +1 -1
  16. package/dist/Button/Button.js +53 -17
  17. package/dist/Calendar/Calendar.d.ts +5 -4
  18. package/dist/Calendar/Calendar.d.ts.map +1 -1
  19. package/dist/Calendar/Calendar.js +82 -43
  20. package/dist/Calendar/hooks/index.d.ts +0 -2
  21. package/dist/Calendar/hooks/index.d.ts.map +1 -1
  22. package/dist/Calendar/hooks/index.js +1 -2
  23. package/dist/Calendar/index.d.ts +3 -3
  24. package/dist/Calendar/index.d.ts.map +1 -1
  25. package/dist/Calendar/index.js +1 -1
  26. package/dist/Calendar/styles.d.ts +3 -9
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +3 -11
  29. package/dist/Carousel/Carousel.d.ts.map +1 -1
  30. package/dist/Carousel/Carousel.js +17 -3
  31. package/dist/Center/Center.d.ts +11 -1
  32. package/dist/Center/Center.d.ts.map +1 -1
  33. package/dist/Center/Center.js +9 -3
  34. package/dist/Chat/ChatMessageList.d.ts +14 -1
  35. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  36. package/dist/Chat/ChatMessageList.js +2 -0
  37. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  38. package/dist/Chat/useTriggerMenu.js +4 -1
  39. package/dist/CheckboxInput/CheckboxInput.d.ts +23 -2
  40. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  41. package/dist/CheckboxInput/CheckboxInput.js +51 -9
  42. package/dist/CheckboxList/CheckboxList.d.ts +13 -1
  43. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  44. package/dist/CheckboxList/CheckboxList.js +39 -9
  45. package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
  46. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  47. package/dist/Citation/Citation.d.ts.map +1 -1
  48. package/dist/Citation/Citation.js +3 -3
  49. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  50. package/dist/CodeBlock/CodeBlock.js +49 -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 +59 -8
  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/EmptyState/EmptyState.d.ts.map +1 -1
  71. package/dist/EmptyState/EmptyState.js +7 -1
  72. package/dist/Field/Field.d.ts +19 -2
  73. package/dist/Field/Field.d.ts.map +1 -1
  74. package/dist/Field/Field.js +4 -0
  75. package/dist/Field/FieldLabel.d.ts +21 -2
  76. package/dist/Field/FieldLabel.d.ts.map +1 -1
  77. package/dist/Field/FieldLabel.js +41 -26
  78. package/dist/FileInput/FileInput.d.ts +22 -1
  79. package/dist/FileInput/FileInput.d.ts.map +1 -1
  80. package/dist/FileInput/FileInput.js +56 -10
  81. package/dist/Grid/Grid.d.ts +11 -1
  82. package/dist/Grid/Grid.d.ts.map +1 -1
  83. package/dist/Grid/Grid.js +8 -0
  84. package/dist/HoverCard/HoverCard.d.ts +2 -2
  85. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  86. package/dist/HoverCard/HoverCard.js +18 -6
  87. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  88. package/dist/HoverCard/useHoverCard.js +7 -3
  89. package/dist/InputGroup/InputGroup.d.ts +2 -2
  90. package/dist/InputGroup/InputGroup.d.ts.map +1 -1
  91. package/dist/InputGroup/InputGroup.js +14 -5
  92. package/dist/InputGroup/InputGroupContext.d.ts +4 -0
  93. package/dist/InputGroup/InputGroupContext.d.ts.map +1 -1
  94. package/dist/InputGroup/InputGroupContext.js +2 -2
  95. package/dist/InputGroup/index.d.ts +1 -0
  96. package/dist/InputGroup/index.d.ts.map +1 -1
  97. package/dist/Kbd/Kbd.d.ts.map +1 -1
  98. package/dist/Kbd/Kbd.js +34 -2
  99. package/dist/Layer/anchorName.d.ts +20 -0
  100. package/dist/Layer/anchorName.d.ts.map +1 -0
  101. package/dist/Layer/anchorName.js +36 -0
  102. package/dist/Layer/layerAnimations.stylex.d.ts +4 -0
  103. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  104. package/dist/Layer/layerAnimations.stylex.js +8 -4
  105. package/dist/Layer/useLayer.d.ts +29 -0
  106. package/dist/Layer/useLayer.d.ts.map +1 -1
  107. package/dist/Layer/useLayer.js +55 -11
  108. package/dist/Layout/Layout.d.ts +4 -11
  109. package/dist/Layout/Layout.d.ts.map +1 -1
  110. package/dist/Layout/Layout.js +10 -11
  111. package/dist/Layout/LayoutFooter.d.ts +2 -2
  112. package/dist/Layout/LayoutFooter.d.ts.map +1 -1
  113. package/dist/Layout/LayoutHeader.d.ts +2 -2
  114. package/dist/Layout/LayoutHeader.d.ts.map +1 -1
  115. package/dist/Layout/LayoutPanel.d.ts +2 -2
  116. package/dist/Layout/LayoutPanel.d.ts.map +1 -1
  117. package/dist/Layout/padding.stylex.d.ts +51 -0
  118. package/dist/Layout/padding.stylex.d.ts.map +1 -1
  119. package/dist/Layout/padding.stylex.js +63 -0
  120. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  121. package/dist/Lightbox/Lightbox.js +4 -2
  122. package/dist/Link/Link.d.ts +17 -2
  123. package/dist/Link/Link.d.ts.map +1 -1
  124. package/dist/Link/Link.js +20 -9
  125. package/dist/Link/LinkProvider.d.ts +1 -0
  126. package/dist/Link/LinkProvider.d.ts.map +1 -1
  127. package/dist/Link/LinkProvider.js +1 -0
  128. package/dist/Markdown/Markdown.d.ts.map +1 -1
  129. package/dist/Markdown/Markdown.js +18 -3
  130. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  131. package/dist/MobileNav/MobileNav.js +13 -0
  132. package/dist/MoreMenu/MoreMenu.d.ts +1 -7
  133. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  134. package/dist/MoreMenu/MoreMenu.js +0 -2
  135. package/dist/MultiSelector/MultiSelector.d.ts +25 -2
  136. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  137. package/dist/MultiSelector/MultiSelector.js +103 -22
  138. package/dist/MultiSelector/hooks.d.ts +12 -1
  139. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  140. package/dist/MultiSelector/hooks.js +14 -1
  141. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  142. package/dist/NavMenu/NavHeadingMenu.js +34 -3
  143. package/dist/NumberInput/NumberInput.d.ts +23 -2
  144. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  145. package/dist/NumberInput/NumberInput.js +62 -12
  146. package/dist/Pagination/Pagination.d.ts +1 -1
  147. package/dist/Pagination/Pagination.d.ts.map +1 -1
  148. package/dist/Pagination/Pagination.js +46 -28
  149. package/dist/Popover/usePopover.d.ts +25 -2
  150. package/dist/Popover/usePopover.d.ts.map +1 -1
  151. package/dist/Popover/usePopover.js +18 -6
  152. package/dist/PowerSearch/PowerSearch.d.ts +11 -1
  153. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  154. package/dist/PowerSearch/PowerSearch.js +6 -1
  155. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  156. package/dist/ProgressBar/ProgressBar.js +3 -5
  157. package/dist/RadioList/RadioList.d.ts +20 -2
  158. package/dist/RadioList/RadioList.d.ts.map +1 -1
  159. package/dist/RadioList/RadioList.js +105 -9
  160. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  161. package/dist/RadioList/RadioListItem.js +13 -2
  162. package/dist/SegmentedControl/SegmentedControl.d.ts +13 -2
  163. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  164. package/dist/SegmentedControl/SegmentedControl.js +78 -44
  165. package/dist/SegmentedControl/SegmentedControlContext.d.ts +7 -0
  166. package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  167. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  168. package/dist/SegmentedControl/SegmentedControlItem.js +13 -2
  169. package/dist/Selector/Selector.d.ts +22 -1
  170. package/dist/Selector/Selector.d.ts.map +1 -1
  171. package/dist/Selector/Selector.js +84 -23
  172. package/dist/Selector/hooks.d.ts +8 -1
  173. package/dist/Selector/hooks.d.ts.map +1 -1
  174. package/dist/Selector/hooks.js +14 -1
  175. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  176. package/dist/SideNav/SideNavItem.js +1 -1
  177. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  178. package/dist/Skeleton/Skeleton.js +8 -2
  179. package/dist/Slider/Slider.d.ts +21 -0
  180. package/dist/Slider/Slider.d.ts.map +1 -1
  181. package/dist/Slider/Slider.js +38 -11
  182. package/dist/Spinner/Spinner.d.ts.map +1 -1
  183. package/dist/Spinner/Spinner.js +1 -1
  184. package/dist/Stack/Stack.d.ts +38 -23
  185. package/dist/Stack/Stack.d.ts.map +1 -1
  186. package/dist/Stack/Stack.js +25 -1
  187. package/dist/Stack/StackItem.d.ts +12 -23
  188. package/dist/Stack/StackItem.d.ts.map +1 -1
  189. package/dist/Stack/StackItem.js +8 -1
  190. package/dist/Switch/Switch.d.ts +23 -2
  191. package/dist/Switch/Switch.d.ts.map +1 -1
  192. package/dist/Switch/Switch.js +41 -11
  193. package/dist/TabList/Tab.d.ts.map +1 -1
  194. package/dist/TabList/Tab.js +6 -0
  195. package/dist/TabList/TabList.d.ts +16 -4
  196. package/dist/TabList/TabList.d.ts.map +1 -1
  197. package/dist/TabList/TabList.js +86 -10
  198. package/dist/TabList/TabListContext.d.ts +5 -0
  199. package/dist/TabList/TabListContext.d.ts.map +1 -1
  200. package/dist/TabList/TabListContext.js +5 -0
  201. package/dist/TabList/TabMenu.d.ts.map +1 -1
  202. package/dist/TabList/TabMenu.js +6 -1
  203. package/dist/Table/BaseTable.d.ts.map +1 -1
  204. package/dist/Table/BaseTable.js +38 -16
  205. package/dist/Table/Table.d.ts.map +1 -1
  206. package/dist/Table/Table.js +36 -7
  207. package/dist/Table/TableCell.d.ts +8 -1
  208. package/dist/Table/TableCell.d.ts.map +1 -1
  209. package/dist/Table/TableCell.js +10 -10
  210. package/dist/Table/TableHeaderCell.d.ts +8 -1
  211. package/dist/Table/TableHeaderCell.d.ts.map +1 -1
  212. package/dist/Table/TableHeaderCell.js +16 -16
  213. package/dist/Table/TableRow.d.ts +6 -1
  214. package/dist/Table/TableRow.d.ts.map +1 -1
  215. package/dist/Table/TableRow.js +15 -7
  216. package/dist/Table/index.d.ts +4 -1
  217. package/dist/Table/index.d.ts.map +1 -1
  218. package/dist/Table/index.js +3 -2
  219. package/dist/Table/plugins/pagination/paginateData.d.ts +2 -1
  220. package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
  221. package/dist/Table/plugins/pagination/paginateData.js +8 -3
  222. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  223. package/dist/Table/plugins/pagination/useTablePagination.js +6 -1
  224. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  225. package/dist/Table/plugins/sortable/useTableSortable.js +59 -3
  226. package/dist/Table/plugins/stickyColumns/index.d.ts +3 -0
  227. package/dist/Table/plugins/stickyColumns/index.d.ts.map +1 -0
  228. package/dist/Table/plugins/stickyColumns/index.js +3 -0
  229. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts +25 -0
  230. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -0
  231. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +369 -0
  232. package/dist/Table/tableContextMenu.d.ts +33 -0
  233. package/dist/Table/tableContextMenu.d.ts.map +1 -0
  234. package/dist/Table/tableContextMenu.js +133 -0
  235. package/dist/Table/types.d.ts +161 -5
  236. package/dist/Table/types.d.ts.map +1 -1
  237. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  238. package/dist/Table/useBaseTablePlugins.js +1 -1
  239. package/dist/Text/text.stylex.d.ts +2 -2
  240. package/dist/Text/text.stylex.js +2 -2
  241. package/dist/TextArea/TextArea.d.ts +22 -1
  242. package/dist/TextArea/TextArea.d.ts.map +1 -1
  243. package/dist/TextArea/TextArea.js +41 -9
  244. package/dist/TextInput/TextInput.d.ts +23 -2
  245. package/dist/TextInput/TextInput.d.ts.map +1 -1
  246. package/dist/TextInput/TextInput.js +55 -10
  247. package/dist/TimeInput/TimeInput.d.ts +23 -1
  248. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  249. package/dist/TimeInput/TimeInput.js +53 -10
  250. package/dist/Timestamp/Timestamp.d.ts +1 -1
  251. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  252. package/dist/Timestamp/Timestamp.js +28 -3
  253. package/dist/Toast/Toast.d.ts.map +1 -1
  254. package/dist/Toast/Toast.js +14 -0
  255. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  256. package/dist/Toast/ToastViewport.js +132 -3
  257. package/dist/ToggleButton/ToggleButton.d.ts +7 -9
  258. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  259. package/dist/ToggleButton/ToggleButton.js +34 -57
  260. package/dist/Tokenizer/Tokenizer.d.ts +12 -1
  261. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  262. package/dist/Tokenizer/Tokenizer.js +27 -5
  263. package/dist/Toolbar/Toolbar.d.ts +3 -3
  264. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  265. package/dist/Toolbar/Toolbar.js +51 -7
  266. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  267. package/dist/Tooltip/useTooltip.js +57 -9
  268. package/dist/TreeList/TreeList.d.ts +1 -1
  269. package/dist/TreeList/TreeList.d.ts.map +1 -1
  270. package/dist/TreeList/TreeList.js +72 -2
  271. package/dist/TreeList/TreeListItem.d.ts +11 -1
  272. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  273. package/dist/TreeList/TreeListItem.js +51 -12
  274. package/dist/TreeList/treeListItem.markers.stylex.d.ts +16 -0
  275. package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +1 -0
  276. package/dist/TreeList/treeListItem.markers.stylex.js +22 -0
  277. package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
  278. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  279. package/dist/Typeahead/BaseTypeahead.js +67 -6
  280. package/dist/Typeahead/Typeahead.d.ts +24 -1
  281. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  282. package/dist/Typeahead/Typeahead.js +29 -5
  283. package/dist/VisuallyHidden/VisuallyHidden.d.ts +61 -0
  284. package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
  285. package/dist/VisuallyHidden/VisuallyHidden.js +65 -0
  286. package/dist/VisuallyHidden/index.d.ts +11 -0
  287. package/dist/VisuallyHidden/index.d.ts.map +1 -0
  288. package/dist/VisuallyHidden/index.js +13 -0
  289. package/dist/astryx.css +40 -1
  290. package/dist/astryx.umd.js +147 -0
  291. package/dist/astryx.umd.js.map +7 -0
  292. package/dist/hooks/focusableSelector.d.ts +22 -0
  293. package/dist/hooks/focusableSelector.d.ts.map +1 -0
  294. package/dist/hooks/focusableSelector.js +24 -0
  295. package/dist/hooks/index.d.ts +12 -2
  296. package/dist/hooks/index.d.ts.map +1 -1
  297. package/dist/hooks/index.js +7 -2
  298. package/dist/hooks/useAnnounce.d.ts +40 -0
  299. package/dist/hooks/useAnnounce.d.ts.map +1 -0
  300. package/dist/hooks/useAnnounce.js +142 -0
  301. package/dist/hooks/useClickableContainer.d.ts +16 -1
  302. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  303. package/dist/hooks/useClickableContainer.js +16 -4
  304. package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
  305. package/dist/hooks/useEntryAnimation.js +4 -4
  306. package/dist/hooks/useFocusTrap.d.ts +18 -0
  307. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  308. package/dist/hooks/useFocusTrap.js +87 -3
  309. package/dist/hooks/useGridFocus.d.ts +88 -1
  310. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  311. package/dist/hooks/useGridFocus.js +261 -62
  312. package/dist/hooks/useKeyboardHint.d.ts +82 -0
  313. package/dist/hooks/useKeyboardHint.d.ts.map +1 -0
  314. package/dist/hooks/useKeyboardHint.js +220 -0
  315. package/dist/hooks/useListFocus.d.ts +78 -7
  316. package/dist/hooks/useListFocus.d.ts.map +1 -1
  317. package/dist/hooks/useListFocus.js +305 -52
  318. package/dist/hooks/useLongPress.d.ts +21 -0
  319. package/dist/hooks/useLongPress.d.ts.map +1 -0
  320. package/dist/hooks/useLongPress.js +89 -0
  321. package/dist/hooks/useTreeFocus.d.ts +157 -0
  322. package/dist/hooks/useTreeFocus.d.ts.map +1 -0
  323. package/dist/hooks/useTreeFocus.js +337 -0
  324. package/dist/hooks/useTypeahead.d.ts +63 -0
  325. package/dist/hooks/useTypeahead.d.ts.map +1 -0
  326. package/dist/hooks/useTypeahead.js +117 -0
  327. package/dist/index.d.ts +1 -0
  328. package/dist/index.d.ts.map +1 -1
  329. package/dist/index.js +1 -0
  330. package/dist/theme/generateThemeRules.js +1 -1
  331. package/dist/theme/syntax/SyntaxTheme.d.ts +1 -1
  332. package/dist/theme/syntax/SyntaxTheme.js +1 -1
  333. package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
  334. package/dist/theme/syntax/defineSyntaxTheme.js +2 -2
  335. package/dist/theme/syntax/presets.d.ts +1 -1
  336. package/dist/theme/syntax/presets.d.ts.map +1 -1
  337. package/dist/theme/syntax/presets.js +1 -1
  338. package/dist/theme/syntax/tokens.d.ts +1 -1
  339. package/dist/theme/syntax/tokens.js +1 -1
  340. package/dist/theme/types.d.ts +1 -1
  341. package/dist/utils/dateTypes.d.ts +12 -0
  342. package/dist/utils/dateTypes.d.ts.map +1 -1
  343. package/dist/utils/dateTypes.js +41 -1
  344. package/dist/utils/index.d.ts +4 -0
  345. package/dist/utils/index.d.ts.map +1 -1
  346. package/dist/utils/index.js +3 -1
  347. package/dist/utils/inputAria.d.ts +29 -0
  348. package/dist/utils/inputAria.d.ts.map +1 -0
  349. package/dist/utils/inputAria.js +30 -0
  350. package/dist/utils/timeParser.d.ts.map +1 -1
  351. package/dist/utils/timeParser.js +5 -2
  352. package/package.json +15 -5
  353. package/src/Avatar/Avatar.test.tsx +37 -0
  354. package/src/Avatar/Avatar.tsx +10 -5
  355. package/src/AvatarGroup/AvatarGroupOverflow.tsx +3 -0
  356. package/src/Banner/Banner.doc.mjs +8 -0
  357. package/src/Banner/Banner.test.tsx +16 -7
  358. package/src/Banner/Banner.tsx +9 -2
  359. package/src/Breadcrumbs/BreadcrumbItem.tsx +25 -7
  360. package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -10
  361. package/src/Button/Button.doc.mjs +6 -0
  362. package/src/Button/Button.test.tsx +105 -11
  363. package/src/Button/Button.tsx +76 -21
  364. package/src/Calendar/Calendar.doc.mjs +4 -4
  365. package/src/Calendar/Calendar.test.tsx +168 -30
  366. package/src/Calendar/Calendar.tsx +121 -66
  367. package/src/Calendar/hooks/index.ts +0 -6
  368. package/src/Calendar/index.ts +1 -3
  369. package/src/Calendar/styles.ts +4 -9
  370. package/src/Carousel/Carousel.doc.mjs +7 -1
  371. package/src/Carousel/Carousel.test.tsx +20 -0
  372. package/src/Carousel/Carousel.tsx +16 -1
  373. package/src/Center/Center.doc.mjs +12 -2
  374. package/src/Center/Center.tsx +28 -3
  375. package/src/Chat/ChatComposer.doc.mjs +3 -0
  376. package/src/Chat/ChatComposerDrawer.doc.mjs +12 -0
  377. package/src/Chat/ChatMessageList.doc.mjs +8 -0
  378. package/src/Chat/ChatMessageList.test.tsx +18 -0
  379. package/src/Chat/ChatMessageList.tsx +16 -0
  380. package/src/Chat/useTriggerMenu.tsx +3 -0
  381. package/src/CheckboxInput/CheckboxInput.doc.mjs +18 -0
  382. package/src/CheckboxInput/CheckboxInput.test.tsx +172 -27
  383. package/src/CheckboxInput/CheckboxInput.tsx +78 -6
  384. package/src/CheckboxList/CheckboxList.doc.mjs +9 -0
  385. package/src/CheckboxList/CheckboxList.test.tsx +169 -16
  386. package/src/CheckboxList/CheckboxList.tsx +56 -3
  387. package/src/CheckboxList/CheckboxListContext.tsx +7 -0
  388. package/src/Citation/Citation.doc.mjs +10 -0
  389. package/src/Citation/Citation.test.tsx +117 -0
  390. package/src/Citation/Citation.tsx +9 -1
  391. package/src/ClickableCard/ClickableCard.doc.mjs +15 -0
  392. package/src/CodeBlock/CodeBlock.test.tsx +115 -0
  393. package/src/CodeBlock/CodeBlock.tsx +61 -33
  394. package/src/CommandPalette/CommandPalette.test.tsx +47 -1
  395. package/src/CommandPalette/CommandPalette.tsx +9 -21
  396. package/src/ContextMenu/ContextMenu.doc.mjs +4 -4
  397. package/src/ContextMenu/ContextMenu.test.tsx +155 -1
  398. package/src/ContextMenu/ContextMenu.tsx +113 -16
  399. package/src/DateInput/DateInput.doc.mjs +281 -56
  400. package/src/DateInput/DateInput.test.tsx +188 -2
  401. package/src/DateInput/DateInput.tsx +89 -10
  402. package/src/DateRangeInput/DateRangeInput.doc.mjs +251 -46
  403. package/src/DateRangeInput/DateRangeInput.test.tsx +182 -20
  404. package/src/DateRangeInput/DateRangeInput.tsx +63 -5
  405. package/src/DateTimeInput/DateTimeInput.doc.mjs +368 -67
  406. package/src/DateTimeInput/DateTimeInput.test.tsx +279 -11
  407. package/src/DateTimeInput/DateTimeInput.tsx +125 -17
  408. package/src/Dialog/Dialog.doc.mjs +2 -2
  409. package/src/Dialog/Dialog.tsx +12 -0
  410. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -8
  411. package/src/DropdownMenu/DropdownMenu.test.tsx +56 -42
  412. package/src/DropdownMenu/DropdownMenu.tsx +52 -18
  413. package/src/EmptyState/EmptyState.test.tsx +4 -2
  414. package/src/EmptyState/EmptyState.tsx +6 -2
  415. package/src/Field/Field.test.tsx +32 -0
  416. package/src/Field/Field.tsx +23 -5
  417. package/src/Field/FieldLabel.tsx +55 -17
  418. package/src/FileInput/FileInput.doc.mjs +17 -0
  419. package/src/FileInput/FileInput.test.tsx +283 -49
  420. package/src/FileInput/FileInput.tsx +76 -4
  421. package/src/Grid/Grid.doc.mjs +14 -4
  422. package/src/Grid/Grid.tsx +20 -0
  423. package/src/HoverCard/HoverCard.doc.mjs +3 -0
  424. package/src/HoverCard/HoverCard.test.tsx +189 -2
  425. package/src/HoverCard/HoverCard.tsx +20 -16
  426. package/src/HoverCard/useHoverCard.tsx +13 -10
  427. package/src/Icon/Icon.doc.mjs +8 -0
  428. package/src/InputGroup/InputGroup.doc.mjs +2 -2
  429. package/src/InputGroup/InputGroup.test.tsx +98 -60
  430. package/src/InputGroup/InputGroup.tsx +21 -4
  431. package/src/InputGroup/InputGroupContext.ts +9 -4
  432. package/src/InputGroup/index.ts +1 -0
  433. package/src/Kbd/Kbd.test.tsx +22 -4
  434. package/src/Kbd/Kbd.tsx +37 -12
  435. package/src/Layer/anchorName.test.ts +146 -0
  436. package/src/Layer/anchorName.ts +46 -0
  437. package/src/Layer/layerAnimations.stylex.ts +20 -4
  438. package/src/Layer/useLayer.doc.mjs +12 -2
  439. package/src/Layer/useLayer.tsx +100 -15
  440. package/src/Layout/Layout.doc.mjs +1 -1
  441. package/src/Layout/Layout.tsx +8 -16
  442. package/src/Layout/LayoutFooter.doc.mjs +4 -4
  443. package/src/Layout/LayoutFooter.tsx +3 -3
  444. package/src/Layout/LayoutHeader.doc.mjs +4 -4
  445. package/src/Layout/LayoutHeader.tsx +3 -3
  446. package/src/Layout/LayoutPanel.tsx +3 -3
  447. package/src/Layout/padding.stylex.ts +52 -0
  448. package/src/Lightbox/Lightbox.doc.mjs +9 -2
  449. package/src/Lightbox/Lightbox.test.tsx +53 -5
  450. package/src/Lightbox/Lightbox.tsx +9 -4
  451. package/src/Link/Link.doc.mjs +14 -0
  452. package/src/Link/Link.test.tsx +52 -2
  453. package/src/Link/Link.tsx +42 -7
  454. package/src/Link/LinkProvider.tsx +1 -0
  455. package/src/List/List.doc.mjs +7 -0
  456. package/src/Markdown/Markdown.doc.mjs +2 -0
  457. package/src/Markdown/Markdown.test.tsx +28 -26
  458. package/src/Markdown/Markdown.tsx +21 -6
  459. package/src/MetadataList/MetadataListItem.doc.mjs +4 -0
  460. package/src/MobileNav/MobileNav.doc.mjs +8 -8
  461. package/src/MobileNav/MobileNav.tsx +13 -0
  462. package/src/MobileNav/MobileNavReopen.test.tsx +118 -0
  463. package/src/MoreMenu/MoreMenu.doc.mjs +2 -17
  464. package/src/MoreMenu/MoreMenu.tsx +0 -9
  465. package/src/MultiSelector/MultiSelector.doc.mjs +25 -0
  466. package/src/MultiSelector/MultiSelector.test.tsx +375 -8
  467. package/src/MultiSelector/MultiSelector.tsx +145 -21
  468. package/src/MultiSelector/hooks.ts +27 -0
  469. package/src/NavMenu/NavHeadingMenu.test.tsx +77 -1
  470. package/src/NavMenu/NavHeadingMenu.tsx +50 -3
  471. package/src/NumberInput/NumberInput.doc.mjs +17 -0
  472. package/src/NumberInput/NumberInput.test.tsx +242 -40
  473. package/src/NumberInput/NumberInput.tsx +89 -11
  474. package/src/OverflowList/OverflowList.doc.mjs +17 -0
  475. package/src/Pagination/Pagination.doc.mjs +3 -3
  476. package/src/Pagination/Pagination.test.tsx +250 -14
  477. package/src/Pagination/Pagination.tsx +54 -29
  478. package/src/Popover/usePopover.doc.mjs +7 -3
  479. package/src/Popover/usePopover.tsx +49 -6
  480. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  481. package/src/PowerSearch/PowerSearch.test.tsx +97 -1
  482. package/src/PowerSearch/PowerSearch.tsx +16 -3
  483. package/src/ProgressBar/ProgressBar.test.tsx +45 -27
  484. package/src/ProgressBar/ProgressBar.tsx +3 -4
  485. package/src/RadioList/RadioList.doc.mjs +9 -0
  486. package/src/RadioList/RadioList.test.tsx +252 -16
  487. package/src/RadioList/RadioList.tsx +166 -10
  488. package/src/RadioList/RadioListItem.tsx +14 -2
  489. package/src/Resizable/Resizable.doc.mjs +1 -1
  490. package/src/Section/Section.doc.mjs +11 -0
  491. package/src/SegmentedControl/SegmentedControl.doc.mjs +9 -0
  492. package/src/SegmentedControl/SegmentedControl.test.tsx +214 -42
  493. package/src/SegmentedControl/SegmentedControl.tsx +102 -54
  494. package/src/SegmentedControl/SegmentedControlContext.ts +7 -0
  495. package/src/SegmentedControl/SegmentedControlItem.tsx +14 -1
  496. package/src/SelectableCard/SelectableCard.doc.mjs +15 -0
  497. package/src/Selector/Selector.doc.mjs +25 -0
  498. package/src/Selector/Selector.test.tsx +322 -15
  499. package/src/Selector/Selector.tsx +126 -40
  500. package/src/Selector/hooks.ts +23 -0
  501. package/src/SideNav/SideNav.test.tsx +16 -0
  502. package/src/SideNav/SideNavItem.tsx +2 -4
  503. package/src/Skeleton/Skeleton.doc.mjs +11 -1
  504. package/src/Skeleton/Skeleton.test.tsx +25 -0
  505. package/src/Skeleton/Skeleton.tsx +11 -1
  506. package/src/Slider/Slider.doc.mjs +19 -0
  507. package/src/Slider/Slider.test.tsx +158 -18
  508. package/src/Slider/Slider.tsx +54 -6
  509. package/src/Spinner/Spinner.tsx +7 -1
  510. package/src/Stack/Stack.doc.mjs +77 -0
  511. package/src/Stack/Stack.test.tsx +55 -5
  512. package/src/Stack/Stack.tsx +73 -24
  513. package/src/Stack/StackItem.test.tsx +23 -0
  514. package/src/Stack/StackItem.tsx +20 -23
  515. package/src/Switch/Switch.doc.mjs +15 -0
  516. package/src/Switch/Switch.test.tsx +162 -27
  517. package/src/Switch/Switch.tsx +61 -21
  518. package/src/TabList/Tab.tsx +6 -0
  519. package/src/TabList/TabList.doc.mjs +12 -0
  520. package/src/TabList/TabList.test.tsx +208 -0
  521. package/src/TabList/TabList.tsx +102 -12
  522. package/src/TabList/TabListContext.ts +7 -3
  523. package/src/TabList/TabMenu.tsx +6 -3
  524. package/src/Table/BaseTable.tsx +72 -41
  525. package/src/Table/Table.doc.mjs +14 -0
  526. package/src/Table/Table.test.tsx +39 -30
  527. package/src/Table/Table.tsx +27 -1
  528. package/src/Table/TableCell.tsx +25 -23
  529. package/src/Table/TableHeaderCell.tsx +28 -29
  530. package/src/Table/TableRow.tsx +37 -7
  531. package/src/Table/index.ts +6 -2
  532. package/src/Table/plugins/pagination/paginateData.ts +10 -3
  533. package/src/Table/plugins/pagination/useTablePagination.test.tsx +16 -0
  534. package/src/Table/plugins/pagination/useTablePagination.tsx +8 -1
  535. package/src/Table/plugins/sortable/useTableSortable.test.tsx +91 -2
  536. package/src/Table/plugins/sortable/useTableSortable.tsx +53 -1
  537. package/src/Table/plugins/stickyColumns/index.ts +4 -0
  538. package/src/Table/plugins/stickyColumns/useTableStickyColumns.test.tsx +163 -0
  539. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +407 -0
  540. package/src/Table/tableContextMenu.test.tsx +157 -0
  541. package/src/Table/tableContextMenu.tsx +142 -0
  542. package/src/Table/types.ts +175 -4
  543. package/src/Table/useBaseTablePlugins.ts +1 -0
  544. package/src/Table/useTableColumnResize.doc.mjs +96 -0
  545. package/src/Table/useTableStickyColumns.doc.mjs +59 -0
  546. package/src/Text/Heading.doc.mjs +15 -2
  547. package/src/Text/Text.doc.mjs +7 -1
  548. package/src/Text/text.stylex.ts +2 -2
  549. package/src/TextArea/TextArea.doc.mjs +17 -0
  550. package/src/TextArea/TextArea.test.tsx +167 -35
  551. package/src/TextArea/TextArea.tsx +62 -15
  552. package/src/TextInput/TextInput.doc.mjs +17 -0
  553. package/src/TextInput/TextInput.test.tsx +168 -34
  554. package/src/TextInput/TextInput.tsx +77 -9
  555. package/src/TimeInput/TimeInput.doc.mjs +335 -60
  556. package/src/TimeInput/TimeInput.test.tsx +144 -7
  557. package/src/TimeInput/TimeInput.tsx +74 -5
  558. package/src/Timestamp/Timestamp.doc.mjs +9 -0
  559. package/src/Timestamp/Timestamp.test.tsx +44 -18
  560. package/src/Timestamp/Timestamp.tsx +30 -10
  561. package/src/Toast/Toast.tsx +14 -0
  562. package/src/Toast/ToastViewport.test.tsx +204 -0
  563. package/src/Toast/ToastViewport.tsx +167 -21
  564. package/src/ToggleButton/ToggleButton.doc.mjs +9 -3
  565. package/src/ToggleButton/ToggleButton.test.tsx +39 -18
  566. package/src/ToggleButton/ToggleButton.tsx +46 -83
  567. package/src/Tokenizer/Tokenizer.doc.mjs +15 -0
  568. package/src/Tokenizer/Tokenizer.test.tsx +105 -1
  569. package/src/Tokenizer/Tokenizer.tsx +41 -2
  570. package/src/Toolbar/Toolbar.doc.mjs +65 -5
  571. package/src/Toolbar/Toolbar.test.tsx +109 -6
  572. package/src/Toolbar/Toolbar.tsx +59 -5
  573. package/src/Tooltip/Tooltip.test.tsx +81 -0
  574. package/src/Tooltip/useTooltip.tsx +57 -11
  575. package/src/TreeList/TreeList.test.tsx +295 -6
  576. package/src/TreeList/TreeList.tsx +73 -1
  577. package/src/TreeList/TreeListItem.tsx +51 -5
  578. package/src/TreeList/treeListItem.markers.stylex.ts +20 -0
  579. package/src/Typeahead/BaseTypeahead.tsx +86 -9
  580. package/src/Typeahead/Typeahead.doc.mjs +136 -30
  581. package/src/Typeahead/Typeahead.test.tsx +331 -1
  582. package/src/Typeahead/Typeahead.tsx +52 -1
  583. package/src/VisuallyHidden/VisuallyHidden.doc.mjs +80 -0
  584. package/src/VisuallyHidden/VisuallyHidden.test.tsx +76 -0
  585. package/src/VisuallyHidden/VisuallyHidden.tsx +113 -0
  586. package/src/VisuallyHidden/index.ts +15 -0
  587. package/src/hooks/focusableSelector.ts +25 -0
  588. package/src/hooks/index.ts +28 -2
  589. package/src/hooks/useAnnounce.test.tsx +101 -0
  590. package/src/hooks/useAnnounce.ts +168 -0
  591. package/src/hooks/useClickableContainer.ts +16 -4
  592. package/src/hooks/useEntryAnimation.ts +16 -4
  593. package/src/hooks/useFocusTrap.test.tsx +132 -0
  594. package/src/hooks/useFocusTrap.ts +98 -4
  595. package/src/hooks/useGridFocus.doc.mjs +40 -2
  596. package/src/hooks/useGridFocus.test.tsx +132 -0
  597. package/src/hooks/useGridFocus.ts +343 -67
  598. package/src/hooks/useImageMode.test.ts +133 -0
  599. package/src/hooks/useKeyboardHint.doc.mjs +101 -0
  600. package/src/hooks/useKeyboardHint.test.tsx +70 -0
  601. package/src/hooks/useKeyboardHint.tsx +332 -0
  602. package/src/hooks/useListFocus.doc.mjs +50 -10
  603. package/src/hooks/useListFocus.test.tsx +375 -0
  604. package/src/hooks/useListFocus.ts +395 -57
  605. package/src/hooks/useLongPress.test.tsx +134 -0
  606. package/src/hooks/useLongPress.ts +119 -0
  607. package/src/hooks/useTreeFocus.doc.mjs +140 -0
  608. package/src/hooks/useTreeFocus.test.tsx +214 -0
  609. package/src/hooks/useTreeFocus.ts +559 -0
  610. package/src/hooks/useTypeahead.test.tsx +109 -0
  611. package/src/hooks/useTypeahead.ts +183 -0
  612. package/src/index.ts +1 -0
  613. package/src/theme/derivedVarRegistry.test.ts +4 -0
  614. package/src/theme/generateThemeRules.test.ts +7 -8
  615. package/src/theme/generateThemeRules.ts +1 -1
  616. package/src/theme/syntax/SyntaxTheme.tsx +1 -1
  617. package/src/theme/syntax/defineSyntaxTheme.ts +20 -11
  618. package/src/theme/syntax/presets.ts +5 -2
  619. package/src/theme/syntax/tokens.ts +1 -1
  620. package/src/theme/types.ts +1 -1
  621. package/src/utils/dateTypes.test.ts +40 -0
  622. package/src/utils/dateTypes.ts +40 -0
  623. package/src/utils/index.ts +9 -0
  624. package/src/utils/inputAria.test.ts +39 -0
  625. package/src/utils/inputAria.ts +58 -0
  626. package/src/utils/timeParser.test.ts +10 -0
  627. package/src/utils/timeParser.ts +5 -2
  628. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts +0 -57
  629. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts.map +0 -1
  630. package/dist/Calendar/hooks/useCalendarRovingTabindex.js +0 -96
  631. package/src/Calendar/hooks/useCalendarRovingTabindex.ts +0 -118
@@ -34,11 +34,39 @@ export const docs = {
34
34
  },
35
35
  {
36
36
  name: 'options.orientation',
37
- type: "'horizontal' | 'vertical'",
38
- description: "navigation orientation. 'horizontal' uses ArrowLeft/ArrowRight, 'vertical' uses ArrowUp/ArrowDown.",
37
+ type: "'horizontal' | 'vertical' | 'both'",
38
+ description: "Navigation orientation. 'horizontal' uses ArrowLeft/ArrowRight, 'vertical' uses ArrowUp/ArrowDown, 'both' accepts all four arrows.",
39
39
  default: "'vertical'",
40
40
  required: false,
41
41
  },
42
+ {
43
+ name: 'options.hasHomeEnd',
44
+ type: 'boolean',
45
+ description: 'Whether Home/End jump to the first/last enabled item.',
46
+ default: 'true',
47
+ required: false,
48
+ },
49
+ {
50
+ name: 'options.isRtl',
51
+ type: 'boolean',
52
+ description: 'Whether the list is in a right-to-left context. When true, ArrowLeft/ArrowRight are swapped for horizontal navigation so it follows visual direction.',
53
+ default: 'false',
54
+ required: false,
55
+ },
56
+ {
57
+ name: 'options.hasRovingTabIndex',
58
+ type: 'boolean',
59
+ description: 'Opt into roving-tabindex ownership: the hook stamps a single tab stop (one item tabindex="0", the rest -1), repairs it as items mount/unmount or toggle disabled, and moves it with arrow navigation. When false, the hook only moves focus and never touches tabindex.',
60
+ default: 'false',
61
+ required: false,
62
+ },
63
+ {
64
+ name: 'options.hasCaretGuard',
65
+ type: 'boolean',
66
+ description: "When true, arrow keys are not stolen from a nested text input/textarea whose caret is not at the boundary in the direction of travel (or that has a selection), and are never stolen from a nested contenteditable (rich-text editor / chat composer). Preserves inline text editing within the list.",
67
+ default: 'false',
68
+ required: false,
69
+ },
42
70
  ],
43
71
  returns: [
44
72
  {
@@ -51,6 +79,11 @@ export const docs = {
51
79
  type: '(e: React.KeyboardEvent) => void',
52
80
  description: 'Key down handler to attach to the list container.',
53
81
  },
82
+ {
83
+ name: 'handleFocus',
84
+ type: '(e: React.FocusEvent) => void',
85
+ description: 'Focus handler for the container. Keeps the roving tab stop in sync when hasRovingTabIndex is enabled; a no-op otherwise, so it is always safe to attach.',
86
+ },
54
87
  {
55
88
  name: 'focusItem',
56
89
  type: '(index: number) => void',
@@ -58,21 +91,22 @@ export const docs = {
58
91
  },
59
92
  {
60
93
  name: 'focusFirst',
61
- type: '() => void',
62
- description: 'Focus the first focusable item in the list.',
94
+ type: '() => boolean',
95
+ description: 'Focus the first enabled item. Returns true when an item was focused.',
63
96
  },
64
97
  {
65
98
  name: 'focusLast',
66
- type: '() => void',
67
- description: 'Focus the last focusable item in the list.',
99
+ type: '() => boolean',
100
+ description: 'Focus the last enabled item. Returns true when an item was focused.',
68
101
  },
69
102
  ],
70
103
  usage: {
71
104
  description:
72
- 'Manages keyboard navigation within a linear list following WAI-ARIA menu/listbox/toolbar patterns. Supports arrow key navigation (vertical or horizontal), Home/End for boundaries, optional wrap-around, and Escape to close. Suitable for dropdown menus, toolbars, and any 1D focusable list.',
105
+ 'Manages keyboard navigation within a linear list following WAI-ARIA menu/listbox/toolbar patterns. Supports arrow key navigation (vertical, horizontal, or both), Home/End for boundaries, optional wrap-around, RTL, and Escape to close. Opt into hasRovingTabIndex for composite widgets (toolbars, segmented controls, tab strips) that own a single tab stop. Suitable for dropdown menus, toolbars, and any 1D focusable list.',
73
106
  bestPractices: [
74
107
  { guidance: true, description: "Set orientation to 'horizontal' for toolbars and tab bars, 'vertical' for dropdown menus." },
75
108
  { guidance: true, description: 'Provide an onEscape callback for menus/dropdowns to return focus to the trigger.' },
109
+ { guidance: true, description: 'Enable hasRovingTabIndex (and hasCaretGuard when the widget can contain text inputs) for toolbar-style composites that should be a single tab stop.' },
76
110
  { guidance: false, description: 'Use for 2D grid navigation; prefer useGridFocus for grids and calendars.' },
77
111
  ],
78
112
  },
@@ -91,14 +125,19 @@ export const docsDense = {
91
125
  'options.itemSelector': 'selector for focusable items in list.',
92
126
  'options.wrap': 'whether arrow navigation wraps around at ends.',
93
127
  'options.onEscape': 'callback when Escape key pressed (e.g. close menu).',
94
- 'options.orientation': "navigation orientation. 'horizontal' uses ArrowLeft/ArrowRight, 'vertical' uses ArrowUp/ArrowDown.",
128
+ 'options.orientation': "navigation orientation. 'horizontal' uses ArrowLeft/ArrowRight, 'vertical' uses ArrowUp/ArrowDown, 'both' accepts all four arrows.",
129
+ 'options.hasHomeEnd': 'whether Home/End jump to first/last enabled item.',
130
+ 'options.isRtl': 'when true, ArrowLeft/ArrowRight swap for horizontal nav (RTL).',
131
+ 'options.hasRovingTabIndex': 'opt into roving-tabindex ownership: hook stamps + repairs a single tab stop across items.',
132
+ 'options.hasCaretGuard': "when true, don't steal arrow keys from a nested text input/textarea mid-line or from a contenteditable.",
95
133
  },
96
134
  returnDescriptions: {
97
135
  listRef: 'ref to attach to list container element.',
98
136
  handleKeyDown: 'key down handler for list container.',
137
+ handleFocus: 'focus handler; keeps roving tab stop in sync when hasRovingTabIndex is on (else no-op).',
99
138
  focusItem: 'focus specific item by index (clamped to valid range).',
100
- focusFirst: 'focus first focusable item in list.',
101
- focusLast: 'focus last focusable item in list.',
139
+ focusFirst: 'focus first enabled item; returns true when focused.',
140
+ focusLast: 'focus last enabled item; returns true when focused.',
102
141
  },
103
142
  usage: {
104
143
  description:
@@ -106,6 +145,7 @@ export const docsDense = {
106
145
  bestPractices: [
107
146
  { guidance: true, description: "Set orientation to 'horizontal' for toolbars + tab bars, 'vertical' for dropdown menus." },
108
147
  { guidance: true, description: 'Provide onEscape callback for menus/dropdowns to return focus to trigger.' },
148
+ { guidance: true, description: 'Enable hasRovingTabIndex (+ hasCaretGuard when widget can contain text inputs) for toolbar-style composites that should be a single tab stop.' },
109
149
  { guidance: false, description: 'Use for 2D grid navigation; prefer useGridFocus for grids + calendars.' },
110
150
  ],
111
151
  },
@@ -0,0 +1,375 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file useListFocus.test.tsx
5
+ * @input Uses vitest, @testing-library/react, useListFocus hook
6
+ * @output Unit tests for useListFocus disabled-item skipping + navigation
7
+ * @position Testing; validates useListFocus.ts keyboard navigation
8
+ *
9
+ * SYNC: When useListFocus.ts changes, update tests to match new behavior
10
+ */
11
+
12
+ import {describe, it, expect} from 'vitest';
13
+ import {render, screen, fireEvent} from '@testing-library/react';
14
+ import {useListFocus} from './useListFocus';
15
+
16
+ const NO_DISABLED: string[] = [];
17
+
18
+ function Menu({
19
+ wrap = true,
20
+ disabledLabels = NO_DISABLED,
21
+ }: {
22
+ wrap?: boolean;
23
+ disabledLabels?: string[];
24
+ }) {
25
+ const {listRef, handleKeyDown} = useListFocus<HTMLDivElement>({wrap});
26
+ const items = ['One', 'Two', 'Three', 'Four'];
27
+ return (
28
+ <div ref={listRef} role="menu" onKeyDown={handleKeyDown}>
29
+ {items.map(label => {
30
+ const disabled = disabledLabels.includes(label);
31
+ return (
32
+ <div
33
+ key={label}
34
+ role="menuitem"
35
+ tabIndex={disabled ? undefined : -1}
36
+ aria-disabled={disabled || undefined}
37
+ data-testid={label}>
38
+ {label}
39
+ </div>
40
+ );
41
+ })}
42
+ </div>
43
+ );
44
+ }
45
+
46
+ describe('useListFocus disabled-item skipping', () => {
47
+ it('ArrowDown skips a disabled item instead of stalling on it', () => {
48
+ render(<Menu disabledLabels={['Two']} />);
49
+ const menu = screen.getByRole('menu');
50
+ screen.getByTestId('One').focus();
51
+
52
+ fireEvent.keyDown(menu, {key: 'ArrowDown'});
53
+ // Should skip disabled "Two" and land on "Three".
54
+ expect(screen.getByTestId('Three')).toHaveFocus();
55
+ });
56
+
57
+ it('ArrowUp skips a disabled item', () => {
58
+ render(<Menu disabledLabels={['Three']} />);
59
+ const menu = screen.getByRole('menu');
60
+ screen.getByTestId('Four').focus();
61
+
62
+ fireEvent.keyDown(menu, {key: 'ArrowUp'});
63
+ // Should skip disabled "Three" and land on "Two".
64
+ expect(screen.getByTestId('Two')).toHaveFocus();
65
+ });
66
+
67
+ it('does not freeze at a leading disabled item (regression: menus-4)', () => {
68
+ render(<Menu disabledLabels={['One']} />);
69
+ const menu = screen.getByRole('menu');
70
+ // Focus starts nowhere; ArrowDown should reach the first ENABLED item.
71
+ fireEvent.keyDown(menu, {key: 'ArrowDown'});
72
+ expect(screen.getByTestId('Two')).toHaveFocus();
73
+ });
74
+
75
+ it('wraps past a disabled item at the end', () => {
76
+ render(<Menu disabledLabels={['Four']} wrap />);
77
+ const menu = screen.getByRole('menu');
78
+ screen.getByTestId('Three').focus();
79
+
80
+ fireEvent.keyDown(menu, {key: 'ArrowDown'});
81
+ // "Four" is disabled, wrap to "One".
82
+ expect(screen.getByTestId('One')).toHaveFocus();
83
+ });
84
+
85
+ it('does not wrap when wrap is false', () => {
86
+ render(<Menu disabledLabels={['Four']} wrap={false} />);
87
+ const menu = screen.getByRole('menu');
88
+ screen.getByTestId('Three').focus();
89
+
90
+ fireEvent.keyDown(menu, {key: 'ArrowDown'});
91
+ // "Four" disabled, no wrap -> focus stays on "Three".
92
+ expect(screen.getByTestId('Three')).toHaveFocus();
93
+ });
94
+
95
+ it('Home focuses the first enabled item, End the last enabled item', () => {
96
+ render(<Menu disabledLabels={['One', 'Four']} />);
97
+ const menu = screen.getByRole('menu');
98
+ screen.getByTestId('Two').focus();
99
+
100
+ fireEvent.keyDown(menu, {key: 'End'});
101
+ expect(screen.getByTestId('Three')).toHaveFocus();
102
+
103
+ fireEvent.keyDown(menu, {key: 'Home'});
104
+ expect(screen.getByTestId('Two')).toHaveFocus();
105
+ });
106
+ });
107
+
108
+ // ---------------------------------------------------------------------------
109
+ // Roving-tabindex mode + composite navigation behaviors.
110
+ // These exercise the opt-in `hasRovingTabIndex`, `isRtl`, `orientation: 'both'`,
111
+ // `hasCaretGuard`, and shortcut-passthrough behaviors.
112
+ // ---------------------------------------------------------------------------
113
+
114
+ const ROVING_LABELS = ['A', 'B', 'C'];
115
+
116
+ function RovingToolbar({
117
+ labels = ROVING_LABELS,
118
+ disabledLabels = NO_DISABLED,
119
+ ...opts
120
+ }: {
121
+ labels?: string[];
122
+ disabledLabels?: string[];
123
+ } & Parameters<typeof useListFocus>[0]) {
124
+ const {listRef, handleKeyDown, handleFocus} = useListFocus<HTMLDivElement>({
125
+ itemSelector: 'button, input, [tabindex]',
126
+ hasRovingTabIndex: true,
127
+ orientation: 'horizontal',
128
+ ...opts,
129
+ });
130
+ return (
131
+ <div
132
+ ref={listRef}
133
+ role="toolbar"
134
+ onKeyDown={handleKeyDown}
135
+ onFocus={handleFocus}>
136
+ {labels.map(label => (
137
+ <button
138
+ key={label}
139
+ type="button"
140
+ disabled={disabledLabels.includes(label)}
141
+ data-testid={label}>
142
+ {label}
143
+ </button>
144
+ ))}
145
+ </div>
146
+ );
147
+ }
148
+
149
+ describe('useListFocus roving tabindex (hasRovingTabIndex)', () => {
150
+ it('stamps a single tab stop (first enabled item is tabbable)', () => {
151
+ render(<RovingToolbar />);
152
+ expect(screen.getByTestId('A')).toHaveAttribute('tabindex', '0');
153
+ expect(screen.getByTestId('B')).toHaveAttribute('tabindex', '-1');
154
+ expect(screen.getByTestId('C')).toHaveAttribute('tabindex', '-1');
155
+ });
156
+
157
+ it('promotes the first ENABLED item when the first is disabled (repair)', () => {
158
+ render(<RovingToolbar disabledLabels={['A']} />);
159
+ expect(screen.getByTestId('B')).toHaveAttribute('tabindex', '0');
160
+ expect(screen.getByTestId('A')).toHaveAttribute('tabindex', '-1');
161
+ });
162
+
163
+ it('ArrowRight moves the tab stop to the next enabled item', () => {
164
+ render(<RovingToolbar />);
165
+ const toolbar = screen.getByRole('toolbar');
166
+ screen.getByTestId('A').focus();
167
+ fireEvent.keyDown(toolbar, {key: 'ArrowRight'});
168
+ expect(screen.getByTestId('B')).toHaveFocus();
169
+ expect(screen.getByTestId('B')).toHaveAttribute('tabindex', '0');
170
+ expect(screen.getByTestId('A')).toHaveAttribute('tabindex', '-1');
171
+ });
172
+
173
+ it('ArrowRight skips a disabled item', () => {
174
+ render(<RovingToolbar disabledLabels={['B']} />);
175
+ const toolbar = screen.getByRole('toolbar');
176
+ screen.getByTestId('A').focus();
177
+ fireEvent.keyDown(toolbar, {key: 'ArrowRight'});
178
+ expect(screen.getByTestId('C')).toHaveFocus();
179
+ });
180
+
181
+ it('wraps at the end by default', () => {
182
+ render(<RovingToolbar />);
183
+ const toolbar = screen.getByRole('toolbar');
184
+ screen.getByTestId('C').focus();
185
+ fireEvent.keyDown(toolbar, {key: 'ArrowRight'});
186
+ expect(screen.getByTestId('A')).toHaveFocus();
187
+ });
188
+
189
+ it('does not wrap when wrap=false', () => {
190
+ render(<RovingToolbar wrap={false} />);
191
+ const toolbar = screen.getByRole('toolbar');
192
+ screen.getByTestId('C').focus();
193
+ fireEvent.keyDown(toolbar, {key: 'ArrowRight'});
194
+ expect(screen.getByTestId('C')).toHaveFocus();
195
+ });
196
+
197
+ it('Home/End jump to first/last enabled items', () => {
198
+ render(<RovingToolbar />);
199
+ const toolbar = screen.getByRole('toolbar');
200
+ screen.getByTestId('B').focus();
201
+ fireEvent.keyDown(toolbar, {key: 'End'});
202
+ expect(screen.getByTestId('C')).toHaveFocus();
203
+ fireEvent.keyDown(toolbar, {key: 'Home'});
204
+ expect(screen.getByTestId('A')).toHaveFocus();
205
+ });
206
+
207
+ it('flips ArrowLeft/ArrowRight under RTL', () => {
208
+ render(<RovingToolbar isRtl />);
209
+ const toolbar = screen.getByRole('toolbar');
210
+ screen.getByTestId('A').focus();
211
+ // In RTL, ArrowLeft is "forward".
212
+ fireEvent.keyDown(toolbar, {key: 'ArrowLeft'});
213
+ expect(screen.getByTestId('B')).toHaveFocus();
214
+ });
215
+
216
+ it('orientation "both" navigates with all four arrows', () => {
217
+ render(<RovingToolbar orientation="both" />);
218
+ const toolbar = screen.getByRole('toolbar');
219
+ screen.getByTestId('A').focus();
220
+ fireEvent.keyDown(toolbar, {key: 'ArrowDown'});
221
+ expect(screen.getByTestId('B')).toHaveFocus();
222
+ fireEvent.keyDown(toolbar, {key: 'ArrowRight'});
223
+ expect(screen.getByTestId('C')).toHaveFocus();
224
+ });
225
+
226
+ it('vertical orientation ignores horizontal arrows', () => {
227
+ render(<RovingToolbar orientation="vertical" />);
228
+ const toolbar = screen.getByRole('toolbar');
229
+ screen.getByTestId('A').focus();
230
+ fireEvent.keyDown(toolbar, {key: 'ArrowDown'});
231
+ expect(screen.getByTestId('B')).toHaveFocus();
232
+ fireEvent.keyDown(toolbar, {key: 'ArrowRight'});
233
+ // ArrowRight is inert in vertical mode.
234
+ expect(screen.getByTestId('B')).toHaveFocus();
235
+ });
236
+
237
+ it('does not manage tabindex when hasRovingTabIndex is off', () => {
238
+ render(<RovingToolbar hasRovingTabIndex={false} />);
239
+ // No tabindex stamped — the buttons keep their intrinsic tab order.
240
+ expect(screen.getByTestId('A')).not.toHaveAttribute('tabindex');
241
+ expect(screen.getByTestId('B')).not.toHaveAttribute('tabindex');
242
+ });
243
+ });
244
+
245
+ function ToolbarWithInput(opts: Parameters<typeof useListFocus>[0]) {
246
+ const {listRef, handleKeyDown, handleFocus} = useListFocus<HTMLDivElement>({
247
+ itemSelector: 'button, input, [tabindex]',
248
+ hasRovingTabIndex: true,
249
+ orientation: 'horizontal',
250
+ hasCaretGuard: true,
251
+ ...opts,
252
+ });
253
+ return (
254
+ <div
255
+ ref={listRef}
256
+ role="toolbar"
257
+ onKeyDown={handleKeyDown}
258
+ onFocus={handleFocus}>
259
+ <button type="button" data-testid="before">
260
+ Before
261
+ </button>
262
+ <input type="text" defaultValue="hello" data-testid="field" />
263
+ <button type="button" data-testid="after">
264
+ After
265
+ </button>
266
+ </div>
267
+ );
268
+ }
269
+
270
+ describe('useListFocus caret-boundary guard (hasCaretGuard, navigation-4)', () => {
271
+ function getField(): HTMLInputElement {
272
+ const el = screen.getByTestId('field');
273
+ if (!(el instanceof HTMLInputElement)) {
274
+ throw new Error('expected an input');
275
+ }
276
+ return el;
277
+ }
278
+
279
+ it('does not steal ArrowRight from a text input mid-line', () => {
280
+ render(<ToolbarWithInput />);
281
+ const toolbar = screen.getByRole('toolbar');
282
+ const field = getField();
283
+ field.focus();
284
+ field.setSelectionRange(1, 1); // caret in the middle of "hello"
285
+ fireEvent.keyDown(toolbar, {key: 'ArrowRight'});
286
+ // Focus stays in the input; caret movement is left to the browser.
287
+ expect(field).toHaveFocus();
288
+ });
289
+
290
+ it('steals ArrowRight when the caret is at the end of the input', () => {
291
+ render(<ToolbarWithInput />);
292
+ const toolbar = screen.getByRole('toolbar');
293
+ const field = getField();
294
+ field.focus();
295
+ field.setSelectionRange(5, 5); // caret at end of "hello"
296
+ fireEvent.keyDown(toolbar, {key: 'ArrowRight'});
297
+ // Now the composite navigates to the next item.
298
+ expect(screen.getByTestId('after')).toHaveFocus();
299
+ });
300
+
301
+ it('does not steal an arrow key when the input has a selection', () => {
302
+ render(<ToolbarWithInput />);
303
+ const toolbar = screen.getByRole('toolbar');
304
+ const field = getField();
305
+ field.focus();
306
+ field.setSelectionRange(0, 5); // whole value selected
307
+ fireEvent.keyDown(toolbar, {key: 'ArrowRight'});
308
+ expect(field).toHaveFocus();
309
+ });
310
+
311
+ it('steals the key from a text input when hasCaretGuard is off', () => {
312
+ render(<ToolbarWithInput hasCaretGuard={false} />);
313
+ const toolbar = screen.getByRole('toolbar');
314
+ const field = getField();
315
+ field.focus();
316
+ field.setSelectionRange(1, 1); // caret mid-line, but no caret guard
317
+ fireEvent.keyDown(toolbar, {key: 'ArrowRight'});
318
+ expect(screen.getByTestId('after')).toHaveFocus();
319
+ });
320
+ });
321
+
322
+ function ToolbarWithEditable(opts: Parameters<typeof useListFocus>[0]) {
323
+ const {listRef, handleKeyDown, handleFocus} = useListFocus<HTMLDivElement>({
324
+ itemSelector: 'button, input, [contenteditable], [tabindex]',
325
+ hasRovingTabIndex: true,
326
+ orientation: 'horizontal',
327
+ hasCaretGuard: true,
328
+ ...opts,
329
+ });
330
+ return (
331
+ <div
332
+ ref={listRef}
333
+ role="toolbar"
334
+ onKeyDown={handleKeyDown}
335
+ onFocus={handleFocus}>
336
+ <button type="button" data-testid="before">
337
+ Before
338
+ </button>
339
+ <div
340
+ contentEditable
341
+ suppressContentEditableWarning
342
+ data-testid="composer">
343
+ hello world
344
+ </div>
345
+ <button type="button" data-testid="after">
346
+ After
347
+ </button>
348
+ </div>
349
+ );
350
+ }
351
+
352
+ describe('useListFocus caret-boundary guard: contenteditable (navigation-4)', () => {
353
+ it('does not steal arrow keys from a non-empty contenteditable', () => {
354
+ render(<ToolbarWithEditable />);
355
+ const toolbar = screen.getByRole('toolbar');
356
+ const composer = screen.getByTestId('composer');
357
+ composer.focus();
358
+ fireEvent.keyDown(toolbar, {key: 'ArrowRight'});
359
+ // Focus stays in the editor; list navigation must not hijack the arrow.
360
+ expect(composer).toHaveFocus();
361
+ fireEvent.keyDown(toolbar, {key: 'ArrowLeft'});
362
+ expect(composer).toHaveFocus();
363
+ });
364
+ });
365
+
366
+ describe('useListFocus shortcut passthrough', () => {
367
+ it('passes browser shortcut chords (Cmd/Ctrl/Alt) through', () => {
368
+ render(<RovingToolbar />);
369
+ const toolbar = screen.getByRole('toolbar');
370
+ screen.getByTestId('A').focus();
371
+ fireEvent.keyDown(toolbar, {key: 'ArrowRight', metaKey: true});
372
+ // Focus should not move on a modified chord.
373
+ expect(screen.getByTestId('A')).toHaveFocus();
374
+ });
375
+ });