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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (631) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/README.md +131 -13
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +11 -5
  5. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  6. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  7. package/dist/AvatarGroup/AvatarGroupOverflow.js +4 -1
  8. package/dist/Banner/Banner.d.ts +7 -0
  9. package/dist/Banner/Banner.d.ts.map +1 -1
  10. package/dist/Banner/Banner.js +9 -2
  11. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +1 -1
  12. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  13. package/dist/Breadcrumbs/BreadcrumbItem.js +24 -5
  14. package/dist/Button/Button.d.ts +12 -2
  15. package/dist/Button/Button.d.ts.map +1 -1
  16. package/dist/Button/Button.js +53 -17
  17. package/dist/Calendar/Calendar.d.ts +5 -4
  18. package/dist/Calendar/Calendar.d.ts.map +1 -1
  19. package/dist/Calendar/Calendar.js +82 -43
  20. package/dist/Calendar/hooks/index.d.ts +0 -2
  21. package/dist/Calendar/hooks/index.d.ts.map +1 -1
  22. package/dist/Calendar/hooks/index.js +1 -2
  23. package/dist/Calendar/index.d.ts +3 -3
  24. package/dist/Calendar/index.d.ts.map +1 -1
  25. package/dist/Calendar/index.js +1 -1
  26. package/dist/Calendar/styles.d.ts +3 -9
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +3 -11
  29. package/dist/Carousel/Carousel.d.ts.map +1 -1
  30. package/dist/Carousel/Carousel.js +17 -3
  31. package/dist/Center/Center.d.ts +11 -1
  32. package/dist/Center/Center.d.ts.map +1 -1
  33. package/dist/Center/Center.js +9 -3
  34. package/dist/Chat/ChatMessageList.d.ts +14 -1
  35. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  36. package/dist/Chat/ChatMessageList.js +2 -0
  37. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  38. package/dist/Chat/useTriggerMenu.js +4 -1
  39. package/dist/CheckboxInput/CheckboxInput.d.ts +23 -2
  40. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  41. package/dist/CheckboxInput/CheckboxInput.js +51 -9
  42. package/dist/CheckboxList/CheckboxList.d.ts +13 -1
  43. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  44. package/dist/CheckboxList/CheckboxList.js +39 -9
  45. package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
  46. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  47. package/dist/Citation/Citation.d.ts.map +1 -1
  48. package/dist/Citation/Citation.js +3 -3
  49. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  50. package/dist/CodeBlock/CodeBlock.js +49 -37
  51. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  52. package/dist/CommandPalette/CommandPalette.js +5 -5
  53. package/dist/ContextMenu/ContextMenu.d.ts +4 -5
  54. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  55. package/dist/ContextMenu/ContextMenu.js +93 -11
  56. package/dist/DateInput/DateInput.d.ts +23 -1
  57. package/dist/DateInput/DateInput.d.ts.map +1 -1
  58. package/dist/DateInput/DateInput.js +59 -8
  59. package/dist/DateRangeInput/DateRangeInput.d.ts +23 -1
  60. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  61. package/dist/DateRangeInput/DateRangeInput.js +42 -9
  62. package/dist/DateTimeInput/DateTimeInput.d.ts +35 -2
  63. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  64. package/dist/DateTimeInput/DateTimeInput.js +78 -15
  65. package/dist/Dialog/Dialog.d.ts.map +1 -1
  66. package/dist/Dialog/Dialog.js +12 -0
  67. package/dist/DropdownMenu/DropdownMenu.d.ts +1 -8
  68. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  69. package/dist/DropdownMenu/DropdownMenu.js +42 -14
  70. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  71. package/dist/EmptyState/EmptyState.js +7 -1
  72. package/dist/Field/Field.d.ts +19 -2
  73. package/dist/Field/Field.d.ts.map +1 -1
  74. package/dist/Field/Field.js +4 -0
  75. package/dist/Field/FieldLabel.d.ts +21 -2
  76. package/dist/Field/FieldLabel.d.ts.map +1 -1
  77. package/dist/Field/FieldLabel.js +41 -26
  78. package/dist/FileInput/FileInput.d.ts +22 -1
  79. package/dist/FileInput/FileInput.d.ts.map +1 -1
  80. package/dist/FileInput/FileInput.js +56 -10
  81. package/dist/Grid/Grid.d.ts +11 -1
  82. package/dist/Grid/Grid.d.ts.map +1 -1
  83. package/dist/Grid/Grid.js +8 -0
  84. package/dist/HoverCard/HoverCard.d.ts +2 -2
  85. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  86. package/dist/HoverCard/HoverCard.js +18 -6
  87. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  88. package/dist/HoverCard/useHoverCard.js +7 -3
  89. package/dist/InputGroup/InputGroup.d.ts +2 -2
  90. package/dist/InputGroup/InputGroup.d.ts.map +1 -1
  91. package/dist/InputGroup/InputGroup.js +14 -5
  92. package/dist/InputGroup/InputGroupContext.d.ts +4 -0
  93. package/dist/InputGroup/InputGroupContext.d.ts.map +1 -1
  94. package/dist/InputGroup/InputGroupContext.js +2 -2
  95. package/dist/InputGroup/index.d.ts +1 -0
  96. package/dist/InputGroup/index.d.ts.map +1 -1
  97. package/dist/Kbd/Kbd.d.ts.map +1 -1
  98. package/dist/Kbd/Kbd.js +34 -2
  99. package/dist/Layer/anchorName.d.ts +20 -0
  100. package/dist/Layer/anchorName.d.ts.map +1 -0
  101. package/dist/Layer/anchorName.js +36 -0
  102. package/dist/Layer/layerAnimations.stylex.d.ts +4 -0
  103. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  104. package/dist/Layer/layerAnimations.stylex.js +8 -4
  105. package/dist/Layer/useLayer.d.ts +29 -0
  106. package/dist/Layer/useLayer.d.ts.map +1 -1
  107. package/dist/Layer/useLayer.js +55 -11
  108. package/dist/Layout/Layout.d.ts +4 -11
  109. package/dist/Layout/Layout.d.ts.map +1 -1
  110. package/dist/Layout/Layout.js +10 -11
  111. package/dist/Layout/LayoutFooter.d.ts +2 -2
  112. package/dist/Layout/LayoutFooter.d.ts.map +1 -1
  113. package/dist/Layout/LayoutHeader.d.ts +2 -2
  114. package/dist/Layout/LayoutHeader.d.ts.map +1 -1
  115. package/dist/Layout/LayoutPanel.d.ts +2 -2
  116. package/dist/Layout/LayoutPanel.d.ts.map +1 -1
  117. package/dist/Layout/padding.stylex.d.ts +51 -0
  118. package/dist/Layout/padding.stylex.d.ts.map +1 -1
  119. package/dist/Layout/padding.stylex.js +63 -0
  120. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  121. package/dist/Lightbox/Lightbox.js +4 -2
  122. package/dist/Link/Link.d.ts +17 -2
  123. package/dist/Link/Link.d.ts.map +1 -1
  124. package/dist/Link/Link.js +20 -9
  125. package/dist/Link/LinkProvider.d.ts +1 -0
  126. package/dist/Link/LinkProvider.d.ts.map +1 -1
  127. package/dist/Link/LinkProvider.js +1 -0
  128. package/dist/Markdown/Markdown.d.ts.map +1 -1
  129. package/dist/Markdown/Markdown.js +18 -3
  130. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  131. package/dist/MobileNav/MobileNav.js +13 -0
  132. package/dist/MoreMenu/MoreMenu.d.ts +1 -7
  133. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  134. package/dist/MoreMenu/MoreMenu.js +0 -2
  135. package/dist/MultiSelector/MultiSelector.d.ts +25 -2
  136. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  137. package/dist/MultiSelector/MultiSelector.js +103 -22
  138. package/dist/MultiSelector/hooks.d.ts +12 -1
  139. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  140. package/dist/MultiSelector/hooks.js +14 -1
  141. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  142. package/dist/NavMenu/NavHeadingMenu.js +34 -3
  143. package/dist/NumberInput/NumberInput.d.ts +23 -2
  144. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  145. package/dist/NumberInput/NumberInput.js +62 -12
  146. package/dist/Pagination/Pagination.d.ts +1 -1
  147. package/dist/Pagination/Pagination.d.ts.map +1 -1
  148. package/dist/Pagination/Pagination.js +46 -28
  149. package/dist/Popover/usePopover.d.ts +25 -2
  150. package/dist/Popover/usePopover.d.ts.map +1 -1
  151. package/dist/Popover/usePopover.js +18 -6
  152. package/dist/PowerSearch/PowerSearch.d.ts +11 -1
  153. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  154. package/dist/PowerSearch/PowerSearch.js +6 -1
  155. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  156. package/dist/ProgressBar/ProgressBar.js +3 -5
  157. package/dist/RadioList/RadioList.d.ts +20 -2
  158. package/dist/RadioList/RadioList.d.ts.map +1 -1
  159. package/dist/RadioList/RadioList.js +105 -9
  160. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  161. package/dist/RadioList/RadioListItem.js +13 -2
  162. package/dist/SegmentedControl/SegmentedControl.d.ts +13 -2
  163. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  164. package/dist/SegmentedControl/SegmentedControl.js +78 -44
  165. package/dist/SegmentedControl/SegmentedControlContext.d.ts +7 -0
  166. package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  167. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  168. package/dist/SegmentedControl/SegmentedControlItem.js +13 -2
  169. package/dist/Selector/Selector.d.ts +22 -1
  170. package/dist/Selector/Selector.d.ts.map +1 -1
  171. package/dist/Selector/Selector.js +84 -23
  172. package/dist/Selector/hooks.d.ts +8 -1
  173. package/dist/Selector/hooks.d.ts.map +1 -1
  174. package/dist/Selector/hooks.js +14 -1
  175. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  176. package/dist/SideNav/SideNavItem.js +1 -1
  177. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  178. package/dist/Skeleton/Skeleton.js +8 -2
  179. package/dist/Slider/Slider.d.ts +21 -0
  180. package/dist/Slider/Slider.d.ts.map +1 -1
  181. package/dist/Slider/Slider.js +38 -11
  182. package/dist/Spinner/Spinner.d.ts.map +1 -1
  183. package/dist/Spinner/Spinner.js +1 -1
  184. package/dist/Stack/Stack.d.ts +38 -23
  185. package/dist/Stack/Stack.d.ts.map +1 -1
  186. package/dist/Stack/Stack.js +25 -1
  187. package/dist/Stack/StackItem.d.ts +12 -23
  188. package/dist/Stack/StackItem.d.ts.map +1 -1
  189. package/dist/Stack/StackItem.js +8 -1
  190. package/dist/Switch/Switch.d.ts +23 -2
  191. package/dist/Switch/Switch.d.ts.map +1 -1
  192. package/dist/Switch/Switch.js +41 -11
  193. package/dist/TabList/Tab.d.ts.map +1 -1
  194. package/dist/TabList/Tab.js +6 -0
  195. package/dist/TabList/TabList.d.ts +16 -4
  196. package/dist/TabList/TabList.d.ts.map +1 -1
  197. package/dist/TabList/TabList.js +86 -10
  198. package/dist/TabList/TabListContext.d.ts +5 -0
  199. package/dist/TabList/TabListContext.d.ts.map +1 -1
  200. package/dist/TabList/TabListContext.js +5 -0
  201. package/dist/TabList/TabMenu.d.ts.map +1 -1
  202. package/dist/TabList/TabMenu.js +6 -1
  203. package/dist/Table/BaseTable.d.ts.map +1 -1
  204. package/dist/Table/BaseTable.js +38 -16
  205. package/dist/Table/Table.d.ts.map +1 -1
  206. package/dist/Table/Table.js +36 -7
  207. package/dist/Table/TableCell.d.ts +8 -1
  208. package/dist/Table/TableCell.d.ts.map +1 -1
  209. package/dist/Table/TableCell.js +10 -10
  210. package/dist/Table/TableHeaderCell.d.ts +8 -1
  211. package/dist/Table/TableHeaderCell.d.ts.map +1 -1
  212. package/dist/Table/TableHeaderCell.js +16 -16
  213. package/dist/Table/TableRow.d.ts +6 -1
  214. package/dist/Table/TableRow.d.ts.map +1 -1
  215. package/dist/Table/TableRow.js +15 -7
  216. package/dist/Table/index.d.ts +4 -1
  217. package/dist/Table/index.d.ts.map +1 -1
  218. package/dist/Table/index.js +3 -2
  219. package/dist/Table/plugins/pagination/paginateData.d.ts +2 -1
  220. package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
  221. package/dist/Table/plugins/pagination/paginateData.js +8 -3
  222. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  223. package/dist/Table/plugins/pagination/useTablePagination.js +6 -1
  224. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  225. package/dist/Table/plugins/sortable/useTableSortable.js +59 -3
  226. package/dist/Table/plugins/stickyColumns/index.d.ts +3 -0
  227. package/dist/Table/plugins/stickyColumns/index.d.ts.map +1 -0
  228. package/dist/Table/plugins/stickyColumns/index.js +3 -0
  229. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts +25 -0
  230. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -0
  231. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +369 -0
  232. package/dist/Table/tableContextMenu.d.ts +33 -0
  233. package/dist/Table/tableContextMenu.d.ts.map +1 -0
  234. package/dist/Table/tableContextMenu.js +133 -0
  235. package/dist/Table/types.d.ts +161 -5
  236. package/dist/Table/types.d.ts.map +1 -1
  237. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  238. package/dist/Table/useBaseTablePlugins.js +1 -1
  239. package/dist/Text/text.stylex.d.ts +2 -2
  240. package/dist/Text/text.stylex.js +2 -2
  241. package/dist/TextArea/TextArea.d.ts +22 -1
  242. package/dist/TextArea/TextArea.d.ts.map +1 -1
  243. package/dist/TextArea/TextArea.js +41 -9
  244. package/dist/TextInput/TextInput.d.ts +23 -2
  245. package/dist/TextInput/TextInput.d.ts.map +1 -1
  246. package/dist/TextInput/TextInput.js +55 -10
  247. package/dist/TimeInput/TimeInput.d.ts +23 -1
  248. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  249. package/dist/TimeInput/TimeInput.js +53 -10
  250. package/dist/Timestamp/Timestamp.d.ts +1 -1
  251. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  252. package/dist/Timestamp/Timestamp.js +28 -3
  253. package/dist/Toast/Toast.d.ts.map +1 -1
  254. package/dist/Toast/Toast.js +14 -0
  255. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  256. package/dist/Toast/ToastViewport.js +132 -3
  257. package/dist/ToggleButton/ToggleButton.d.ts +7 -9
  258. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  259. package/dist/ToggleButton/ToggleButton.js +34 -57
  260. package/dist/Tokenizer/Tokenizer.d.ts +12 -1
  261. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  262. package/dist/Tokenizer/Tokenizer.js +27 -5
  263. package/dist/Toolbar/Toolbar.d.ts +3 -3
  264. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  265. package/dist/Toolbar/Toolbar.js +51 -7
  266. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  267. package/dist/Tooltip/useTooltip.js +57 -9
  268. package/dist/TreeList/TreeList.d.ts +1 -1
  269. package/dist/TreeList/TreeList.d.ts.map +1 -1
  270. package/dist/TreeList/TreeList.js +72 -2
  271. package/dist/TreeList/TreeListItem.d.ts +11 -1
  272. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  273. package/dist/TreeList/TreeListItem.js +51 -12
  274. package/dist/TreeList/treeListItem.markers.stylex.d.ts +16 -0
  275. package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +1 -0
  276. package/dist/TreeList/treeListItem.markers.stylex.js +22 -0
  277. package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
  278. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  279. package/dist/Typeahead/BaseTypeahead.js +67 -6
  280. package/dist/Typeahead/Typeahead.d.ts +24 -1
  281. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  282. package/dist/Typeahead/Typeahead.js +29 -5
  283. package/dist/VisuallyHidden/VisuallyHidden.d.ts +61 -0
  284. package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
  285. package/dist/VisuallyHidden/VisuallyHidden.js +65 -0
  286. package/dist/VisuallyHidden/index.d.ts +11 -0
  287. package/dist/VisuallyHidden/index.d.ts.map +1 -0
  288. package/dist/VisuallyHidden/index.js +13 -0
  289. package/dist/astryx.css +40 -1
  290. package/dist/astryx.umd.js +147 -0
  291. package/dist/astryx.umd.js.map +7 -0
  292. package/dist/hooks/focusableSelector.d.ts +22 -0
  293. package/dist/hooks/focusableSelector.d.ts.map +1 -0
  294. package/dist/hooks/focusableSelector.js +24 -0
  295. package/dist/hooks/index.d.ts +12 -2
  296. package/dist/hooks/index.d.ts.map +1 -1
  297. package/dist/hooks/index.js +7 -2
  298. package/dist/hooks/useAnnounce.d.ts +40 -0
  299. package/dist/hooks/useAnnounce.d.ts.map +1 -0
  300. package/dist/hooks/useAnnounce.js +142 -0
  301. package/dist/hooks/useClickableContainer.d.ts +16 -1
  302. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  303. package/dist/hooks/useClickableContainer.js +16 -4
  304. package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
  305. package/dist/hooks/useEntryAnimation.js +4 -4
  306. package/dist/hooks/useFocusTrap.d.ts +18 -0
  307. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  308. package/dist/hooks/useFocusTrap.js +87 -3
  309. package/dist/hooks/useGridFocus.d.ts +88 -1
  310. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  311. package/dist/hooks/useGridFocus.js +261 -62
  312. package/dist/hooks/useKeyboardHint.d.ts +82 -0
  313. package/dist/hooks/useKeyboardHint.d.ts.map +1 -0
  314. package/dist/hooks/useKeyboardHint.js +220 -0
  315. package/dist/hooks/useListFocus.d.ts +78 -7
  316. package/dist/hooks/useListFocus.d.ts.map +1 -1
  317. package/dist/hooks/useListFocus.js +305 -52
  318. package/dist/hooks/useLongPress.d.ts +21 -0
  319. package/dist/hooks/useLongPress.d.ts.map +1 -0
  320. package/dist/hooks/useLongPress.js +89 -0
  321. package/dist/hooks/useTreeFocus.d.ts +157 -0
  322. package/dist/hooks/useTreeFocus.d.ts.map +1 -0
  323. package/dist/hooks/useTreeFocus.js +337 -0
  324. package/dist/hooks/useTypeahead.d.ts +63 -0
  325. package/dist/hooks/useTypeahead.d.ts.map +1 -0
  326. package/dist/hooks/useTypeahead.js +117 -0
  327. package/dist/index.d.ts +1 -0
  328. package/dist/index.d.ts.map +1 -1
  329. package/dist/index.js +1 -0
  330. package/dist/theme/generateThemeRules.js +1 -1
  331. package/dist/theme/syntax/SyntaxTheme.d.ts +1 -1
  332. package/dist/theme/syntax/SyntaxTheme.js +1 -1
  333. package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
  334. package/dist/theme/syntax/defineSyntaxTheme.js +2 -2
  335. package/dist/theme/syntax/presets.d.ts +1 -1
  336. package/dist/theme/syntax/presets.d.ts.map +1 -1
  337. package/dist/theme/syntax/presets.js +1 -1
  338. package/dist/theme/syntax/tokens.d.ts +1 -1
  339. package/dist/theme/syntax/tokens.js +1 -1
  340. package/dist/theme/types.d.ts +1 -1
  341. package/dist/utils/dateTypes.d.ts +12 -0
  342. package/dist/utils/dateTypes.d.ts.map +1 -1
  343. package/dist/utils/dateTypes.js +41 -1
  344. package/dist/utils/index.d.ts +4 -0
  345. package/dist/utils/index.d.ts.map +1 -1
  346. package/dist/utils/index.js +3 -1
  347. package/dist/utils/inputAria.d.ts +29 -0
  348. package/dist/utils/inputAria.d.ts.map +1 -0
  349. package/dist/utils/inputAria.js +30 -0
  350. package/dist/utils/timeParser.d.ts.map +1 -1
  351. package/dist/utils/timeParser.js +5 -2
  352. package/package.json +15 -5
  353. package/src/Avatar/Avatar.test.tsx +37 -0
  354. package/src/Avatar/Avatar.tsx +10 -5
  355. package/src/AvatarGroup/AvatarGroupOverflow.tsx +3 -0
  356. package/src/Banner/Banner.doc.mjs +8 -0
  357. package/src/Banner/Banner.test.tsx +16 -7
  358. package/src/Banner/Banner.tsx +9 -2
  359. package/src/Breadcrumbs/BreadcrumbItem.tsx +25 -7
  360. package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -10
  361. package/src/Button/Button.doc.mjs +6 -0
  362. package/src/Button/Button.test.tsx +105 -11
  363. package/src/Button/Button.tsx +76 -21
  364. package/src/Calendar/Calendar.doc.mjs +4 -4
  365. package/src/Calendar/Calendar.test.tsx +168 -30
  366. package/src/Calendar/Calendar.tsx +121 -66
  367. package/src/Calendar/hooks/index.ts +0 -6
  368. package/src/Calendar/index.ts +1 -3
  369. package/src/Calendar/styles.ts +4 -9
  370. package/src/Carousel/Carousel.doc.mjs +7 -1
  371. package/src/Carousel/Carousel.test.tsx +20 -0
  372. package/src/Carousel/Carousel.tsx +16 -1
  373. package/src/Center/Center.doc.mjs +12 -2
  374. package/src/Center/Center.tsx +28 -3
  375. package/src/Chat/ChatComposer.doc.mjs +3 -0
  376. package/src/Chat/ChatComposerDrawer.doc.mjs +12 -0
  377. package/src/Chat/ChatMessageList.doc.mjs +8 -0
  378. package/src/Chat/ChatMessageList.test.tsx +18 -0
  379. package/src/Chat/ChatMessageList.tsx +16 -0
  380. package/src/Chat/useTriggerMenu.tsx +3 -0
  381. package/src/CheckboxInput/CheckboxInput.doc.mjs +18 -0
  382. package/src/CheckboxInput/CheckboxInput.test.tsx +172 -27
  383. package/src/CheckboxInput/CheckboxInput.tsx +78 -6
  384. package/src/CheckboxList/CheckboxList.doc.mjs +9 -0
  385. package/src/CheckboxList/CheckboxList.test.tsx +169 -16
  386. package/src/CheckboxList/CheckboxList.tsx +56 -3
  387. package/src/CheckboxList/CheckboxListContext.tsx +7 -0
  388. package/src/Citation/Citation.doc.mjs +10 -0
  389. package/src/Citation/Citation.test.tsx +117 -0
  390. package/src/Citation/Citation.tsx +9 -1
  391. package/src/ClickableCard/ClickableCard.doc.mjs +15 -0
  392. package/src/CodeBlock/CodeBlock.test.tsx +115 -0
  393. package/src/CodeBlock/CodeBlock.tsx +61 -33
  394. package/src/CommandPalette/CommandPalette.test.tsx +47 -1
  395. package/src/CommandPalette/CommandPalette.tsx +9 -21
  396. package/src/ContextMenu/ContextMenu.doc.mjs +4 -4
  397. package/src/ContextMenu/ContextMenu.test.tsx +155 -1
  398. package/src/ContextMenu/ContextMenu.tsx +113 -16
  399. package/src/DateInput/DateInput.doc.mjs +281 -56
  400. package/src/DateInput/DateInput.test.tsx +188 -2
  401. package/src/DateInput/DateInput.tsx +89 -10
  402. package/src/DateRangeInput/DateRangeInput.doc.mjs +251 -46
  403. package/src/DateRangeInput/DateRangeInput.test.tsx +182 -20
  404. package/src/DateRangeInput/DateRangeInput.tsx +63 -5
  405. package/src/DateTimeInput/DateTimeInput.doc.mjs +368 -67
  406. package/src/DateTimeInput/DateTimeInput.test.tsx +279 -11
  407. package/src/DateTimeInput/DateTimeInput.tsx +125 -17
  408. package/src/Dialog/Dialog.doc.mjs +2 -2
  409. package/src/Dialog/Dialog.tsx +12 -0
  410. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -8
  411. package/src/DropdownMenu/DropdownMenu.test.tsx +56 -42
  412. package/src/DropdownMenu/DropdownMenu.tsx +52 -18
  413. package/src/EmptyState/EmptyState.test.tsx +4 -2
  414. package/src/EmptyState/EmptyState.tsx +6 -2
  415. package/src/Field/Field.test.tsx +32 -0
  416. package/src/Field/Field.tsx +23 -5
  417. package/src/Field/FieldLabel.tsx +55 -17
  418. package/src/FileInput/FileInput.doc.mjs +17 -0
  419. package/src/FileInput/FileInput.test.tsx +283 -49
  420. package/src/FileInput/FileInput.tsx +76 -4
  421. package/src/Grid/Grid.doc.mjs +14 -4
  422. package/src/Grid/Grid.tsx +20 -0
  423. package/src/HoverCard/HoverCard.doc.mjs +3 -0
  424. package/src/HoverCard/HoverCard.test.tsx +189 -2
  425. package/src/HoverCard/HoverCard.tsx +20 -16
  426. package/src/HoverCard/useHoverCard.tsx +13 -10
  427. package/src/Icon/Icon.doc.mjs +8 -0
  428. package/src/InputGroup/InputGroup.doc.mjs +2 -2
  429. package/src/InputGroup/InputGroup.test.tsx +98 -60
  430. package/src/InputGroup/InputGroup.tsx +21 -4
  431. package/src/InputGroup/InputGroupContext.ts +9 -4
  432. package/src/InputGroup/index.ts +1 -0
  433. package/src/Kbd/Kbd.test.tsx +22 -4
  434. package/src/Kbd/Kbd.tsx +37 -12
  435. package/src/Layer/anchorName.test.ts +146 -0
  436. package/src/Layer/anchorName.ts +46 -0
  437. package/src/Layer/layerAnimations.stylex.ts +20 -4
  438. package/src/Layer/useLayer.doc.mjs +12 -2
  439. package/src/Layer/useLayer.tsx +100 -15
  440. package/src/Layout/Layout.doc.mjs +1 -1
  441. package/src/Layout/Layout.tsx +8 -16
  442. package/src/Layout/LayoutFooter.doc.mjs +4 -4
  443. package/src/Layout/LayoutFooter.tsx +3 -3
  444. package/src/Layout/LayoutHeader.doc.mjs +4 -4
  445. package/src/Layout/LayoutHeader.tsx +3 -3
  446. package/src/Layout/LayoutPanel.tsx +3 -3
  447. package/src/Layout/padding.stylex.ts +52 -0
  448. package/src/Lightbox/Lightbox.doc.mjs +9 -2
  449. package/src/Lightbox/Lightbox.test.tsx +53 -5
  450. package/src/Lightbox/Lightbox.tsx +9 -4
  451. package/src/Link/Link.doc.mjs +14 -0
  452. package/src/Link/Link.test.tsx +52 -2
  453. package/src/Link/Link.tsx +42 -7
  454. package/src/Link/LinkProvider.tsx +1 -0
  455. package/src/List/List.doc.mjs +7 -0
  456. package/src/Markdown/Markdown.doc.mjs +2 -0
  457. package/src/Markdown/Markdown.test.tsx +28 -26
  458. package/src/Markdown/Markdown.tsx +21 -6
  459. package/src/MetadataList/MetadataListItem.doc.mjs +4 -0
  460. package/src/MobileNav/MobileNav.doc.mjs +8 -8
  461. package/src/MobileNav/MobileNav.tsx +13 -0
  462. package/src/MobileNav/MobileNavReopen.test.tsx +118 -0
  463. package/src/MoreMenu/MoreMenu.doc.mjs +2 -17
  464. package/src/MoreMenu/MoreMenu.tsx +0 -9
  465. package/src/MultiSelector/MultiSelector.doc.mjs +25 -0
  466. package/src/MultiSelector/MultiSelector.test.tsx +375 -8
  467. package/src/MultiSelector/MultiSelector.tsx +145 -21
  468. package/src/MultiSelector/hooks.ts +27 -0
  469. package/src/NavMenu/NavHeadingMenu.test.tsx +77 -1
  470. package/src/NavMenu/NavHeadingMenu.tsx +50 -3
  471. package/src/NumberInput/NumberInput.doc.mjs +17 -0
  472. package/src/NumberInput/NumberInput.test.tsx +242 -40
  473. package/src/NumberInput/NumberInput.tsx +89 -11
  474. package/src/OverflowList/OverflowList.doc.mjs +17 -0
  475. package/src/Pagination/Pagination.doc.mjs +3 -3
  476. package/src/Pagination/Pagination.test.tsx +250 -14
  477. package/src/Pagination/Pagination.tsx +54 -29
  478. package/src/Popover/usePopover.doc.mjs +7 -3
  479. package/src/Popover/usePopover.tsx +49 -6
  480. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  481. package/src/PowerSearch/PowerSearch.test.tsx +97 -1
  482. package/src/PowerSearch/PowerSearch.tsx +16 -3
  483. package/src/ProgressBar/ProgressBar.test.tsx +45 -27
  484. package/src/ProgressBar/ProgressBar.tsx +3 -4
  485. package/src/RadioList/RadioList.doc.mjs +9 -0
  486. package/src/RadioList/RadioList.test.tsx +252 -16
  487. package/src/RadioList/RadioList.tsx +166 -10
  488. package/src/RadioList/RadioListItem.tsx +14 -2
  489. package/src/Resizable/Resizable.doc.mjs +1 -1
  490. package/src/Section/Section.doc.mjs +11 -0
  491. package/src/SegmentedControl/SegmentedControl.doc.mjs +9 -0
  492. package/src/SegmentedControl/SegmentedControl.test.tsx +214 -42
  493. package/src/SegmentedControl/SegmentedControl.tsx +102 -54
  494. package/src/SegmentedControl/SegmentedControlContext.ts +7 -0
  495. package/src/SegmentedControl/SegmentedControlItem.tsx +14 -1
  496. package/src/SelectableCard/SelectableCard.doc.mjs +15 -0
  497. package/src/Selector/Selector.doc.mjs +25 -0
  498. package/src/Selector/Selector.test.tsx +322 -15
  499. package/src/Selector/Selector.tsx +126 -40
  500. package/src/Selector/hooks.ts +23 -0
  501. package/src/SideNav/SideNav.test.tsx +16 -0
  502. package/src/SideNav/SideNavItem.tsx +2 -4
  503. package/src/Skeleton/Skeleton.doc.mjs +11 -1
  504. package/src/Skeleton/Skeleton.test.tsx +25 -0
  505. package/src/Skeleton/Skeleton.tsx +11 -1
  506. package/src/Slider/Slider.doc.mjs +19 -0
  507. package/src/Slider/Slider.test.tsx +158 -18
  508. package/src/Slider/Slider.tsx +54 -6
  509. package/src/Spinner/Spinner.tsx +7 -1
  510. package/src/Stack/Stack.doc.mjs +77 -0
  511. package/src/Stack/Stack.test.tsx +55 -5
  512. package/src/Stack/Stack.tsx +73 -24
  513. package/src/Stack/StackItem.test.tsx +23 -0
  514. package/src/Stack/StackItem.tsx +20 -23
  515. package/src/Switch/Switch.doc.mjs +15 -0
  516. package/src/Switch/Switch.test.tsx +162 -27
  517. package/src/Switch/Switch.tsx +61 -21
  518. package/src/TabList/Tab.tsx +6 -0
  519. package/src/TabList/TabList.doc.mjs +12 -0
  520. package/src/TabList/TabList.test.tsx +208 -0
  521. package/src/TabList/TabList.tsx +102 -12
  522. package/src/TabList/TabListContext.ts +7 -3
  523. package/src/TabList/TabMenu.tsx +6 -3
  524. package/src/Table/BaseTable.tsx +72 -41
  525. package/src/Table/Table.doc.mjs +14 -0
  526. package/src/Table/Table.test.tsx +39 -30
  527. package/src/Table/Table.tsx +27 -1
  528. package/src/Table/TableCell.tsx +25 -23
  529. package/src/Table/TableHeaderCell.tsx +28 -29
  530. package/src/Table/TableRow.tsx +37 -7
  531. package/src/Table/index.ts +6 -2
  532. package/src/Table/plugins/pagination/paginateData.ts +10 -3
  533. package/src/Table/plugins/pagination/useTablePagination.test.tsx +16 -0
  534. package/src/Table/plugins/pagination/useTablePagination.tsx +8 -1
  535. package/src/Table/plugins/sortable/useTableSortable.test.tsx +91 -2
  536. package/src/Table/plugins/sortable/useTableSortable.tsx +53 -1
  537. package/src/Table/plugins/stickyColumns/index.ts +4 -0
  538. package/src/Table/plugins/stickyColumns/useTableStickyColumns.test.tsx +163 -0
  539. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +407 -0
  540. package/src/Table/tableContextMenu.test.tsx +157 -0
  541. package/src/Table/tableContextMenu.tsx +142 -0
  542. package/src/Table/types.ts +175 -4
  543. package/src/Table/useBaseTablePlugins.ts +1 -0
  544. package/src/Table/useTableColumnResize.doc.mjs +96 -0
  545. package/src/Table/useTableStickyColumns.doc.mjs +59 -0
  546. package/src/Text/Heading.doc.mjs +15 -2
  547. package/src/Text/Text.doc.mjs +7 -1
  548. package/src/Text/text.stylex.ts +2 -2
  549. package/src/TextArea/TextArea.doc.mjs +17 -0
  550. package/src/TextArea/TextArea.test.tsx +167 -35
  551. package/src/TextArea/TextArea.tsx +62 -15
  552. package/src/TextInput/TextInput.doc.mjs +17 -0
  553. package/src/TextInput/TextInput.test.tsx +168 -34
  554. package/src/TextInput/TextInput.tsx +77 -9
  555. package/src/TimeInput/TimeInput.doc.mjs +335 -60
  556. package/src/TimeInput/TimeInput.test.tsx +144 -7
  557. package/src/TimeInput/TimeInput.tsx +74 -5
  558. package/src/Timestamp/Timestamp.doc.mjs +9 -0
  559. package/src/Timestamp/Timestamp.test.tsx +44 -18
  560. package/src/Timestamp/Timestamp.tsx +30 -10
  561. package/src/Toast/Toast.tsx +14 -0
  562. package/src/Toast/ToastViewport.test.tsx +204 -0
  563. package/src/Toast/ToastViewport.tsx +167 -21
  564. package/src/ToggleButton/ToggleButton.doc.mjs +9 -3
  565. package/src/ToggleButton/ToggleButton.test.tsx +39 -18
  566. package/src/ToggleButton/ToggleButton.tsx +46 -83
  567. package/src/Tokenizer/Tokenizer.doc.mjs +15 -0
  568. package/src/Tokenizer/Tokenizer.test.tsx +105 -1
  569. package/src/Tokenizer/Tokenizer.tsx +41 -2
  570. package/src/Toolbar/Toolbar.doc.mjs +65 -5
  571. package/src/Toolbar/Toolbar.test.tsx +109 -6
  572. package/src/Toolbar/Toolbar.tsx +59 -5
  573. package/src/Tooltip/Tooltip.test.tsx +81 -0
  574. package/src/Tooltip/useTooltip.tsx +57 -11
  575. package/src/TreeList/TreeList.test.tsx +295 -6
  576. package/src/TreeList/TreeList.tsx +73 -1
  577. package/src/TreeList/TreeListItem.tsx +51 -5
  578. package/src/TreeList/treeListItem.markers.stylex.ts +20 -0
  579. package/src/Typeahead/BaseTypeahead.tsx +86 -9
  580. package/src/Typeahead/Typeahead.doc.mjs +136 -30
  581. package/src/Typeahead/Typeahead.test.tsx +331 -1
  582. package/src/Typeahead/Typeahead.tsx +52 -1
  583. package/src/VisuallyHidden/VisuallyHidden.doc.mjs +80 -0
  584. package/src/VisuallyHidden/VisuallyHidden.test.tsx +76 -0
  585. package/src/VisuallyHidden/VisuallyHidden.tsx +113 -0
  586. package/src/VisuallyHidden/index.ts +15 -0
  587. package/src/hooks/focusableSelector.ts +25 -0
  588. package/src/hooks/index.ts +28 -2
  589. package/src/hooks/useAnnounce.test.tsx +101 -0
  590. package/src/hooks/useAnnounce.ts +168 -0
  591. package/src/hooks/useClickableContainer.ts +16 -4
  592. package/src/hooks/useEntryAnimation.ts +16 -4
  593. package/src/hooks/useFocusTrap.test.tsx +132 -0
  594. package/src/hooks/useFocusTrap.ts +98 -4
  595. package/src/hooks/useGridFocus.doc.mjs +40 -2
  596. package/src/hooks/useGridFocus.test.tsx +132 -0
  597. package/src/hooks/useGridFocus.ts +343 -67
  598. package/src/hooks/useImageMode.test.ts +133 -0
  599. package/src/hooks/useKeyboardHint.doc.mjs +101 -0
  600. package/src/hooks/useKeyboardHint.test.tsx +70 -0
  601. package/src/hooks/useKeyboardHint.tsx +332 -0
  602. package/src/hooks/useListFocus.doc.mjs +50 -10
  603. package/src/hooks/useListFocus.test.tsx +375 -0
  604. package/src/hooks/useListFocus.ts +395 -57
  605. package/src/hooks/useLongPress.test.tsx +134 -0
  606. package/src/hooks/useLongPress.ts +119 -0
  607. package/src/hooks/useTreeFocus.doc.mjs +140 -0
  608. package/src/hooks/useTreeFocus.test.tsx +214 -0
  609. package/src/hooks/useTreeFocus.ts +559 -0
  610. package/src/hooks/useTypeahead.test.tsx +109 -0
  611. package/src/hooks/useTypeahead.ts +183 -0
  612. package/src/index.ts +1 -0
  613. package/src/theme/derivedVarRegistry.test.ts +4 -0
  614. package/src/theme/generateThemeRules.test.ts +7 -8
  615. package/src/theme/generateThemeRules.ts +1 -1
  616. package/src/theme/syntax/SyntaxTheme.tsx +1 -1
  617. package/src/theme/syntax/defineSyntaxTheme.ts +20 -11
  618. package/src/theme/syntax/presets.ts +5 -2
  619. package/src/theme/syntax/tokens.ts +1 -1
  620. package/src/theme/types.ts +1 -1
  621. package/src/utils/dateTypes.test.ts +40 -0
  622. package/src/utils/dateTypes.ts +40 -0
  623. package/src/utils/index.ts +9 -0
  624. package/src/utils/inputAria.test.ts +39 -0
  625. package/src/utils/inputAria.ts +58 -0
  626. package/src/utils/timeParser.test.ts +10 -0
  627. package/src/utils/timeParser.ts +5 -2
  628. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts +0 -57
  629. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts.map +0 -1
  630. package/dist/Calendar/hooks/useCalendarRovingTabindex.js +0 -96
  631. package/src/Calendar/hooks/useCalendarRovingTabindex.ts +0 -118
