@astryxdesign/core 0.4.7 → 0.5.0-canary.009bcb3

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 (730) hide show
  1. package/CHANGELOG.md +161 -1
  2. package/dist/AlertDialog/AlertDialog.d.ts +1 -1
  3. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  4. package/dist/AlertDialog/AlertDialog.js +45 -20
  5. package/dist/AppShell/AppShell.d.ts.map +1 -1
  6. package/dist/AppShell/AppShell.js +3 -2
  7. package/dist/AspectRatio/AspectRatio.d.ts +24 -0
  8. package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
  9. package/dist/AspectRatio/AspectRatio.js +23 -4
  10. package/dist/Avatar/Avatar.d.ts +25 -1
  11. package/dist/Avatar/Avatar.d.ts.map +1 -1
  12. package/dist/Avatar/Avatar.js +58 -19
  13. package/dist/Avatar/index.d.ts +2 -2
  14. package/dist/Avatar/index.d.ts.map +1 -1
  15. package/dist/Avatar/index.js +1 -1
  16. package/dist/AvatarGroup/AvatarGroup.d.ts +9 -2
  17. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  18. package/dist/AvatarGroup/AvatarGroup.js +8 -4
  19. package/dist/AvatarGroup/AvatarGroupContext.d.ts +2 -1
  20. package/dist/AvatarGroup/AvatarGroupContext.d.ts.map +1 -1
  21. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  22. package/dist/AvatarGroup/AvatarGroupOverflow.js +10 -6
  23. package/dist/Banner/Banner.d.ts +49 -15
  24. package/dist/Banner/Banner.d.ts.map +1 -1
  25. package/dist/Banner/Banner.js +62 -20
  26. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  27. package/dist/BottomSheet/BottomSheet.js +17 -4
  28. package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
  29. package/dist/BottomSheet/BottomSheetSwitcher.js +3 -2
  30. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  31. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -10
  32. package/dist/Button/Button.d.ts.map +1 -1
  33. package/dist/Button/Button.js +11 -6
  34. package/dist/ButtonGroup/ButtonGroup.d.ts +5 -1
  35. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  36. package/dist/ButtonGroup/ButtonGroup.js +26 -7
  37. package/dist/Calendar/Calendar.d.ts +7 -3
  38. package/dist/Calendar/Calendar.d.ts.map +1 -1
  39. package/dist/Calendar/Calendar.js +21 -23
  40. package/dist/Calendar/getInitialFocusDate.d.ts +46 -0
  41. package/dist/Calendar/getInitialFocusDate.d.ts.map +1 -0
  42. package/dist/Calendar/getInitialFocusDate.js +62 -0
  43. package/dist/Calendar/styles.d.ts +5 -14
  44. package/dist/Calendar/styles.d.ts.map +1 -1
  45. package/dist/Calendar/styles.js +15 -15
  46. package/dist/Card/Card.d.ts +12 -12
  47. package/dist/Card/Card.d.ts.map +1 -1
  48. package/dist/Card/Card.js +17 -9
  49. package/dist/Card/index.d.ts +35 -1
  50. package/dist/Card/index.d.ts.map +1 -1
  51. package/dist/Card/index.js +21 -1
  52. package/dist/Carousel/Carousel.d.ts.map +1 -1
  53. package/dist/Carousel/Carousel.js +3 -3
  54. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  55. package/dist/Chat/ChatComposerInput.js +2 -3
  56. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  57. package/dist/Chat/ChatLayout.js +3 -3
  58. package/dist/Chat/ChatMessageList.d.ts +22 -3
  59. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  60. package/dist/Chat/ChatMessageList.js +6 -3
  61. package/dist/Chat/ChatTokenizedText.d.ts +2 -0
  62. package/dist/Chat/ChatTokenizedText.d.ts.map +1 -1
  63. package/dist/Chat/ChatTokenizedText.js +11 -6
  64. package/dist/Chat/useChatComposerTokens.js +1 -1
  65. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  66. package/dist/CheckboxInput/CheckboxInput.js +10 -3
  67. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  68. package/dist/ClickableCard/ClickableCard.js +3 -3
  69. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  70. package/dist/CodeBlock/CodeBlock.js +15 -4
  71. package/dist/CodeBlock/highlightRanges.js +1 -1
  72. package/dist/CodeBlock/highlightStyles.js +13 -13
  73. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  74. package/dist/CommandPalette/CommandPaletteInput.js +3 -2
  75. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  76. package/dist/CommandPalette/CommandPaletteItem.js +3 -2
  77. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  78. package/dist/ComplexSelector/ComplexSelector.js +1 -3
  79. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  80. package/dist/ContextMenu/ContextMenu.js +4 -3
  81. package/dist/DateInput/DateInput.d.ts +43 -0
  82. package/dist/DateInput/DateInput.d.ts.map +1 -1
  83. package/dist/DateInput/DateInput.js +22 -2
  84. package/dist/DateInput/NativeDateField.d.ts +10 -0
  85. package/dist/DateInput/NativeDateField.d.ts.map +1 -0
  86. package/dist/DateInput/NativeDateField.js +494 -0
  87. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  88. package/dist/DateInput/TouchDateField.js +82 -23
  89. package/dist/DateInput/index.d.ts +1 -1
  90. package/dist/DateInput/index.d.ts.map +1 -1
  91. package/dist/DateInput/nativeDateSegments.d.ts +22 -0
  92. package/dist/DateInput/nativeDateSegments.d.ts.map +1 -0
  93. package/dist/DateInput/nativeDateSegments.js +111 -0
  94. package/dist/DateRangeInput/DateRangeInput.d.ts +4 -2
  95. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  96. package/dist/DateTimeInput/DateTimeInput.d.ts +21 -1
  97. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  98. package/dist/DateTimeInput/DateTimeInput.js +406 -12
  99. package/dist/DateTimeInput/index.d.ts +1 -1
  100. package/dist/DateTimeInput/index.d.ts.map +1 -1
  101. package/dist/Dialog/Dialog.d.ts +21 -1
  102. package/dist/Dialog/Dialog.d.ts.map +1 -1
  103. package/dist/Dialog/Dialog.js +110 -58
  104. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  105. package/dist/DropdownMenu/DropdownMenu.js +4 -13
  106. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  107. package/dist/DropdownMenu/DropdownMenuSubMenu.js +6 -5
  108. package/dist/Field/FieldLabel.d.ts.map +1 -1
  109. package/dist/Field/FieldLabel.js +9 -1
  110. package/dist/Field/InputClearButton.d.ts +9 -3
  111. package/dist/Field/InputClearButton.d.ts.map +1 -1
  112. package/dist/Field/InputClearButton.js +14 -3
  113. package/dist/FileInput/FileInput.d.ts.map +1 -1
  114. package/dist/FileInput/FileInput.js +3 -2
  115. package/dist/Heading/Heading.d.ts.map +1 -1
  116. package/dist/Heading/Heading.js +6 -3
  117. package/dist/HoverCard/HoverCard.d.ts +6 -0
  118. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  119. package/dist/HoverCard/HoverCard.js +104 -3
  120. package/dist/HoverCard/useHoverCard.d.ts +19 -2
  121. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  122. package/dist/HoverCard/useHoverCard.js +74 -26
  123. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
  124. package/dist/Indicator/CheckboxIndicator.js +6 -2
  125. package/dist/Item/Item.d.ts.map +1 -1
  126. package/dist/Item/Item.js +4 -2
  127. package/dist/Kbd/Kbd.d.ts.map +1 -1
  128. package/dist/Kbd/Kbd.js +2 -19
  129. package/dist/Layer/LayerDepthContext.d.ts +52 -0
  130. package/dist/Layer/LayerDepthContext.d.ts.map +1 -0
  131. package/dist/Layer/LayerDepthContext.js +66 -0
  132. package/dist/Layer/gestureCounter.d.ts +8 -0
  133. package/dist/Layer/gestureCounter.d.ts.map +1 -0
  134. package/dist/Layer/gestureCounter.js +53 -0
  135. package/dist/Layer/index.d.ts +3 -0
  136. package/dist/Layer/index.d.ts.map +1 -1
  137. package/dist/Layer/index.js +4 -0
  138. package/dist/Layer/layerStack.d.ts +98 -0
  139. package/dist/Layer/layerStack.d.ts.map +1 -0
  140. package/dist/Layer/layerStack.js +306 -0
  141. package/dist/Layer/useLayer.d.ts +23 -13
  142. package/dist/Layer/useLayer.d.ts.map +1 -1
  143. package/dist/Layer/useLayer.js +128 -8
  144. package/dist/Layer/useLayerDismissal.d.ts +93 -0
  145. package/dist/Layer/useLayerDismissal.d.ts.map +1 -0
  146. package/dist/Layer/useLayerDismissal.js +91 -0
  147. package/dist/Lightbox/Lightbox.d.ts +1 -0
  148. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  149. package/dist/Lightbox/Lightbox.js +103 -81
  150. package/dist/List/List.d.ts +1 -1
  151. package/dist/List/List.d.ts.map +1 -1
  152. package/dist/List/List.js +15 -4
  153. package/dist/Markdown/Markdown.d.ts +1 -1
  154. package/dist/Markdown/Markdown.d.ts.map +1 -1
  155. package/dist/Markdown/Markdown.js +20 -7
  156. package/dist/Markdown/index.d.ts +1 -1
  157. package/dist/Markdown/index.d.ts.map +1 -1
  158. package/dist/Markdown/parser.d.ts +38 -1
  159. package/dist/Markdown/parser.d.ts.map +1 -1
  160. package/dist/Markdown/parser.js +182 -32
  161. package/dist/MetadataList/MetadataListItem.d.ts +1 -1
  162. package/dist/MetadataList/MetadataListItem.d.ts.map +1 -1
  163. package/dist/MetadataList/MetadataListItem.js +4 -1
  164. package/dist/MobileNav/MobileNav.d.ts +6 -1
  165. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  166. package/dist/MobileNav/MobileNav.js +61 -36
  167. package/dist/MultiSelector/MultiSelector.d.ts +32 -1
  168. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  169. package/dist/MultiSelector/MultiSelector.js +73 -15
  170. package/dist/MultiSelector/hooks.d.ts +7 -1
  171. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  172. package/dist/MultiSelector/hooks.js +3 -2
  173. package/dist/MultiSelector/index.d.ts +1 -1
  174. package/dist/MultiSelector/index.d.ts.map +1 -1
  175. package/dist/NavIcon/NavIcon.d.ts.map +1 -1
  176. package/dist/NavIcon/NavIcon.js +5 -1
  177. package/dist/NavMenu/NavHeadingMenu.d.ts +1 -1
  178. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  179. package/dist/NavMenu/NavHeadingMenu.js +9 -5
  180. package/dist/NumberInput/NumberInput.d.ts +6 -1
  181. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  182. package/dist/NumberInput/NumberInput.js +54 -104
  183. package/dist/NumberInput/numberInputCommit.d.ts +34 -0
  184. package/dist/NumberInput/numberInputCommit.d.ts.map +1 -0
  185. package/dist/NumberInput/numberInputCommit.js +71 -0
  186. package/dist/NumberInput/numberParser.d.ts +64 -0
  187. package/dist/NumberInput/numberParser.d.ts.map +1 -0
  188. package/dist/NumberInput/numberParser.js +350 -0
  189. package/dist/Outline/Outline.d.ts.map +1 -1
  190. package/dist/Outline/Outline.js +3 -2
  191. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  192. package/dist/OverflowList/OverflowList.js +3 -2
  193. package/dist/Overlay/Overlay.d.ts.map +1 -1
  194. package/dist/Overlay/Overlay.js +3 -2
  195. package/dist/Popover/Popover.d.ts.map +1 -1
  196. package/dist/Popover/Popover.js +1 -9
  197. package/dist/Popover/usePopover.d.ts +7 -41
  198. package/dist/Popover/usePopover.d.ts.map +1 -1
  199. package/dist/Popover/usePopover.js +40 -21
  200. package/dist/PowerSearch/PowerSearch.d.ts +8 -2
  201. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  202. package/dist/PowerSearch/PowerSearch.js +19 -6
  203. package/dist/PowerSearch/PowerSearchEditPopover.d.ts +3 -1
  204. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  205. package/dist/PowerSearch/PowerSearchEditPopover.js +28 -6
  206. package/dist/PowerSearch/PowerSearchValueEditor.d.ts +3 -1
  207. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  208. package/dist/PowerSearch/PowerSearchValueEditor.js +110 -27
  209. package/dist/PowerSearch/types.d.ts +2 -0
  210. package/dist/PowerSearch/types.d.ts.map +1 -1
  211. package/dist/PowerSearch/usePowerSearchSource.d.ts +6 -1
  212. package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
  213. package/dist/PowerSearch/usePowerSearchSource.js +14 -5
  214. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  215. package/dist/ProgressBar/ProgressBar.js +18 -5
  216. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  217. package/dist/Resizable/ResizeHandle.js +3 -2
  218. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  219. package/dist/SegmentedControl/SegmentedControl.js +3 -2
  220. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  221. package/dist/SelectableCard/SelectableCard.js +12 -3
  222. package/dist/Selector/Selector.d.ts +18 -1
  223. package/dist/Selector/Selector.d.ts.map +1 -1
  224. package/dist/Selector/Selector.js +91 -18
  225. package/dist/Selector/hooks.d.ts +7 -1
  226. package/dist/Selector/hooks.d.ts.map +1 -1
  227. package/dist/Selector/hooks.js +3 -2
  228. package/dist/SideNav/SideNav.d.ts.map +1 -1
  229. package/dist/SideNav/SideNav.js +4 -3
  230. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  231. package/dist/SideNav/SideNavHeading.js +8 -7
  232. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  233. package/dist/SideNav/SideNavItem.js +4 -3
  234. package/dist/Slider/Slider.d.ts.map +1 -1
  235. package/dist/Slider/Slider.js +37 -5
  236. package/dist/Spinner/Spinner.d.ts +3 -3
  237. package/dist/Spinner/Spinner.d.ts.map +1 -1
  238. package/dist/Spinner/Spinner.js +130 -78
  239. package/dist/StatusDot/StatusDot.d.ts.map +1 -1
  240. package/dist/StatusDot/StatusDot.js +6 -1
  241. package/dist/Stepper/Step.d.ts +140 -0
  242. package/dist/Stepper/Step.d.ts.map +1 -0
  243. package/dist/Stepper/Step.js +1045 -0
  244. package/dist/Stepper/StepStatus.d.ts +24 -0
  245. package/dist/Stepper/StepStatus.d.ts.map +1 -0
  246. package/dist/Stepper/StepStatus.js +1 -0
  247. package/dist/Stepper/Stepper.d.ts +93 -0
  248. package/dist/Stepper/Stepper.d.ts.map +1 -0
  249. package/dist/Stepper/Stepper.js +184 -0
  250. package/dist/Stepper/StepperContext.d.ts +41 -0
  251. package/dist/Stepper/StepperContext.d.ts.map +1 -0
  252. package/dist/Stepper/StepperContext.js +34 -0
  253. package/dist/Stepper/index.d.ts +8 -0
  254. package/dist/Stepper/index.d.ts.map +1 -0
  255. package/dist/Stepper/index.js +7 -0
  256. package/dist/Stepper/stepper.stylex.d.ts +15 -0
  257. package/dist/Stepper/stepper.stylex.d.ts.map +1 -0
  258. package/dist/Stepper/stepper.stylex.js +20 -0
  259. package/dist/Switch/Switch.d.ts.map +1 -1
  260. package/dist/Switch/Switch.js +8 -3
  261. package/dist/TabList/Tab.d.ts +15 -2
  262. package/dist/TabList/Tab.d.ts.map +1 -1
  263. package/dist/TabList/Tab.js +32 -7
  264. package/dist/TabList/TabList.d.ts +46 -7
  265. package/dist/TabList/TabList.d.ts.map +1 -1
  266. package/dist/TabList/TabList.js +353 -23
  267. package/dist/TabList/TabListContext.d.ts +11 -1
  268. package/dist/TabList/TabListContext.d.ts.map +1 -1
  269. package/dist/TabList/TabListContext.js +11 -2
  270. package/dist/TabList/TabMenu.d.ts.map +1 -1
  271. package/dist/TabList/TabMenu.js +3 -2
  272. package/dist/TabList/index.d.ts +2 -2
  273. package/dist/TabList/index.d.ts.map +1 -1
  274. package/dist/Table/BaseTable.d.ts.map +1 -1
  275. package/dist/Table/BaseTable.js +10 -2
  276. package/dist/Table/Table.d.ts.map +1 -1
  277. package/dist/Table/Table.js +3 -6
  278. package/dist/Table/TableRow.d.ts +16 -5
  279. package/dist/Table/TableRow.d.ts.map +1 -1
  280. package/dist/Table/TableRow.js +16 -5
  281. package/dist/Table/plugins/filtering/useTableFiltering.d.ts.map +1 -1
  282. package/dist/Table/plugins/filtering/useTableFiltering.js +73 -4
  283. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts +26 -0
  284. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  285. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +54 -11
  286. package/dist/Table/plugins/pagination/useTablePagination.d.ts +3 -0
  287. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  288. package/dist/Table/plugins/pagination/useTablePagination.js +21 -4
  289. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  290. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +7 -2
  291. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -1
  292. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +10 -3
  293. package/dist/Table/plugins/selection/useTableSelection.d.ts +16 -0
  294. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  295. package/dist/Table/plugins/selection/useTableSelection.js +43 -5
  296. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  297. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +3 -4
  298. package/dist/Table/types.d.ts +22 -4
  299. package/dist/Table/types.d.ts.map +1 -1
  300. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  301. package/dist/Table/useBaseTablePlugins.js +5 -0
  302. package/dist/Text/Text.d.ts.map +1 -1
  303. package/dist/Text/Text.js +6 -3
  304. package/dist/TextArea/TextArea.d.ts.map +1 -1
  305. package/dist/TextArea/TextArea.js +16 -10
  306. package/dist/TextInput/TextInput.d.ts.map +1 -1
  307. package/dist/TextInput/TextInput.js +3 -2
  308. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  309. package/dist/TimeInput/TimeInput.js +3 -2
  310. package/dist/Timestamp/Timestamp.d.ts +1 -1
  311. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  312. package/dist/Timestamp/Timestamp.js +15 -21
  313. package/dist/Toast/Toast.d.ts.map +1 -1
  314. package/dist/Toast/Toast.js +22 -7
  315. package/dist/Toast/ToastViewport.d.ts +1 -0
  316. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  317. package/dist/Toast/ToastViewport.js +71 -18
  318. package/dist/Token/Token.d.ts +1 -1
  319. package/dist/Token/Token.d.ts.map +1 -1
  320. package/dist/Token/Token.js +20 -9
  321. package/dist/Token/TokenLink.d.ts +1 -1
  322. package/dist/Token/TokenLink.d.ts.map +1 -1
  323. package/dist/Token/TokenLink.js +7 -4
  324. package/dist/Tokenizer/Tokenizer.d.ts +14 -1
  325. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  326. package/dist/Tokenizer/Tokenizer.js +33 -21
  327. package/dist/Tooltip/Tooltip.d.ts +5 -0
  328. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  329. package/dist/Tooltip/useTooltip.d.ts +5 -0
  330. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  331. package/dist/Tooltip/useTooltip.js +39 -24
  332. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  333. package/dist/TopNav/TopNavHeading.js +5 -4
  334. package/dist/TopNav/TopNavMegaMenu.d.ts +1 -1
  335. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  336. package/dist/TopNav/TopNavMegaMenu.js +31 -11
  337. package/dist/TopNav/TopNavMenu.d.ts +1 -1
  338. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  339. package/dist/TopNav/TopNavMenu.js +22 -7
  340. package/dist/TreeList/TreeList.d.ts +1 -1
  341. package/dist/TreeList/TreeList.d.ts.map +1 -1
  342. package/dist/TreeList/TreeList.js +7 -2
  343. package/dist/Typeahead/BaseTypeahead.d.ts +35 -0
  344. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  345. package/dist/Typeahead/BaseTypeahead.js +119 -37
  346. package/dist/Typeahead/Typeahead.d.ts +8 -1
  347. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  348. package/dist/Typeahead/Typeahead.js +5 -2
  349. package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
  350. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  351. package/dist/Typeahead/TypeaheadItem.js +7 -2
  352. package/dist/astryx.css +113 -11
  353. package/dist/hooks/index.d.ts +1 -0
  354. package/dist/hooks/index.d.ts.map +1 -1
  355. package/dist/hooks/index.js +1 -0
  356. package/dist/hooks/useFocusTrap.d.ts +7 -4
  357. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  358. package/dist/hooks/useFocusTrap.js +60 -95
  359. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  360. package/dist/hooks/useHotkeys.js +3 -18
  361. package/dist/hooks/useMergedRefs.d.ts +18 -0
  362. package/dist/hooks/useMergedRefs.d.ts.map +1 -0
  363. package/dist/hooks/useMergedRefs.js +25 -0
  364. package/dist/index.d.ts +1 -0
  365. package/dist/index.d.ts.map +1 -1
  366. package/dist/index.js +1 -0
  367. package/dist/theme/Theme.d.ts.map +1 -1
  368. package/dist/theme/Theme.js +46 -10
  369. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  370. package/dist/theme/derivedVarRegistry.js +24 -4
  371. package/dist/theme/generateThemeRules.d.ts +21 -0
  372. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  373. package/dist/theme/generateThemeRules.js +26 -0
  374. package/dist/theme/syntax/tokens.d.ts +1 -1
  375. package/dist/theme/syntax/tokens.d.ts.map +1 -1
  376. package/dist/theme/syntax/tokens.js +5 -2
  377. package/dist/utils/groupItems.d.ts +5 -1
  378. package/dist/utils/groupItems.d.ts.map +1 -1
  379. package/dist/utils/groupItems.js +9 -9
  380. package/dist/utils/index.d.ts +1 -1
  381. package/dist/utils/index.d.ts.map +1 -1
  382. package/dist/utils/isApplePlatform.d.ts +18 -0
  383. package/dist/utils/isApplePlatform.d.ts.map +1 -0
  384. package/dist/utils/isApplePlatform.js +37 -0
  385. package/dist/utils/sharedResizeObserver.d.ts.map +1 -1
  386. package/dist/utils/sharedResizeObserver.js +10 -1
  387. package/dist/utils/timeParser.d.ts +1 -1
  388. package/dist/utils/timeParser.d.ts.map +1 -1
  389. package/dist/utils/timeParser.js +26 -12
  390. package/locales/af-ZA.json +6 -6
  391. package/locales/ar-SA.json +6 -6
  392. package/locales/ca-ES.json +5 -5
  393. package/locales/cs-CZ.json +6 -6
  394. package/locales/da-DK.json +6 -6
  395. package/locales/de-DE.json +6 -6
  396. package/locales/el-GR.json +6 -6
  397. package/locales/en.json +163 -11
  398. package/locales/es-ES.json +5 -5
  399. package/locales/fi-FI.json +6 -6
  400. package/locales/fr-FR.json +6 -6
  401. package/locales/he-IL.json +6 -6
  402. package/locales/hu-HU.json +6 -6
  403. package/locales/it-IT.json +6 -6
  404. package/locales/ja-JP.json +6 -6
  405. package/locales/ko-KR.json +6 -6
  406. package/locales/nl-NL.json +6 -6
  407. package/locales/no-NO.json +6 -6
  408. package/locales/pl-PL.json +6 -6
  409. package/locales/pseudo.json +114 -0
  410. package/locales/pt-BR.json +6 -6
  411. package/locales/pt-PT.json +6 -6
  412. package/locales/ro-RO.json +6 -6
  413. package/locales/ru-RU.json +6 -6
  414. package/locales/sr-SP.json +6 -6
  415. package/locales/sv-SE.json +6 -6
  416. package/locales/tr-TR.json +6 -6
  417. package/locales/uk-UA.json +6 -6
  418. package/locales/vi-VN.json +6 -6
  419. package/locales/zh-CN.json +6 -6
  420. package/locales/zh-TW.json +6 -6
  421. package/package.json +8 -3
  422. package/src/AlertDialog/AlertDialog.doc.mjs +114 -30
  423. package/src/AlertDialog/AlertDialog.test.tsx +211 -2
  424. package/src/AlertDialog/AlertDialog.tsx +62 -20
  425. package/src/AppShell/AppShell.tsx +3 -2
  426. package/src/AspectRatio/AspectRatio.doc.mjs +6 -3
  427. package/src/AspectRatio/AspectRatio.test.tsx +57 -10
  428. package/src/AspectRatio/AspectRatio.tsx +40 -1
  429. package/src/Avatar/Avatar.doc.mjs +14 -3
  430. package/src/Avatar/Avatar.test.tsx +97 -0
  431. package/src/Avatar/Avatar.tsx +125 -52
  432. package/src/Avatar/index.ts +2 -2
  433. package/src/AvatarGroup/AvatarGroup.doc.mjs +8 -2
  434. package/src/AvatarGroup/AvatarGroup.test.tsx +24 -0
  435. package/src/AvatarGroup/AvatarGroup.tsx +15 -6
  436. package/src/AvatarGroup/AvatarGroupContext.ts +5 -3
  437. package/src/AvatarGroup/AvatarGroupOverflow.tsx +10 -4
  438. package/src/Banner/Banner.doc.mjs +22 -11
  439. package/src/Banner/Banner.test.tsx +177 -25
  440. package/src/Banner/Banner.tsx +93 -27
  441. package/src/BottomSheet/BottomSheet.test.tsx +123 -0
  442. package/src/BottomSheet/BottomSheet.tsx +17 -4
  443. package/src/BottomSheet/BottomSheetSwitcher.tsx +3 -2
  444. package/src/Breadcrumbs/BreadcrumbItem.tsx +9 -5
  445. package/src/Button/Button.doc.mjs +24 -1
  446. package/src/Button/Button.test.tsx +32 -4
  447. package/src/Button/Button.tsx +12 -5
  448. package/src/ButtonGroup/ButtonGroup.doc.mjs +10 -1
  449. package/src/ButtonGroup/ButtonGroup.test.tsx +404 -1
  450. package/src/ButtonGroup/ButtonGroup.tsx +23 -5
  451. package/src/Calendar/Calendar.doc.mjs +7 -6
  452. package/src/Calendar/Calendar.test.tsx +143 -0
  453. package/src/Calendar/Calendar.tsx +30 -26
  454. package/src/Calendar/getInitialFocusDate.test.ts +94 -0
  455. package/src/Calendar/getInitialFocusDate.ts +93 -0
  456. package/src/Calendar/styles.ts +49 -31
  457. package/src/Card/Card.doc.mjs +10 -7
  458. package/src/Card/Card.test.tsx +113 -19
  459. package/src/Card/Card.tsx +67 -89
  460. package/src/Card/index.ts +36 -1
  461. package/src/Carousel/Carousel.tsx +3 -2
  462. package/src/Chat/ChatComposerInput.tsx +5 -1
  463. package/src/Chat/ChatLayout.tsx +3 -2
  464. package/src/Chat/ChatMessageList.doc.mjs +9 -1
  465. package/src/Chat/ChatMessageList.test.tsx +46 -0
  466. package/src/Chat/ChatMessageList.tsx +28 -4
  467. package/src/Chat/ChatTokenizedText.tsx +17 -10
  468. package/src/Chat/useChatComposerTokens.ts +1 -1
  469. package/src/CheckboxInput/CheckboxInput.doc.mjs +2 -0
  470. package/src/CheckboxInput/CheckboxInput.test.tsx +15 -0
  471. package/src/CheckboxInput/CheckboxInput.tsx +8 -2
  472. package/src/ClickableCard/ClickableCard.tsx +3 -2
  473. package/src/CodeBlock/CodeBlock.doc.mjs +16 -4
  474. package/src/CodeBlock/CodeBlock.test.tsx +37 -0
  475. package/src/CodeBlock/CodeBlock.tsx +20 -4
  476. package/src/CodeBlock/highlightRanges.ts +1 -1
  477. package/src/CodeBlock/highlightStyles.ts +13 -13
  478. package/src/CommandPalette/CommandPaletteInput.tsx +3 -2
  479. package/src/CommandPalette/CommandPaletteItem.tsx +3 -2
  480. package/src/ComplexSelector/ComplexSelector.test.tsx +32 -0
  481. package/src/ComplexSelector/ComplexSelector.tsx +1 -3
  482. package/src/ContextMenu/ContextMenu.doc.mjs +5 -0
  483. package/src/ContextMenu/ContextMenu.tsx +4 -3
  484. package/src/DateInput/DateInput.doc.mjs +16 -0
  485. package/src/DateInput/DateInput.tsx +55 -2
  486. package/src/DateInput/DateInputTouch.test.tsx +146 -32
  487. package/src/DateInput/NativeDateField.test.tsx +635 -0
  488. package/src/DateInput/NativeDateField.tsx +641 -0
  489. package/src/DateInput/TouchDateField.tsx +108 -24
  490. package/src/DateInput/index.ts +1 -0
  491. package/src/DateInput/nativeDateSegments.ts +127 -0
  492. package/src/DateRangeInput/DateRangeInput.doc.mjs +2 -2
  493. package/src/DateRangeInput/DateRangeInput.test.tsx +22 -0
  494. package/src/DateRangeInput/DateRangeInput.tsx +4 -2
  495. package/src/DateTimeInput/DateTimeInput.doc.mjs +18 -0
  496. package/src/DateTimeInput/DateTimeInput.test.tsx +1088 -0
  497. package/src/DateTimeInput/DateTimeInput.tsx +556 -24
  498. package/src/DateTimeInput/index.ts +1 -0
  499. package/src/Dialog/Dialog.doc.mjs +8 -3
  500. package/src/Dialog/Dialog.test.tsx +429 -2
  501. package/src/Dialog/Dialog.tsx +133 -58
  502. package/src/DropdownMenu/DropdownMenu.doc.mjs +6 -0
  503. package/src/DropdownMenu/DropdownMenu.test.tsx +44 -10
  504. package/src/DropdownMenu/DropdownMenu.tsx +4 -13
  505. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +8 -5
  506. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +8 -11
  507. package/src/Field/Field.doc.mjs +13 -2
  508. package/src/Field/FieldLabel.test.tsx +29 -0
  509. package/src/Field/FieldLabel.tsx +7 -0
  510. package/src/Field/InputClearButton.test.tsx +15 -1
  511. package/src/Field/InputClearButton.tsx +28 -4
  512. package/src/FileInput/FileInput.tsx +3 -2
  513. package/src/Heading/Heading.test.tsx +23 -1
  514. package/src/Heading/Heading.tsx +6 -3
  515. package/src/HoverCard/HoverCard.doc.mjs +13 -2
  516. package/src/HoverCard/HoverCard.test.tsx +154 -4
  517. package/src/HoverCard/HoverCard.tsx +143 -3
  518. package/src/HoverCard/useHoverCard.tsx +104 -33
  519. package/src/Indicator/CheckboxIndicator.tsx +14 -8
  520. package/src/Indicator/Indicator.doc.mjs +2 -0
  521. package/src/Item/Item.tsx +5 -4
  522. package/src/Kbd/Kbd.tsx +2 -19
  523. package/src/Layer/LayerDepthContext.tsx +62 -0
  524. package/src/Layer/gestureCounter.ts +57 -0
  525. package/src/Layer/index.ts +9 -0
  526. package/src/Layer/layerDismissalFamilies.test.tsx +359 -0
  527. package/src/Layer/layerDismissalInvariants.test.tsx +417 -0
  528. package/src/Layer/layerStack.ts +362 -0
  529. package/src/Layer/useLayer.test.tsx +324 -15
  530. package/src/Layer/useLayer.tsx +164 -8
  531. package/src/Layer/useLayerDismissal.test.tsx +469 -0
  532. package/src/Layer/useLayerDismissal.ts +173 -0
  533. package/src/Layout/Layout.doc.mjs +2 -2
  534. package/src/Layout/__tests__/edgeCompensation.test.tsx +1 -1
  535. package/src/Lightbox/Lightbox.doc.mjs +12 -0
  536. package/src/Lightbox/Lightbox.tsx +124 -102
  537. package/src/Link/Link.doc.mjs +39 -0
  538. package/src/List/List.test.tsx +38 -0
  539. package/src/List/List.tsx +8 -1
  540. package/src/Markdown/Markdown.doc.mjs +5 -0
  541. package/src/Markdown/Markdown.test.tsx +37 -0
  542. package/src/Markdown/Markdown.tsx +16 -5
  543. package/src/Markdown/incremental.test.ts +16 -2
  544. package/src/Markdown/index.ts +1 -0
  545. package/src/Markdown/parser.perf.test.ts +96 -1
  546. package/src/Markdown/parser.test.ts +178 -2
  547. package/src/Markdown/parser.ts +253 -32
  548. package/src/MetadataList/MetadataList.test.tsx +75 -2
  549. package/src/MetadataList/MetadataListItem.tsx +5 -2
  550. package/src/MobileNav/MobileNav.tsx +62 -36
  551. package/src/MoreMenu/MoreMenu.doc.mjs +7 -1
  552. package/src/MultiSelector/MultiSelector.doc.mjs +49 -1
  553. package/src/MultiSelector/MultiSelector.test.tsx +311 -0
  554. package/src/MultiSelector/MultiSelector.tsx +151 -15
  555. package/src/MultiSelector/hooks.ts +9 -2
  556. package/src/MultiSelector/index.ts +1 -0
  557. package/src/NavIcon/NavIcon.doc.mjs +8 -2
  558. package/src/NavIcon/NavIcon.test.tsx +9 -0
  559. package/src/NavIcon/NavIcon.tsx +5 -1
  560. package/src/NavMenu/NavHeadingMenu.test.tsx +30 -0
  561. package/src/NavMenu/NavHeadingMenu.tsx +9 -5
  562. package/src/NumberInput/NumberInput.doc.mjs +31 -7
  563. package/src/NumberInput/NumberInput.test.tsx +436 -8
  564. package/src/NumberInput/NumberInput.tsx +62 -114
  565. package/src/NumberInput/numberInputCommit.test.ts +72 -0
  566. package/src/NumberInput/numberInputCommit.ts +103 -0
  567. package/src/NumberInput/numberParser.docblock.test.ts +234 -0
  568. package/src/NumberInput/numberParser.test.ts +222 -0
  569. package/src/NumberInput/numberParser.ts +403 -0
  570. package/src/Outline/Outline.tsx +3 -2
  571. package/src/OverflowList/OverflowList.tsx +3 -2
  572. package/src/Overlay/Overlay.tsx +3 -2
  573. package/src/Pagination/Pagination.test.tsx +3 -2
  574. package/src/Popover/Popover.test.tsx +14 -0
  575. package/src/Popover/Popover.tsx +1 -9
  576. package/src/Popover/usePopover.tsx +57 -32
  577. package/src/PowerSearch/PowerSearch.doc.mjs +160 -28
  578. package/src/PowerSearch/PowerSearch.test.tsx +240 -1
  579. package/src/PowerSearch/PowerSearch.tsx +34 -5
  580. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +288 -0
  581. package/src/PowerSearch/PowerSearchEditPopover.tsx +29 -2
  582. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +111 -1
  583. package/src/PowerSearch/PowerSearchValueEditor.tsx +124 -10
  584. package/src/PowerSearch/types.ts +2 -0
  585. package/src/PowerSearch/usePowerSearchSource.test.ts +82 -2
  586. package/src/PowerSearch/usePowerSearchSource.ts +13 -3
  587. package/src/ProgressBar/ProgressBar.doc.mjs +42 -6
  588. package/src/ProgressBar/ProgressBar.test.tsx +24 -0
  589. package/src/ProgressBar/ProgressBar.tsx +28 -8
  590. package/src/Resizable/ResizeHandle.tsx +3 -2
  591. package/src/SegmentedControl/SegmentedControl.tsx +3 -2
  592. package/src/SelectableCard/SelectableCard.doc.mjs +3 -0
  593. package/src/SelectableCard/SelectableCard.tsx +19 -2
  594. package/src/Selector/Selector.doc.mjs +26 -1
  595. package/src/Selector/Selector.test.tsx +346 -1
  596. package/src/Selector/Selector.tsx +148 -21
  597. package/src/Selector/hooks.ts +17 -2
  598. package/src/SideNav/SideNav.doc.mjs +6 -6
  599. package/src/SideNav/SideNav.tsx +4 -2
  600. package/src/SideNav/SideNavHeading.tsx +12 -12
  601. package/src/SideNav/SideNavItem.tsx +4 -2
  602. package/src/Slider/Slider.test.tsx +106 -1
  603. package/src/Slider/Slider.tsx +55 -3
  604. package/src/Spinner/Spinner.test.tsx +147 -1
  605. package/src/Spinner/Spinner.tsx +120 -101
  606. package/src/StatusDot/StatusDot.doc.mjs +8 -2
  607. package/src/StatusDot/StatusDot.test.tsx +9 -0
  608. package/src/StatusDot/StatusDot.tsx +7 -1
  609. package/src/Stepper/Step.doc.mjs +98 -0
  610. package/src/Stepper/Step.tsx +1638 -0
  611. package/src/Stepper/StepStatus.ts +26 -0
  612. package/src/Stepper/Stepper.doc.mjs +370 -0
  613. package/src/Stepper/Stepper.test.tsx +1024 -0
  614. package/src/Stepper/Stepper.tsx +258 -0
  615. package/src/Stepper/StepperContext.ts +71 -0
  616. package/src/Stepper/index.ts +18 -0
  617. package/src/Stepper/stepper.stylex.ts +19 -0
  618. package/src/Switch/Switch.doc.mjs +2 -0
  619. package/src/Switch/Switch.test.tsx +10 -0
  620. package/src/Switch/Switch.tsx +6 -2
  621. package/src/TabList/Tab.doc.mjs +13 -1
  622. package/src/TabList/Tab.tsx +67 -7
  623. package/src/TabList/TabList.doc.mjs +22 -6
  624. package/src/TabList/TabList.test.tsx +559 -0
  625. package/src/TabList/TabList.tsx +490 -22
  626. package/src/TabList/TabListContext.ts +13 -2
  627. package/src/TabList/TabMenu.tsx +3 -2
  628. package/src/TabList/index.ts +6 -2
  629. package/src/Table/BaseTable.tsx +12 -4
  630. package/src/Table/Table.doc.mjs +9 -2
  631. package/src/Table/Table.test.tsx +132 -14
  632. package/src/Table/Table.tsx +3 -8
  633. package/src/Table/TableBody.doc.mjs +40 -0
  634. package/src/Table/TableFooter.doc.mjs +40 -0
  635. package/src/Table/TableHeader.doc.mjs +40 -0
  636. package/src/Table/TableRow.tsx +16 -5
  637. package/src/Table/plugins/filtering/useTableFiltering.test.tsx +44 -6
  638. package/src/Table/plugins/filtering/useTableFiltering.tsx +100 -4
  639. package/src/Table/plugins/groupedRows/useTableGroupedRows-perf.test.tsx +112 -0
  640. package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +100 -0
  641. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +67 -12
  642. package/src/Table/plugins/pagination/useTablePagination.test.tsx +34 -4
  643. package/src/Table/plugins/pagination/useTablePagination.tsx +35 -3
  644. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +9 -2
  645. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +13 -7
  646. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +12 -3
  647. package/src/Table/plugins/selection/useTableSelection.test.tsx +138 -0
  648. package/src/Table/plugins/selection/useTableSelection.tsx +63 -7
  649. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +1 -2
  650. package/src/Table/types.ts +22 -4
  651. package/src/Table/useBaseTablePlugins.ts +5 -0
  652. package/src/Table/useTableGroupedRows.doc.mjs +5 -4
  653. package/src/Table/useTablePagination.doc.mjs +7 -0
  654. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  655. package/src/Table/useTableSelection.doc.mjs +31 -0
  656. package/src/Text/Text.test.tsx +67 -1
  657. package/src/Text/Text.tsx +6 -3
  658. package/src/TextArea/TextArea.doc.mjs +12 -2
  659. package/src/TextArea/TextArea.test.tsx +11 -0
  660. package/src/TextArea/TextArea.tsx +37 -24
  661. package/src/TextInput/TextInput.doc.mjs +10 -0
  662. package/src/TextInput/TextInput.tsx +3 -2
  663. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  664. package/src/TimeInput/TimeInput.tsx +2 -2
  665. package/src/Timestamp/Timestamp.test.tsx +58 -0
  666. package/src/Timestamp/Timestamp.tsx +12 -10
  667. package/src/Toast/Toast.doc.mjs +26 -12
  668. package/src/Toast/Toast.tsx +28 -4
  669. package/src/Toast/ToastViewport.test.tsx +536 -8
  670. package/src/Toast/ToastViewport.tsx +81 -12
  671. package/src/Token/Token.test.tsx +95 -0
  672. package/src/Token/Token.tsx +15 -10
  673. package/src/Token/TokenLink.tsx +14 -5
  674. package/src/Tokenizer/Tokenizer.doc.mjs +43 -3
  675. package/src/Tokenizer/Tokenizer.test.tsx +161 -0
  676. package/src/Tokenizer/Tokenizer.tsx +53 -24
  677. package/src/Toolbar/Toolbar.doc.mjs +5 -0
  678. package/src/Tooltip/Tooltip.doc.mjs +5 -0
  679. package/src/Tooltip/Tooltip.test.tsx +32 -0
  680. package/src/Tooltip/Tooltip.tsx +5 -0
  681. package/src/Tooltip/useTooltip.tsx +47 -24
  682. package/src/TopNav/TopNavHeading.tsx +5 -4
  683. package/src/TopNav/TopNavMegaMenu.test.tsx +104 -0
  684. package/src/TopNav/TopNavMegaMenu.tsx +47 -5
  685. package/src/TopNav/TopNavMenu.test.tsx +133 -1
  686. package/src/TopNav/TopNavMenu.tsx +38 -7
  687. package/src/TreeList/TreeList.test.tsx +49 -0
  688. package/src/TreeList/TreeList.tsx +7 -2
  689. package/src/Typeahead/BaseTypeahead.doc.mjs +24 -0
  690. package/src/Typeahead/BaseTypeahead.tsx +181 -39
  691. package/src/Typeahead/Typeahead.doc.mjs +11 -0
  692. package/src/Typeahead/Typeahead.test.tsx +203 -0
  693. package/src/Typeahead/Typeahead.tsx +12 -2
  694. package/src/Typeahead/TypeaheadItem.test.tsx +43 -0
  695. package/src/Typeahead/TypeaheadItem.tsx +8 -1
  696. package/src/hooks/index.ts +2 -0
  697. package/src/hooks/useAnnounce.doc.mjs +96 -0
  698. package/src/hooks/useContainerReveal.doc.mjs +2 -2
  699. package/src/hooks/useDevWarning.doc.mjs +96 -0
  700. package/src/hooks/useFocusTrap.test.tsx +14 -0
  701. package/src/hooks/useFocusTrap.ts +62 -111
  702. package/src/hooks/useFocusTrapEscapeShim.test.tsx +165 -0
  703. package/src/hooks/useHotkeys.ts +3 -18
  704. package/src/hooks/useIndicatorFocusRing.doc.mjs +104 -0
  705. package/src/hooks/useInputStatusIcon.doc.mjs +142 -0
  706. package/src/hooks/useInteractiveRole.doc.mjs +119 -0
  707. package/src/hooks/useLongPress.doc.mjs +123 -0
  708. package/src/hooks/useMergedRefs.doc.mjs +79 -0
  709. package/src/hooks/useMergedRefs.test.tsx +62 -0
  710. package/src/hooks/useMergedRefs.ts +36 -0
  711. package/src/hooks/useTypeahead.doc.mjs +153 -0
  712. package/src/index.ts +1 -0
  713. package/src/raw-import.d.ts +14 -0
  714. package/src/theme/MediaTheme.doc.mjs +5 -5
  715. package/src/theme/Theme.test.tsx +104 -2
  716. package/src/theme/Theme.tsx +49 -14
  717. package/src/theme/defineTheme.test.ts +23 -0
  718. package/src/theme/derivedVarRegistry.test.ts +26 -3
  719. package/src/theme/derivedVarRegistry.ts +23 -4
  720. package/src/theme/generateThemeRules.test.ts +99 -1
  721. package/src/theme/generateThemeRules.ts +28 -0
  722. package/src/theme/syntax/tokens.ts +5 -2
  723. package/src/utils/groupItems.ts +12 -6
  724. package/src/utils/index.ts +1 -1
  725. package/src/utils/isApplePlatform.test.ts +61 -0
  726. package/src/utils/isApplePlatform.ts +37 -0
  727. package/src/utils/sharedResizeObserver.ts +10 -2
  728. package/src/utils/timeParser.test.ts +6 -1
  729. package/src/utils/timeParser.ts +35 -13
  730. package/src/__tests__/inputWidthContract.test.tsx +0 -69
