@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
@@ -179,11 +179,16 @@ export function BreadcrumbItem({
179
179
  className,
180
180
  style,
181
181
  'data-testid': testId,
182
+ ...rest
182
183
  }: BreadcrumbItemProps) {
183
184
  const ctx = use(BreadcrumbContext);
184
185
  const LinkComponent = useLinkComponent(as);
185
186
  const isSupporting = ctx.variant === 'supporting';
186
187
  const liRef = useRef<HTMLLIElement>(null);
188
+ // Points at the element we render to hold the item's content (the
189
+ // link/button/span in the auto-candidate path). Auto-current detection sets
190
+ // aria-current on this instead of guessing the <li>'s last child.
191
+ const contentRef = useRef<HTMLElement>(null);
187
192
 
188
193
  const isCurrent = isCurrentProp === true;
189
194
  const isAutoCandidate = isCurrentProp == null;
@@ -191,6 +196,10 @@ export function BreadcrumbItem({
191
196
  // Auto-detect: if no sibling has aria-current="page" and this is the last
192
197
  // non-separator item, set aria-current on our content element.
193
198
  // Runs as useEffect (not layout) — only sets an aria attribute, no visual change.
199
+ // Placed on the item's content element (the link/button/span after the
200
+ // separator), matching where the explicit `isCurrent` path sets it, so
201
+ // aria-current lands on the actual interactive element — including when the
202
+ // last item is a link — rather than on the outer <li> (navigation-11).
194
203
  useEffect(() => {
195
204
  if (!isAutoCandidate) {
196
205
  return;
@@ -211,12 +220,16 @@ export function BreadcrumbItem({
211
220
  const hasExplicit = ol.querySelector('[aria-current="page"]');
212
221
 
213
222
  if (isLast && !hasExplicit) {
214
- li.setAttribute('aria-current', 'page');
223
+ // We control the element that holds the content (see the auto-candidate
224
+ // render path below), so set aria-current on that ref rather than
225
+ // assuming a positional last child. Fall back to the <li> only if the
226
+ // ref is somehow unresolved.
227
+ const target = contentRef.current ?? li;
228
+ target.setAttribute('aria-current', 'page');
229
+ return () => {
230
+ target.removeAttribute('aria-current');
231
+ };
215
232
  }
216
-
217
- return () => {
218
- li.removeAttribute('aria-current');
219
- };
220
233
  });
221
234
 
222
235
  const content = (
@@ -240,7 +253,8 @@ export function BreadcrumbItem({
240
253
  className,
241
254
  style,
242
255
  )}
243
- data-testid={testId}>
256
+ data-testid={testId}
257
+ {...rest}>
244
258
  <span aria-hidden="true" {...stylex.props(itemStyles.separator)}>
245
259
  {ctx.separator}
246
260
  </span>
@@ -274,12 +288,14 @@ export function BreadcrumbItem({
274
288
  className,
275
289
  style,
276
290
  )}
277
- data-testid={testId}>
291
+ data-testid={testId}
292
+ {...rest}>
278
293
  <span aria-hidden="true" {...stylex.props(itemStyles.separator)}>
279
294
  {ctx.separator}
280
295
  </span>
281
296
  {href != null ? (
282
297
  <LinkComponent
298
+ ref={contentRef}
283
299
  href={href}
284
300
  onClick={onClick}
285
301
  {...stylex.props(
@@ -290,6 +306,7 @@ export function BreadcrumbItem({
290
306
  </LinkComponent>
291
307
  ) : onClick != null ? (
292
308
  <button
309
+ ref={contentRef as React.RefObject<HTMLButtonElement | null>}
293
310
  type="button"
294
311
  onClick={onClick}
295
312
  {...stylex.props(
@@ -301,6 +318,7 @@ export function BreadcrumbItem({
301
318
  </button>
302
319
  ) : (
303
320
  <span
321
+ ref={contentRef}
304
322
  {...stylex.props(
305
323
  itemStyles.contentWrapper,
306
324
  itemStyles.current,
@@ -180,12 +180,31 @@ describe('BreadcrumbItem', () => {
180
180
  <BreadcrumbItem>Last Item</BreadcrumbItem>
181
181
  </Breadcrumbs>,
182
182
  );
183
- // aria-current is set via useEffect, so we need to wait for it
184
- const lastLi = screen.getByText('Last Item').closest('li')!;
183
+ // aria-current is set via useEffect on the content element (matching the
184
+ // explicit isCurrent path), not the outer <li>.
185
+ const lastContent = screen.getByText('Last Item');
185
186
  await waitFor(() => {
186
- expect(lastLi).toHaveAttribute('aria-current', 'page');
187
+ expect(lastContent).toHaveAttribute('aria-current', 'page');
187
188
  });
188
- expect(screen.getByText('Last Item').tagName).toBe('SPAN');
189
+ expect(lastContent.tagName).toBe('SPAN');
190
+ // The <li> wrapper must NOT carry aria-current.
191
+ expect(lastContent.closest('li')).not.toHaveAttribute('aria-current');
192
+ });
193
+
194
+ it('auto-detects aria-current on the anchor when the last item is a link', async () => {
195
+ render(
196
+ <Breadcrumbs>
197
+ <BreadcrumbItem href="/">Home</BreadcrumbItem>
198
+ <BreadcrumbItem href="/projects/current">Current</BreadcrumbItem>
199
+ </Breadcrumbs>,
200
+ );
201
+ const lastLink = screen.getByText('Current');
202
+ await waitFor(() => {
203
+ expect(lastLink).toHaveAttribute('aria-current', 'page');
204
+ });
205
+ // aria-current is on the anchor itself, not the <li>.
206
+ expect(lastLink.tagName).toBe('A');
207
+ expect(lastLink.closest('li')).not.toHaveAttribute('aria-current');
189
208
  });
190
209
 
191
210
  it('does not auto-detect when isCurrent is explicitly set', async () => {
@@ -270,11 +289,12 @@ describe('BreadcrumbItem', () => {
270
289
  <BreadcrumbItem>Only Item</BreadcrumbItem>
271
290
  </Breadcrumbs>,
272
291
  );
273
- const li = screen.getByText('Only Item').closest('li')!;
292
+ const content = screen.getByText('Only Item');
274
293
  await waitFor(() => {
275
- expect(li).toHaveAttribute('aria-current', 'page');
294
+ expect(content).toHaveAttribute('aria-current', 'page');
276
295
  });
277
- expect(screen.getByText('Only Item').tagName).toBe('SPAN');
296
+ expect(content.tagName).toBe('SPAN');
297
+ expect(content.closest('li')).not.toHaveAttribute('aria-current');
278
298
  });
279
299
 
280
300
  it('auto-detects last child as current with supporting variant', async () => {
@@ -284,11 +304,12 @@ describe('BreadcrumbItem', () => {
284
304
  <BreadcrumbItem>Last</BreadcrumbItem>
285
305
  </Breadcrumbs>,
286
306
  );
287
- const li = screen.getByText('Last').closest('li')!;
307
+ const content = screen.getByText('Last');
288
308
  await waitFor(() => {
289
- expect(li).toHaveAttribute('aria-current', 'page');
309
+ expect(content).toHaveAttribute('aria-current', 'page');
290
310
  });
291
- expect(screen.getByText('Last').tagName).toBe('SPAN');
311
+ expect(content.tagName).toBe('SPAN');
312
+ expect(content.closest('li')).not.toHaveAttribute('aria-current');
292
313
  });
293
314
 
294
315
  it('renders custom component for non-current items when as is provided', () => {
@@ -78,6 +78,12 @@ export const docs = {
78
78
  description: 'Shows a loading spinner and disables interaction. Announces "Loading" via a live region.',
79
79
  default: 'false',
80
80
  },
81
+ {
82
+ name: 'isInterruptible',
83
+ type: 'boolean',
84
+ description: 'Keep the button clickable while a clickAction is pending: the spinner and aria-busy still show, but the button is not disabled and the action is not deduped, so a re-click lands and interrupts the in-flight action with a fresh one.',
85
+ default: 'false',
86
+ },
81
87
  {
82
88
  name: 'isDisabled',
83
89
  type: 'boolean',
@@ -10,7 +10,7 @@
10
10
  */
11
11
 
12
12
  import {describe, it, expect, vi} from 'vitest';
13
- import {render, screen} from '@testing-library/react';
13
+ import {render, screen, fireEvent, act} from '@testing-library/react';
14
14
  import userEvent from '@testing-library/user-event';
15
15
  import {Button} from './Button';
16
16
  import {Badge} from '../Badge/Badge';
@@ -71,6 +71,32 @@ describe('Button', () => {
71
71
  expect(button).toBeDisabled();
72
72
  });
73
73
 
74
+ it('sets aria-busy synchronously while clickAction is pending', async () => {
75
+ // The spinner reveal is visually delayed (CSS animation-delay), but the
76
+ // loading DOM state — aria-busy and disabled — must not be delayed.
77
+ const user = userEvent.setup();
78
+ let resolveAction: (() => void) | undefined;
79
+ const clickAction = vi.fn(
80
+ async () =>
81
+ new Promise<void>(resolve => {
82
+ resolveAction = resolve;
83
+ }),
84
+ );
85
+ render(<Button label="Save" clickAction={clickAction} />);
86
+ const button = screen.getByRole('button');
87
+
88
+ await user.click(button);
89
+ expect(button).toHaveAttribute('aria-busy', 'true');
90
+ expect(button).toBeDisabled();
91
+
92
+ await act(async () => {
93
+ resolveAction?.();
94
+ await Promise.resolve();
95
+ });
96
+ expect(button).not.toHaveAttribute('aria-busy', 'true');
97
+ expect(button).not.toBeDisabled();
98
+ });
99
+
74
100
  it('renders the loading spinner with the inherit shade for every variant (#2717)', () => {
75
101
  // The spinner must follow the button's resolved foreground color rather
76
102
  // than a hardcoded white, so it keeps contrast on themed variants like the
@@ -228,11 +254,7 @@ describe('Button', () => {
228
254
  order.push('clickAction');
229
255
  });
230
256
  render(
231
- <Button
232
- label="Test"
233
- onClick={handleClick}
234
- clickAction={handleAction}
235
- />,
257
+ <Button label="Test" onClick={handleClick} clickAction={handleAction} />,
236
258
  );
237
259
 
238
260
  await user.click(screen.getByRole('button'));
@@ -246,11 +268,7 @@ describe('Button', () => {
246
268
  const handleClick = vi.fn((e: React.MouseEvent) => e.preventDefault());
247
269
  const handleAction = vi.fn();
248
270
  render(
249
- <Button
250
- label="Test"
251
- onClick={handleClick}
252
- clickAction={handleAction}
253
- />,
271
+ <Button label="Test" onClick={handleClick} clickAction={handleAction} />,
254
272
  );
255
273
 
256
274
  await user.click(screen.getByRole('button'));
@@ -258,6 +276,82 @@ describe('Button', () => {
258
276
  expect(handleAction).not.toHaveBeenCalled();
259
277
  });
260
278
 
279
+ it('fires clickAction once on a fast double-click (no double-submit)', async () => {
280
+ let resolveAction: (() => void) | undefined;
281
+ const handleAction = vi.fn(
282
+ async () =>
283
+ new Promise<void>(resolve => {
284
+ resolveAction = resolve;
285
+ }),
286
+ );
287
+ render(<Button label="Pay" clickAction={handleAction} />);
288
+
289
+ const button = screen.getByRole('button');
290
+ await act(async () => {
291
+ fireEvent.click(button);
292
+ fireEvent.click(button);
293
+ });
294
+ expect(handleAction).toHaveBeenCalledTimes(1);
295
+
296
+ await act(async () => {
297
+ resolveAction?.();
298
+ await Promise.resolve();
299
+ });
300
+ });
301
+
302
+ it('stays clickable (not disabled) while a clickAction is pending when isInterruptible', async () => {
303
+ const user = userEvent.setup();
304
+ let resolveAction: (() => void) | undefined;
305
+ const clickAction = vi.fn(
306
+ async () =>
307
+ new Promise<void>(resolve => {
308
+ resolveAction = resolve;
309
+ }),
310
+ );
311
+ render(<Button label="Toggle" isInterruptible clickAction={clickAction} />);
312
+ const button = screen.getByRole('button');
313
+
314
+ await user.click(button);
315
+ // Loading is announced via aria-busy, but the button is not disabled so it
316
+ // can be re-clicked to interrupt the in-flight action.
317
+ expect(button).toHaveAttribute('aria-busy', 'true');
318
+ expect(button).not.toBeDisabled();
319
+
320
+ await act(async () => {
321
+ resolveAction?.();
322
+ await Promise.resolve();
323
+ });
324
+ expect(button).not.toHaveAttribute('aria-busy', 'true');
325
+ expect(button).not.toBeDisabled();
326
+ });
327
+
328
+ it('re-fires clickAction on re-click while pending when isInterruptible (no dedupe)', async () => {
329
+ // Unlike the fire-once default, an interruptible action is not deduped: a
330
+ // re-click while pending starts a fresh action that interrupts the prior.
331
+ const resolvers: (() => void)[] = [];
332
+ const clickAction = vi.fn(
333
+ async () =>
334
+ new Promise<void>(resolve => {
335
+ resolvers.push(resolve);
336
+ }),
337
+ );
338
+ render(<Button label="Toggle" isInterruptible clickAction={clickAction} />);
339
+
340
+ const button = screen.getByRole('button');
341
+ await act(async () => {
342
+ fireEvent.click(button);
343
+ });
344
+ await act(async () => {
345
+ fireEvent.click(button);
346
+ });
347
+ expect(clickAction).toHaveBeenCalledTimes(2);
348
+
349
+ await act(async () => {
350
+ resolvers.forEach(resolve => resolve());
351
+ await Promise.resolve();
352
+ });
353
+ });
354
+
261
355
  // type/name/value/form props
262
356
  it('defaults type to button', () => {
263
357
  render(<Button label="Test" />);
@@ -15,7 +15,7 @@
15
15
  * - /apps/storybook/stories/Button.stories.tsx (storybook stories)
16
16
  * - /packages/cli/templates/blocks/components/Button/ (showcase blocks)
17
17
  *
18
- * Last synced props: label, variant, size, isDisabled, isLoading, clickAction, icon, isIconOnly, children, tooltip, endContent, href, as, target, rel
18
+ * Last synced props: label, variant, size, isDisabled, isLoading, isInterruptible, clickAction, icon, isIconOnly, children, tooltip, endContent, href, as, target, rel
19
19
  */
20
20
 
21
21
  import {useRef, useTransition, type ReactNode} from 'react';
@@ -34,6 +34,7 @@ import {
34
34
  } from '../theme/tokens.stylex';
35
35
  import {Tooltip} from '../Tooltip/Tooltip';
36
36
  import {Spinner} from '../Spinner';
37
+ import {VisuallyHidden} from '../VisuallyHidden';
37
38
 
38
39
  import {EDGE_COMP_ATTR} from '../Layout/edgeCompensation.stylex';
39
40
  import {useSize} from '../SizeContext/SizeContext';
@@ -124,17 +125,6 @@ const styles = stylex.create({
124
125
  textOverflow: 'ellipsis',
125
126
  minWidth: 0,
126
127
  },
127
- visuallyHidden: {
128
- position: 'absolute',
129
- width: '1px',
130
- height: '1px',
131
- padding: 0,
132
- margin: '-1px',
133
- overflow: 'hidden',
134
- clip: 'rect(0, 0, 0, 0)',
135
- whiteSpace: 'nowrap',
136
- borderWidth: 0,
137
- },
138
128
  link: {
139
129
  textDecoration: 'none',
140
130
  },
@@ -320,6 +310,16 @@ export interface ButtonProps extends BaseProps<HTMLButtonElement> {
320
310
  * @default false
321
311
  */
322
312
  isLoading?: boolean;
313
+ /**
314
+ * Keep the button interactive while a `clickAction` is pending. The loading
315
+ * state still renders the spinner and `aria-busy`, but the button is not
316
+ * disabled and the in-flight action is not deduped — so a re-click lands and
317
+ * interrupts the previous action with a fresh one. Use for interruptible
318
+ * actions (e.g. a toggle whose action can be re-triggered before the previous
319
+ * one settles), not fire-once actions (submit/save/pay).
320
+ * @default false
321
+ */
322
+ isInterruptible?: boolean;
323
323
  /**
324
324
  * Click handler. For async actions that should show a loading state,
325
325
  * use `clickAction` instead.
@@ -380,6 +380,22 @@ export interface ButtonProps extends BaseProps<HTMLButtonElement> {
380
380
  rel?: string;
381
381
  }
382
382
 
383
+ const spinnerReveal = stylex.keyframes({
384
+ from: {opacity: 0},
385
+ to: {opacity: 1},
386
+ });
387
+
388
+ const contentHide = stylex.keyframes({
389
+ from: {color: 'inherit'},
390
+ to: {color: 'transparent'},
391
+ });
392
+
393
+ // Hold the loading swap for a short delay so a fast action (e.g. clickAction)
394
+ // that settles within the delay never flashes a spinner. The spinner fade-in
395
+ // and the content hide share the same delay so the button never shows an empty
396
+ // frame in between. Reduced motion is instant.
397
+ const SPINNER_DELAY = durationVars['--duration-medium-min'];
398
+
383
399
  const loadingStyles = stylex.create({
384
400
  // Hide the button's own content while the spinner overlay is shown. Applied
385
401
  // to the content wrapper (not the button) so the button keeps its variant
@@ -387,6 +403,17 @@ const loadingStyles = stylex.create({
387
403
  hiddenContent: {
388
404
  color: 'transparent',
389
405
  },
406
+ // Delayed variant: keep content visible, then hide it in lockstep with the
407
+ // spinner reveal once the delay elapses.
408
+ hiddenContentDelayed: {
409
+ animationName: contentHide,
410
+ animationDuration: '1ms',
411
+ animationFillMode: 'forwards',
412
+ animationDelay: {
413
+ default: SPINNER_DELAY,
414
+ '@media (prefers-reduced-motion: reduce)': '0s',
415
+ },
416
+ },
390
417
  spinnerOverlay: {
391
418
  position: 'absolute',
392
419
  top: 0,
@@ -396,6 +423,15 @@ const loadingStyles = stylex.create({
396
423
  display: 'grid',
397
424
  placeItems: 'center',
398
425
  },
426
+ spinnerDelayed: {
427
+ animationName: spinnerReveal,
428
+ animationDuration: durationVars['--duration-fast'],
429
+ animationFillMode: 'backwards',
430
+ animationDelay: {
431
+ default: SPINNER_DELAY,
432
+ '@media (prefers-reduced-motion: reduce)': '0s',
433
+ },
434
+ },
399
435
  });
400
436
 
401
437
  const groupStyles = stylex.create({
@@ -493,6 +529,7 @@ export function Button({
493
529
  type = 'button',
494
530
  isDisabled = false,
495
531
  isLoading = false,
532
+ isInterruptible = false,
496
533
  clickAction,
497
534
  icon,
498
535
  isIconOnly = false,
@@ -513,10 +550,23 @@ export function Button({
513
550
  const buttonGroup = useButtonGroup();
514
551
 
515
552
  const [isPending, startTransition] = useTransition();
553
+ // clickAction is normally fire-once (submit/save/pay), so a same-tick
554
+ // double-click must dedupe — which neither isPending nor useOptimistic do.
555
+ // Hence the ref guard. Interruptible callers (e.g. ToggleButton) opt out so a
556
+ // re-click can land and interrupt the in-flight action with a fresh one.
516
557
  const actionInFlightRef = useRef(false);
517
558
  const isLoadingState = isLoading || isPending;
559
+ // Delay the spinner reveal for action-driven loading (clickAction's own
560
+ // transition) so a fast action that settles within the delay does not flash
561
+ // a spinner. Interruptible loading is delayed too, so rapid re-clicks settle
562
+ // before any spinner shows. Explicit isLoading-only stays immediate, since
563
+ // the consumer is deliberately showing it.
564
+ const delaySpinner = isPending || isInterruptible;
518
565
  const groupDisabled = buttonGroup?.isDisabled ?? false;
519
- const buttonDisabled = isDisabled || groupDisabled || isLoadingState;
566
+ // When interruptible, the loading state drives the spinner and aria-busy but
567
+ // not disabled, so clicks keep landing and can interrupt the in-flight action.
568
+ const buttonDisabled =
569
+ isDisabled || groupDisabled || (isLoadingState && !isInterruptible);
520
570
  // isIconOnly prop is the source of truth for icon-only rendering.
521
571
  // When false (default), label is always rendered as visible text.
522
572
 
@@ -531,7 +581,9 @@ export function Button({
531
581
  const useAriaDisabled = tooltip != null && buttonDisabled;
532
582
 
533
583
  const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
534
- if (buttonDisabled || actionInFlightRef.current) {
584
+ // The ref guard dedupes fire-once actions. Interruptible callers skip it so
585
+ // a re-click while pending starts a fresh action that interrupts the prior.
586
+ if (buttonDisabled || (actionInFlightRef.current && !isInterruptible)) {
535
587
  e.preventDefault();
536
588
  return;
537
589
  }
@@ -598,7 +650,10 @@ export function Button({
598
650
  <>
599
651
  {isLoadingState && (
600
652
  <span
601
- {...stylex.props(loadingStyles.spinnerOverlay)}
653
+ {...stylex.props(
654
+ loadingStyles.spinnerOverlay,
655
+ delaySpinner && loadingStyles.spinnerDelayed,
656
+ )}
602
657
  aria-hidden="true">
603
658
  <Spinner size="sm" shade="inherit" />
604
659
  </span>
@@ -606,7 +661,10 @@ export function Button({
606
661
  <span
607
662
  {...stylex.props(
608
663
  styles.contentWrapper,
609
- isLoadingState && loadingStyles.hiddenContent,
664
+ isLoadingState &&
665
+ (delaySpinner
666
+ ? loadingStyles.hiddenContentDelayed
667
+ : loadingStyles.hiddenContent),
610
668
  )}
611
669
  aria-hidden={isLoadingState || undefined}>
612
670
  {icon && (
@@ -622,12 +680,9 @@ export function Button({
622
680
  )}
623
681
  </span>
624
682
  {/* Live region for loading state announcements */}
625
- <span
626
- {...stylex.props(styles.visuallyHidden)}
627
- role="status"
628
- aria-live="polite">
683
+ <VisuallyHidden role="status" aria-live="polite">
629
684
  {isLoadingState ? 'Loading' : ''}
630
- </span>
685
+ </VisuallyHidden>
631
686
  </>
632
687
  );
633
688
 
@@ -103,8 +103,8 @@ export const docs = {
103
103
  },
104
104
  {
105
105
  name: 'weekStartsOn',
106
- type: '0 | 1 | 2 | 3 | 4 | 5 | 6',
107
- description: 'First day of week (0=Sunday).',
106
+ type: "0 | 1 | 2 | 3 | 4 | 5 | 6 | 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat'",
107
+ description: 'First day of week. Accepts a number (0=Sunday) or a three-letter day name (e.g. "mon").',
108
108
  default: '0',
109
109
  },
110
110
  ],
@@ -147,7 +147,7 @@ export const docsZh = {
147
147
  {name: 'hasOutsideDays', type: 'boolean', description: '显示相邻月份的日期。', default: 'true'},
148
148
  {name: 'hasWeekNumbers', type: 'boolean', description: '显示 ISO 周数。', default: 'false'},
149
149
  {name: 'hasVariableRowCount', type: 'boolean', description: '可变行数与固定 6 行网格。', default: 'false'},
150
- {name: 'weekStartsOn', type: '0 | 1 | 2 | 3 | 4 | 5 | 6', description: '每周起始日(0=周日)。', default: '0'},
150
+ {name: 'weekStartsOn', type: "0 | 1 | 2 | 3 | 4 | 5 | 6 | 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat'", description: '每周起始日。可为数字(0=周日)或三字母星期缩写(如 "mon")。', default: '0'},
151
151
  ],
152
152
  theming: {
153
153
  targets: [
@@ -200,6 +200,6 @@ export const docsDense = {
200
200
  hasOutsideDays: 'show days from adjacent months',
201
201
  hasWeekNumbers: 'show ISO week numbers',
202
202
  hasVariableRowCount: 'variable vs fixed 6-row grid',
203
- weekStartsOn: 'first day of week (0=Sunday)',
203
+ weekStartsOn: 'first day of week (0=Sunday, or name e.g. "mon")',
204
204
  },
205
205
  };