@@ -4,7 +4,7 @@
4
4
 
5
5
  /**
6
6
  * @file MultiSelector.tsx
7
- * @input Uses React, StyleX, usePopover, CheckboxInput, Field, Badge, Icon
7
+ * @input Uses React, StyleX, usePopover, useTooltip, CheckboxInput, Field, Badge, Icon
8
8
  * @output Exports MultiSelector component
9
9
  * @position Core implementation; consumed by index.ts
10
10
  *
@@ -27,6 +27,7 @@ import React, {
27
27
  } from 'react';
28
28
  import * as stylex from '@stylexjs/stylex';
29
29
  import {usePopover} from '../Popover/usePopover';
30
+ import {useTooltip} from '../Tooltip';
30
31
  import {Icon, renderIconSlot, type IconType} from '../Icon';
31
32
  import type {IconName} from '../Icon';
32
33
  import {
@@ -65,6 +66,7 @@ import {
65
66
  } from '../Selector/utils';
66
67
  import {useMultiCombobox} from './hooks';
67
68
  import {mergeProps} from '../utils';
69
+ import {useAnnounce} from '../hooks/useAnnounce';
68
70
  import type {BaseProps} from '../BaseProps';
69
71
  import type {SizeValue} from '../utils/types';
70
72
  import {useSize} from '../SizeContext/SizeContext';
@@ -397,6 +399,30 @@ export interface MultiSelectorProps<
397
399
  */
