@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,70 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file useKeyboardHint.test.tsx
5
+ * @input Uses React Testing Library, useKeyboardHint
6
+ * @output Unit tests for keyboard hint rendering
7
+ */
8
+
9
+ import {render} from '@testing-library/react';
10
+ import {describe, expect, it} from 'vitest';
11
+ import {useKeyboardHint, type KeyboardHintOrientation} from './useKeyboardHint';
12
+
13
+ function TestHint({orientation}: {orientation?: KeyboardHintOrientation}) {
14
+ const {hintElement} = useKeyboardHint({orientation});
15
+ return <div>{hintElement}</div>;
16
+ }
17
+
18
+ function getKeyLabels(container: HTMLElement): (string | null)[] {
19
+ return Array.from(container.querySelectorAll('.astryx-kbd')).map(key =>
20
+ key.getAttribute('aria-label'),
21
+ );
22
+ }
23
+
24
+ function getKeyText(container: HTMLElement): string[] {
25
+ return Array.from(container.querySelectorAll('kbd')).map(
26
+ key => key.textContent ?? '',
27
+ );
28
+ }
29
+
30
+ describe('useKeyboardHint', () => {
31
+ it('renders horizontal arrow keys with Kbd', () => {
32
+ const {container} = render(<TestHint orientation="horizontal" />);
33
+
34
+ expect(getKeyLabels(container)).toEqual(['Left arrow', 'Right arrow']);
35
+ expect(getKeyText(container)).toEqual(['←', '→']);
36
+ });
37
+
38
+ it('renders vertical arrow keys with Kbd', () => {
39
+ const {container} = render(<TestHint orientation="vertical" />);
40
+
41
+ expect(getKeyLabels(container)).toEqual(['Up arrow', 'Down arrow']);
42
+ expect(getKeyText(container)).toEqual(['↑', '↓']);
43
+ });
44
+
45
+ it('renders all arrow keys for both-axis navigation', () => {
46
+ const {container} = render(<TestHint orientation="both" />);
47
+
48
+ expect(getKeyLabels(container)).toEqual([
49
+ 'Left arrow',
50
+ 'Right arrow',
51
+ 'Up arrow',
52
+ 'Down arrow',
53
+ ]);
54
+ expect(getKeyText(container)).toEqual(['←', '→', '↑', '↓']);
55
+ });
56
+
57
+ it('positions the hint farther from the anchor', () => {
58
+ const {container} = render(<TestHint />);
59
+ const hint = container.querySelector('[popover="manual"]') as HTMLElement;
60
+
61
+ expect(hint.style.marginBlockStart).toBe('var(--spacing-2)');
62
+ });
63
+
64
+ it('keeps the hint surface padding after useLayer reset styles', () => {
65
+ const {container} = render(<TestHint />);
66
+ const hint = container.querySelector('[popover="manual"]') as HTMLElement;
67
+
68
+ expect(hint.className).toContain('useKeyboardHint__styles.hint');
69
+ });
70
+ });
@@ -0,0 +1,332 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useKeyboardHint.tsx
7
+ * @input Uses React, StyleX, Kbd, useLayer
8
+ * @output Exports useKeyboardHint hook — ephemeral arrow-key navigation hint
9
+ * @position Core hook; shows sighted keyboard users how to navigate composite
10
+ * widgets that use roving tabindex (single Tab stop, arrows inside)
11
+ *
12
+ * SYNC: When modified, update:
13
+ * - /packages/core/src/hooks/index.ts
14
+ * - /packages/core/src/hooks/useKeyboardHint.doc.mjs
15
+ * - /apps/storybook/stories/useKeyboardHint.stories.tsx
16
+ * - /packages/cli/templates/blocks/components/Hooks/useKeyboardHintHookUsage.tsx
17
+ */
18
+
19
+ import React, {useCallback, useEffect, useRef, type ReactNode} from 'react';
20
+ import * as stylex from '@stylexjs/stylex';
21
+ import {
22
+ colorVars,
23
+ radiusVars,
24
+ shadowVars,
25
+ spacingVars,
26
+ typeScaleVars,
27
+ } from '../theme/tokens.stylex';
28
+ import {Kbd} from '../Kbd';
29
+ import {useLayer} from '../Layer/useLayer';
30
+
31
+ // ---------------------------------------------------------------------------
32
+ // Types
33
+ // ---------------------------------------------------------------------------
34
+
35
+ export type KeyboardHintOrientation = 'horizontal' | 'vertical' | 'both';
36
+
37
+ export interface UseKeyboardHintOptions {
38
+ /**
39
+ * Orientation of the arrow-key navigation. Controls which arrow icons are
40
+ * shown in the hint badge.
41
+ * - `'horizontal'` → ← →
42
+ * - `'vertical'` → ↑ ↓
43
+ * - `'both'` → ← → ↑ ↓
44
+ * @default 'horizontal'
45
+ */
46
+ orientation?: KeyboardHintOrientation;
47
+
48
+ /**
49
+ * Milliseconds before the hint auto-dismisses after appearing.
50
+ * @default 3000
51
+ */
52
+ dismissAfterMs?: number;
53
+
54
+ /**
55
+ * Whether the hint is enabled. Set to false to disable for a specific
56
+ * instance (e.g. when the widget is read-only or the user has dismissed
57
+ * globally).
58
+ * @default true
59
+ */
60
+ isEnabled?: boolean;
61
+ }
62
+
63
+ export interface UseKeyboardHintReturn {
64
+ /**
65
+ * The popover hint element to render inside your component tree (portals to
66
+ * top layer via `popover="manual"`). Render unconditionally — it manages its
67
+ * own visibility.
68
+ */
69
+ hintElement: ReactNode;
70
+
71
+ /**
72
+ * Attach to the composite container's `onFocus`. Shows the hint on the first
73
+ * keyboard-focus (`:focus-visible`) entry from outside.
74
+ */
75
+ onFocus: (e: React.FocusEvent) => void;
76
+
77
+ /**
78
+ * Attach to the composite container's `onBlur`. Hides the hint when focus
79
+ * leaves the composite entirely.
80
+ */
81
+ onBlur: (e: React.FocusEvent) => void;
82
+
83
+ /**
84
+ * Attach to the composite container's `onKeyDown`. Dismisses the hint on the
85
+ * first arrow press (the user discovered the interaction).
86
+ */
87
+ onKeyDown: (e: React.KeyboardEvent) => void;
88
+ }
89
+
90
+ // ---------------------------------------------------------------------------
91
+ // Styles
92
+ // ---------------------------------------------------------------------------
93
+
94
+ const ARROW_KEYS = new Set(['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']);
95
+
96
+ const ARROW_HINT_KEYS: Record<
97
+ KeyboardHintOrientation,
98
+ ReadonlyArray<string>
99
+ > = {
100
+ horizontal: ['left', 'right'],
101
+ vertical: ['up', 'down'],
102
+ both: ['left', 'right', 'up', 'down'],
103
+ };
104
+
105
+ const styles = stylex.create({
106
+ hint: {
107
+ // Top layer + anchor positioned
108
+ position: 'fixed',
109
+ inset: 'auto',
110
+ margin: 0,
111
+ border: 'none',
112
+
113
+ // Surface
114
+ backgroundColor: colorVars['--color-background-popover'],
115
+ borderRadius: radiusVars['--radius-element'],
116
+ boxShadow: shadowVars['--shadow-low'],
117
+ paddingBlockStart: spacingVars['--spacing-1'],
118
+ paddingBlockEnd: spacingVars['--spacing-1'],
119
+ paddingInlineStart: spacingVars['--spacing-2'],
120
+ paddingInlineEnd: spacingVars['--spacing-2'],
121
+
122
+ // Typography
123
+ fontSize: typeScaleVars['--text-supporting-size'],
124
+ lineHeight: typeScaleVars['--text-supporting-leading'],
125
+ color: colorVars['--color-text-secondary'],
126
+ whiteSpace: 'nowrap',
127
+
128
+ // Animation
129
+ opacity: {
130
+ default: 0,
131
+ ':popover-open': 1,
132
+ },
133
+ transitionProperty: 'opacity, display, overlay',
134
+ transitionDuration: '150ms',
135
+ transitionBehavior: 'allow-discrete',
136
+
137
+ // Don't capture pointer events (hint floats above content)
138
+ pointerEvents: 'none',
139
+ },
140
+ keys: {
141
+ display: 'inline-flex',
142
+ alignItems: 'center',
143
+ gap: spacingVars['--spacing-1'],
144
+ },
145
+ label: {
146
+ marginInlineStart: spacingVars['--spacing-1'],
147
+ },
148
+ });
149
+
150
+ // ---------------------------------------------------------------------------
151
+ // Hook
152
+ // ---------------------------------------------------------------------------
153
+
154
+ /**
155
+ * Shows an ephemeral visual hint ("← → to navigate") anchored to the focused
156
+ * item when a composite widget first receives keyboard focus. Teaches sighted
157
+ * keyboard users that arrows navigate within the group.
158
+ *
159
+ * The hint renders in the top layer (popover="manual") and is CSS-anchor-
160
+ * positioned to the currently focused element, so it is never clipped by
161
+ * overflow containers. It auto-dismisses on first arrow press, timeout, or
162
+ * blur, and does not re-show for that instance.
163
+ *
164
+ * @example
165
+ * ```
166
+ * const hint = useKeyboardHint({orientation: 'horizontal'});
167
+ * <div role="toolbar" onFocus={hint.onFocus} onBlur={hint.onBlur} onKeyDown={hint.onKeyDown}>
168
+ * {children}
169
+ * {hint.hintElement}
170
+ * </div>
171
+ * ```
172
+ */
173
+ export function useKeyboardHint(
174
+ options: UseKeyboardHintOptions = {},
175
+ ): UseKeyboardHintReturn {
176
+ const {
177
+ orientation = 'horizontal',
178
+ dismissAfterMs = 3000,
179
+ isEnabled = true,
180
+ } = options;
181
+
182
+ const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
183
+ const dismissedRef = useRef(false);
184
+ const isVisibleRef = useRef(false);
185
+ const layerAnchorRef = useRef<(el: HTMLElement | null) => void>(() => {});
186
+
187
+ const clearDismissTimeout = useCallback(() => {
188
+ if (timeoutRef.current) {
189
+ clearTimeout(timeoutRef.current);
190
+ timeoutRef.current = null;
191
+ }
192
+ }, []);
193
+
194
+ const handleLayerShow = useCallback(() => {
195
+ isVisibleRef.current = true;
196
+ }, []);
197
+
198
+ const handleLayerHide = useCallback(() => {
199
+ isVisibleRef.current = false;
200
+ clearDismissTimeout();
201
+ layerAnchorRef.current(null);
202
+ }, [clearDismissTimeout]);
203
+
204
+ const layer = useLayer({
205
+ mode: 'context',
206
+ onShow: handleLayerShow,
207
+ onHide: handleLayerHide,
208
+ });
209
+ layerAnchorRef.current = layer.ref;
210
+
211
+ // Hide + mark dismissed (won't re-show for this instance)
212
+ const dismiss = useCallback(() => {
213
+ dismissedRef.current = true;
214
+ clearDismissTimeout();
215
+ layer.hide();
216
+ isVisibleRef.current = false;
217
+ layerAnchorRef.current(null);
218
+ }, [clearDismissTimeout, layer]);
219
+
220
+ // Show the layer anchored to the focused element
221
+ const show = useCallback(
222
+ (anchor: HTMLElement) => {
223
+ if (dismissedRef.current || !isEnabled) {
224
+ return;
225
+ }
226
+
227
+ layerAnchorRef.current(anchor);
228
+ layer.show();
229
+
230
+ clearDismissTimeout();
231
+ timeoutRef.current = setTimeout(() => {
232
+ dismiss();
233
+ }, dismissAfterMs);
234
+ },
235
+ [clearDismissTimeout, dismiss, dismissAfterMs, isEnabled, layer],
236
+ );
237
+
238
+ // Cleanup on unmount
239
+ useEffect(
240
+ () => () => {
241
+ clearDismissTimeout();
242
+ layerAnchorRef.current(null);
243
+ },
244
+ [clearDismissTimeout],
245
+ );
246
+
247
+ // --- Handlers ---
248
+
249
+ const onFocus = useCallback(
250
+ (e: React.FocusEvent) => {
251
+ if (dismissedRef.current || !isEnabled) {
252
+ return;
253
+ }
254
+ // Only show on keyboard focus (focus-visible)
255
+ const target = e.target as HTMLElement;
256
+ if (!target.matches(':focus-visible')) {
257
+ return;
258
+ }
259
+ // Only show when focus enters from outside the container
260
+ const container = e.currentTarget as HTMLElement;
261
+ if (
262
+ e.relatedTarget instanceof Node &&
263
+ container.contains(e.relatedTarget)
264
+ ) {
265
+ return;
266
+ }
267
+ show(target);
268
+ },
269
+ [show, isEnabled],
270
+ );
271
+
272
+ const onBlur = useCallback(
273
+ (e: React.FocusEvent) => {
274
+ if (!isVisibleRef.current) {
275
+ return;
276
+ }
277
+ const container = e.currentTarget as HTMLElement;
278
+ // Only dismiss when focus leaves the container entirely
279
+ if (
280
+ e.relatedTarget instanceof Node &&
281
+ container.contains(e.relatedTarget)
282
+ ) {
283
+ // Focus moved within — re-anchor to the new target
284
+ if (!dismissedRef.current && e.relatedTarget instanceof HTMLElement) {
285
+ layerAnchorRef.current(e.relatedTarget);
286
+ }
287
+ return;
288
+ }
289
+ dismiss();
290
+ },
291
+ [dismiss],
292
+ );
293
+
294
+ const onKeyDown = useCallback(
295
+ (e: React.KeyboardEvent) => {
296
+ if (!isVisibleRef.current) {
297
+ return;
298
+ }
299
+ if (ARROW_KEYS.has(e.key)) {
300
+ dismiss();
301
+ }
302
+ },
303
+ [dismiss],
304
+ );
305
+
306
+ // --- Render the hint element ---
307
+
308
+ const arrowContent = (
309
+ <span {...stylex.props(styles.keys)}>
310
+ {ARROW_HINT_KEYS[orientation].map(key => (
311
+ <Kbd key={key} keys={key} />
312
+ ))}
313
+ </span>
314
+ );
315
+
316
+ const hintElement = layer.render(
317
+ <span aria-hidden="true">
318
+ {arrowContent}
319
+ <span {...stylex.props(styles.label)}>to navigate</span>
320
+ </span>,
321
+ {
322
+ placement: 'below',
323
+ alignment: 'start',
324
+ xstyle: styles.hint,
325
+ style: {
326
+ marginBlockStart: spacingVars['--spacing-2'],
327
+ },
328
+ },
329
+ );
330
+
331
+ return {hintElement, onFocus, onBlur, onKeyDown};
332
+ }
@@ -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
  },