@astryxdesign/core 0.4.7 → 0.5.0-canary.07f6c33

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 (464) hide show
  1. package/CHANGELOG.md +161 -1
  2. package/dist/AppShell/AppShell.d.ts.map +1 -1
  3. package/dist/AppShell/AppShell.js +3 -2
  4. package/dist/AspectRatio/AspectRatio.d.ts +24 -0
  5. package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
  6. package/dist/AspectRatio/AspectRatio.js +23 -4
  7. package/dist/Avatar/Avatar.d.ts.map +1 -1
  8. package/dist/Avatar/Avatar.js +15 -10
  9. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  10. package/dist/AvatarGroup/AvatarGroup.js +3 -2
  11. package/dist/Banner/Banner.d.ts +40 -15
  12. package/dist/Banner/Banner.d.ts.map +1 -1
  13. package/dist/Banner/Banner.js +51 -18
  14. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  15. package/dist/BottomSheet/BottomSheet.js +17 -4
  16. package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
  17. package/dist/BottomSheet/BottomSheetSwitcher.js +3 -2
  18. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  19. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -10
  20. package/dist/Button/Button.d.ts.map +1 -1
  21. package/dist/Button/Button.js +5 -4
  22. package/dist/ButtonGroup/ButtonGroup.d.ts +5 -1
  23. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  24. package/dist/ButtonGroup/ButtonGroup.js +26 -7
  25. package/dist/Calendar/Calendar.d.ts +3 -1
  26. package/dist/Calendar/Calendar.d.ts.map +1 -1
  27. package/dist/Calendar/Calendar.js +13 -14
  28. package/dist/Calendar/getInitialFocusDate.d.ts +46 -0
  29. package/dist/Calendar/getInitialFocusDate.d.ts.map +1 -0
  30. package/dist/Calendar/getInitialFocusDate.js +62 -0
  31. package/dist/Carousel/Carousel.d.ts.map +1 -1
  32. package/dist/Carousel/Carousel.js +3 -3
  33. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  34. package/dist/Chat/ChatComposerInput.js +2 -3
  35. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  36. package/dist/Chat/ChatLayout.js +3 -3
  37. package/dist/Chat/ChatMessageList.d.ts +22 -3
  38. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  39. package/dist/Chat/ChatMessageList.js +6 -3
  40. package/dist/Chat/ChatTokenizedText.d.ts +2 -0
  41. package/dist/Chat/ChatTokenizedText.d.ts.map +1 -1
  42. package/dist/Chat/ChatTokenizedText.js +11 -6
  43. package/dist/Chat/useChatComposerTokens.js +1 -1
  44. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  45. package/dist/CheckboxInput/CheckboxInput.js +3 -2
  46. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  47. package/dist/ClickableCard/ClickableCard.js +3 -3
  48. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  49. package/dist/CommandPalette/CommandPaletteInput.js +3 -2
  50. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  51. package/dist/CommandPalette/CommandPaletteItem.js +3 -2
  52. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  53. package/dist/ContextMenu/ContextMenu.js +4 -3
  54. package/dist/DateInput/DateInput.d.ts.map +1 -1
  55. package/dist/DateInput/DateInput.js +3 -2
  56. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  57. package/dist/DateInput/TouchDateField.js +38 -4
  58. package/dist/DateTimeInput/DateTimeInput.d.ts +21 -1
  59. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  60. package/dist/DateTimeInput/DateTimeInput.js +406 -12
  61. package/dist/DateTimeInput/index.d.ts +1 -1
  62. package/dist/DateTimeInput/index.d.ts.map +1 -1
  63. package/dist/Dialog/Dialog.d.ts +7 -1
  64. package/dist/Dialog/Dialog.d.ts.map +1 -1
  65. package/dist/Dialog/Dialog.js +89 -58
  66. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  67. package/dist/DropdownMenu/DropdownMenuSubMenu.js +6 -5
  68. package/dist/FileInput/FileInput.d.ts.map +1 -1
  69. package/dist/FileInput/FileInput.js +3 -2
  70. package/dist/Heading/Heading.d.ts.map +1 -1
  71. package/dist/Heading/Heading.js +6 -2
  72. package/dist/HoverCard/HoverCard.d.ts +6 -0
  73. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  74. package/dist/HoverCard/useHoverCard.d.ts +5 -0
  75. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  76. package/dist/HoverCard/useHoverCard.js +67 -25
  77. package/dist/Item/Item.d.ts.map +1 -1
  78. package/dist/Item/Item.js +4 -2
  79. package/dist/Kbd/Kbd.d.ts.map +1 -1
  80. package/dist/Kbd/Kbd.js +10 -3
  81. package/dist/Layer/LayerDepthContext.d.ts +52 -0
  82. package/dist/Layer/LayerDepthContext.d.ts.map +1 -0
  83. package/dist/Layer/LayerDepthContext.js +66 -0
  84. package/dist/Layer/index.d.ts +3 -0
  85. package/dist/Layer/index.d.ts.map +1 -1
  86. package/dist/Layer/index.js +4 -0
  87. package/dist/Layer/layerStack.d.ts +98 -0
  88. package/dist/Layer/layerStack.d.ts.map +1 -0
  89. package/dist/Layer/layerStack.js +306 -0
  90. package/dist/Layer/useLayerDismissal.d.ts +93 -0
  91. package/dist/Layer/useLayerDismissal.d.ts.map +1 -0
  92. package/dist/Layer/useLayerDismissal.js +91 -0
  93. package/dist/Lightbox/Lightbox.d.ts +1 -0
  94. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  95. package/dist/Lightbox/Lightbox.js +103 -81
  96. package/dist/Markdown/index.d.ts +1 -1
  97. package/dist/Markdown/index.d.ts.map +1 -1
  98. package/dist/Markdown/parser.d.ts +38 -1
  99. package/dist/Markdown/parser.d.ts.map +1 -1
  100. package/dist/Markdown/parser.js +177 -29
  101. package/dist/MobileNav/MobileNav.d.ts +6 -1
  102. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  103. package/dist/MobileNav/MobileNav.js +61 -36
  104. package/dist/MultiSelector/MultiSelector.d.ts +15 -1
  105. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  106. package/dist/MultiSelector/MultiSelector.js +17 -6
  107. package/dist/MultiSelector/index.d.ts +1 -1
  108. package/dist/MultiSelector/index.d.ts.map +1 -1
  109. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  110. package/dist/NavMenu/NavHeadingMenu.js +3 -2
  111. package/dist/Outline/Outline.d.ts.map +1 -1
  112. package/dist/Outline/Outline.js +3 -2
  113. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  114. package/dist/OverflowList/OverflowList.js +3 -2
  115. package/dist/Overlay/Overlay.d.ts.map +1 -1
  116. package/dist/Overlay/Overlay.js +3 -2
  117. package/dist/Popover/usePopover.d.ts.map +1 -1
  118. package/dist/Popover/usePopover.js +20 -17
  119. package/dist/PowerSearch/PowerSearch.d.ts +8 -2
  120. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  121. package/dist/PowerSearch/PowerSearch.js +19 -6
  122. package/dist/PowerSearch/PowerSearchEditPopover.d.ts +3 -1
  123. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  124. package/dist/PowerSearch/PowerSearchEditPopover.js +28 -6
  125. package/dist/PowerSearch/PowerSearchValueEditor.d.ts +3 -1
  126. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  127. package/dist/PowerSearch/PowerSearchValueEditor.js +19 -9
  128. package/dist/PowerSearch/types.d.ts +2 -0
  129. package/dist/PowerSearch/types.d.ts.map +1 -1
  130. package/dist/PowerSearch/usePowerSearchSource.d.ts +6 -1
  131. package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
  132. package/dist/PowerSearch/usePowerSearchSource.js +14 -5
  133. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  134. package/dist/Resizable/ResizeHandle.js +3 -2
  135. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  136. package/dist/SegmentedControl/SegmentedControl.js +3 -2
  137. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  138. package/dist/SelectableCard/SelectableCard.js +3 -3
  139. package/dist/Selector/Selector.d.ts.map +1 -1
  140. package/dist/Selector/Selector.js +5 -0
  141. package/dist/SideNav/SideNav.d.ts.map +1 -1
  142. package/dist/SideNav/SideNav.js +4 -3
  143. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  144. package/dist/SideNav/SideNavHeading.js +6 -5
  145. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  146. package/dist/SideNav/SideNavItem.js +4 -3
  147. package/dist/Slider/Slider.d.ts.map +1 -1
  148. package/dist/Slider/Slider.js +3 -3
  149. package/dist/Spinner/Spinner.d.ts +3 -3
  150. package/dist/Spinner/Spinner.d.ts.map +1 -1
  151. package/dist/Spinner/Spinner.js +130 -78
  152. package/dist/Stepper/Step.d.ts +140 -0
  153. package/dist/Stepper/Step.d.ts.map +1 -0
  154. package/dist/Stepper/Step.js +1045 -0
  155. package/dist/Stepper/StepStatus.d.ts +24 -0
  156. package/dist/Stepper/StepStatus.d.ts.map +1 -0
  157. package/dist/Stepper/StepStatus.js +1 -0
  158. package/dist/Stepper/Stepper.d.ts +93 -0
  159. package/dist/Stepper/Stepper.d.ts.map +1 -0
  160. package/dist/Stepper/Stepper.js +184 -0
  161. package/dist/Stepper/StepperContext.d.ts +41 -0
  162. package/dist/Stepper/StepperContext.d.ts.map +1 -0
  163. package/dist/Stepper/StepperContext.js +34 -0
  164. package/dist/Stepper/index.d.ts +8 -0
  165. package/dist/Stepper/index.d.ts.map +1 -0
  166. package/dist/Stepper/index.js +7 -0
  167. package/dist/Stepper/stepper.stylex.d.ts +15 -0
  168. package/dist/Stepper/stepper.stylex.d.ts.map +1 -0
  169. package/dist/Stepper/stepper.stylex.js +20 -0
  170. package/dist/Switch/Switch.d.ts.map +1 -1
  171. package/dist/Switch/Switch.js +3 -2
  172. package/dist/TabList/Tab.d.ts +15 -2
  173. package/dist/TabList/Tab.d.ts.map +1 -1
  174. package/dist/TabList/Tab.js +32 -7
  175. package/dist/TabList/TabList.d.ts +46 -7
  176. package/dist/TabList/TabList.d.ts.map +1 -1
  177. package/dist/TabList/TabList.js +353 -23
  178. package/dist/TabList/TabListContext.d.ts +11 -1
  179. package/dist/TabList/TabListContext.d.ts.map +1 -1
  180. package/dist/TabList/TabListContext.js +11 -2
  181. package/dist/TabList/TabMenu.d.ts.map +1 -1
  182. package/dist/TabList/TabMenu.js +3 -2
  183. package/dist/TabList/index.d.ts +2 -2
  184. package/dist/TabList/index.d.ts.map +1 -1
  185. package/dist/Table/BaseTable.d.ts.map +1 -1
  186. package/dist/Table/BaseTable.js +4 -1
  187. package/dist/Table/TableRow.d.ts +16 -5
  188. package/dist/Table/TableRow.d.ts.map +1 -1
  189. package/dist/Table/TableRow.js +16 -5
  190. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  191. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +20 -8
  192. package/dist/Table/plugins/pagination/useTablePagination.d.ts +3 -0
  193. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  194. package/dist/Table/plugins/pagination/useTablePagination.js +21 -4
  195. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  196. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +7 -2
  197. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -1
  198. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +10 -3
  199. package/dist/Table/plugins/selection/useTableSelection.d.ts +16 -0
  200. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  201. package/dist/Table/plugins/selection/useTableSelection.js +19 -5
  202. package/dist/Table/types.d.ts +22 -4
  203. package/dist/Table/types.d.ts.map +1 -1
  204. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  205. package/dist/Table/useBaseTablePlugins.js +5 -0
  206. package/dist/Text/Text.d.ts.map +1 -1
  207. package/dist/Text/Text.js +6 -2
  208. package/dist/TextArea/TextArea.d.ts.map +1 -1
  209. package/dist/TextArea/TextArea.js +3 -2
  210. package/dist/TextInput/TextInput.d.ts.map +1 -1
  211. package/dist/TextInput/TextInput.js +3 -2
  212. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  213. package/dist/TimeInput/TimeInput.js +3 -2
  214. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  215. package/dist/Timestamp/Timestamp.js +5 -3
  216. package/dist/Token/TokenLink.d.ts.map +1 -1
  217. package/dist/Token/TokenLink.js +2 -2
  218. package/dist/Tokenizer/Tokenizer.d.ts +3 -1
  219. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  220. package/dist/Tokenizer/Tokenizer.js +2 -0
  221. package/dist/Tooltip/Tooltip.d.ts +5 -0
  222. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  223. package/dist/Tooltip/useTooltip.d.ts +5 -0
  224. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  225. package/dist/Tooltip/useTooltip.js +39 -24
  226. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  227. package/dist/TopNav/TopNavHeading.js +3 -2
  228. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  229. package/dist/TopNav/TopNavMegaMenu.js +3 -3
  230. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  231. package/dist/TopNav/TopNavMenu.js +4 -3
  232. package/dist/Typeahead/BaseTypeahead.d.ts +2 -0
  233. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  234. package/dist/Typeahead/BaseTypeahead.js +68 -29
  235. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  236. package/dist/Typeahead/Typeahead.js +3 -2
  237. package/dist/astryx.css +68 -2
  238. package/dist/hooks/index.d.ts +1 -0
  239. package/dist/hooks/index.d.ts.map +1 -1
  240. package/dist/hooks/index.js +1 -0
  241. package/dist/hooks/useFocusTrap.d.ts +7 -4
  242. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  243. package/dist/hooks/useFocusTrap.js +60 -95
  244. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  245. package/dist/hooks/useHotkeys.js +10 -3
  246. package/dist/hooks/useMergedRefs.d.ts +18 -0
  247. package/dist/hooks/useMergedRefs.d.ts.map +1 -0
  248. package/dist/hooks/useMergedRefs.js +25 -0
  249. package/dist/index.d.ts +1 -0
  250. package/dist/index.d.ts.map +1 -1
  251. package/dist/index.js +1 -0
  252. package/dist/utils/groupItems.d.ts +5 -1
  253. package/dist/utils/groupItems.d.ts.map +1 -1
  254. package/dist/utils/groupItems.js +9 -9
  255. package/dist/utils/index.d.ts +1 -1
  256. package/dist/utils/index.d.ts.map +1 -1
  257. package/dist/utils/sharedResizeObserver.d.ts.map +1 -1
  258. package/dist/utils/sharedResizeObserver.js +10 -1
  259. package/dist/utils/timeParser.d.ts +1 -1
  260. package/dist/utils/timeParser.d.ts.map +1 -1
  261. package/dist/utils/timeParser.js +26 -12
  262. package/locales/en.json +31 -7
  263. package/locales/pseudo.json +18 -0
  264. package/package.json +8 -3
  265. package/src/AppShell/AppShell.tsx +3 -2
  266. package/src/AspectRatio/AspectRatio.doc.mjs +6 -3
  267. package/src/AspectRatio/AspectRatio.test.tsx +57 -10
  268. package/src/AspectRatio/AspectRatio.tsx +40 -1
  269. package/src/Avatar/Avatar.doc.mjs +2 -1
  270. package/src/Avatar/Avatar.test.tsx +17 -0
  271. package/src/Avatar/Avatar.tsx +61 -38
  272. package/src/AvatarGroup/AvatarGroup.tsx +3 -2
  273. package/src/Banner/Banner.doc.mjs +13 -11
  274. package/src/Banner/Banner.test.tsx +102 -15
  275. package/src/Banner/Banner.tsx +72 -25
  276. package/src/BottomSheet/BottomSheet.test.tsx +123 -0
  277. package/src/BottomSheet/BottomSheet.tsx +17 -4
  278. package/src/BottomSheet/BottomSheetSwitcher.tsx +3 -2
  279. package/src/Breadcrumbs/BreadcrumbItem.tsx +9 -5
  280. package/src/Button/Button.doc.mjs +22 -0
  281. package/src/Button/Button.test.tsx +13 -0
  282. package/src/Button/Button.tsx +5 -4
  283. package/src/ButtonGroup/ButtonGroup.doc.mjs +10 -1
  284. package/src/ButtonGroup/ButtonGroup.test.tsx +391 -1
  285. package/src/ButtonGroup/ButtonGroup.tsx +23 -5
  286. package/src/Calendar/Calendar.doc.mjs +4 -3
  287. package/src/Calendar/Calendar.test.tsx +52 -0
  288. package/src/Calendar/Calendar.tsx +18 -15
  289. package/src/Calendar/getInitialFocusDate.test.ts +94 -0
  290. package/src/Calendar/getInitialFocusDate.ts +93 -0
  291. package/src/Carousel/Carousel.tsx +3 -2
  292. package/src/Chat/ChatComposerInput.tsx +5 -1
  293. package/src/Chat/ChatLayout.tsx +3 -2
  294. package/src/Chat/ChatMessageList.doc.mjs +9 -1
  295. package/src/Chat/ChatMessageList.test.tsx +46 -0
  296. package/src/Chat/ChatMessageList.tsx +28 -4
  297. package/src/Chat/ChatTokenizedText.tsx +17 -10
  298. package/src/Chat/useChatComposerTokens.ts +1 -1
  299. package/src/CheckboxInput/CheckboxInput.tsx +3 -2
  300. package/src/ClickableCard/ClickableCard.tsx +3 -2
  301. package/src/CodeBlock/CodeBlock.doc.mjs +6 -0
  302. package/src/CommandPalette/CommandPaletteInput.tsx +3 -2
  303. package/src/CommandPalette/CommandPaletteItem.tsx +3 -2
  304. package/src/ContextMenu/ContextMenu.doc.mjs +5 -0
  305. package/src/ContextMenu/ContextMenu.tsx +4 -3
  306. package/src/DateInput/DateInput.tsx +3 -2
  307. package/src/DateInput/DateInputTouch.test.tsx +36 -0
  308. package/src/DateInput/TouchDateField.tsx +38 -4
  309. package/src/DateTimeInput/DateTimeInput.doc.mjs +18 -0
  310. package/src/DateTimeInput/DateTimeInput.test.tsx +1088 -0
  311. package/src/DateTimeInput/DateTimeInput.tsx +556 -24
  312. package/src/DateTimeInput/index.ts +1 -0
  313. package/src/Dialog/Dialog.doc.mjs +8 -3
  314. package/src/Dialog/Dialog.test.tsx +261 -1
  315. package/src/Dialog/Dialog.tsx +109 -58
  316. package/src/DropdownMenu/DropdownMenu.doc.mjs +6 -0
  317. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +8 -5
  318. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +8 -11
  319. package/src/Field/Field.doc.mjs +13 -2
  320. package/src/FileInput/FileInput.tsx +3 -2
  321. package/src/Heading/Heading.test.tsx +23 -1
  322. package/src/Heading/Heading.tsx +6 -2
  323. package/src/HoverCard/HoverCard.doc.mjs +5 -0
  324. package/src/HoverCard/HoverCard.tsx +6 -0
  325. package/src/HoverCard/useHoverCard.tsx +81 -30
  326. package/src/Item/Item.tsx +5 -4
  327. package/src/Kbd/Kbd.test.tsx +17 -0
  328. package/src/Kbd/Kbd.tsx +10 -3
  329. package/src/Layer/LayerDepthContext.tsx +62 -0
  330. package/src/Layer/index.ts +9 -0
  331. package/src/Layer/layerDismissalFamilies.test.tsx +359 -0
  332. package/src/Layer/layerDismissalInvariants.test.tsx +417 -0
  333. package/src/Layer/layerStack.ts +362 -0
  334. package/src/Layer/useLayerDismissal.test.tsx +469 -0
  335. package/src/Layer/useLayerDismissal.ts +173 -0
  336. package/src/Layout/Layout.doc.mjs +2 -2
  337. package/src/Lightbox/Lightbox.doc.mjs +12 -0
  338. package/src/Lightbox/Lightbox.tsx +124 -102
  339. package/src/Link/Link.doc.mjs +39 -0
  340. package/src/Markdown/Markdown.doc.mjs +5 -0
  341. package/src/Markdown/incremental.test.ts +16 -2
  342. package/src/Markdown/index.ts +1 -0
  343. package/src/Markdown/parser.perf.test.ts +96 -1
  344. package/src/Markdown/parser.test.ts +145 -2
  345. package/src/Markdown/parser.ts +244 -29
  346. package/src/MobileNav/MobileNav.tsx +62 -36
  347. package/src/MoreMenu/MoreMenu.doc.mjs +7 -1
  348. package/src/MultiSelector/MultiSelector.doc.mjs +27 -0
  349. package/src/MultiSelector/MultiSelector.test.tsx +106 -0
  350. package/src/MultiSelector/MultiSelector.tsx +50 -6
  351. package/src/MultiSelector/index.ts +1 -0
  352. package/src/NavMenu/NavHeadingMenu.tsx +3 -2
  353. package/src/NumberInput/NumberInput.doc.mjs +5 -0
  354. package/src/Outline/Outline.tsx +3 -2
  355. package/src/OverflowList/OverflowList.tsx +3 -2
  356. package/src/Overlay/Overlay.tsx +3 -2
  357. package/src/Popover/usePopover.tsx +31 -28
  358. package/src/PowerSearch/PowerSearch.doc.mjs +160 -28
  359. package/src/PowerSearch/PowerSearch.test.tsx +240 -1
  360. package/src/PowerSearch/PowerSearch.tsx +34 -5
  361. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +201 -0
  362. package/src/PowerSearch/PowerSearchEditPopover.tsx +29 -2
  363. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +47 -1
  364. package/src/PowerSearch/PowerSearchValueEditor.tsx +15 -0
  365. package/src/PowerSearch/types.ts +2 -0
  366. package/src/PowerSearch/usePowerSearchSource.test.ts +82 -2
  367. package/src/PowerSearch/usePowerSearchSource.ts +13 -3
  368. package/src/Resizable/ResizeHandle.tsx +3 -2
  369. package/src/SegmentedControl/SegmentedControl.tsx +3 -2
  370. package/src/SelectableCard/SelectableCard.tsx +3 -2
  371. package/src/Selector/Selector.doc.mjs +12 -1
  372. package/src/Selector/Selector.test.tsx +45 -1
  373. package/src/Selector/Selector.tsx +11 -1
  374. package/src/SideNav/SideNav.doc.mjs +6 -6
  375. package/src/SideNav/SideNav.tsx +4 -2
  376. package/src/SideNav/SideNavHeading.tsx +10 -10
  377. package/src/SideNav/SideNavItem.tsx +4 -2
  378. package/src/Slider/Slider.tsx +3 -2
  379. package/src/Spinner/Spinner.test.tsx +147 -1
  380. package/src/Spinner/Spinner.tsx +120 -101
  381. package/src/Stepper/Step.doc.mjs +98 -0
  382. package/src/Stepper/Step.tsx +1638 -0
  383. package/src/Stepper/StepStatus.ts +26 -0
  384. package/src/Stepper/Stepper.doc.mjs +370 -0
  385. package/src/Stepper/Stepper.test.tsx +1024 -0
  386. package/src/Stepper/Stepper.tsx +258 -0
  387. package/src/Stepper/StepperContext.ts +71 -0
  388. package/src/Stepper/index.ts +18 -0
  389. package/src/Stepper/stepper.stylex.ts +19 -0
  390. package/src/Switch/Switch.tsx +3 -2
  391. package/src/TabList/Tab.doc.mjs +13 -1
  392. package/src/TabList/Tab.tsx +67 -7
  393. package/src/TabList/TabList.doc.mjs +22 -6
  394. package/src/TabList/TabList.test.tsx +559 -0
  395. package/src/TabList/TabList.tsx +490 -22
  396. package/src/TabList/TabListContext.ts +13 -2
  397. package/src/TabList/TabMenu.tsx +3 -2
  398. package/src/TabList/index.ts +6 -2
  399. package/src/Table/BaseTable.tsx +6 -3
  400. package/src/Table/Table.doc.mjs +6 -1
  401. package/src/Table/Table.test.tsx +132 -14
  402. package/src/Table/TableBody.doc.mjs +40 -0
  403. package/src/Table/TableFooter.doc.mjs +40 -0
  404. package/src/Table/TableHeader.doc.mjs +40 -0
  405. package/src/Table/TableRow.tsx +16 -5
  406. package/src/Table/plugins/groupedRows/useTableGroupedRows-perf.test.tsx +112 -0
  407. package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +100 -0
  408. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +17 -8
  409. package/src/Table/plugins/pagination/useTablePagination.test.tsx +34 -4
  410. package/src/Table/plugins/pagination/useTablePagination.tsx +35 -3
  411. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +9 -2
  412. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +13 -7
  413. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +12 -3
  414. package/src/Table/plugins/selection/useTableSelection.test.tsx +76 -0
  415. package/src/Table/plugins/selection/useTableSelection.tsx +40 -7
  416. package/src/Table/types.ts +22 -4
  417. package/src/Table/useBaseTablePlugins.ts +5 -0
  418. package/src/Table/useTableGroupedRows.doc.mjs +5 -4
  419. package/src/Table/useTablePagination.doc.mjs +7 -0
  420. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  421. package/src/Table/useTableSelection.doc.mjs +31 -0
  422. package/src/Text/Text.test.tsx +23 -1
  423. package/src/Text/Text.tsx +6 -2
  424. package/src/TextArea/TextArea.tsx +3 -2
  425. package/src/TextInput/TextInput.doc.mjs +10 -0
  426. package/src/TextInput/TextInput.tsx +3 -2
  427. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  428. package/src/TimeInput/TimeInput.tsx +2 -2
  429. package/src/Timestamp/Timestamp.tsx +4 -2
  430. package/src/Toast/Toast.doc.mjs +6 -0
  431. package/src/Token/TokenLink.tsx +2 -2
  432. package/src/Tokenizer/Tokenizer.doc.mjs +27 -0
  433. package/src/Tokenizer/Tokenizer.tsx +4 -0
  434. package/src/Toolbar/Toolbar.doc.mjs +5 -0
  435. package/src/Tooltip/Tooltip.doc.mjs +5 -0
  436. package/src/Tooltip/Tooltip.test.tsx +32 -0
  437. package/src/Tooltip/Tooltip.tsx +5 -0
  438. package/src/Tooltip/useTooltip.tsx +47 -24
  439. package/src/TopNav/TopNavHeading.tsx +3 -2
  440. package/src/TopNav/TopNavMegaMenu.tsx +3 -2
  441. package/src/TopNav/TopNavMenu.tsx +4 -3
  442. package/src/Typeahead/BaseTypeahead.doc.mjs +11 -0
  443. package/src/Typeahead/BaseTypeahead.tsx +85 -33
  444. package/src/Typeahead/Typeahead.doc.mjs +5 -0
  445. package/src/Typeahead/Typeahead.test.tsx +49 -0
  446. package/src/Typeahead/Typeahead.tsx +3 -2
  447. package/src/hooks/index.ts +2 -0
  448. package/src/hooks/useContainerReveal.doc.mjs +2 -2
  449. package/src/hooks/useFocusTrap.test.tsx +14 -0
  450. package/src/hooks/useFocusTrap.ts +62 -111
  451. package/src/hooks/useFocusTrapEscapeShim.test.tsx +165 -0
  452. package/src/hooks/useHotkeys.test.ts +18 -0
  453. package/src/hooks/useHotkeys.ts +10 -3
  454. package/src/hooks/useMergedRefs.doc.mjs +79 -0
  455. package/src/hooks/useMergedRefs.test.tsx +62 -0
  456. package/src/hooks/useMergedRefs.ts +36 -0
  457. package/src/index.ts +1 -0
  458. package/src/theme/MediaTheme.doc.mjs +5 -5
  459. package/src/utils/groupItems.ts +12 -6
  460. package/src/utils/index.ts +1 -1
  461. package/src/utils/sharedResizeObserver.ts +10 -2
  462. package/src/utils/timeParser.test.ts +6 -1
  463. package/src/utils/timeParser.ts +35 -13
  464. package/src/__tests__/inputWidthContract.test.tsx +0 -69