398
400
  isDisabled?: boolean;
399
401
 
402
+ /**
403
+ * Explains why the selector is disabled. When set together with
404
+ * `isDisabled`, the selector shows a tooltip with this text on hover and
405
+ * keyboard focus, and the trigger stays focusable (via `aria-disabled`)
406
+ * so the reason is discoverable by keyboard and assistive technology.
407
+ * Activation stays blocked.
408
+ *
409
+ * Use this instead of wrapping a disabled selector in `Tooltip` — disabled
410
+ * controls don't emit the pointer events an external tooltip needs.
411
+ *
412
+ * @example
413
+ * ```
414
+ * <MultiSelector
415
+ * label="Columns"
416
+ * options={columns}
417
+ * value={selected}
418
+ * onChange={setSelected}
419
+ * isDisabled
420
+ * disabledMessage="Select a table first"
421
+ * />
422
+ * ```
423
+ */
424
+ disabledMessage?: string;
425
+
400
426
  /**
401
427
  * The options to display in the selector.
402
428
  * Can be strings, objects, dividers, or sections.
@@ -545,6 +571,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
545
571
  isOptional = false,
546
572
  isRequired = false,
547
573
  isDisabled = false,
574
+ disabledMessage,
548
575
  options,
549
576
  value,
550
577
  onChange,
@@ -592,11 +619,26 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
592
619
  const [optimisticValue, setOptimisticValue] = useOptimistic(value);
593
620
  const isBusy = isLoading || optimisticValue !== value;
594
621
 
622
+ // Disabled-reason tooltip. Disabled controls swallow pointer events, so the
623
+ // tooltip listeners attach to the trigger container (which already exists)
624
+ // and the trigger button stays perceivable via aria-disabled instead of the
625
+ // disabled attribute. Activation is blocked by the isDisabled guards in
626
+ // useMultiCombobox (onTriggerClick / onKeyDown).
627
+ const showsDisabledMessage = isDisabled && !!disabledMessage;
628
+ const disabledMessageTooltip = useTooltip({
629
+ placement: 'above',
630
+ // The container div is not naturally focusable; focusin bubbles up from
631
+ // the trigger button, so always attach focus listeners.
632
+ focusTrigger: 'always',
633
+ isEnabled: showsDisabledMessage,
634
+ });
635
+
595
636
  // Build aria-describedby
596
637
  const ariaDescribedBy =
597
638
  [
598
639
  description ? descriptionId : null,
599
640
  status?.message ? statusMessageId : null,
641
+ showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
600
642
  ]
601
643
  .filter(Boolean)
602
644
  .join(' ') || undefined;
@@ -607,6 +649,25 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
607
649
  [options],
608
650
  );
609
651
 
652
+ // Announce selection-count changes politely (comboboxes-7 announce path).
653
+ // Toggling options / select-all previously produced no audible feedback.
654
+ const announce = useAnnounce();
655
+ const announceSelection = useCallback(
656
+ (nextValue: string[]) => {
657
+ const total = selectableItems.length;
658
+ const selectableSet = new Set(selectableItems.map(item => item.value));
659
+ const selectedCount = nextValue.filter(v => selectableSet.has(v)).length;
660
+ if (selectedCount === 0) {
661
+ announce('Selection cleared');
662
+ } else if (total > 0 && selectedCount === total) {
663
+ announce('All selected');
664
+ } else {
665
+ announce(`${selectedCount} of ${total} selected`);
666
+ }
667
+ },
668
+ [announce, selectableItems],
669
+ );
670
+
610
671
  // Filter items by search query
611
672
  const filteredItems = useMemo(() => {
612
673
  if (!searchQuery) {
@@ -696,7 +757,9 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
696
757
  onHide: handleLayerHide,
697
758
  hasCloseButton: false,
698
759
  hasAutoFocus: false,
699
- dialogLabel: `${label} options`,
760
+ // The popup's own role="listbox" is the exposed semantics; the trigger
761
+ // keeps DOM focus, so wrapping it in a modal dialog would misrepresent it.
762
+ role: 'none',
700
763
  });
701
764
 
702
765
  // Open dropdown on mount when isDefaultOpen is true
@@ -708,19 +771,34 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
708
771
  }, []);
709
772
 
710
773
  // Handle toggle
711
- // Handle clear button click
774
+ // Clear all selected values. Shared by the clear button and the keyboard
775
+ // Delete/Backspace path so clearing is reachable without a mouse.
776
+ const clearValues = useCallback(() => {
777
+ onChange([]);
778
+ announceSelection([]);
779
+ if (changeAction) {
780
+ startTransition(async () => {
781
+ setOptimisticValue([]);
782
+ await changeAction([]);
783
+ });
784
+ }
785
+ }, [
786
+ onChange,
787
+ changeAction,
788
+ startTransition,
789
+ setOptimisticValue,
790
+ announceSelection,
791
+ ]);
792
+
793
+ // Whether there is at least one selected value (clearing is meaningful).
794
+ const hasValue = optimisticValue.length > 0;
795
+
712
796
  const handleClear = useCallback(
713
797
  (e: React.MouseEvent) => {
714
798
  e.stopPropagation(); // Don't open dropdown
715
- onChange([]);
716
- if (changeAction) {
717
- startTransition(async () => {
718
- setOptimisticValue([]);
719
- await changeAction([]);
720
- });
721
- }
799
+ clearValues();
722
800
  },
723
- [onChange, changeAction, startTransition, setOptimisticValue],
801
+ [clearValues],
724
802
  );
725
803
 
726
804
  const handleToggle = useCallback(
@@ -730,6 +808,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
730
808
  : [...optimisticValue, itemValue];
731
809
 
732
810
  onChange(newValue);
811
+ announceSelection(newValue);
733
812
  if (changeAction) {
734
813
  startTransition(async () => {
735
814
  setOptimisticValue(newValue);
@@ -743,6 +822,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
743
822
  changeAction,
744
823
  startTransition,
745
824
  setOptimisticValue,
825
+ announceSelection,
746
826
  ],
747
827
  );
748
828
 
@@ -788,6 +868,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
788
868
  }
789
869
 
790
870
  onChange(newValue);
871
+ announceSelection(newValue);
791
872
  if (changeAction) {
792
873
  startTransition(async () => {
793
874
  setOptimisticValue(newValue);
@@ -802,6 +883,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
802
883
  changeAction,
803
884
  startTransition,
804
885
  setOptimisticValue,
886
+ announceSelection,
805
887
  ]);
806
888
 
807
889
  // Route toggle: select-all sentinel → handleSelectAll, everything else → handleToggle
@@ -843,9 +925,24 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
843
925
  }, [popover, hasSearch, optimisticValue]),
844
926
  onClose: popover.hide,
845
927
  onToggle: handleNavigableToggle,
928
+ onClear: hasClear ? clearValues : undefined,
929
+ hasValue,
846
930
  listboxId,
847
931
  });
848
932
 
933
+ // Keep the highlighted option visible during keyboard navigation. The
934
+ // listbox is a fixed-height scroll container, so without this the virtual
935
+ // cursor walks off-screen once navigation passes the visible window. Mirrors
936
+ // CommandPaletteItem's scrollIntoView({block: 'nearest'}) behavior.
937
+ useEffect(() => {
938
+ if (!popover.isOpen || highlightedIndex < 0) {
939
+ return;
940
+ }
941
+ document
942
+ .getElementById(getItemId(highlightedIndex))
943
+ ?.scrollIntoView?.({block: 'nearest'});
944
+ }, [popover.isOpen, highlightedIndex, getItemId]);
945
+
849
946
  // Build trigger display content
850
947
  const selectedLabels = useMemo(() => {
851
948
  return optimisticValue.map(v => {
@@ -906,17 +1003,30 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
906
1003
  <input
907
1004
  ref={searchRef}
908
1005
  id={searchId}
909
- role="searchbox"
1006
+ // When hasSearch is set, focus moves into this input on open, so it —
1007
+ // not the trigger — must be the combobox reporting the highlighted
1008
+ // option via aria-activedescendant (comboboxes-4).
1009
+ role="combobox"
1010
+ aria-expanded={popover.isOpen}
910
1011
  aria-controls={listboxId}
1012
+ aria-autocomplete="list"
1013
+ aria-activedescendant={
1014
+ popover.isOpen && highlightedIndex >= 0
1015
+ ? getItemId(highlightedIndex)
1016
+ : undefined
1017
+ }
911
1018
  aria-label="Search options"
912
1019
  type="text"
913
1020
  value={searchQuery}
914
1021
  onChange={e => setSearchQuery(e.target.value)}
915
1022
  onKeyDown={e => {
916
- // Let ArrowDown/Up/Escape/Tab propagate to parent handler
1023
+ // Arrow keys navigate options; Enter toggles; Escape/Tab close.
1024
+ // Space and Home/End are left to the input (type a space / move
1025
+ // the caret), not forwarded to option navigation.
917
1026
  if (
918
1027
  e.key === 'ArrowDown' ||
919
1028
  e.key === 'ArrowUp' ||
1029
+ e.key === 'Enter' ||
920
1030
  e.key === 'Escape' ||
921
1031
  e.key === 'Tab'
922
1032
  ) {
@@ -935,6 +1045,9 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
935
1045
  searchQuery,
936
1046
  searchPlaceholder,
937
1047
  onKeyDown,
1048
+ popover.isOpen,
1049
+ highlightedIndex,
1050
+ getItemId,
938
1051
  ]);
939
1052
 
940
1053
  // Render an individual item (index-based)
@@ -1062,9 +1175,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1062
1175
 
1063
1176
  if (isDivider(option)) {
1064
1177
  flushPending();
1065
- elements.push(
1066
- <Divider key={`divider-${i}`} xstyle={styles.divider} />,
1067
- );
1178
+ elements.push(<Divider key={`divider-${i}`} xstyle={styles.divider} />);
1068
1179
  } else if (isSection(option)) {
1069
1180
  flushPending();
1070
1181
  const count = option.options.length;
@@ -1120,6 +1231,10 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1120
1231
  <div
1121
1232
  ref={el => {
1122
1233
  popover.triggerRef(el);
1234
+ // Anchor + hover/focus listeners for the disabled-message tooltip.
1235
+ // Handlers are gated internally by isEnabled, and anchor names
1236
+ // compose, so attaching unconditionally is safe.
1237
+ disabledMessageTooltip.ref(el);
1123
1238
  }}
1124
1239
  onClick={onTriggerClick}
1125
1240
  data-testid={testId}
@@ -1144,12 +1259,15 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1144
1259
  ref={triggerRef}
1145
1260
  id={triggerId}
1146
1261
  type="button"
1147
- role="combobox"
1262
+ // In hasSearch mode the popup's search input is the combobox (it owns
1263
+ // focus + aria-activedescendant, comboboxes-4), so the trigger is a
1264
+ // plain button that opens the listbox — not a second combobox.
1265
+ role={hasSearch ? undefined : 'combobox'}
1148
1266
  aria-haspopup="listbox"
1149
1267
  aria-expanded={popover.isOpen}
1150
1268
  aria-controls={listboxId}
1151
1269
  aria-activedescendant={
1152
- popover.isOpen && highlightedIndex >= 0
1270
+ !hasSearch && popover.isOpen && highlightedIndex >= 0
1153
1271
  ? getItemId(highlightedIndex)
1154
1272
  : undefined
1155
1273
  }
@@ -1157,9 +1275,13 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1157
1275
  aria-required={isRequired ? 'true' : undefined}
1158
1276
  aria-invalid={status?.type === 'error' ? 'true' : undefined}
1159
1277
  aria-busy={isBusy || undefined}
1160
- disabled={isDisabled}
1278
+ // With a disabledMessage the trigger keeps focusability via
1279
+ // aria-disabled so the reason is focus-discoverable; activation is
1280
+ // still blocked by the isDisabled guards in useMultiCombobox.
1281
+ disabled={isDisabled && !showsDisabledMessage}
1282
+ aria-disabled={showsDisabledMessage ? 'true' : undefined}
1161
1283
  onKeyDown={onKeyDown}
1162
- tabIndex={-1}
1284
+ tabIndex={isDisabled && !showsDisabledMessage ? -1 : 0}
1163
1285
  {...stylex.props(styles.trigger)}>
1164
1286
  <span {...stylex.props(styles.triggerContent)}>
1165
1287
  {renderTriggerContent()}
@@ -1169,7 +1291,6 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1169
1291
  {hasClear && value.length > 0 && !isDisabled && (
1170
1292
  <button
1171
1293
  type="button"
1172
- tabIndex={-1}
1173
1294
  onClick={handleClear}
1174
1295
  aria-label={`Clear all ${label}`}
1175
1296
  {...stylex.props(styles.clearButton)}>
@@ -1211,6 +1332,9 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1211
1332
  xstyle: styles.popover,
1212
1333
  },
1213
1334
  )}
1335
+
1336
+ {showsDisabledMessage &&
1337
+ disabledMessageTooltip.renderTooltip(disabledMessage)}
1214
1338
  </Field>
1215
1339
  );
1216
1340
  }
@@ -23,6 +23,17 @@ interface UseMultiComboboxOptions {
23
23
  onOpen: () => void;
24
24
  onClose: () => void;
25
25
  onToggle: (itemValue: string) => void;
26
+ /**
27
+ * Clear all selected values. When provided, pressing Delete or Backspace on
28
+ * the closed trigger clears the selection — a keyboard equivalent of the
29
+ * clear button (comboboxes-2). No-op when the popup is open or search is on.
30
+ */
31
+ onClear?: () => void;
32
+ /**
33
+ * Whether at least one value is selected (i.e. there is something to clear).
34
+ * The Delete/Backspace clear path is skipped when false.
35
+ */
36
+ hasValue?: boolean;
26
37
  listboxId: string;
