@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
@@ -4,14 +4,23 @@
4
4
 
5
5
  /**
6
6
  * @file useListFocus.ts
7
- * @input Uses React useCallback, useRef
7
+ * @input Uses React useCallback, useRef, useIsomorphicLayoutEffect
8
8
  * @output Exports useListFocus hook for linear list keyboard navigation
9
- * @position Core hook; used by TabMenu for dropdown menu navigation, Toolbar for roving tabindex
9
+ * @position Core hook; used by TabMenu for dropdown menu navigation, Toolbar
10
+ * for roving tabindex, ButtonGroup, ContextMenu, NavHeadingMenu, and more.
10
11
  *
11
12
  * SYNC: When modified, update:
12
13
  * - /packages/core/src/hooks/index.ts
13
14
  */
14
15
  import { useCallback, useRef } from 'react';
16
+ import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect.js";
17
+
18
+ /**
19
+ * Navigation orientation for a linear list.
20
+ * - `'horizontal'`: ArrowLeft/ArrowRight move between items.
21
+ * - `'vertical'`: ArrowUp/ArrowDown move between items.
22
+ * - `'both'`: all four arrows move between items (in linear DOM order).
23
+ */
15
24
 
16
25
  /**
17
26
  * Configuration for list focus behavior
@@ -21,6 +30,91 @@ import { useCallback, useRef } from 'react';
21
30
  * Return type for useListFocus hook
22
31
  */
23
32
 