@@ -11,6 +11,7 @@
11
11
  *
12
12
  * SYNC: When modified, update these files to stay in sync:
13
13
  * - /packages/core/src/Calendar/Calendar.doc.mjs (props table, features, implementation notes)
14
+ * - /packages/core/src/Calendar/getInitialFocusDate.ts (which month the calendar opens on)
14
15
  * - /packages/core/src/Calendar/Calendar.test.tsx (tests for new/changed behavior)
15
16
  * - /packages/core/src/Calendar/index.ts (exports if types change)
16
17
  * - /apps/storybook/stories/Calendar.stories.tsx (storybook stories)
@@ -60,6 +61,7 @@ import {
60
61
  } from '../utils/plainDate';
61
62
  import {mergeProps, composeEventHandlers, rtlStyles} from '../utils';
62
63
  import {focusOutlineProps} from '../utils/focusOutline.stylex';
64
+ import {getInitialFocusDate} from './getInitialFocusDate';
63
65
  import {
64
66
  computeDayCellState,
65
67
  computeRangeRounding,
@@ -142,7 +144,9 @@ interface CalendarBaseProps extends Omit<
142
144
 
143
145
  /**
144
146
  * Controlled focus date (which month is visible).
145
- * If not provided, defaults to selected date or today.
147
+ * If not provided, defaults to the selected date, else today clamped into
148
+ * the `min`/`max` window (so a window that excludes today opens on the
149
+ * bound nearest to it, not on an all-disabled month).
146
150
  */
147
151
  focusDate?: ISODateString;
148
152
 
@@ -277,20 +281,19 @@ export function Calendar({ref, ...props}: CalendarProps) {
277
281
  // Determine effective value
278
282
  const effectiveValue = value !== undefined ? value : internalValue;
279
283
 
280
- // Focus date state (which month is visible)
281
- const [internalFocusDate, setInternalFocusDate] = useState<PlainDate>(() => {
282
- if (focusDateProp) {
283
- return plainDateFromISO(focusDateProp);
284
- }
285
- if (effectiveValue) {
286
- if (typeof effectiveValue === 'string') {
287
- return plainDateFromISO(effectiveValue);
288
- } else {
289
- return plainDateFromISO(effectiveValue.start);
290
- }
291
- }
292
- return plainDateToday();
293
- });
284
+ // Focus date state (which month is visible). Falls back to today, clamped
285
+ // into the min/max window so a window that doesn't contain today doesn't
286
+ // open on an all-disabled month.
287
+ const [internalFocusDate, setInternalFocusDate] = useState<PlainDate>(() =>
288
+ getInitialFocusDate({
289
+ focusDate: focusDateProp,
290
+ value: effectiveValue,
291
+ min,
292
+ max,
293
+ numberOfMonths,
294
+ today,
295
+ }),
296
+ );
294
297
 
295
298
  // Use controlled focusDate if callback is provided, otherwise use internal state
296
299
  const isControlledFocus =
@@ -0,0 +1,94 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file getInitialFocusDate.test.ts
5
+ * @input Uses vitest
6
+ * @output Test suite for getInitialFocusDate
7
+ * @position Tests for getInitialFocusDate.ts
8
+ *
9
+ * SYNC: When getInitialFocusDate.ts changes, update tests accordingly
10
+ */
11
+
12
+ import {describe, it, expect} from 'vitest';
13
+ import {getInitialFocusDate} from './getInitialFocusDate';
14
+ import {plainDateFromISO, plainDateToISO} from '../utils/plainDate';
15
+
16
+ const TODAY = plainDateFromISO('2026-08-21');
17
+
18
+ function focusISO(options: Partial<Parameters<typeof getInitialFocusDate>[0]>) {
19
+ return plainDateToISO(
20
+ getInitialFocusDate({numberOfMonths: 1, today: TODAY, ...options}),
21
+ );
22
+ }
23
+
24
+ describe('getInitialFocusDate', () => {
25
+ it('opens on today when there are no bounds', () => {
26
+ expect(focusISO({})).toBe('2026-08-21');
27
+ });
28
+
29
+ it('opens on today when today is inside the min/max window', () => {
30
+ expect(focusISO({min: '2026-01-01', max: '2026-12-31'})).toBe('2026-08-21');
31
+ });
32
+
33
+ it('treats the bounds as inclusive', () => {
34
+ expect(focusISO({min: '2026-08-21'})).toBe('2026-08-21');
35
+ expect(focusISO({max: '2026-08-21'})).toBe('2026-08-21');
36
+ });
37
+
38
+ it('opens on min when the whole window is in the future', () => {
39
+ expect(focusISO({min: '2027-03-04', max: '2027-06-30'})).toBe('2027-03-04');
40
+ });
41
+
42
+ it('opens on max when the whole window is in the past', () => {
43
+ expect(focusISO({min: '2019-01-01', max: '2019-04-30'})).toBe('2019-04-01');
44
+ });
45
+
46
+ it('clamps against a one-sided min', () => {
47
+ expect(focusISO({min: '2030-05-17'})).toBe('2030-05-17');
48
+ });
49
+
50
+ it('clamps against a one-sided max', () => {
51
+ expect(focusISO({max: '2020-02-09'})).toBe('2020-02-01');
52
+ });
53
+
54
+ it('lands max in the last pane in the two-month layout', () => {
55
+ // Opening on max's month would spend the second pane entirely
56
+ // out of bounds, so the window's end sits on the right instead.
57
+ expect(focusISO({max: '2020-02-09', numberOfMonths: 2})).toBe('2020-01-01');
58
+ });
59
+
60
+ it('does not shift the two-month layout back past min', () => {
61
+ expect(
62
+ focusISO({min: '2020-02-03', max: '2020-02-09', numberOfMonths: 2}),
63
+ ).toBe('2020-02-03');
64
+ });
65
+
66
+ it('prefers the selected value over the bounds', () => {
67
+ expect(focusISO({value: '2031-07-04', min: '2019-01-01'})).toBe(
68
+ '2031-07-04',
69
+ );
70
+ });
71
+
72
+ it('uses a range value start as the visible month', () => {
73
+ expect(focusISO({value: {start: '2019-03-08', end: '2019-03-19'}})).toBe(
74
+ '2019-03-08',
75
+ );
76
+ });
77
+
78
+ it('prefers an explicit focusDate over everything else', () => {
79
+ expect(
80
+ focusISO({
81
+ focusDate: '2015-11-02',
82
+ value: '2031-07-04',
83
+ min: '2026-01-01',
84
+ max: '2026-12-31',
85
+ }),
86
+ ).toBe('2015-11-02');
87
+ });
88
+
89
+ it('prefers min when the bounds are inverted', () => {
90
+ // Degenerate input (min after max) — resolve it deterministically
91
+ // rather than reading the second bound off a contradiction.
92
+ expect(focusISO({min: '2027-01-01', max: '2020-01-01'})).toBe('2027-01-01');
93
+ });
94
+ });
@@ -0,0 +1,93 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file getInitialFocusDate.ts
5
+ * @input PlainDate utilities, min/max bounds, today
6
+ * @output Exports getInitialFocusDate — the month Calendar opens on
7
+ * @position Calendar-specific helper; used by Calendar.tsx
8
+ *
9
+ * SYNC: When modified, update:
10
+ * - /packages/core/src/Calendar/getInitialFocusDate.test.ts
11
+ * - /packages/core/src/Calendar/Calendar.tsx (the only caller)
12
+ */
13
+
14
+ import type {ISODateString, DateRange} from '../utils/dateTypes';
15
+ import {
16
+ type PlainDate,
17
+ plainDateFromISO,
18
+ plainDateSetFirstOfMonth,
19
+ plainDateAddMonths,
20
+ plainDateIsBefore,
21
+ plainDateIsAfter,
22
+ } from '../utils/plainDate';
23
+
24
+ export interface InitialFocusDateOptions {
25
+ /** Controlled visible month, if the consumer supplied one. */
26
+ focusDate?: ISODateString;
27
+ /** Selected value (single date or range), controlled or uncontrolled. */
28
+ value?: ISODateString | DateRange;
29
+ /** Earliest selectable date. */
30
+ min?: ISODateString;
31
+ /** Latest selectable date. */
32
+ max?: ISODateString;
33
+ /** How many month panes the calendar renders (1 or 2). */
34
+ numberOfMonths: number;
35
+ /** Today, injected so the caller keeps a single memoized source of "now". */
36
+ today: PlainDate;
37
+ }
38
+
39
+ /**
40
+ * Picks the date whose month the calendar opens on.
41
+ *
42
+ * An explicit `focusDate` wins, then the selected value — both are the
43
+ * consumer's own instruction about where to look, so neither is second-guessed
44
+ * against `min`/`max`.
45
+ *
46
+ * Otherwise the calendar opens on today, clamped into the `min`/`max` window.
47
+ * Without the clamp a window that doesn't contain today (a 2019 audit range, a
48
+ * booking window opening next spring) opened on today's month with every day
49
+ * disabled, leaving prev/next clicking as the only way in.
50
+ *
51
+ * When clamping forward to `min`, that month leads: the whole selectable window
52
+ * runs ahead of it. When clamping back to `max`, the last pane lands on `max`'s
53
+ * month instead — with `numberOfMonths={2}` opening on `[max, max + 1]` would
54
+ * spend half the calendar on a month that is entirely out of bounds. That
55
+ * shift never crosses `min`'s month.
56
+ */
57
+ export function getInitialFocusDate(
58
+ options: InitialFocusDateOptions,
59
+ ): PlainDate {
60
+ const {focusDate, value, min, max, numberOfMonths, today} = options;
61
+
62
+ if (focusDate) {
63
+ return plainDateFromISO(focusDate);
64
+ }
65
+
66
+ if (value) {
67
+ return plainDateFromISO(typeof value === 'string' ? value : value.start);
68
+ }
69
+
70
+ const minDate = min ? plainDateFromISO(min) : null;
71
+ const maxDate = max ? plainDateFromISO(max) : null;
72
+
73
+ if (minDate && plainDateIsBefore(today, minDate)) {
74
+ return minDate;
75
+ }
76
+
77
+ if (maxDate && plainDateIsAfter(today, maxDate)) {
78
+ const lastPaneOffset = Math.max(0, numberOfMonths - 1);
79
+ const shifted = plainDateAddMonths(
80
+ plainDateSetFirstOfMonth(maxDate),
81
+ -lastPaneOffset,
82
+ );
83
+ if (
84
+ minDate &&
85
+ plainDateIsBefore(shifted, plainDateSetFirstOfMonth(minDate))
86
+ ) {
87
+ return minDate;
88
+ }
89
+ return shifted;
90
+ }
91
+
92
+ return today;
93
+ }
@@ -46,11 +46,12 @@ import {useLayer} from '../Layer';
46
46
  import {useScrollOverflow} from '../hooks/useScrollOverflow';
47
47
  import {isRtlElement} from '../hooks/isRtlElement';
48
48
  import type {BaseProps} from '../BaseProps';
49
- import {mergeProps, mergeRefs, rtlStyles} from '../utils';
49
+ import {mergeProps, rtlStyles} from '../utils';
50
50
  import type {SpacingStep} from '../utils/types';
51
51
  import {themeProps} from '../utils/themeProps';
52
52
  import {useTranslator} from '../i18n';
53
53
 
54
+ import {useMergedRefs} from '../hooks/useMergedRefs';
54
55
  /**
55
56
  * Imperative control surface for the Carousel, accessed via the `handleRef`
56
57
  * prop. Methods drive the same native-scroll machinery as the built-in
@@ -506,7 +507,7 @@ export function Carousel({
506
507
 
507
508
  return (
508
509
  <div
509
- ref={mergeRefs(ref, layer.ref as React.Ref<HTMLDivElement>)}
510
+ ref={useMergedRefs(ref, layer.ref as React.Ref<HTMLDivElement>)}
510
511
  data-testid={testId}
511
512
  {...htmlProps}
512
513
  {...mergeProps(
@@ -255,6 +255,10 @@ const styles = stylex.create({
255
255
  opacity: 0.5,
256
256
  pointerEvents: 'none' as const,
257
257
  },
258
+ tokenSpan: {
259
+ display: 'inline-flex',
260
+ verticalAlign: 'middle',
261
+ },
258
262
  });
259
263
 
260
264
  // =============================================================================
@@ -749,7 +753,7 @@ export function ChatComposerTokenElement({token}: {token: ChatComposerToken}) {
749
753
  data-astryx-token=""
750
754
  data-astryx-token-value={token.value}
751
755
  contentEditable={false}
752
- style={{display: 'inline-flex', verticalAlign: 'baseline'}}>
756
+ {...stylex.props(styles.tokenSpan)}>
753
757
  {isCustomToken(token) ? (
754
758
  token.render()
755
759
  ) : (
@@ -33,7 +33,7 @@ import {type ReactNode, useMemo, useRef} from 'react';
33
33
  import * as stylex from '@stylexjs/stylex';
34
34
  import {spacingVars} from '../theme/tokens.stylex';
35
35
  import type {BaseProps} from '../BaseProps';
36
- import {mergeProps, mergeRefs} from '../utils';
36
+ import {mergeProps} from '../utils';
37
37
  import {useChatStreamScroll} from './useChatStreamScroll';
38
38
  import {useChatNewMessages} from './useChatNewMessages';
39
39
  import {ChatLayoutScrollButton} from './ChatLayoutScrollButton';
@@ -41,6 +41,7 @@ import {ChatLayoutContext} from './ChatContext';
41
41
  import {themeProps} from '../utils/themeProps';
42
42
  import {useTranslator} from '../i18n';
43
43
 
44
+ import {useMergedRefs} from '../hooks/useMergedRefs';
44
45
  // =============================================================================
45
46
  // Types
46
47
  // =============================================================================
@@ -343,7 +344,7 @@ export function ChatLayout({
343
344
  <ChatLayoutContext value={layoutContext}>
344
345
  <div
345
346
  {...rest}
346
- ref={mergeRefs(ref, rootRef)}
347
+ ref={useMergedRefs(ref, rootRef)}
347
348
  data-testid={testId}
348
349
  {...mergeProps(
349
350
  themeProps('chat-layout', {density}),
@@ -7,7 +7,7 @@ export const docs = {
7
7
  subComponentOf: 'Chat',
8
8
  displayName: 'Chat Message List',
9
9
  isHiddenFromOverview: true,
10
- description: `Presentational message container with density context and infinite scroll support. Provides role="log" with aria-live="polite" for accessibility. A flex spacer pushes messages to the bottom when the list isn't full.`,
10
+ description: `Presentational message container with density context and infinite scroll support. Provides role="log" with aria-live="polite" for accessibility. A flex spacer pushes messages to the bottom when the list isn't full; set align="top" to start messages at the top instead.`,
11
11
  props: [
12
12
  {
13
13
  name: 'children',
@@ -45,6 +45,12 @@ export const docs = {
45
45
  type: '0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10',
46
46
  description: 'Gap between top-level message rows. Defaults to the selected density; override for LLM event streams or independent rows that need different spacing from density.',
47
47
  },
48
+ {
49
+ name: 'align',
50
+ type: "'top' | 'bottom'",
51
+ description: "Vertical alignment when the list is shorter than its container. 'bottom' fills free space with a spacer so short conversations sit above the composer; 'top' omits the spacer so messages start at the top. Only affects a non-full list; overflowing lists scroll identically, preserving auto-scroll-to-bottom.",
52
+ default: "'bottom'",
53
+ },
48
54
  {
49
55
  name: 'isStreaming',
50
56
  type: 'boolean',
@@ -65,6 +71,7 @@ export const docsZh = {
65
71
  scrollToTopAction: '用户滚动到顶部时触发的异步操作。用于加载更早的消息。',
66
72
  density: '视觉密度,通过上下文传递给子消息。',
67
73
  gap: '顶层消息行之间的间距。默认跟随密度;当 LLM 事件流等独立行需要不同间距时可覆盖。',
74
+ align: "列表内容不足一屏时的垂直对齐方式。'bottom' 用占位块填满剩余空间,让简短对话贴近输入框;'top' 省略占位块,让消息从顶部开始。仅影响未填满的列表,溢出后两种模式滚动一致,保持自动滚动到底部。",
68
75
  isStreaming: '是否有助手消息正在流式输出。标记日志为 aria-busy,让屏幕阅读器等待并在完成后一次性播报,而不是每个 token 都重复播报。',
69
76
  },
70
77
  };
@@ -80,6 +87,7 @@ export const docsDense = {
80
87
  scrollToTopAction: 'async action at scroll top; load older msgs',
81
88
  density: 'visual density; flows to children via context',
82
89
  gap: 'top-level row gap; defaults to density spacing; override for independent LLM/tool rows',
90
+ align: "'top'|'bottom' (def bottom); bottom spacer pushes short lists down, top starts at top; overflow scrolls same either way",
83
91
  isStreaming: 'assistant streaming? marks log aria-busy so SR announces the completed msg once',
84
92
  },
85
93
  };
@@ -83,4 +83,50 @@ describe('ChatMessageList', () => {
83
83
  );
84
84
  expect(screen.getByTestId('chat-list')).toBeTruthy();
85
85
  });
86
+
87
+ // With no scrollToTopAction the spacer is the only aria-hidden element in
88
+ // the list, so its presence maps 1:1 to the aria-hidden count.
89
+ it('renders the bottom spacer by default', () => {
90
+ render(
91
+ <ChatMessageList data-testid="list">
92
+ <div>msg</div>
93
+ </ChatMessageList>,
94
+ );
95
+ expect(
96
+ screen.getByTestId('list').querySelectorAll('[aria-hidden]'),
97
+ ).toHaveLength(1);
98
+ });
99
+
100
+ it('renders the bottom spacer when align="bottom"', () => {
101
+ render(
102
+ <ChatMessageList align="bottom" data-testid="list">
103
+ <div>msg</div>
104
+ </ChatMessageList>,
105
+ );
106
+ expect(
107
+ screen.getByTestId('list').querySelectorAll('[aria-hidden]'),
108
+ ).toHaveLength(1);
109
+ });
110
+
111
+ it('omits the spacer when align="top"', () => {
112
+ render(
113
+ <ChatMessageList align="top" data-testid="list">
114
+ <div>msg</div>
115
+ </ChatMessageList>,
116
+ );
117
+ expect(
118
+ screen.getByTestId('list').querySelectorAll('[aria-hidden]'),
119
+ ).toHaveLength(0);
120
+ });
121
+
122
+ it('still renders children when align="top"', () => {
123
+ render(
124
+ <ChatMessageList align="top">
125
+ <ChatMessage sender="assistant">
126
+ <ChatMessageBubble>Hello</ChatMessageBubble>
127
+ </ChatMessage>
128
+ </ChatMessageList>,
129
+ );
130
+ expect(screen.getByText('Hello')).toBeTruthy();
131
+ });
86
132
  });
@@ -10,7 +10,8 @@
10
10
  *
11
11
  * Renders a container with role="log" for chat message histories.
12
12
  * Handles density context, configurable gap, empty state,
13
- * a spacer that pushes messages to the bottom, and an infinite scroll sentinel.
13
+ * a configurable spacer (align) that pushes messages to the bottom,
14
+ * and an infinite scroll sentinel.
14
15
  *
15
16
  * Auto-scroll and the scroll-to-bottom button are owned by
16
17
  * ChatLayout. When used standalone (without a layout), the list
@@ -73,6 +74,24 @@ export interface ChatMessageListProps extends BaseProps<HTMLDivElement> {
73
74
  */
74
75
  gap?: SpacingStep;
75
76
 
77
+ /**
78
+ * Vertical alignment of messages when the list is shorter than its
79
+ * container.
80
+ *
81
+ * - `'bottom'` (default): a spacer fills the free space and pushes
82
+ * messages to the bottom, so a short conversation sits just above the
83
+ * composer — the familiar messaging-app layout.
84
+ * - `'top'`: the spacer is omitted, so messages start at the top and grow
85
+ * downward — better for document-style or log-style lists.
86
+ *
87
+ * This only changes the resting position of a non-full list. Once messages
88
+ * overflow the container the spacer collapses to zero in both modes, so
89
+ * ChatLayout auto-scroll-to-bottom behavior is identical either way.
90
+ *
91
+ * @default 'bottom'
92
+ */
93
+ align?: 'top' | 'bottom';
94
+
76
95
  /**
77
96
  * Whether an assistant message is actively streaming into the list.
78
97
  *
@@ -183,7 +202,8 @@ const gapStyles = stylex.create({
183
202
  *
184
203
  * Renders messages in a flex column with density-based spacing.
185
204
  * Override gap to tune row spacing separately from density.
186
- * A spacer pushes content to the bottom when the list isn't full.
205
+ * By default a spacer pushes content to the bottom when the list isn't full;
206
+ * set `align='top'` to start messages at the top instead.
187
207
  * Supports loading older messages via `scrollToTopAction`.
188
208
  *
189
209
  * Auto-scroll and the scroll-to-bottom button are owned by
@@ -204,6 +224,7 @@ export function ChatMessageList({
204
224
  scrollToTopAction,
205
225
  density = 'balanced',
206
226
  gap,
227
+ align = 'bottom',
207
228
  isStreaming = false,
208
229
  xstyle,
209
230
  className,
@@ -291,8 +312,11 @@ export function ChatMessageList({
291
312
  </div>
292
313
  )}
293
314
 
294
- {/* Spacer pushes messages to bottom when list isn't full */}
295
- <div {...stylex.props(styles.spacer)} aria-hidden />
315
+ {/* Spacer pushes messages to bottom when the list isn't full.
316
+ Omitted for top alignment so messages start at the top. */}
317
+ {align === 'bottom' && (
318
+ <div {...stylex.props(styles.spacer)} aria-hidden />
319
+ )}
296
320
 
297
321
  {/* Messages or empty state */}
298
322
  {hasChildren ? (
@@ -14,6 +14,8 @@
14
14
  *
15
15
  * SYNC: When modified, update:
16
16
  * - /packages/core/src/Chat/index.ts
17
+ * - /packages/core/src/Chat/ChatComposerInput.tsx (token alignment must match,
18
+ * or a token moves when the message is sent)
17
19
  * - /packages/cli/assets/templates/blocks/components/ChatTokenizedText/ (block examples)
18
20
  */
19
21
 
@@ -33,6 +35,10 @@ const styles = stylex.create({
33
35
  root: {
34
36
  display: 'inline',
35
37
  },
38
+ token: {
39
+ display: 'inline-flex',
40
+ verticalAlign: 'middle',
41
+ },
36
42
  });
37
43
 
38
44
  // =============================================================================
@@ -156,16 +162,17 @@ function renderTokens(text: string, tokens: ChatComposerToken[]): ReactNode[] {
156
162
  const token = tokenMap.get(matched);
157
163
  if (token) {
158
164
  parts.push(
159
- isCustomToken(token) ? (
160
- <span key={`${matched}-${match.index}`}>{token.render()}</span>
161
- ) : (
162
- <Badge
163
- key={`${matched}-${match.index}`}
164
- label={token.label}
165
- variant={token.variant}
166
- icon={token.icon}
167
- />
168
- ),
165
+ <span key={`${matched}-${match.index}`} {...stylex.props(styles.token)}>
166
+ {isCustomToken(token) ? (
167
+ token.render()
168
+ ) : (
169
+ <Badge
170
+ label={token.label}
171
+ variant={token.variant}
172
+ icon={token.icon}
173
+ />
174
+ )}
175
+ </span>,
169
176
  );
170
177
  }
171
178
 
@@ -120,7 +120,7 @@ export function useChatComposerTokens({
120
120
  span.setAttribute('data-astryx-token-id', id);
121
121
  span.contentEditable = 'false';
122
122
  span.style.display = 'inline-flex';
123
- span.style.verticalAlign = 'baseline';
123
+ span.style.verticalAlign = 'middle';
124
124
 
125
125
  range.deleteContents();
126
126
  range.insertNode(span);
@@ -44,7 +44,7 @@ import type {IconType} from '../Icon';
44
44
  import type {InputStatus} from '../Field/types';
45
45
  import {Spinner} from '../Spinner';
46
46
  import {useTooltip} from '../Tooltip';
47
- import {mergeProps, mergeRefs} from '../utils';
47
+ import {mergeProps} from '../utils';
48
48
  import {indicatorScope} from '../Indicator/indicator.markers.stylex';
49
49
  import {useIndicatorFocusRing} from '../hooks/useIndicatorFocusRing';
50
50
  import {useResolvedRequired} from '../hooks/useResolvedRequired';
@@ -52,6 +52,7 @@ import {useIndicator} from '../Indicator';
52
52
  import {themeProps} from '../utils/themeProps';
53
53
  import {CheckboxListContext} from '../CheckboxList/CheckboxListContext';
54
54
 
55
+ import {useMergedRefs} from '../hooks/useMergedRefs';
55
56
  const styles = stylex.create({
56
57
  container: {
57
58
  display: 'flex',
@@ -409,7 +410,7 @@ export function CheckboxInput({
409
410
  {...focusProps}>
410
411
  <input
411
412
  {...rest}
412
- ref={mergeRefs(
413
+ ref={useMergedRefs(
413
414
  ref,
414
415
  indeterminateRef,
415
416
  disabledMessageTooltip.positionRef,
@@ -39,7 +39,7 @@ import {
39
39
  easeVars,
40
40
  } from '../theme/tokens.stylex';
41
41
  import type {SizeValue, SpacingStep, Elevation} from '../utils/types';
42
- import {mergeProps, mergeRefs} from '../utils';
42
+ import {mergeProps} from '../utils';
43
43
  import {Card} from '../Card/Card';
44
44
  import type {CardVariant} from '../Card/Card';
45
45
  import {useClickableContainer} from '../hooks/useClickableContainer';
@@ -48,6 +48,7 @@ import {useLinkComponent} from '../Link/useLinkComponent';
48
48
  import {themeProps} from '../utils/themeProps';
49
49
  import {focusOutlineProps} from '../utils/focusOutline.stylex';
50
50
 
51
+ import {useMergedRefs} from '../hooks/useMergedRefs';
51
52
  // =============================================================================
52
53
  // Styles — only the interactive layer, Card handles everything else
53
54
  // =============================================================================
@@ -297,7 +298,7 @@ export function ClickableCard({
297
298
 
298
299
  return (
299
300
  <Card
300
- ref={mergeRefs(ref, containerRef)}
301
+ ref={useMergedRefs(ref, containerRef)}
301
302
  width={width}
302
303
  height={height}
303
304
  maxWidth={maxWidth}
@@ -95,6 +95,12 @@ export const docs = {
95
95
  type: 'SyntaxThemeDefinition',
96
96
  description: 'Per-instance syntax theme override. Shorthand for wrapping the block in <SyntaxTheme theme={...}>. Accepts a preset from @astryxdesign/core/theme/syntax or a theme created with defineSyntaxTheme(). Defaults to the nearest SyntaxTheme ancestor or the theme-level syntax colors.',
97
97
  },
98
+ {
99
+ name: 'highlightMode',
100
+ type: "'auto' | 'ranges' | 'spans'",
101
+ description: 'Syntax highlighting rendering mode.',
102
+ default: "'auto'",
103
+ },
98
104
  {
99
105
  name: 'isCollapsible',
100
106
  type: 'boolean',
@@ -16,7 +16,7 @@ import {useCallback, useEffect, useRef, type ReactNode} from 'react';
16
16
  import * as stylex from '@stylexjs/stylex';
17
17
  import {Icon} from '../Icon';
18
18
  import {Spinner} from '../Spinner';
19
- import {mergeProps, mergeRefs} from '../utils';
19
+ import {mergeProps} from '../utils';
20
20
  import {
21
21
  colorVars,
22
22
  typeScaleVars,
@@ -29,6 +29,7 @@ import type {BaseProps} from '../BaseProps';
29
29
  import {themeProps} from '../utils/themeProps';
30
30
  import {useTranslator} from '../i18n';
31
31
 
32
+ import {useMergedRefs} from '../hooks/useMergedRefs';
32
33
  const styles = stylex.create({
33
34
  wrapper: {
34
35
  display: 'flex',
@@ -216,7 +217,7 @@ export function CommandPaletteInput({
216
217
  <Icon icon="search" size="sm" color="inherit" />
217
218
  </span>
218
219
  <input
219
- ref={mergeRefs(ref, inputRef)}
220
+ ref={useMergedRefs(ref, inputRef)}
220
221
  type="text"
221
222
  role="combobox"
222
223
  aria-expanded={ctx?.isOpen ?? true}
@@ -14,7 +14,7 @@
14
14
  import {useCallback, useEffect, useMemo, useRef, type ReactNode} from 'react';
15
15
  import * as stylex from '@stylexjs/stylex';
16
16
  import type {BaseProps} from '../BaseProps';
17
- import {mergeProps, mergeRefs} from '../utils';
17
+ import {mergeProps} from '../utils';
18
18
  import {composeEventHandlers} from '../utils/composeEventHandlers';
19
19
  import {
20
20
  colorVars,
@@ -27,6 +27,7 @@ import {useCommandPaletteContext} from './CommandPaletteContext';
27
27
  import {useDialogContext} from '../Dialog/DialogContext';
28
28
  import {themeProps} from '../utils/themeProps';
29
29
 
30
+ import {useMergedRefs} from '../hooks/useMergedRefs';
30
31
  const HOVER_HOVER = '@media (hover: hover)';
31
32
 
32
33
  const styles = stylex.create({
@@ -191,7 +192,7 @@ export function CommandPaletteItem({
191
192
 
192
193
  return (
193
194
  <div
194
- ref={mergeRefs(ref, itemRef)}
195
+ ref={useMergedRefs(ref, itemRef)}
195
196
  {...props}
196
197
  id={ctx && itemIndex >= 0 ? ctx.getItemId(itemIndex) : undefined}
197
198
  role="option"
@@ -58,6 +58,11 @@ export const docs = {
58
58
  description: 'Accessible name for the menu surface, announced when it opens.',
59
59
  default: "'Context menu'",
60
60
  },
61
+ {
62
+ name: 'onOpenChange',
63
+ type: '(isOpen: boolean) => void',
64
+ description: 'Callback fired when the menu opens or closes.',
65
+ },
61
66
  {
62
67
  name: 'isDisabled',
63
68
  type: 'boolean',