27
38
  }
28
39
 
@@ -49,6 +60,8 @@ export function useMultiCombobox({
49
60
  onOpen,
50
61
  onClose,
51
62
  onToggle,
63
+ onClear,
64
+ hasValue = false,
52
65
  listboxId,
53
66
  }: UseMultiComboboxOptions): UseMultiComboboxResult {
54
67
  const [highlightedIndex, setHighlightedIndex] = useState<number>(-1);
@@ -179,6 +192,18 @@ export function useMultiCombobox({
179
192
  }
180
193
  break;
181
194
 
195
+ case 'Delete':
196
+ case 'Backspace':
197
+ // Keyboard equivalent of the clear button (comboboxes-2): clear all
198
+ // selected values from the closed trigger so clearing is not
199
+ // mouse-only. Skipped in search mode (keys edit the query) and while
200
+ // the popup is open (arrow navigation owns interaction).
201
+ if (!hasSearch && !isOpen && onClear != null && hasValue) {
202
+ e.preventDefault();
203
+ onClear();
204
+ }
205
+ break;
206
+
182
207
  default:
183
208
  // Typeahead only when search is not present
184
209
  if (!hasSearch && e.key.length === 1 && !e.ctrlKey && !e.metaKey) {
@@ -218,6 +243,8 @@ export function useMultiCombobox({
218
243
  getEnabledIndices,
219
244
  typeahead,
220
245
  hasSearch,
246
+ onClear,
247
+ hasValue,
221
248
  ],
222
249
  );
223
250
 
@@ -1,7 +1,7 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
3
  import {describe, it, expect, vi} from 'vitest';
4
- import {render, screen} from '@testing-library/react';
4
+ import {render, screen, fireEvent} from '@testing-library/react';
5
5
  import userEvent from '@testing-library/user-event';
6
6
  import {NavHeadingMenu} from './NavHeadingMenu';
7
7
  import {NavHeadingMenuItem} from './NavHeadingMenuItem';
@@ -204,6 +204,82 @@ describe('keyboard navigation', () => {
204
204
  await user.keyboard('{End}');
205
205
  expect(document.activeElement).toBe(items[2]);
206
206
  });
207
+
208
+ it('activates a focused onClick-only item with Enter', async () => {
209
+ const user = userEvent.setup();
210
+ const onClick = vi.fn();
211
+ render(
212
+ <NavHeadingMenu>
213
+ <NavHeadingMenuItem label="Action" onClick={onClick} />
214
+ </NavHeadingMenu>,
215
+ );
216
+ const item = screen.getByRole('menuitem');
217
+ item.focus();
218
+
219
+ await user.keyboard('{Enter}');
220
+ expect(onClick).toHaveBeenCalledOnce();
221
+ });
222
+
223
+ it('activates a focused onClick-only item with Space', async () => {
224
+ const user = userEvent.setup();
225
+ const onClick = vi.fn();
226
+ render(
227
+ <NavHeadingMenu>
228
+ <NavHeadingMenuItem label="Action" onClick={onClick} />
229
+ </NavHeadingMenu>,
230
+ );
231
+ const item = screen.getByRole('menuitem');
232
+ item.focus();
233
+
234
+ await user.keyboard('{ }');
235
+ expect(onClick).toHaveBeenCalledOnce();
236
+ });
237
+
238
+ it('does not activate a disabled item with Enter', async () => {
239
+ const user = userEvent.setup();
240
+ const onClick = vi.fn();
241
+ render(
242
+ <NavHeadingMenu>
243
+ <NavHeadingMenuItem label="Action" onClick={onClick} isDisabled />
244
+ </NavHeadingMenu>,
245
+ );
246
+ const item = screen.getByRole('menuitem');
247
+ item.focus();
248
+
249
+ await user.keyboard('{Enter}');
250
+ expect(onClick).not.toHaveBeenCalled();
251
+ });
252
+
253
+ it('typeahead focuses the item matching the typed character (menus-11)', () => {
254
+ render(
255
+ <NavHeadingMenu>
256
+ <NavHeadingMenuItem label="Cut" />
257
+ <NavHeadingMenuItem label="Paste" />
258
+ </NavHeadingMenu>,
259
+ );
260
+ const menu = screen.getByRole('menu');
261
+ fireEvent.keyDown(menu, {key: 'p'});
262
+ expect(screen.getByRole('menuitem', {name: 'Paste'})).toHaveFocus();
263
+ });
264
+
265
+ it('typeahead skips a disabled menuitem (menus-11)', () => {
266
+ render(
267
+ <NavHeadingMenu>
268
+ <NavHeadingMenuItem label="Cut" />
269
+ <NavHeadingMenuItem label="Paste (disabled)" isDisabled />
270
+ <NavHeadingMenuItem label="Paste special" />
271
+ </NavHeadingMenu>,
272
+ );
273
+ const menu = screen.getByRole('menu');
274
+ // Typing "p" must land on the enabled "Paste special", never the disabled
275
+ // "Paste (disabled)" — the disabled item is excluded from the typeahead
276
+ // selector entirely.
277
+ fireEvent.keyDown(menu, {key: 'p'});
278
+ expect(screen.getByRole('menuitem', {name: 'Paste special'})).toHaveFocus();
279
+ expect(
280
+ screen.getByRole('menuitem', {name: 'Paste (disabled)'}),
281
+ ).not.toHaveFocus();
282
+ });
207
283
  });
208
284
 
209
285
  describe('context forwarding', () => {
@@ -12,12 +12,13 @@
12
12
  * - /packages/core/src/NavMenu/index.ts
13
13
  */
14
14
 
15
- import React, {useMemo, type ReactNode} from 'react';
15
+ import React, {useCallback, useMemo, type ReactNode} from 'react';
16
16
  import * as stylex from '@stylexjs/stylex';
17
17
  import {spacingVars} from '../theme/tokens.stylex';
18
18
  import {mergeProps, mergeRefs} from '../utils';
19
19
  import type {BaseProps} from '../BaseProps';
20
20
  import {useListFocus} from '../hooks/useListFocus';
21
+ import {useTypeahead} from '../hooks/useTypeahead';
21
22
  import {themeProps} from '../utils/themeProps';
22
23
  import {
23
24
  NavHeadingMenuContext,
@@ -98,10 +99,56 @@ export function NavHeadingMenu({
98
99
  const closeCtx = useNavHeadingCloseContext();
99
100
  const closeMenu = closeCtx?.closeMenu;
100
101
 
101
- const {listRef, handleKeyDown} = useListFocus({
102
+ const {listRef, handleKeyDown, focusItem} = useListFocus({
103
+ itemSelector: '[role="menuitem"]:not([aria-disabled="true"])',
102
104
  onEscape: closeMenu,
103
105
  });
104
106
 
107
+ // First-character typeahead over the (enabled) menu items (menus-11).
108
+ const getMenuItems = useCallback(
109
+ (): HTMLElement[] =>
110
+ listRef.current
111
+ ? Array.from(
112
+ listRef.current.querySelectorAll<HTMLElement>(
113
+ '[role="menuitem"]:not([aria-disabled="true"])',
114
+ ),
115
+ )
116
+ : [],
117
+ [listRef],
118
+ );
119
+ const typeahead = useTypeahead({
120
+ getItemLabels: () => getMenuItems().map(el => el.textContent),
121
+ onMatch: focusItem,
122
+ getCurrentIndex: () =>
123
+ getMenuItems().findIndex(
124
+ el =>
125
+ el === document.activeElement || el.contains(document.activeElement),
126
+ ),
127
+ });
128
+
129
+ // Extend useListFocus with Enter/Space activation. Items rendered without an
130
+ // `href` are `<div role="menuitem">` elements, which have no native keyboard
131
+ // activation — without this, Enter/Space on a focused onClick-only item does
132
+ // nothing. Anchor items (with `href`) already activate on Enter natively.
133
+ const listKeyDown = useCallback(
134
+ (e: React.KeyboardEvent) => {
135
+ if (e.key === 'Enter' || e.key === ' ') {
136
+ const focused = document.activeElement as HTMLElement | null;
137
+ if (focused?.getAttribute('role') === 'menuitem') {
138
+ e.preventDefault();
139
+ focused.click();
140
+ return;
141
+ }
142
+ }
143
+ if (typeahead.onKeyDown(e)) {
144
+ e.preventDefault();
145
+ return;
146
+ }
147
+ handleKeyDown(e);
148
+ },
149
+ [handleKeyDown, typeahead],
150
+ );
151
+
105
152
  const ctx = useMemo(
106
153
  () => ({
107
154
  closeMenu: closeMenu ?? (() => {}),
@@ -117,7 +164,7 @@ export function NavHeadingMenu({
117
164
  <div
118
165
  ref={mergeRefs(ref, listRef)}
119
166
  role="menu"
120
- onKeyDown={handleKeyDown}
167
+ onKeyDown={listKeyDown}
121
168
  data-testid={testId}
122
169
  {...mergeProps(
123
170
  themeProps('nav-heading-menu', {size}),
@@ -62,6 +62,12 @@ export const docs = {
62
62
  type: 'boolean',
63
63
  description: 'Whether the input is disabled.',
64
64
  },
65
+ {
66
+ name: 'disabledMessage',
67
+ type: 'string',
68
+ description:
69
+ 'Explains why the input is disabled. With isDisabled, shows a tooltip on hover/keyboard focus and keeps the input focusable via aria-disabled (the field becomes read-only). Use this instead of wrapping a disabled NumberInput in Tooltip.',
70
+ },
65
71
  {
66
72
  name: 'placeholder',
67
73
  type: 'string',
@@ -166,6 +172,7 @@ export const docs = {
166
172
  { guidance: true, description: 'Show units (e.g. "%" or "GB") so users know what the number represents.' },
167
173
  { guidance: false, description: 'Use NumberInput for free-form text that happens to contain numbers; use TextInput instead.' },
168
174
  { guidance: false, description: 'Set both isOptional and isRequired on the same field.' },
175
+ { guidance: false, description: "Wrap a disabled NumberInput in Tooltip to explain why it's disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead." },
169
176
  ],
170
177
  anatomy: [
171
178
  {name: 'Label', required: true, description: 'The label for the number input.'},
@@ -236,6 +243,12 @@ export const docsZh = {
236
243
  type: 'boolean',
237
244
  description: '输入框是否禁用。',
238
245
  },
246
+ {
247
+ name: 'disabledMessage',
248
+ type: 'string',
249
+ description:
250
+ '说明输入框被禁用的原因。与 isDisabled 一起使用时,在悬停/键盘聚焦时显示工具提示,并通过 aria-disabled 保持输入框可聚焦(字段变为只读)。请使用此属性,而不是用 Tooltip 包裹已禁用的 NumberInput。',
251
+ },
239
252
  {
240
253
  name: 'placeholder',
241
254
  type: 'string',
@@ -339,6 +352,7 @@ export const docsZh = {
339
352
  { guidance: true, description: 'Show units (e.g. "%" or "GB") so users know what the number represents.' },
340
353
  { guidance: false, description: 'Use NumberInput for free-form text that happens to contain numbers; use TextInput instead.' },
341
354
  { guidance: false, description: 'Set both isOptional and isRequired on the same field.' },
355
+ { guidance: false, description: "Wrap a disabled NumberInput in Tooltip to explain why it's disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead." },
342
356
  ],
343
357
  anatomy: [
344
358
  {name: 'Label', required: true, description: 'The label for the number input.'},
@@ -361,6 +375,7 @@ export const docsDense = {
361
375
  { guidance: true, description: 'Show units (e.g. "%" or "GB") so users know what the number represents.' },
362
376
  { guidance: false, description: 'Use NumberInput for free-form text that happens to contain numbers; use TextInput instead.' },
363
377
  { guidance: false, description: 'Set both isOptional and isRequired on the same field.' },
378
+ { guidance: false, description: "Wrap a disabled NumberInput in Tooltip to explain why it's disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead." },
364
379
  ],
365
380
  anatomy: [
366
381
  {name: 'Label', required: true, description: 'The label for the number input.'},
@@ -380,6 +395,8 @@ export const docsDense = {
380
395
  isOptional: 'Field optional (mutually exclusive w/ isRequired).',
381
396
  isRequired: 'Field required (mutually exclusive w/ isOptional).',
382
397
  isDisabled: 'Input disabled.',
398
+ disabledMessage:
399
+ 'Explains why input is disabled. With isDisabled, shows tooltip on hover/focus + keeps input focusable via aria-disabled (field becomes read-only). Use instead of wrapping a disabled NumberInput in Tooltip.',
383
400
  placeholder: 'Placeholder text.',
384
401
  labelTooltip: 'Tooltip text in info icon at label end.',
385
402
  startIcon: 'Icon at input start.',