33
+ // Text-editing inputs whose caret must not be hijacked by arrow navigation.
34
+ const TEXT_INPUT_TYPES = new Set(['text', 'search', 'url', 'tel', 'email', 'password', 'number']);
35
+
36
+ /**
37
+ * The nearest `contenteditable` root for `el`, or null when `el` is not inside
38
+ * an editable region. Prefers the browser's `isContentEditable` property and
39
+ * falls back to the closest `[contenteditable]` ancestor whose value is not
40
+ * `"false"` (so environments without `isContentEditable` still work).
41
+ */
42
+ function getContentEditableRoot(el) {
43
+ if (el.isContentEditable) {
44
+ return el;
45
+ }
46
+ const candidate = el.closest('[contenteditable]');
47
+ if (candidate && candidate.getAttribute('contenteditable') !== 'false') {
48
+ return candidate;
49
+ }
50
+ return null;
51
+ }
52
+
53
+ /**
54
+ * Whether an arrow/Home/End key should be left to the browser because the
55
+ * event target is a text-editing element whose caret is not yet at the boundary
56
+ * in the direction of travel (or a selection is present). Returns true when the
57
+ * list should NOT steal the key.
58
+ *
59
+ * Covers three editing surfaces:
60
+ * - `<textarea>` and text-type `<input>` — use `selectionStart`/`selectionEnd`
61
+ * to steal only at the boundary in the travel direction.
62
+ * - `contenteditable` (rich-text editor / chat composer) — precise caret
63
+ * boundary detection in an arbitrary editable subtree is fragile, so we err
64
+ * on the side of never hijacking an active editor: defer on every caret key
65
+ * whenever focus is inside a non-empty contenteditable. (An empty editable
66
+ * has nothing to caret through, so navigation may proceed.)
67
+ */
68
+ function shouldDeferToCaret(target, key) {
69
+ if (!(target instanceof HTMLElement)) {
70
+ return false;
71
+ }
72
+
73
+ // contenteditable (covers an element with contenteditable="" / "true" and
74
+ // descendants that inherit it). `isContentEditable` is the reliable property
75
+ // in the browser; fall back to the nearest `contenteditable` ancestor for
76
+ // environments (e.g. jsdom) that don't implement the property.
77
+ const editableRoot = getContentEditableRoot(target);
78
+ if (editableRoot) {
79
+ const selection = typeof window !== 'undefined' ? window.getSelection() : null;
80
+ // A non-collapsed selection means the user is selecting text — never steal.
81
+ if (selection && selection.rangeCount > 0 && !selection.isCollapsed) {
82
+ return true;
83
+ }
84
+ // Collapsed caret (or no selection API): defer whenever the editor has
85
+ // content, so arrow keys stay with the active rich-text editor.
86
+ return (editableRoot.textContent ?? '').length > 0;
87
+ }
88
+ const isTextarea = target.tagName === 'TEXTAREA';
89
+ const isTextInput = target.tagName === 'INPUT' && TEXT_INPUT_TYPES.has(target.type);
90
+ if (!isTextarea && !isTextInput) {
91
+ return false;
92
+ }
93
+ const input = target;
94
+ const {
95
+ selectionStart,
96
+ selectionEnd,
97
+ value
98
+ } = input;
99
+ // A non-collapsed selection means the user is selecting text — never steal.
100
+ if (selectionStart !== selectionEnd) {
101
+ return true;
102
+ }
103
+ // number inputs report null selection; treat as "always defer" for caret keys.
104
+ if (selectionStart == null) {
105
+ return true;
106
+ }
107
+ if (key === 'ArrowLeft' || key === 'ArrowUp' || key === 'Home') {
108
+ // Defer unless the caret is at the very start.
109
+ return selectionStart > 0;
110
+ }
111
+ if (key === 'ArrowRight' || key === 'ArrowDown' || key === 'End') {
112
+ // Defer unless the caret is at the very end.
113
+ return selectionStart < value.length;
114
+ }
115
+ return false;
116
+ }
117
+
24
118
  /**
25
119
  * Hook for managing keyboard navigation within a linear list.
26
120
  *
@@ -31,6 +125,12 @@ import { useCallback, useRef } from 'react';
31
125
  * - End: Move to last item
32
126
  * - Escape: Custom callback (e.g., close menu)
33
127
  *
128
+ * By default the hook only *moves* focus and leaves `tabindex` management to
129
+ * the caller. Opt into {@link UseListFocusOptions.hasRovingTabIndex} for a hook
130
+ * that owns a single tab stop (roving tabindex) across the items — stamping and
131
+ * repairing it as items mount/unmount or toggle disabled — for toolbars,
132
+ * segmented controls, tab strips, and similar composite widgets.
133
+ *
34
134
  * @example
35
135
  * ```
36
136
  * const {listRef, handleKeyDown} = useListFocus({
@@ -41,16 +141,45 @@ import { useCallback, useRef } from 'react';
41
141
  * {items.map(item => <div role="menuitem" tabIndex={0}>{item}</div>)}
42
142
  * </div>
43
143
  * ```
144
+ *
145
+ * Roving-tabindex composite (e.g. a toolbar):
146
+ *
147
+ * @example
148
+ * ```
149
+ * const {listRef, handleKeyDown, handleFocus} = useListFocus<HTMLDivElement>({
150
+ * itemSelector: 'button, input, [tabindex]',
151
+ * orientation: 'horizontal',
152
+ * hasRovingTabIndex: true,
153
+ * hasCaretGuard: true,
154
+ * });
155
+ *
156
+ * <div ref={listRef} role="toolbar" onKeyDown={handleKeyDown} onFocus={handleFocus}>
157
+ * {children}
158
+ * </div>
159
+ * ```
44
160
  */
45
161
  export function useListFocus(options = {}) {
46
162
  const {
47
163
  itemSelector = '[role="menuitem"]',
48
164
  wrap = true,
49
165
  onEscape,
50
- orientation = 'vertical'
166
+ orientation = 'vertical',
167
+ hasHomeEnd = true,
168
+ isRtl = false,
169
+ hasRovingTabIndex = false,
170
+ hasCaretGuard = false
51
171
  } = options;
52
172
  const listRef = useRef(null);
53
173
 
174
+ /**
175
+ * Whether an item is disabled and therefore cannot receive DOM focus.
176
+ * A `.focus()` call on such an element silently no-ops, so navigation must
177
+ * skip these to avoid freezing on a disabled item (menus-4, navigation-5).
178
+ */
179
+ const isItemDisabled = useCallback(el => {
180
+ return el.getAttribute('aria-disabled') === 'true' || el.disabled === true || el.hasAttribute('disabled');
181
+ }, []);
182
+
54
183
  /**
55
184
  * Get all focusable items in the list.
56
185
  */
@@ -61,6 +190,34 @@ export function useListFocus(options = {}) {
61
190
  return Array.from(listRef.current.querySelectorAll(itemSelector));
62
191
  }, [itemSelector]);