@@ -0,0 +1,1638 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file Step.tsx
7
+ * @input Uses React, stylex, theme tokens (including the motion duration token
8
+ * the connector fill animates on), StepperContext
9
+ * @output Exports Step component and StepProps
10
+ * @position Individual step item; used inside Stepper
11
+ *
12
+ * Every connector this file draws — the separated progress bars and the
13
+ * on-track segments alike — paints its accent fill as a scaled ::before rather
14
+ * than by swapping a background color, so a step the flow advances into grows
15
+ * its line along the track. See the CONNECTOR FILL block in the styles below
16
+ * for the axis/RTL/reduced-motion rules that go with it.
17
+ *
18
+ * Exactly one change animates: advancing a single step. Going back, jumping
19
+ * forward by more than one, and mounting mid-flow all land at once. The rule
20
+ * and the reasoning behind it sit above the styles; the arithmetic that keeps
21
+ * the one animated span reading as a single front — an on-track span is drawn
22
+ * by two steps, three where a content slot splits it — is in the component.
23
+ * Nothing about it is public API: Stepper hands each Step the step it came
24
+ * from through context, and the rest is derived.
25
+ *
26
+ * SYNC: When modified, update these files to stay in sync:
27
+ * - /packages/core/src/Stepper/Stepper.doc.mjs
28
+ * - /packages/core/src/Stepper/Stepper.test.tsx
29
+ * - /packages/core/src/Stepper/index.ts
30
+ * - /apps/storybook/stories/Stepper.stories.tsx
31
+ * - /packages/cli/assets/templates/blocks/components/Stepper/ (showcase blocks)
32
+ * - /packages/cli/assets/templates/blocks/components/Step/ (showcase blocks)
33
+ */
34
+
35
+ import {useEffect, type ReactNode} from 'react';
36
+ import * as stylex from '@stylexjs/stylex';
37
+
38
+ import {
39
+ colorVars,
40
+ spacingVars,
41
+ radiusVars,
42
+ fontWeightVars,
43
+ typeScaleVars,
44
+ textSizeVars,
45
+ durationVars,
46
+ easeVars,
47
+ } from '../theme/tokens.stylex';
48
+ import {
49
+ focusOutlineStyles,
50
+ mergeProps,
51
+ isRenderable,
52
+ themeProps,
53
+ } from '../utils';
54
+ import type {BaseProps} from '../BaseProps';
55
+ import {Icon} from '../Icon';
56
+ import {VisuallyHidden} from '../VisuallyHidden';
57
+ import {useTranslator} from '../i18n';
58
+ import {useStepperContext} from './StepperContext';
59
+ import {stepMarker} from './stepper.stylex';
60
+ import type {StepStatus} from './StepStatus';
61
+
62
+ /**
63
+ * Built-in indicator presets. Anything other than these strings passed to
64
+ * `indicator` is treated as a custom ReactNode (e.g. an `<Icon />`).
65
+ * - 'auto': numbered badge for not-yet-reached steps, a check once completed
66
+ * (default)
67
+ * - 'number': always a numbered badge
68
+ * - 'none': no indicator — just the progress bar and label
69
+ */
70
+ export type StepIndicatorPreset = 'auto' | 'number' | 'none';
71
+ export type StepDensity = 'compact' | 'balanced' | 'spacious';
72
+
73
+ export interface StepProps extends BaseProps<HTMLLIElement> {
74
+ /** Ref forwarded to the root element */
75
+ ref?: React.Ref<HTMLLIElement>;
76
+ /**
77
+ * Zero-based index of this step. Used to derive progress (completed /
78
+ * active / not-started) relative to the parent's `activeStep`.
79
+ */
80
+ step: number;
81
+ /**
82
+ * Step label text.
83
+ */
84
+ label: string;
85
+ /**
86
+ * Optional description shown below the label.
87
+ */
88
+ description?: string;
89
+ /**
90
+ * Content rendered below the label and description. Useful in vertical
91
+ * steppers to show form fields or detailed content for each step.
92
+ */
93
+ children?: ReactNode;
94
+
95
+ /**
96
+ * Semantic status for the step, mapped to the global Astryx semantic tokens
97
+ * (`accent`, `success`, `warning`, `error`). In the default `auto` indicator
98
+ * mode it sets both the indicator color and a matching glyph: `success` shows
99
+ * a green check-circle, `warning`/`error` show the shared Input status icons.
100
+ * `accent` is color-only. The current (in-progress) step always keeps its
101
+ * current-step indicator regardless of `status`. Never recolors the
102
+ * connector/track.
103
+ *
104
+ * Because the indicator glyphs are decorative (aria-hidden), the status also
105
+ * reaches assistive technology as text: visually hidden "completed" /
106
+ * "warning" / "error" next to the label, and composed into the accessible
107
+ * name of clickable steps.
108
+ */
109
+ status?: StepStatus;
110
+ /**
111
+ * Disable interaction for this step.
112
+ * @default false
113
+ */
114
+ isDisabled?: boolean;
115
+ /**
116
+ * Marks the step as optional, appending an "Optional" affordance after the
117
+ * label.
118
+ * @default false
119
+ */
120
+ isOptional?: boolean;
121
+ /**
122
+ * Trailing content rendered at the end of the label row (e.g. a timestamp
123
+ * or status chip).
124
+ */
125
+ endContent?: ReactNode;
126
+ /**
127
+ * What to show as the step indicator. Accepts a preset string or any
128
+ * ReactNode:
129
+ * - 'auto': numbered badge until completed, then a check (default)
130
+ * - 'number': always a numbered badge
131
+ * - 'none': no indicator, just the bar + label
132
+ * - ReactNode: any custom icon or element to render as the indicator
133
+ * @default 'auto'
134
+ */
135
+ indicator?: StepIndicatorPreset | ReactNode;
136
+ /**
137
+ * Controls vertical padding of the step. Falls back to the stepper-level
138
+ * density when unset.
139
+ * - 'compact': minimal padding (4px block)
140
+ * - 'balanced': default (8px block)
141
+ * - 'spacious': generous (12px block, 12px inline)
142
+ */
143
+ density?: StepDensity;
144
+ }
145
+
146
+ // --- Default progress icons (16px) ---
147
+ //
148
+ // The `completed` progress state and the current-step ring are drawn as local
149
+ // <svg> glyphs rather than sourced from the Icon registry. This is a deliberate
150
+ // exception to the "glyphs come from the registry" convention (audit rule T17),
151
+ // for two reasons:
152
+ //
153
+ // 1. `CurrentIcon` (a dot inside a ring) has no registry equivalent — it is a
154
+ // progress affordance, not a general-purpose icon.
155
+ // 2. `CheckCircleIcon` (a filled circle with a check) is intentionally NOT the
156
+ // registry `success` glyph, even though the two look similar. They mean
157
+ // different things and must stay independently restyleable: `completed`
158
+ // marks *progress through the sequence* (every step you've passed), while
159
+ // the semantic `success` status marks a step's *outcome*. A stepper can
160
+ // show a completed step that also carries a `warning`/`error` status, so
161
+ // collapsing the two glyphs would conflate progress with outcome. The
162
+ // semantic `success`/`warning`/`error` STATUS glyphs DO defer to the themed
163
+ // registry (see the status branch below), so those share one visual
164
+ // language; only the progress marks are local.
165
+ //
166
+ // Both glyphs paint via `currentColor`, so the indicator's color is still
167
+ // controlled by tokens on the wrapper (never hardcoded), and the wrapper
168
+ // carries the `astryx-step-indicator` theme target.
169
+
170
+ /** Filled circle with a check — shown for a completed step in 'auto' mode. */
171
+ function CheckCircleIcon() {
172
+ return (
173
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
174
+ <circle cx="8" cy="8" r="8" fill="currentColor" />
175
+ <path
176
+ d="M4.75 8.25 7 10.5l4.25-4.5"
177
+ stroke={colorVars['--color-background-surface']}
178
+ strokeWidth="1.75"
179
+ strokeLinecap="round"
180
+ strokeLinejoin="round"
181
+ />
182
+ </svg>
183
+ );
184
+ }
185
+
186
+ /** Filled dot in a ring — shown for the active step in 'auto' mode. */
187
+ function CurrentIcon() {
188
+ return (
189
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
190
+ <circle cx="8" cy="8" r="7" stroke="currentColor" strokeWidth="2" />
191
+ <circle cx="8" cy="8" r="4" fill="currentColor" />
192
+ </svg>
193
+ );
194
+ }
195
+
196
+ // --- Styles ---
197
+
198
+ const BAR_WIDTH = spacingVars['--spacing-1'];
199
+ // Every indicator — check, ring, custom icon, number badge — occupies the same
200
+ // box, so the `auto` mode swapping a number for a check as a step completes
201
+ // never nudges the label or the track.
202
+ const INDICATOR_SIZE = spacingVars['--spacing-4'];
203
+ // Indicator (16px) + the label-row gap (8px). The indent that lines a
204
+ // description or a content slot up with the label above it.
205
+ const INDICATOR_GUTTER = spacingVars['--spacing-6'];
206
+
207
+ // --- Connector fill motion ---
208
+ //
209
+ // One change animates: advancing exactly one step. Going back, jumping
210
+ // forward by more than one, and mounting mid-flow all apply at once.
211
+ //
212
+ // Animating the others was tried and read worse than not animating them. A
213
+ // retreat is the clearest case: run forward, the transition ends with a bar
214
+ // that is nearly full, and nearly full is indistinguishable from full, so the
215
+ // eye calls it arrived well before it settles. Run backward, the same curve
216
+ // ends with a shrinking stub of accent still on the track, and a remnant that
217
+ // is still there reads unmistakably as not finished. Identical arithmetic in
218
+ // both directions — measurably so — and the backward one still feels slow,
219
+ // because the tail is what is visible rather than what is hidden. No easing
220
+ // fixes that; only not animating it does. A multi-step jump fails differently:
221
+ // it is a navigation rather than a progression, and sweeping a front across
222
+ // four segments makes the user sit through a journey they asked to skip.
223
+ //
224
+ // What is left is the one gesture where the movement carries the meaning: the
225
+ // flow went forward one step, and the line grows to show it. That span takes
226
+ // the medium band — a fill crossing a whole segment is a spatial change, not a
227
+ // micro-interaction, so per `astryx docs motion` it sits above the fast band
228
+ // reserved for high-frequency states like hover.
229
+ const FILL_SPAN_TIME = durationVars['--duration-medium'];
230
+
231
+ // Share of one node-to-node span owned by the segment *arriving* at the far
232
+ // node; the segment leaving the near node takes the rest. On-track spans are
233
+ // drawn by two steps (see the ON-TRACK section), and the time has to be split
234
+ // the way the length is or the two halves sweep at visibly different speeds.
235
+ //
236
+ // Horizontal: both halves are `flex: 1` inside equal-width steps, so an even
237
+ // split is the proportional one.
238
+ //
239
+ // Vertical: the halves are nothing like equal, and no constant can be right
240
+ // for all of them. The arriving half is otSegLeadV, a cap fixed at one density
241
+ // space; the leaving half is otSegFlexV, grown to whatever is left of the step
242
+ // row, so its length turns on label and description lengths this file cannot
243
+ // see. Measured in the browser the cap is 40% of a plain balanced span (8 of
244
+ // 20px) but 21% once the step carries a description (8 of 38px). 0.3 is the
245
+ // value that minimises the worse of those two: it leaves the cap ~1.6x off the
246
+ // rail's speed either way, where tuning to one case puts the other past 2x.
247
+ // Matching each case exactly would mean measuring the rendered rail, a layout
248
+ // read this system avoids.
249
+ const OT_ARRIVAL_SHARE_H = 0.5;
250
+ const OT_ARRIVAL_SHARE_V = 0.3;
251
+ // A vertical step carrying a content slot draws a third segment down the side
252
+ // of it (otContentSegV) which continues the leaving half past the row, so the
253
+ // leaving half's time splits again. A slot runs several times the rail left
254
+ // above it — 34px of rail to 108px of slot in the spacious content story — and
255
+ // this is the middle of that range. Matching it exactly would mean measuring
256
+ // the rendered slot; the ordering is what keeps the front contiguous, and this
257
+ // keeps its speed in the right neighbourhood.
258
+ const OT_RAIL_SHARE_OF_LEAVING = 0.25;
259
+
260
+ /** Scale a CSS `<time>` by a unitless factor, collapsing the trivial cases. */
261
+ function fillTimeSlice(time: string, factor: number): string {
262
+ if (factor <= 0) {
263
+ return '0s';
264
+ }
265
+ if (factor === 1) {
266
+ return time;
267
+ }
268
+ // Rounded because the factors are sums of shares, and float noise would
269
+ // otherwise reach the emitted custom property as `calc(... * 0.7000000001)`.
270
+ return `calc(${time} * ${Number(factor.toFixed(4))})`;
271
+ }
272
+
273
+ const styles = stylex.create({
274
+ // ===================== VERTICAL LAYOUT =====================
275
+ verticalRoot: {
276
+ display: 'flex',
277
+ flexDirection: 'row',
278
+ alignItems: 'stretch',
279
+ position: 'relative',
280
+ gap: spacingVars['--spacing-0-5'],
281
+ },
282
+
283
+ // 4px progress bar segment. Its filled/unfilled paint comes from the shared
284
+ // connector styles below, not from here.
285
+ verticalBar: {
286
+ width: BAR_WIDTH,
287
+ borderRadius: radiusVars['--radius-full'],
288
+ flexShrink: 0,
289
+ alignSelf: 'stretch',
290
+ },
291
+
292
+ // Step body — the selectable area
293
+ verticalBody: {
294
+ display: 'flex',
295
+ flexDirection: 'column',
296
+ flex: 1,
297
+ },
298
+
299
+ // ===================== HORIZONTAL LAYOUT =====================
300
+ horizontalStep: {
301
+ display: 'flex',
302
+ flexDirection: 'column',
303
+ // Stretch, not flex-start: a flex-start child is sized to fit-content,
304
+ // which is floored at its own min-content and would spill the label past
305
+ // the step's slice of the track and into the next step. Stretched, the
306
+ // label row is exactly as wide as the step and the label wraps inside it.
307
+ alignItems: 'stretch',
308
+ flex: 1,
309
+ // `flex: 1` alone does not make the steps equal: a flex item's default
310
+ // `min-width: auto` floors it at its own min-content width, so a step
311
+ // labelled "Integrations" claims a wider slice of the track than one
312
+ // labelled "Team". Zeroing the floor lets the even distribution actually
313
+ // apply, which is what keeps every progress segment the same width.
314
+ minWidth: 0,
315
+ // density padding applied via density styles below
316
+ },
317
+
318
+ // Full-width progress bar segment sitting above each horizontal step.
319
+ // Each step owns its own segment (filled from its derived progress) so the
320
+ // parent never has to introspect children to build the bar.
321
+ horizontalBar: {
322
+ width: '100%',
323
+ height: BAR_WIDTH,
324
+ borderRadius: radiusVars['--radius-full'],
325
+ flexShrink: 0,
326
+ marginBlockEnd: spacingVars['--spacing-0-5'],
327
+ },
328
+
329
+ // ===================== CONNECTOR FILL =====================
330
+ // Shared by every connector the four layout combinations draw: the separated
331
+ // bars (verticalBar, horizontalBar) and the on-track segments (otSegBaseV,
332
+ // otSegH, otContentSegV).
333
+ //
334
+ // The segment element is the *unfilled* track and never changes color; the
335
+ // accent fill is a ::before scaled along the track axis. Advancing a step
336
+ // grows it out of the segment's leading edge, so the line reads as progress
337
+ // travelling rather than a bar changing state. The obvious alternative,
338
+ // transitioning `background-color` between accent and border on the segment
339
+ // itself, lights the whole segment at once: a flash, with no direction in
340
+ // it. Going the other way the scale still returns to zero, but instantly —
341
+ // only a single step forward is given a duration to cross (see the motion
342
+ // block above).
343
+ //
344
+ // A pseudo-element rather than a real child element, because the connector is
345
+ // emitted from seven places and several of them size themselves in ways a
346
+ // child would have to re-derive (otSegFlexV grows, otSegLeadV is fixed,
347
+ // otContentSegV is absolutely positioned). ::before inherits the box for
348
+ // free, so one style covers all of them.
349
+ //
350
+ // Mount is deliberately not animated, and falls out of how transitions work:
351
+ // interpolation needs a previous computed value and a freshly rendered
352
+ // element has none. A stepper that opens on step 3 paints the segments behind
353
+ // it filled, instantly, instead of playing its whole history back.
354
+ connectorTrack: {
355
+ position: 'relative',
356
+ backgroundColor: colorVars['--color-border'],
357
+ '::before': {
358
+ content: '""',
359
+ position: 'absolute',
360
+ inset: 0,
361
+ // Follows whatever the segment itself rounds to: the full pill of a
362
+ // separated bar, the square ends the on-track segments need in order to
363
+ // stack into one continuous line.
364
+ borderRadius: 'inherit',
365
+ backgroundColor: colorVars['--color-accent'],
366
+ transitionProperty: 'transform',
367
+ // Resting duration, for a segment moving on its own. connectorTiming
368
+ // below replaces it on every connector — each one is handed either its
369
+ // slice of the animated span or a zero — so this is the fallback that
370
+ // applies if a connector is ever rendered without it.
371
+ transitionDuration: {
372
+ default: FILL_SPAN_TIME,
373
+ '@media (prefers-reduced-motion: reduce)': '0s',
374
+ },
375
+ // Linear, deliberately, and not the `--ease-standard` the rest of the
376
+ // system transitions on. A node-to-node span is not always one element:
377
+ // in the on-track layouts the segment leaving one node and the segment
378
+ // arriving at the next draw it between them, and a vertical step with a
379
+ // content slot splits it three ways. A curve applied per segment
380
+ // restarts its deceleration at every seam, so the front lurches, stalls
381
+ // at the joint, and the joint becomes the most visible thing in the
382
+ // animation — the fill stops reading as one line growing and starts
383
+ // reading as two pieces taking turns. Linear is the only timing that
384
+ // stitches into a single constant-speed front. The separated bars run
385
+ // linear too: a lone segment gives up very little by it, and the two
386
+ // layouts should not animate with different character.
387
+ transitionTimingFunction: 'linear',
388
+ },
389
+ },
390
+ // This segment's slice of the animated span, or a pair of zeros for a
391
+ // segment that is not part of it (see the motion block above the styles).
392
+ // Dynamic because which segments animate, and over what part of the span,
393
+ // depends on where the active step just moved from — known only at render.
394
+ // StyleX routes the values through a custom property, so these are still
395
+ // generated rules and not inline styles.
396
+ //
397
+ // Restates the reduced-motion escape connectorTrack declares rather than
398
+ // relying on it: a StyleX style that redefines a property replaces the
399
+ // earlier definition wholesale, conditions included. Without it a
400
+ // reduced-motion user would get an on-track span as two instant snaps spaced
401
+ // apart by the delay — motion smuggled back in as timing. Zeroing the delay
402
+ // alongside the duration lands the whole span at once instead.
403
+ connectorTiming: (duration: string, delay: string) => ({
404
+ '::before': {
405
+ transitionDuration: {
406
+ default: duration,
407
+ '@media (prefers-reduced-motion: reduce)': '0s',
408
+ },
409
+ transitionDelay: {
410
+ default: delay,
411
+ '@media (prefers-reduced-motion: reduce)': '0s',
412
+ },
413
+ },
414
+ }),
415
+ // Vertical track: progress runs down the block axis under both LTR and RTL
416
+ // (direction only mirrors the inline axis), so the growth anchors at the
417
+ // physical top either way.
418
+ connectorFillV: {
419
+ '::before': {transformOrigin: 'center top', transform: 'scaleY(1)'},
420
+ },
421
+ connectorEmptyV: {
422
+ '::before': {transformOrigin: 'center top', transform: 'scaleY(0)'},
423
+ },
424
+ // Horizontal track: progress runs along the reading direction, so the growth
425
+ // anchors at the inline-start edge — physically left in LTR, right in RTL.
426
+ // `transform-origin` accepts no logical keywords, so the flip is spelled out
427
+ // with the same `:is([dir="rtl"] *)` hook Switch's thumb travel and the
428
+ // shared rtlStyles.mirror use. Stated on both states rather than hoisted onto
429
+ // connectorTrack, which is axis-agnostic and shared with the vertical
430
+ // segments.
431
+ connectorFillH: {
432
+ '::before': {
433
+ transformOrigin: {
434
+ default: 'left center',
435
+ ':is([dir="rtl"] *)': 'right center',
436
+ },
437
+ transform: 'scaleX(1)',
438
+ },
439
+ },
440
+ connectorEmptyH: {
441
+ '::before': {
442
+ transformOrigin: {
443
+ default: 'left center',
444
+ ':is([dir="rtl"] *)': 'right center',
445
+ },
446
+ transform: 'scaleX(0)',
447
+ },
448
+ },
449
+
450
+ // ===================== SHARED =====================
451
+
452
+ // Icon + Label in one row
453
+ iconLabelRow: {
454
+ display: 'flex',
455
+ flexDirection: 'row',
456
+ alignItems: 'center',
457
+ gap: spacingVars['--spacing-2'],
458
+ },
459
+
460
+ // Indicator icon container
461
+ icon: {
462
+ display: 'flex',
463
+ alignItems: 'center',
464
+ justifyContent: 'center',
465
+ width: INDICATOR_SIZE,
466
+ height: INDICATOR_SIZE,
467
+ flexShrink: 0,
468
+ },
469
+ iconCompleted: {
470
+ color: colorVars['--color-accent'],
471
+ },
472
+ iconInProgress: {
473
+ color: colorVars['--color-accent'],
474
+ },
475
+ iconNotStarted: {
476
+ color: colorVars['--color-icon-secondary'],
477
+ },
478
+ iconDisabled: {
479
+ color: colorVars['--color-icon-disabled'],
480
+ opacity: 0.5,
481
+ },
482
+ // Semantic status overrides for the icon — color only.
483
+ iconAccent: {
484
+ color: colorVars['--color-accent'],
485
+ },
486
+ iconSuccess: {
487
+ color: colorVars['--color-success'],
488
+ },
489
+ iconWarning: {
490
+ color: colorVars['--color-warning'],
491
+ },
492
+ iconError: {
493
+ color: colorVars['--color-error'],
494
+ },
495
+
496
+ // Number badge — same box as every other indicator (see INDICATOR_SIZE).
497
+ numberBadge: {
498
+ display: 'grid',
499
+ placeItems: 'center',
500
+ width: INDICATOR_SIZE,
501
+ height: INDICATOR_SIZE,
502
+ borderRadius: radiusVars['--radius-full'],
503
+ // The digit has to read at the same optical weight as the check glyph it
504
+ // alternates with inside a 16px circle, which the supporting size (12px)
505
+ // overfills — it leaves no room for a two-digit step. This is the scale
506
+ // step below it, still a token so themes rescale it. The digit is centered
507
+ // by the grid (placeItems), so no explicit line-height is needed — this
508
+ // matches how Avatar/AvatarGroup center a single glyph in a circle.
509
+ fontSize: textSizeVars['--font-size-xs'],
510
+ fontWeight: fontWeightVars['--font-weight-semibold'],
511
+ flexShrink: 0,
512
+ textAlign: 'center',
513
+ },
514
+ numberCompleted: {
515
+ backgroundColor: colorVars['--color-accent'],
516
+ color: colorVars['--color-background-surface'],
517
+ },
518
+ numberInProgress: {
519
+ backgroundColor: colorVars['--color-accent'],
520
+ color: colorVars['--color-background-surface'],
521
+ },
522
+ numberNotStarted: {
523
+ backgroundColor: colorVars['--color-background-muted'],
524
+ color: colorVars['--color-text-secondary'],
525
+ },
526
+ numberDisabled: {
527
+ backgroundColor: colorVars['--color-background-muted'],
528
+ color: colorVars['--color-text-disabled'],
529
+ opacity: 0.5,
530
+ },
531
+ // Semantic status overrides for the number badge — color only.
532
+ numberAccent: {
533
+ backgroundColor: colorVars['--color-accent'],
534
+ color: colorVars['--color-on-accent'],
535
+ },
536
+ numberSuccess: {
537
+ backgroundColor: colorVars['--color-success'],
538
+ color: colorVars['--color-on-success'],
539
+ },
540
+ numberWarning: {
541
+ backgroundColor: colorVars['--color-warning'],
542
+ color: colorVars['--color-on-warning'],
543
+ },
544
+ numberError: {
545
+ backgroundColor: colorVars['--color-error'],
546
+ color: colorVars['--color-on-error'],
547
+ },
548
+
549
+ // Label
550
+ label: {
551
+ fontSize: typeScaleVars['--text-body-size'],
552
+ lineHeight: typeScaleVars['--text-body-leading'],
553
+ fontWeight: fontWeightVars['--font-weight-normal'],
554
+ color: colorVars['--color-text-primary'],
555
+ // Horizontal steps divide the track evenly, so a label can end up with
556
+ // less room than it wants. Let it shrink past its longest word and break
557
+ // that word rather than overlap the neighbouring step. Inert wherever
558
+ // there is room, which is every vertical stepper.
559
+ minWidth: 0,
560
+ overflowWrap: 'break-word',
561
+ },
562
+ labelInProgress: {
563
+ fontWeight: fontWeightVars['--font-weight-semibold'],
564
+ },
565
+ labelNotStarted: {
566
+ color: colorVars['--color-text-secondary'],
567
+ },
568
+ labelDisabled: {
569
+ color: colorVars['--color-text-disabled'],
570
+ },
571
+
572
+ // Optional tag
573
+ optionalDot: {
574
+ fontSize: typeScaleVars['--text-body-size'],
575
+ color: colorVars['--color-text-secondary'],
576
+ },
577
+ optionalText: {
578
+ fontSize: typeScaleVars['--text-body-size'],
579
+ color: colorVars['--color-text-secondary'],
580
+ },
581
+
582
+ // Description
583
+ descriptionRow: {
584
+ paddingInlineStart: spacingVars['--spacing-0'],
585
+ },
586
+ descriptionRowWithIndicator: {
587
+ paddingInlineStart: INDICATOR_GUTTER,
588
+ },
589
+ description: {
590
+ // Block, not inline. An inline span is laid out in a line box belonging to
591
+ // its parent, and that box is floored by the parent's strut — the invisible
592
+ // box every block container reserves from its own font and line-height. The
593
+ // row inherits the page's 16px/24px, so an inline description sat in a 24px
594
+ // line box no matter how tight its own leading was, padding the gap under
595
+ // the label. Blockifying moves the strut onto this span, where the metrics
596
+ // below apply and the box hugs the text at 16px.
597
+ display: 'block',
598
+ fontSize: typeScaleVars['--text-supporting-size'],
599
+ // The supporting-leading token (1.667 → 20px at 12px) reads too loose for
600
+ // a caption sitting under its label; a fixed 16px line box (the --spacing-4
601
+ // step) is tighter without collapsing the text the way capsize trim did,
602
+ // and stays on the spacing scale rather than a raw literal.
603
+ lineHeight: spacingVars['--spacing-4'],
604
+ color: colorVars['--color-text-secondary'],
605
+ },
606
+
607
+ // Step content (children / flex slot)
608
+ stepContent: {
609
+ paddingBlockStart: spacingVars['--spacing-2'],
610
+ },
611
+ // The label row sits inside the density-padded hover target, but the content
612
+ // slot is that target's *sibling* — a <button> cannot contain the buttons and
613
+ // inputs a content slot usually holds, so the slot has to live outside it.
614
+ // Left alone the slot therefore starts at the step edge while the label above
615
+ // starts one density pad in. Reproducing that pad here is what keeps the two
616
+ // flush; the gutter on top of it is what lines the content up with the label
617
+ // rather than with the indicator.
618
+ contentIndent: (inlinePad: string, gutter: string) => ({
619
+ paddingInlineStart: `calc(${inlinePad} + ${gutter})`,
620
+ paddingInlineEnd: inlinePad,
621
+ }),
622
+
623
+ // Density. Block padding only — the inline half comes from densityInline,
624
+ // which is passed in so the content slot outside this wrapper can apply the
625
+ // same value.
626
+ densityCompact: {
627
+ paddingBlock: spacingVars['--spacing-1'],
628
+ },
629
+ densityBalanced: {
630
+ paddingBlock: spacingVars['--spacing-2'],
631
+ },
632
+ densitySpacious: {
633
+ paddingBlock: spacingVars['--spacing-3'],
634
+ },
635
+ densityInline: (value: string) => ({
636
+ paddingInline: value,
637
+ }),
638
+
639
+ // Button reset for clickable steps
640
+ buttonReset: {
641
+ all: 'unset',
642
+ textAlign: 'start',
643
+ alignItems: 'stretch',
644
+ display: 'flex',
645
+ flexDirection: 'column',
646
+ width: '100%',
647
+ cursor: {
648
+ default: 'pointer',
649
+ ':is(:disabled,[aria-disabled="true"])': 'default',
650
+ },
651
+ borderRadius: radiusVars['--radius-element'],
652
+ transitionProperty: 'background-color',
653
+ transitionDuration: {
654
+ default: durationVars['--duration-fast-min'],
655
+ '@media (prefers-reduced-motion: reduce)': '0s',
656
+ },
657
+ transitionTimingFunction: easeVars['--ease-standard'],
658
+ backgroundColor: {
659
+ default: 'transparent',
660
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
661
+ '@media (hover: hover)': colorVars['--color-overlay-hover'],
662
+ },
663
+ ':active': colorVars['--color-overlay-pressed'],
664
+ },
665
+ },
666
+
667
+ // ===================== ON-TRACK LAYOUT =====================
668
+ // Indicator is slotted into the connector line as a node on the track.
669
+ // Each step draws the connector segments that flank its own indicator; the
670
+ // segment *before* the indicator is hidden on the first step (step === 0) so
671
+ // the track starts at the first node. Segments abut the indicator directly,
672
+ // so no child introspection or total-count is needed.
673
+
674
+ otVerticalRoot: {
675
+ display: 'flex',
676
+ flexDirection: 'column',
677
+ position: 'relative',
678
+ },
679
+ otHorizontalRoot: {
680
+ display: 'flex',
681
+ flexDirection: 'column',
682
+ flex: 1,
683
+ minWidth: 0,
684
+ },
685
+
686
+ // Interactive wrapper (indicator + label act as one click target).
687
+ otInteractive: {
688
+ all: 'unset',
689
+ boxSizing: 'border-box',
690
+ // `all: unset` leaves the <button> UA default of centered text; force
691
+ // start so vertical labels/descriptions read left-aligned. Horizontal
692
+ // labels re-center via otLabelWrapH (a deeper element).
693
+ textAlign: 'start',
694
+ cursor: {
695
+ default: 'pointer',
696
+ ':is(:disabled,[aria-disabled="true"])': 'default',
697
+ },
698
+ borderRadius: radiusVars['--radius-element'],
699
+ transitionProperty: 'background-color',
700
+ transitionDuration: {
701
+ default: durationVars['--duration-fast-min'],
702
+ '@media (prefers-reduced-motion: reduce)': '0s',
703
+ },
704
+ transitionTimingFunction: easeVars['--ease-standard'],
705
+ backgroundColor: {
706
+ default: 'transparent',
707
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
708
+ '@media (hover: hover)': colorVars['--color-overlay-hover'],
709
+ },
710
+ ':active': colorVars['--color-overlay-pressed'],
711
+ },
712
+ },
713
+
714
+ otRowWrap: {
715
+ display: 'flex',
716
+ flexDirection: 'row',
717
+ alignItems: 'stretch',
718
+ gap: spacingVars['--spacing-2'],
719
+ width: '100%',
720
+ },
721
+ otColWrap: {
722
+ display: 'flex',
723
+ flexDirection: 'column',
724
+ alignItems: 'stretch',
725
+ width: '100%',
726
+ },
727
+
728
+ // Vertical indicator column: [segment] [indicator] [segment] stacked.
729
+ otIndicatorColV: {
730
+ display: 'flex',
731
+ flexDirection: 'column',
732
+ alignItems: 'center',
733
+ width: INDICATOR_SIZE,
734
+ flexShrink: 0,
735
+ alignSelf: 'stretch',
736
+ },
737
+ // Shared segment base: square ends so stacked segments read as one
738
+ // continuous line (no radius).
739
+ otSegBaseV: {
740
+ width: BAR_WIDTH,
741
+ flexShrink: 0,
742
+ borderRadius: 0,
743
+ },
744
+ // Flexible segment (below the node) — grows to fill the step height and
745
+ // meets the next node's leading segment.
746
+ otSegFlexV: {
747
+ flex: 1,
748
+ minHeight: spacingVars['--spacing-2'],
749
+ },
750
+ // Fixed leading segment (above the node) — its height offsets the node down
751
+ // so it aligns with the label's first line instead of the step's center.
752
+ otSegLeadV: (value: string) => ({
753
+ height: value,
754
+ }),
755
+
756
+ // Horizontal track row: [segment] [indicator] [segment] in a line.
757
+ otTrackRowH: {
758
+ display: 'flex',
759
+ flexDirection: 'row',
760
+ alignItems: 'center',
761
+ width: '100%',
762
+ },
763
+ otSegH: {
764
+ height: BAR_WIDTH,
765
+ flex: 1,
766
+ minWidth: spacingVars['--spacing-2'],
767
+ borderRadius: 0,
768
+ },
769
+
770
+ otSegHiddenIfFirst: {
771
+ // Structural first/last hiding keyed off the step's own <li> position via
772
+ // stepMarker, so it never depends on the parent counting children (which
773
+ // breaks when steps are grouped in a fragment). Scoped to stepMarker so only
774
+ // the parent <li> is checked, not the outer <ol> (also a :first/:last-child).
775
+ visibility: {
776
+ default: 'visible',
777
+ [stylex.when.ancestor(':first-child', stepMarker)]: 'hidden',
778
+ },
779
+ },
780
+ otSegHiddenIfLast: {
781
+ visibility: {
782
+ default: 'visible',
783
+ [stylex.when.ancestor(':last-child', stepMarker)]: 'hidden',
784
+ },
785
+ },
786
+
787
+ otBodyV: {
788
+ display: 'flex',
789
+ flexDirection: 'column',
790
+ justifyContent: 'flex-start',
791
+ flex: 1,
792
+ gap: spacingVars['--spacing-0-5'],
793
+ minWidth: 0,
794
+ },
795
+ otLabelWrapH: {
796
+ display: 'flex',
797
+ flexDirection: 'column',
798
+ // Stretch rather than center for the same reason as horizontalStep: a
799
+ // centered child is fit-content sized and would spill past its node.
800
+ // The label still reads centered under the node — otLabelRowCenter
801
+ // centers it inside the full-width row.
802
+ alignItems: 'stretch',
803
+ gap: spacingVars['--spacing-0-5'],
804
+ textAlign: 'center',
805
+ },
806
+ otLabelRowStart: {
807
+ display: 'flex',
808
+ flexDirection: 'row',
809
+ alignItems: 'center',
810
+ gap: spacingVars['--spacing-2'],
811
+ },
812
+ otLabelRowCenter: {
813
+ display: 'flex',
814
+ flexDirection: 'row',
815
+ alignItems: 'center',
816
+ justifyContent: 'center',
817
+ flexWrap: 'wrap',
818
+ gap: spacingVars['--spacing-1'],
819
+ },
820
+
821
+ // Inline offset comes from contentIndent, and only in the vertical layout —
822
+ // a horizontal on-track step pads its hover target on the block axis alone,
823
+ // so its content slot already starts flush with the column above it.
824
+ otContent: {
825
+ paddingBlockStart: spacingVars['--spacing-2'],
826
+ },
827
+ // A vertical on-track step draws its connector inside the row holding the
828
+ // indicator and label, and the content slot sits below that row rather than
829
+ // in it (the row is a <button> when the step is clickable, which cannot
830
+ // contain the controls a content slot usually holds). So the line stops at
831
+ // the label and the track breaks open wherever a step carries content.
832
+ // otContentSegV continues it: a segment spanning the slot's full height, on
833
+ // the same rail the row's segments run down, which closes the gap between
834
+ // this step's indicator and the next one's.
835
+ otContentWrapV: {
836
+ position: 'relative',
837
+ },
838
+ otContentSegV: (inlinePad: string) => ({
839
+ position: 'absolute',
840
+ insetBlock: 0,
841
+ // Center on the rail: the row pads itself by inlinePad, then centers a
842
+ // BAR_WIDTH line inside an INDICATOR_SIZE-wide column.
843
+ insetInlineStart: `calc(${inlinePad} + (${INDICATOR_SIZE} - ${BAR_WIDTH}) / 2)`,
844
+ width: BAR_WIDTH,
845
+ }),
846
+
847
+ // Density-driven spacing. The connector runs along the "track axis"
848
+ // (vertical = block, horizontal = inline), so density is only ever applied
849
+ // to the *cross* axis — padding the track axis would break the line into
850
+ // gapped segments.
851
+ //
852
+ // Vertical: pad the whole step row (indicator rail + label) so density adds
853
+ // breathing room *around the indicators*, not just the text. Applied to the
854
+ // hover target itself.
855
+ otRowPadV: (value: string) => ({
856
+ paddingBlock: value,
857
+ paddingInline: value,
858
+ }),
859
+ // The rail draws the connector, so it must span the row's full block extent
860
+ // to keep the line continuous. A negative block margin cancels the wrapper's
861
+ // block padding, letting the line bridge step-to-step while the label still
862
+ // sits inside the padding.
863
+ otRailBridgeV: (value: string) => ({
864
+ marginBlock: `calc(-1 * ${value})`,
865
+ }),
866
+ // Horizontal: block padding around the track+label column (inside the hover
867
+ // target) adds vertical breathing room while the inline track stays flush.
868
+ otPadBlock: (value: string) => ({
869
+ paddingBlock: value,
870
+ }),
871
+ otMarginTop: (value: string) => ({
872
+ marginBlockStart: value,
873
+ }),
874
+ });
875
+
876
+ /**
877
+ * An individual step within an Stepper. Renders a 4px progress-bar segment,
878
+ * an indicator (numbered badge, check, or any custom icon), a label with
879
+ * optional description, and an optional content slot.
880
+ *
881
+ * Progress (completed / active / not-started) is derived from the parent's
882
+ * `activeStep` and this step's `step` prop. The optional `status` prop layers a
883
+ * semantic meaning on top: in the default `auto` indicator mode it recolors the
884
+ * indicator and swaps in a matching glyph (`success` → green check-circle,
885
+ * `warning`/`error` → the shared Input status icons). The current step always
886
+ * keeps its current-step ring. `status` never recolors the connector/track.
887
+ *
888
+ * @example
889
+ * ```
890
+ * <Step step={0} label="Account details" description="Enter your email" />
891
+ * ```
892
+ *
893
+ * @example
894
+ * ```
895
+ * <Step step={1} label="Payment" status="error" />
896
+ * ```
897
+ */
898
+ export function Step({
899
+ step,
900
+ label,
901
+ description,
902
+ children,
903
+ status,
904
+ isDisabled = false,
905
+ isOptional = false,
906
+ endContent,
907
+ indicator: indicatorProp,
908
+ density: densityProp,
909
+ xstyle,
910
+ className,
911
+ style,
912
+ ref,
913
+ 'data-testid': dataTestId,
914
+ ...rest
915
+ }: StepProps) {
916
+ const t = useTranslator();
917
+ const ctx = useStepperContext();
918
+ const {
919
+ activeStep,
920
+ previousActiveStep,
921
+ orientation,
922
+ onStepClick,
923
+ density: ctxDensity,
924
+ indicatorPosition,
925
+ registerStep,
926
+ } = ctx;
927
+
928
+ // Register this step index with the parent Stepper for duplicate detection.
929
+ useEffect(() => registerStep(step), [registerStep, step]);
930
+
931
+ const density = densityProp ?? ctxDensity;
932
+ // Inline padding of a separated step's hover target. Density varies the block
933
+ // padding freely, but the inline value has to be readable from out here: the
934
+ // content slot renders outside that target and re-applies it to stay aligned
935
+ // (see contentIndent), so keeping it in one place is what stops the two
936
+ // drifting apart.
937
+ const separatedDensityInline =
938
+ density === 'spacious'
939
+ ? spacingVars['--spacing-3']
940
+ : spacingVars['--spacing-2'];
941
+
942
+ // Resolve indicator prop — may be a preset string or a custom ReactNode.
943
+ const isCustomIndicator =
944
+ indicatorProp != null && typeof indicatorProp !== 'string';
945
+ const indicator: StepIndicatorPreset = isCustomIndicator
946
+ ? 'auto'
947
+ : ((indicatorProp as StepIndicatorPreset | undefined) ?? 'auto');
948
+
949
+ // Internal progress, derived from the parent's activeStep. This is NOT the
950
+ // public `status` prop — `status` controls semantic color only.
951
+ const progress: 'completed' | 'in-progress' | 'not-started' =
952
+ step === activeStep
953
+ ? 'in-progress'
954
+ : step < activeStep
955
+ ? 'completed'
956
+ : 'not-started';
957
+
958
+ const isVertical = orientation === 'vertical';
959
+ const isActive = progress === 'in-progress';
960
+ // Any non-disabled step is navigable when an onStepClick handler is provided,
961
+ // including not-started steps (free navigation across the flow).
962
+ const isClickable = !isDisabled && onStepClick != null;
963
+
964
+ const handleClick = () => {
965
+ if (isClickable && onStepClick) {
966
+ onStepClick(step);
967
+ }
968
+ };
969
+
970
+ // Bar fill is purely progress-based. `status` never recolors the bar — it
971
+ // only recolors the indicator (icon / number badge) below.
972
+ const isBarFilled = progress === 'completed' || progress === 'in-progress';
973
+
974
+ // --- This step's slice of the animated span ---
975
+ // Both layouts are measured in the same unit, the node-to-node span: on-track
976
+ // span k runs from node k to node k+1, and a separated step's lone bar is the
977
+ // span arriving at it (bar s fills on exactly the condition span s-1 does).
978
+ // So one rule serves both, and neither needs to know the step count.
979
+ //
980
+ // Only a single forward step animates, and the span it crosses is the one
981
+ // leaving the step the flow came from. Every other segment — the untouched
982
+ // ones, and every segment of a jump or a retreat — is handed a zero
983
+ // duration, so it lands at once and carries no stale delay into a later
984
+ // change.
985
+ const isSingleAdvance = activeStep === previousActiveStep + 1;
986
+ const animatedSpan = previousActiveStep;
987
+
988
+ /**
989
+ * Timing for the segment covering `[offset, offset + share)` of span
990
+ * `spanIndex`, both expressed as fractions of that span.
991
+ */
992
+ const fillTiming = (spanIndex: number, offset: number, share: number) => {
993
+ if (!isSingleAdvance || spanIndex !== animatedSpan) {
994
+ return styles.connectorTiming('0s', '0s');
995
+ }
996
+ return styles.connectorTiming(
997
+ fillTimeSlice(FILL_SPAN_TIME, share),
998
+ fillTimeSlice(FILL_SPAN_TIME, offset),
999
+ );
1000
+ };
1001
+
1002
+ // A separated step draws the whole span arriving at it, so it fills for the
1003
+ // span's entire slice with nothing to sequence against.
1004
+ const barTiming = fillTiming(step - 1, 0, 1);
1005
+
1006
+ // --- Build indicator node ---
1007
+ // 'auto': number for not-started, check/dot icon once reached
1008
+ // 'number': always number badge
1009
+ // 'none': nothing
1010
+ // custom ReactNode (or `icon` prop): render as-is
1011
+ let indicatorNode: ReactNode = null;
1012
+
1013
+ const customIcon = isCustomIndicator ? indicatorProp : null;
1014
+
1015
+ // Semantic `status` drives a distinct indicator glyph (default 'auto' mode,
1016
+ // no custom icon), all sourced from the themed Icon registry so a step reads
1017
+ // the same as the rest of the system:
1018
+ // - success → the themed `success` glyph (same check-circle as a completed
1019
+ // step), tinted success — i.e. a green check
1020
+ // - warning → the themed `warning` glyph
1021
+ // - error → the themed `error` glyph
1022
+ // The current (in-progress) step always shows the current-step ring — its
1023
+ // indicator "replaces" any status glyph. `accent` has no distinct glyph and
1024
+ // falls through to the progress-derived default.
1025
+ const statusGlyph: 'success' | 'warning' | 'error' | null =
1026
+ indicator === 'auto' &&
1027
+ customIcon == null &&
1028
+ !isActive &&
1029
+ (status === 'success' || status === 'warning' || status === 'error')
1030
+ ? status
1031
+ : null;
1032
+
1033
+ if (indicator !== 'none') {
1034
+ const showNumber =
1035
+ customIcon == null &&
1036
+ statusGlyph == null &&
1037
+ (indicator === 'number' ||
1038
+ (indicator === 'auto' && progress === 'not-started'));
1039
+
1040
+ if (showNumber) {
1041
+ // Status only fills the badge once the step is reached. A not-started
1042
+ // step stays neutral — otherwise `accent` (which has no glyph to swap in)
1043
+ // would paint an inverted accent badge on an upcoming step, making it
1044
+ // read as active/completed.
1045
+ const isReached = progress === 'completed' || progress === 'in-progress';
1046
+ const numberColorStyle = isDisabled
1047
+ ? styles.numberDisabled
1048
+ : isReached && status === 'accent'
1049
+ ? styles.numberAccent
1050
+ : isReached && status === 'success'
1051
+ ? styles.numberSuccess
1052
+ : isReached && status === 'warning'
1053
+ ? styles.numberWarning
1054
+ : isReached && status === 'error'
1055
+ ? styles.numberError
1056
+ : progress === 'completed'
1057
+ ? styles.numberCompleted
1058
+ : progress === 'in-progress'
1059
+ ? styles.numberInProgress
1060
+ : styles.numberNotStarted;
1061
+
1062
+ indicatorNode = (
1063
+ <div
1064
+ aria-hidden="true"
1065
+ {...mergeProps(
1066
+ themeProps('step-indicator', {
1067
+ progress,
1068
+ status: status ?? undefined,
1069
+ }),
1070
+ stylex.props(styles.numberBadge, numberColorStyle),
1071
+ )}>
1072
+ {step + 1}
1073
+ </div>
1074
+ );
1075
+ } else {
1076
+ // Priority: explicit custom icon → status glyph (non-current steps) →
1077
+ // progress-derived default (check when completed, ring when current).
1078
+ const iconContent: ReactNode =
1079
+ customIcon != null ? (
1080
+ customIcon
1081
+ ) : statusGlyph === 'success' ? (
1082
+ <Icon
1083
+ icon="success"
1084
+ size="sm"
1085
+ color={isDisabled ? 'disabled' : 'success'}
1086
+ />
1087
+ ) : statusGlyph === 'warning' ? (
1088
+ <Icon
1089
+ icon="warning"
1090
+ size="sm"
1091
+ color={isDisabled ? 'disabled' : 'warning'}
1092
+ />
1093
+ ) : statusGlyph === 'error' ? (
1094
+ <Icon
1095
+ icon="error"
1096
+ size="sm"
1097
+ color={isDisabled ? 'disabled' : 'error'}
1098
+ />
1099
+ ) : progress === 'completed' ? (
1100
+ <CheckCircleIcon />
1101
+ ) : (
1102
+ <CurrentIcon />
1103
+ );
1104
+
1105
+ // Wrapper tint drives the currentColor glyphs (check-circle / ring /
1106
+ // custom icon). The <Icon> status glyphs set their own color, but we tint
1107
+ // the wrapper to match so the indicator's color reflects `status` too.
1108
+ const iconColorStyle = isDisabled
1109
+ ? styles.iconDisabled
1110
+ : customIcon != null
1111
+ ? status === 'accent'
1112
+ ? styles.iconAccent
1113
+ : status === 'success'
1114
+ ? styles.iconSuccess
1115
+ : status === 'warning'
1116
+ ? styles.iconWarning
1117
+ : status === 'error'
1118
+ ? styles.iconError
1119
+ : progress === 'completed'
1120
+ ? styles.iconCompleted
1121
+ : progress === 'in-progress'
1122
+ ? styles.iconInProgress
1123
+ : styles.iconNotStarted
1124
+ : statusGlyph === 'success'
1125
+ ? styles.iconSuccess
1126
+ : statusGlyph === 'warning'
1127
+ ? styles.iconWarning
1128
+ : statusGlyph === 'error'
1129
+ ? styles.iconError
1130
+ : progress === 'completed'
1131
+ ? styles.iconCompleted
1132
+ : progress === 'in-progress'
1133
+ ? styles.iconInProgress
1134
+ : styles.iconNotStarted;
1135
+
1136
+ indicatorNode = (
1137
+ <div
1138
+ aria-hidden="true"
1139
+ {...mergeProps(
1140
+ themeProps('step-indicator', {
1141
+ progress,
1142
+ status: status ?? undefined,
1143
+ }),
1144
+ stylex.props(styles.icon, iconColorStyle),
1145
+ )}>
1146
+ {iconContent}
1147
+ </div>
1148
+ );
1149
+ }
1150
+ }
1151
+
1152
+ // Every indicator glyph above is aria-hidden (pure decoration), so the
1153
+ // step's progress/status must also reach assistive tech as text
1154
+ // (WCAG 1.4.1 / 1.3.1). `error`/`warning` announce the semantic status;
1155
+ // `success` and a completed step both announce "completed". The current
1156
+ // step is announced via aria-current="step" instead, and not-started steps
1157
+ // stay silent (the default state needs no qualifier).
1158
+ const statusText: string | null =
1159
+ status === 'error'
1160
+ ? t('@astryx.step.status.error')
1161
+ : status === 'warning'
1162
+ ? t('@astryx.step.status.warning')
1163
+ : status === 'success' || progress === 'completed'
1164
+ ? t('@astryx.step.status.completed')
1165
+ : null;
1166
+
1167
+ // Rendered next to the label: hidden text for static steps, and composed
1168
+ // into the button's accessible name for clickable ones (an aria-label on
1169
+ // the button would otherwise override the hidden text). Two separate keys
1170
+ // rather than string concatenation so translations control the joiner.
1171
+ const statusTextNode = isRenderable(statusText) ? (
1172
+ <VisuallyHidden>{statusText}</VisuallyHidden>
1173
+ ) : null;
1174
+ const stepAriaLabel =
1175
+ statusText != null
1176
+ ? t('@astryx.step.goToStepWithStatus', {
1177
+ stepNumber: step + 1,
1178
+ label,
1179
+ status: statusText,
1180
+ })
1181
+ : t('@astryx.step.goToStep', {stepNumber: step + 1, label});
1182
+
1183
+ const hasIndicator = indicator !== 'none';
1184
+
1185
+ const labelColorStyle = isDisabled
1186
+ ? styles.labelDisabled
1187
+ : progress === 'not-started'
1188
+ ? styles.labelNotStarted
1189
+ : isActive
1190
+ ? styles.labelInProgress
1191
+ : undefined;
1192
+
1193
+ // Indicator + Label row
1194
+ const iconLabelNode = (
1195
+ <div {...stylex.props(styles.iconLabelRow)}>
1196
+ {indicatorNode}
1197
+ <span {...stylex.props(styles.label, labelColorStyle)}>{label}</span>
1198
+ {statusTextNode}
1199
+ {isOptional && (
1200
+ <>
1201
+ <span {...stylex.props(styles.optionalDot)}>•</span>
1202
+ <span {...stylex.props(styles.optionalText)}>
1203
+ {t('@astryx.step.optional')}
1204
+ </span>
1205
+ </>
1206
+ )}
1207
+ {endContent}
1208
+ </div>
1209
+ );
1210
+
1211
+ const descriptionNode = isRenderable(description) ? (
1212
+ <div
1213
+ {...stylex.props(
1214
+ hasIndicator
1215
+ ? styles.descriptionRowWithIndicator
1216
+ : styles.descriptionRow,
1217
+ )}>
1218
+ <span {...stylex.props(styles.description)}>{description}</span>
1219
+ </div>
1220
+ ) : null;
1221
+
1222
+ const contentNode = isRenderable(children) ? (
1223
+ <div
1224
+ {...stylex.props(
1225
+ styles.stepContent,
1226
+ styles.contentIndent(
1227
+ separatedDensityInline,
1228
+ hasIndicator ? INDICATOR_GUTTER : '0px',
1229
+ ),
1230
+ )}>
1231
+ {children}
1232
+ </div>
1233
+ ) : null;
1234
+
1235
+ // Theme data attributes reflect progress + optional semantic status.
1236
+ const stepThemeProps = themeProps('step', {
1237
+ progress,
1238
+ status: status ?? undefined,
1239
+ });
1240
+
1241
+ // ======= ON-TRACK: indicator is a node on the connector =======
1242
+ if (indicatorPosition === 'on-track') {
1243
+ // Connector fill is purely progress-based (matches the separated bar):
1244
+ // the segment before the indicator is "reached" once we're at/past this
1245
+ // step; the segment after is filled only once this step is completed.
1246
+ // `status` never recolors the connector — only the indicator.
1247
+ const beforeFilled = step <= activeStep;
1248
+ const afterFilled = step < activeStep;
1249
+ // Both segments animate their fill along the track (see connectorTrack), so
1250
+ // the pair is picked by orientation: the vertical column scales down its
1251
+ // block axis, the horizontal row along the reading direction.
1252
+ const segFilledStyle = isVertical
1253
+ ? styles.connectorFillV
1254
+ : styles.connectorFillH;
1255
+ const segEmptyStyle = isVertical
1256
+ ? styles.connectorEmptyV
1257
+ : styles.connectorEmptyH;
1258
+ const beforeSegStyle = beforeFilled ? segFilledStyle : segEmptyStyle;
1259
+ const afterSegStyle = afterFilled ? segFilledStyle : segEmptyStyle;
1260
+ // First/last connector visibility is decided structurally from the step's
1261
+ // own <li> position (see otSegHiddenIfFirst/Last), not by counting children
1262
+ // in the parent — so grouping steps in a fragment can't break it.
1263
+
1264
+ // A node-to-node span is drawn by two steps: this one's trailing segments
1265
+ // leave its own node, the next one's leading cap arrives at the next node.
1266
+ // Both flip on the same condition, so left to themselves they fill together
1267
+ // and one gap between two nodes reads as two dashes converging on the space
1268
+ // between them. Handing each its own slice of the span's time, in track
1269
+ // order — the leaving half first, then the arriving one — is what closes
1270
+ // that into a single line growing.
1271
+ const arrivalShare = isVertical ? OT_ARRIVAL_SHARE_V : OT_ARRIVAL_SHARE_H;
1272
+ const leavingShare = 1 - arrivalShare;
1273
+ // A content slot's segment continues the leaving half below the row, so the
1274
+ // two divide the leaving time between them; with no slot the rail segment
1275
+ // takes all of it. Each step can work this out for its own trailing side
1276
+ // alone: the arriving cap always takes the span's last `arrivalShare`, so
1277
+ // the step drawing it never has to know whether the step before it carries
1278
+ // a slot.
1279
+ const hasContentSeg = isVertical && isRenderable(children);
1280
+ const railShare =
1281
+ leavingShare * (hasContentSeg ? OT_RAIL_SHARE_OF_LEAVING : 1);
1282
+ const contentShare = leavingShare - railShare;
1283
+
1284
+ const beforeTiming = fillTiming(step - 1, leavingShare, arrivalShare);
1285
+ const railTiming = fillTiming(step, 0, railShare);
1286
+ const contentTiming = fillTiming(step, railShare, contentShare);
1287
+
1288
+ const densitySpace =
1289
+ density === 'compact'
1290
+ ? spacingVars['--spacing-1']
1291
+ : density === 'spacious'
1292
+ ? spacingVars['--spacing-3']
1293
+ : spacingVars['--spacing-2'];
1294
+
1295
+ const labelLineNode = (
1296
+ <div
1297
+ {...stylex.props(
1298
+ isVertical ? styles.otLabelRowStart : styles.otLabelRowCenter,
1299
+ )}>
1300
+ <span {...stylex.props(styles.label, labelColorStyle)}>{label}</span>
1301
+ {statusTextNode}
1302
+ {isOptional && (
1303
+ <>
1304
+ <span {...stylex.props(styles.optionalDot)}>•</span>
1305
+ <span {...stylex.props(styles.optionalText)}>
1306
+ {t('@astryx.step.optional')}
1307
+ </span>
1308
+ </>
1309
+ )}
1310
+ {endContent}
1311
+ </div>
1312
+ );
1313
+
1314
+ const otDescriptionNode = isRenderable(description) ? (
1315
+ <span {...stylex.props(styles.description)}>{description}</span>
1316
+ ) : null;
1317
+
1318
+ const otContentNode = !isRenderable(children) ? null : isVertical ? (
1319
+ <div {...stylex.props(styles.otContentWrapV)}>
1320
+ <div
1321
+ aria-hidden="true"
1322
+ {...mergeProps(
1323
+ themeProps('step-connector'),
1324
+ stylex.props(
1325
+ // connectorTrack first: it declares `position: relative` to
1326
+ // anchor its fill layer, but this segment is absolutely placed
1327
+ // on the rail beside the content slot and must keep that. An
1328
+ // absolute box is a containing block too, so the fill still
1329
+ // pins to it.
1330
+ styles.connectorTrack,
1331
+ styles.otContentSegV(densitySpace),
1332
+ afterSegStyle,
1333
+ contentTiming,
1334
+ styles.otSegHiddenIfLast,
1335
+ ),
1336
+ )}
1337
+ />
1338
+ <div
1339
+ {...stylex.props(
1340
+ styles.otContent,
1341
+ styles.contentIndent(densitySpace, INDICATOR_GUTTER),
1342
+ )}>
1343
+ {children}
1344
+ </div>
1345
+ </div>
1346
+ ) : (
1347
+ <div {...stylex.props(styles.otContent)}>{children}</div>
1348
+ );
1349
+
1350
+ if (isVertical) {
1351
+ const inner = (
1352
+ <>
1353
+ <div
1354
+ {...stylex.props(
1355
+ styles.otIndicatorColV,
1356
+ styles.otRailBridgeV(densitySpace),
1357
+ )}>
1358
+ <div
1359
+ aria-hidden="true"
1360
+ {...mergeProps(
1361
+ themeProps('step-connector'),
1362
+ stylex.props(
1363
+ styles.otSegBaseV,
1364
+ styles.otSegLeadV(densitySpace),
1365
+ styles.connectorTrack,
1366
+ beforeSegStyle,
1367
+ beforeTiming,
1368
+ styles.otSegHiddenIfFirst,
1369
+ ),
1370
+ )}
1371
+ />
1372
+ {indicatorNode}
1373
+ <div
1374
+ aria-hidden="true"
1375
+ {...mergeProps(
1376
+ themeProps('step-connector'),
1377
+ stylex.props(
1378
+ styles.otSegBaseV,
1379
+ styles.otSegFlexV,
1380
+ styles.connectorTrack,
1381
+ afterSegStyle,
1382
+ railTiming,
1383
+ styles.otSegHiddenIfLast,
1384
+ ),
1385
+ )}
1386
+ />
1387
+ </div>
1388
+ <div {...stylex.props(styles.otBodyV)}>
1389
+ {labelLineNode}
1390
+ {otDescriptionNode}
1391
+ </div>
1392
+ </>
1393
+ );
1394
+
1395
+ return (
1396
+ <li
1397
+ ref={ref}
1398
+ {...mergeProps(
1399
+ stepThemeProps,
1400
+ stylex.props(stepMarker, styles.otVerticalRoot, xstyle),
1401
+ className,
1402
+ style,
1403
+ )}
1404
+ aria-current={isActive ? 'step' : undefined}
1405
+ data-testid={dataTestId}
1406
+ {...rest}>
1407
+ {isClickable ? (
1408
+ <button
1409
+ type="button"
1410
+ onClick={handleClick}
1411
+ aria-label={stepAriaLabel}
1412
+ {...stylex.props(
1413
+ styles.otInteractive,
1414
+ styles.otRowWrap,
1415
+ styles.otRowPadV(densitySpace),
1416
+ focusOutlineStyles.focusVisible,
1417
+ )}>
1418
+ {inner}
1419
+ </button>
1420
+ ) : (
1421
+ <div
1422
+ {...stylex.props(
1423
+ styles.otRowWrap,
1424
+ styles.otRowPadV(densitySpace),
1425
+ )}>
1426
+ {inner}
1427
+ </div>
1428
+ )}
1429
+ {otContentNode}
1430
+ </li>
1431
+ );
1432
+ }
1433
+
1434
+ // Horizontal on-track
1435
+ const innerH = (
1436
+ <>
1437
+ <div {...stylex.props(styles.otTrackRowH)}>
1438
+ <div
1439
+ aria-hidden="true"
1440
+ {...mergeProps(
1441
+ themeProps('step-connector'),
1442
+ stylex.props(
1443
+ styles.otSegH,
1444
+ styles.connectorTrack,
1445
+ beforeSegStyle,
1446
+ beforeTiming,
1447
+ styles.otSegHiddenIfFirst,
1448
+ ),
1449
+ )}
1450
+ />
1451
+ {indicatorNode}
1452
+ <div
1453
+ aria-hidden="true"
1454
+ {...mergeProps(
1455
+ themeProps('step-connector'),
1456
+ stylex.props(
1457
+ styles.otSegH,
1458
+ styles.connectorTrack,
1459
+ afterSegStyle,
1460
+ railTiming,
1461
+ styles.otSegHiddenIfLast,
1462
+ ),
1463
+ )}
1464
+ />
1465
+ </div>
1466
+ <div
1467
+ {...stylex.props(
1468
+ styles.otLabelWrapH,
1469
+ styles.otMarginTop(densitySpace),
1470
+ )}>
1471
+ {labelLineNode}
1472
+ {otDescriptionNode}
1473
+ </div>
1474
+ </>
1475
+ );
1476
+
1477
+ return (
1478
+ <li
1479
+ ref={ref}
1480
+ {...mergeProps(
1481
+ stepThemeProps,
1482
+ stylex.props(stepMarker, styles.otHorizontalRoot, xstyle),
1483
+ className,
1484
+ style,
1485
+ )}
1486
+ aria-current={isActive ? 'step' : undefined}
1487
+ data-testid={dataTestId}
1488
+ {...rest}>
1489
+ {isClickable ? (
1490
+ <button
1491
+ type="button"
1492
+ onClick={handleClick}
1493
+ aria-label={stepAriaLabel}
1494
+ {...stylex.props(
1495
+ styles.otInteractive,
1496
+ styles.otColWrap,
1497
+ styles.otPadBlock(densitySpace),
1498
+ focusOutlineStyles.focusVisible,
1499
+ )}>
1500
+ {innerH}
1501
+ </button>
1502
+ ) : (
1503
+ <div
1504
+ {...stylex.props(
1505
+ styles.otColWrap,
1506
+ styles.otPadBlock(densitySpace),
1507
+ )}>
1508
+ {innerH}
1509
+ </div>
1510
+ )}
1511
+ {otContentNode}
1512
+ </li>
1513
+ );
1514
+ }
1515
+
1516
+ // ======= VERTICAL =======
1517
+ if (isVertical) {
1518
+ return (
1519
+ <li
1520
+ ref={ref}
1521
+ {...mergeProps(
1522
+ stepThemeProps,
1523
+ stylex.props(stepMarker, styles.verticalRoot, xstyle),
1524
+ className,
1525
+ style,
1526
+ )}
1527
+ aria-current={isActive ? 'step' : undefined}
1528
+ data-testid={dataTestId}
1529
+ {...rest}>
1530
+ {/* 4px progress bar */}
1531
+ <div
1532
+ {...mergeProps(
1533
+ themeProps('step-bar'),
1534
+ stylex.props(
1535
+ styles.verticalBar,
1536
+ styles.connectorTrack,
1537
+ isBarFilled ? styles.connectorFillV : styles.connectorEmptyV,
1538
+ barTiming,
1539
+ ),
1540
+ )}
1541
+ aria-hidden="true"
1542
+ />
1543
+ {/* Body: button wraps only label area, children render outside */}
1544
+ <div {...stylex.props(styles.verticalBody)}>
1545
+ {isClickable ? (
1546
+ <button
1547
+ type="button"
1548
+ onClick={handleClick}
1549
+ aria-label={stepAriaLabel}
1550
+ {...stylex.props(
1551
+ styles.buttonReset,
1552
+ focusOutlineStyles.focusVisible,
1553
+ density === 'compact' && styles.densityCompact,
1554
+ density === 'balanced' && styles.densityBalanced,
1555
+ density === 'spacious' && styles.densitySpacious,
1556
+ styles.densityInline(separatedDensityInline),
1557
+ )}>
1558
+ {iconLabelNode}
1559
+ {descriptionNode}
1560
+ </button>
1561
+ ) : (
1562
+ <div
1563
+ {...stylex.props(
1564
+ density === 'compact' && styles.densityCompact,
1565
+ density === 'balanced' && styles.densityBalanced,
1566
+ density === 'spacious' && styles.densitySpacious,
1567
+ styles.densityInline(separatedDensityInline),
1568
+ )}>
1569
+ {iconLabelNode}
1570
+ {descriptionNode}
1571
+ </div>
1572
+ )}
1573
+ {contentNode}
1574
+ </div>
1575
+ </li>
1576
+ );
1577
+ }
1578
+
1579
+ // ======= HORIZONTAL =======
1580
+ return (
1581
+ <li
1582
+ ref={ref}
1583
+ {...mergeProps(
1584
+ stepThemeProps,
1585
+ stylex.props(stepMarker, styles.horizontalStep, xstyle),
1586
+ className,
1587
+ style,
1588
+ )}
1589
+ aria-current={isActive ? 'step' : undefined}
1590
+ data-testid={dataTestId}
1591
+ {...rest}>
1592
+ {/* 4px progress bar segment for this step */}
1593
+ <div
1594
+ {...mergeProps(
1595
+ themeProps('step-bar'),
1596
+ stylex.props(
1597
+ styles.horizontalBar,
1598
+ styles.connectorTrack,
1599
+ isBarFilled ? styles.connectorFillH : styles.connectorEmptyH,
1600
+ barTiming,
1601
+ ),
1602
+ )}
1603
+ aria-hidden="true"
1604
+ />
1605
+ {isClickable ? (
1606
+ <button
1607
+ type="button"
1608
+ onClick={handleClick}
1609
+ aria-label={stepAriaLabel}
1610
+ {...stylex.props(
1611
+ styles.buttonReset,
1612
+ focusOutlineStyles.focusVisible,
1613
+ density === 'compact' && styles.densityCompact,
1614
+ density === 'balanced' && styles.densityBalanced,
1615
+ density === 'spacious' && styles.densitySpacious,
1616
+ styles.densityInline(separatedDensityInline),
1617
+ )}>
1618
+ {iconLabelNode}
1619
+ {descriptionNode}
1620
+ </button>
1621
+ ) : (
1622
+ <div
1623
+ {...stylex.props(
1624
+ density === 'compact' && styles.densityCompact,
1625
+ density === 'balanced' && styles.densityBalanced,
1626
+ density === 'spacious' && styles.densitySpacious,
1627
+ styles.densityInline(separatedDensityInline),
1628
+ )}>
1629
+ {iconLabelNode}
1630
+ {descriptionNode}
1631
+ </div>
1632
+ )}
1633
+ {contentNode}
1634
+ </li>
1635
+ );
1636
+ }
1637
+
1638
+ Step.displayName = 'Step';