63
192
 
193
+ /**
194
+ * Find the next enabled item index from `start`, moving by `step`, optionally
195
+ * wrapping. Returns -1 when no enabled item exists in range. Skipping
196
+ * disabled items here (rather than relying on the selector) keeps navigation
197
+ * from stalling on an item whose `.focus()` silently no-ops.
198
+ */
199
+ const findEnabledIndex = useCallback((items, start, step, shouldWrap) => {
200
+ const count = items.length;
201
+ if (count === 0) {
202
+ return -1;
203
+ }
204
+ let index = start;
205
+ for (let i = 0; i < count; i++) {
206
+ if (index < 0 || index >= count) {
207
+ if (!shouldWrap) {
208
+ return -1;
209
+ }
210
+ index = (index + count) % count;
211
+ }
212
+ const item = items[index];
213
+ if (item && !isItemDisabled(item)) {
214
+ return index;
215
+ }
216
+ index += step;
217
+ }
218
+ return -1;
219
+ }, [isItemDisabled]);
220
+
64
221
  /**
65
222
  * Get the currently focused item index.
66
223
  */
@@ -70,6 +227,62 @@ export function useListFocus(options = {}) {
70
227
  return items.findIndex(item => item === active || item.contains(active));
71
228
  }, [getItems]);
72
229
 
230
+ // --- Roving tabindex ownership (opt-in via `hasRovingTabIndex`) -------------
231
+
232
+ /**
233
+ * Set `tabindex` on an item, but only when it differs (avoids redundant DOM
234
+ * writes). Uses setAttribute so the value reflects even for elements (like
235
+ * `<button>`) whose default tabIndex is already 0.
236
+ */
237
+ const setTabIndex = useCallback((el, value) => {
238
+ if (el.getAttribute('tabindex') !== String(value)) {
239
+ el.setAttribute('tabindex', String(value));
240
+ }
241
+ }, []);
242
+
243
+ /**
244
+ * Stamp the roving tab stop: exactly one enabled item is tabbable (0), the
245
+ * rest are -1. Prefer keeping the currently-tabbable item if it is still
246
+ * enabled; otherwise promote the first enabled item (tab-stop repair).
247
+ */
248
+ const syncTabStops = useCallback(() => {
249
+ const items = getItems();
250
+ const enabled = items.filter(el => !isItemDisabled(el));
251
+ if (enabled.length === 0) {
252
+ return;
253
+ }
254
+ const current = enabled.find(el => el.getAttribute('tabindex') === '0');
255
+ const tabbable = current ?? enabled[0];
256
+ for (const el of items) {
257
+ setTabIndex(el, el === tabbable ? 0 : -1);
258
+ }
259
+ }, [getItems, isItemDisabled, setTabIndex]);
260
+
261
+ // Keep the tab stop valid across renders (items added/removed, disabled
262
+ // toggled). Runs after every commit but only when roving tabindex is on.
263
+ useIsomorphicLayoutEffect(() => {
264
+ if (hasRovingTabIndex) {
265
+ syncTabStops();
266
+ }
267
+ });
268
+
269
+ /**
270
+ * Move focus to `items[index]`. When roving tabindex is enabled, also move
271
+ * the tab stop so `index` becomes the sole tabbable item.
272
+ */
273
+ const focusIndex = useCallback((items, index) => {
274
+ const target = items[index];
275
+ if (!target) {
276
+ return;
277
+ }
278
+ if (hasRovingTabIndex) {
279
+ for (const el of items) {
280
+ setTabIndex(el, el === target ? 0 : -1);
281
+ }
282
+ }
283
+ target.focus();
284
+ }, [hasRovingTabIndex, setTabIndex]);
285
+
73
286
  /**
74
287
  * Focus an item by index, clamping to valid range.
75
288
  */
@@ -79,76 +292,116 @@ export function useListFocus(options = {}) {
79
292
  return;
80
293
  }
81
294
  const clampedIndex = Math.max(0, Math.min(index, items.length - 1));
82
- items[clampedIndex]?.focus();
83
- }, [getItems]);
295
+ focusIndex(items, clampedIndex);
296
+ }, [getItems, focusIndex]);
84
297
 
85
298
  /**
86
- * Focus the first item.
299
+ * Focus the first enabled item. Returns true when an item was focused.
87
300
  */
88
301
  const focusFirst = useCallback(() => {
89
302
  const items = getItems();
90
- items[0]?.focus();
91
- }, [getItems]);
303
+ const index = findEnabledIndex(items, 0, 1, false);
304
+ if (index !== -1) {
305
+ focusIndex(items, index);
306
+ return true;
307
+ }
308
+ return false;
309
+ }, [getItems, findEnabledIndex, focusIndex]);
92
310
 
93
311
  /**
94
- * Focus the last item.
312
+ * Focus the last enabled item. Returns true when an item was focused.
95
313
  */
96
314
  const focusLast = useCallback(() => {
97
315
  const items = getItems();
98
- items[items.length - 1]?.focus();
99
- }, [getItems]);
316
+ const index = findEnabledIndex(items, items.length - 1, -1, false);
317
+ if (index !== -1) {
318
+ focusIndex(items, index);
319
+ return true;
320
+ }
321
+ return false;
322
+ }, [getItems, findEnabledIndex, focusIndex]);
323
+
324
+ /**
325
+ * Keep the roving stop pointing at whatever ended up focused (e.g. a click
326
+ * or programmatic focus) so the next Tab behaves correctly. No-op unless
327
+ * roving tabindex is enabled.
328
+ */
329
+ const handleFocus = useCallback(() => {
330
+ if (hasRovingTabIndex) {
331
+ syncTabStops();
332
+ }
333
+ }, [hasRovingTabIndex, syncTabStops]);
100
334
 
101
335
  /**
102
336
  * Handle keyboard navigation.
103
337
  */
104
338
  const handleKeyDown = useCallback(e => {
339
+ // Let browser/OS shortcut chords (Ctrl/Cmd/Alt + key) through untouched.
340
+ if (e.ctrlKey || e.metaKey || e.altKey) {
341
+ return;
342
+ }
343
+
344
+ // Escape is handled regardless of orientation. Preserve the historical
345
+ // behavior of always consuming Escape here (preventDefault) so consumers
346
+ // that relied on it are unaffected.
347
+ if (e.key === 'Escape') {
348
+ e.preventDefault();
349
+ onEscape?.();
350
+ return;
351
+ }
352
+ const horizontal = orientation === 'horizontal' || orientation === 'both';
353
+ const vertical = orientation === 'vertical' || orientation === 'both';
354
+
355
+ // Resolve which keys advance vs retreat, honoring RTL for horizontal.
356
+ const nextKeys = [];
357
+ const prevKeys = [];
358
+ if (horizontal) {
359
+ nextKeys.push(isRtl ? 'ArrowLeft' : 'ArrowRight');
360
+ prevKeys.push(isRtl ? 'ArrowRight' : 'ArrowLeft');
361
+ }
362
+ if (vertical) {
363
+ nextKeys.push('ArrowDown');
364
+ prevKeys.push('ArrowUp');
365
+ }
366
+ const isNext = nextKeys.includes(e.key);
367
+ const isPrev = prevKeys.includes(e.key);
368
+ const isHome = hasHomeEnd && e.key === 'Home';
369
+ const isEnd = hasHomeEnd && e.key === 'End';
370
+ if (!isNext && !isPrev && !isHome && !isEnd) {
371
+ return;
372
+ }
373
+
374
+ // Never hijack a caret key from a nested text input mid-line. Check both
375
+ // the React event target and the actual focused element (events may
376
+ // bubble up to the container).
377
+ if (hasCaretGuard && (shouldDeferToCaret(e.target, e.key) || shouldDeferToCaret(document.activeElement, e.key))) {
378
+ return;
379
+ }
105
380
  const currentIndex = getCurrentIndex();
106
381
  const items = getItems();
107
- let handled = true;
108
- const nextKey = orientation === 'horizontal' ? 'ArrowRight' : 'ArrowDown';
109
- const prevKey = orientation === 'horizontal' ? 'ArrowLeft' : 'ArrowUp';
110
- switch (e.key) {
111
- case nextKey:
112
- {
113
- if (currentIndex === -1) {
114
- items[0]?.focus();
115
- } else if (currentIndex < items.length - 1) {
116
- items[currentIndex + 1]?.focus();
117
- } else if (wrap) {
118
- items[0]?.focus();
119
- }
120
- break;
121
- }
122
- case prevKey:
123
- {
124
- if (currentIndex === -1) {
125
- items[items.length - 1]?.focus();
126
- } else if (currentIndex > 0) {
127
- items[currentIndex - 1]?.focus();
128
- } else if (wrap) {
129
- items[items.length - 1]?.focus();
130
- }
131
- break;
132
- }
133
- case 'Home':
134
- focusFirst();
135
- break;
136
- case 'End':
137
- focusLast();
138
- break;
139
- case 'Escape':
140
- onEscape?.();
141
- break;
142
- default:
143
- handled = false;
144
- }
145
- if (handled) {
146
- e.preventDefault();
382
+ if (isNext) {
383
+ const from = currentIndex === -1 ? 0 : currentIndex + 1;
384
+ const next = findEnabledIndex(items, from, 1, wrap);
385
+ if (next !== -1) {
386
+ focusIndex(items, next);
387
+ }
388
+ } else if (isPrev) {
389
+ const from = currentIndex === -1 ? items.length - 1 : currentIndex - 1;
390
+ const prev = findEnabledIndex(items, from, -1, wrap);
391
+ if (prev !== -1) {
392
+ focusIndex(items, prev);
393
+ }
394
+ } else if (isHome) {
395
+ focusFirst();
396
+ } else if (isEnd) {
397
+ focusLast();
147
398
  }
148
- }, [getCurrentIndex, getItems, wrap, orientation, focusFirst, focusLast, onEscape]);
399
+ e.preventDefault();
400
+ }, [getCurrentIndex, getItems, wrap, orientation, isRtl, hasHomeEnd, hasCaretGuard, findEnabledIndex, focusIndex, focusFirst, focusLast, onEscape]);
149
401
  return {
150
402
  listRef,
151
403
  handleKeyDown,
404
+ handleFocus,
152
405
  focusItem,
153
406
  focusFirst,
154
407
  focusLast
@@ -0,0 +1,21 @@
1
+ export interface UseLongPressOptions {
2
+ /** Fired with the touch start point once the press is held for `delayMs`. */
3
+ onLongPress: (point: {
4
+ x: number;
5
+ y: number;
6
+ }) => void;
7
+ /** When true, touch handlers are inert. */
8
+ disabled?: boolean;
9
+ /** Hold duration before the press fires, in ms. Defaults to 500. */
10
+ delayMs?: number;
11
+ /** Movement past this distance (px, either axis) cancels the press. Defaults to 10. */
12
+ moveCancelPx?: number;
13
+ }
14
+ export interface UseLongPressHandlers {
15
+ onTouchStart: (e: React.TouchEvent) => void;
16
+ onTouchMove: (e: React.TouchEvent) => void;
17
+ onTouchEnd: () => void;
18
+ onTouchCancel: () => void;
19
+ }
20
+ export declare function useLongPress(options: UseLongPressOptions): UseLongPressHandlers;
21
+ //# sourceMappingURL=useLongPress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLongPress.d.ts","sourceRoot":"","sources":["../../src/hooks/useLongPress.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,mBAAmB;IAClC,6EAA6E;IAC7E,WAAW,EAAE,CAAC,KAAK,EAAE;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAC,KAAK,IAAI,CAAC;IACrD,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uFAAuF;IACvF,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAC5C,WAAW,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAC3C,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,wBAAgB,YAAY,CAC1B,OAAO,EAAE,mBAAmB,GAC3B,oBAAoB,CAsEtB"}
@@ -0,0 +1,89 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useLongPress.ts
7
+ * @input Long-press options: onLongPress callback, disabled, delayMs, moveCancelPx
8
+ * @output Touch handlers to spread onto an element
9
+ * @position Core hook for touch long-press invocation; used by ContextMenu
10
+ *
11
+ * SYNC: When modified, update the hooks barrel /packages/core/src/hooks/index.ts
12
+ *
13
+ * Detects a single-finger long-press: fires `onLongPress` with the touch
14
+ * start point after `delayMs`. Cancels if the finger moves past
15
+ * `moveCancelPx` (treated as a scroll/drag), lifts, or the touch is
16
+ * cancelled. The pending timer is also cleared on unmount.
17
+ *
18
+ * Motivation: iOS Safari never synthesizes a `contextmenu` event on
19
+ * long-press, so long-press is the only touch affordance for opening
20
+ * cursor-positioned surfaces.
21
+ */
22
+ import { useCallback, useEffect, useRef } from 'react';
23
+
24
+ // Default long-press tuning.
25
+ const DEFAULT_DELAY_MS = 500;
26
+ const DEFAULT_MOVE_CANCEL_PX = 10;
27
+ export function useLongPress(options) {
28
+ const {
29
+ onLongPress,
30
+ disabled = false,
31
+ delayMs = DEFAULT_DELAY_MS,
32
+ moveCancelPx = DEFAULT_MOVE_CANCEL_PX
33
+ } = options;
34
+ const timerRef = useRef(null);
35
+ const startRef = useRef(null);
36
+ // Keep the latest callback without re-creating handlers on every render.
37
+ const onLongPressRef = useRef(onLongPress);
38
+ onLongPressRef.current = onLongPress;
39
+ const clear = useCallback(() => {
40
+ if (timerRef.current != null) {
41
+ clearTimeout(timerRef.current);
42
+ timerRef.current = null;
43
+ }
44
+ startRef.current = null;
45
+ }, []);
46
+ const onTouchStart = useCallback(e => {
47
+ if (disabled || e.touches.length !== 1) {
48
+ return;
49
+ }
50
+ const touch = e.touches[0];
51
+ // Clear any stale timer first, THEN record the start point — clearing
52
+ // also nulls startRef, so order matters.
53
+ clear();
54
+ startRef.current = {
55
+ x: touch.clientX,
56
+ y: touch.clientY
57
+ };
58
+ timerRef.current = setTimeout(() => {
59
+ const start = startRef.current;
60
+ if (start == null) {
61
+ return;
62
+ }
63
+ onLongPressRef.current({
64
+ x: start.x,
65
+ y: start.y
66
+ });
67
+ }, delayMs);
68
+ }, [disabled, delayMs, clear]);
69
+ const onTouchMove = useCallback(e => {
70
+ const start = startRef.current;
71
+ if (start == null || e.touches.length !== 1) {
72
+ return;
73
+ }
74
+ const touch = e.touches[0];
75
+ if (Math.abs(touch.clientX - start.x) > moveCancelPx || Math.abs(touch.clientY - start.y) > moveCancelPx) {
76
+ // Treat as a scroll/drag, not a long-press.
77
+ clear();
78
+ }
79
+ }, [moveCancelPx, clear]);
80
+
81
+ // Cancel any pending long-press timer on unmount.
82
+ useEffect(() => clear, [clear]);
83
+ return {
84
+ onTouchStart,
85
+ onTouchMove,
86
+ onTouchEnd: clear,
87
+ onTouchCancel: clear
88
+ };
89
+ }