@astryxdesign/core 0.1.7 → 0.1.8-canary.0c4d8d7

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 (428) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +30 -19
  3. package/dist/AppShell/AppShell.d.ts.map +1 -1
  4. package/dist/AppShell/AppShell.js +6 -2
  5. package/dist/AppShell/AppShellMobileContext.d.ts +8 -0
  6. package/dist/AppShell/AppShellMobileContext.d.ts.map +1 -1
  7. package/dist/Avatar/Avatar.d.ts +10 -5
  8. package/dist/Avatar/Avatar.d.ts.map +1 -1
  9. package/dist/Avatar/Avatar.js +12 -8
  10. package/dist/Avatar/AvatarSizeContext.d.ts +1 -1
  11. package/dist/Avatar/AvatarSizeContext.js +1 -1
  12. package/dist/Avatar/AvatarStatusDot.d.ts +2 -2
  13. package/dist/Avatar/AvatarStatusDot.js +2 -2
  14. package/dist/AvatarGroup/AvatarGroup.d.ts +2 -2
  15. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  16. package/dist/AvatarGroup/AvatarGroup.js +2 -2
  17. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  18. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  19. package/dist/AvatarGroup/AvatarGroupOverflow.js +16 -14
  20. package/dist/Banner/Banner.d.ts +9 -1
  21. package/dist/Banner/Banner.d.ts.map +1 -1
  22. package/dist/Banner/Banner.js +31 -2
  23. package/dist/Button/Button.d.ts +8 -2
  24. package/dist/Button/Button.d.ts.map +1 -1
  25. package/dist/Button/Button.js +31 -2
  26. package/dist/ButtonGroup/ButtonGroup.d.ts +9 -1
  27. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  28. package/dist/ButtonGroup/ButtonGroup.js +30 -1
  29. package/dist/Card/Card.d.ts +8 -2
  30. package/dist/Card/Card.d.ts.map +1 -1
  31. package/dist/Card/Card.js +28 -2
  32. package/dist/Carousel/Carousel.d.ts +6 -2
  33. package/dist/Carousel/Carousel.d.ts.map +1 -1
  34. package/dist/Carousel/Carousel.js +55 -5
  35. package/dist/Chat/ChatComposer.d.ts +8 -0
  36. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  37. package/dist/Chat/ChatComposer.js +31 -6
  38. package/dist/Chat/ChatComposerDrawer.d.ts +2 -0
  39. package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
  40. package/dist/Chat/ChatComposerDrawer.js +11 -1
  41. package/dist/Chat/ChatComposerInput.d.ts +20 -3
  42. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  43. package/dist/Chat/ChatComposerInput.js +19 -3
  44. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  45. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  46. package/dist/Chat/ChatMessage.d.ts +1 -1
  47. package/dist/Chat/ChatMessage.d.ts.map +1 -1
  48. package/dist/Chat/ChatMessage.js +1 -1
  49. package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
  50. package/dist/Chat/ChatSendButton.d.ts.map +1 -1
  51. package/dist/Chat/ChatToolCalls.d.ts +5 -1
  52. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  53. package/dist/Chat/ChatToolCalls.js +12 -3
  54. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  55. package/dist/Citation/Citation.d.ts.map +1 -1
  56. package/dist/Citation/Citation.js +10 -4
  57. package/dist/ClickableCard/ClickableCard.d.ts +9 -2
  58. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  59. package/dist/ClickableCard/ClickableCard.js +3 -0
  60. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  61. package/dist/CodeBlock/CodeBlock.js +8 -1
  62. package/dist/Collapsible/Collapsible.d.ts +11 -1
  63. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  64. package/dist/Collapsible/Collapsible.js +36 -4
  65. package/dist/CommandPalette/CommandPaletteInput.d.ts +7 -1
  66. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  67. package/dist/CommandPalette/CommandPaletteInput.js +6 -1
  68. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  69. package/dist/ContextMenu/ContextMenu.js +4 -3
  70. package/dist/ContextMenu/index.d.ts +3 -0
  71. package/dist/ContextMenu/index.d.ts.map +1 -1
  72. package/dist/ContextMenu/index.js +7 -1
  73. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  74. package/dist/DateTimeInput/DateTimeInput.js +10 -1
  75. package/dist/Dialog/Dialog.d.ts.map +1 -1
  76. package/dist/Dialog/Dialog.js +2 -1
  77. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  78. package/dist/DropdownMenu/DropdownMenu.js +8 -4
  79. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +88 -0
  80. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -0
  81. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +169 -0
  82. package/dist/DropdownMenu/DropdownMenuContext.d.ts +17 -1
  83. package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
  84. package/dist/DropdownMenu/DropdownMenuContext.js +18 -0
  85. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +68 -0
  86. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -0
  87. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +69 -0
  88. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +69 -0
  89. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -0
  90. package/dist/DropdownMenu/DropdownMenuRadioItem.js +178 -0
  91. package/dist/DropdownMenu/index.d.ts +5 -1
  92. package/dist/DropdownMenu/index.d.ts.map +1 -1
  93. package/dist/DropdownMenu/index.js +10 -1
  94. package/dist/DropdownMenu/menuItemRoles.d.ts +13 -0
  95. package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -0
  96. package/dist/DropdownMenu/menuItemRoles.js +15 -0
  97. package/dist/FieldStatus/FieldStatus.d.ts +8 -0
  98. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  99. package/dist/FieldStatus/FieldStatus.js +22 -4
  100. package/dist/FileInput/FileInput.d.ts.map +1 -1
  101. package/dist/FileInput/FileInput.js +10 -2
  102. package/dist/HoverCard/HoverCard.d.ts +9 -1
  103. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  104. package/dist/HoverCard/HoverCard.js +2 -0
  105. package/dist/HoverCard/useHoverCard.d.ts +8 -0
  106. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  107. package/dist/HoverCard/useHoverCard.js +7 -2
  108. package/dist/Icon/Icon.d.ts +43 -13
  109. package/dist/Icon/Icon.d.ts.map +1 -1
  110. package/dist/Icon/Icon.js +62 -27
  111. package/dist/Layer/useLayer.d.ts +6 -0
  112. package/dist/Layer/useLayer.d.ts.map +1 -1
  113. package/dist/Layer/useLayer.js +2 -0
  114. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  115. package/dist/MobileNav/MobileNav.js +6 -1
  116. package/dist/MobileNav/MobileNavToggle.d.ts.map +1 -1
  117. package/dist/MobileNav/MobileNavToggle.js +4 -0
  118. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  119. package/dist/MultiSelector/MultiSelector.js +39 -12
  120. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  121. package/dist/MultiSelector/hooks.js +16 -0
  122. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  123. package/dist/Outline/Outline.d.ts +68 -3
  124. package/dist/Outline/Outline.d.ts.map +1 -1
  125. package/dist/Outline/Outline.js +128 -26
  126. package/dist/Outline/useScrollSpy.d.ts +27 -7
  127. package/dist/Outline/useScrollSpy.d.ts.map +1 -1
  128. package/dist/Outline/useScrollSpy.js +202 -70
  129. package/dist/OverflowList/OverflowList.d.ts +20 -3
  130. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  131. package/dist/OverflowList/OverflowList.js +29 -4
  132. package/dist/PowerSearch/PowerSearchEditPopover.js +1 -1
  133. package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
  134. package/dist/SelectableCard/SelectableCard.d.ts +9 -2
  135. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  136. package/dist/SelectableCard/SelectableCard.js +14 -11
  137. package/dist/Selector/Selector.d.ts.map +1 -1
  138. package/dist/Selector/Selector.js +4 -2
  139. package/dist/Selector/hooks.d.ts.map +1 -1
  140. package/dist/Selector/hooks.js +16 -0
  141. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  142. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  143. package/dist/SideNav/SideNavHeading.js +21 -8
  144. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  145. package/dist/Slider/Slider.d.ts.map +1 -1
  146. package/dist/Slider/Slider.js +15 -1
  147. package/dist/TabList/Tab.d.ts.map +1 -1
  148. package/dist/TabList/Tab.js +2 -2
  149. package/dist/TabList/TabList.d.ts.map +1 -1
  150. package/dist/TabList/TabList.js +2 -0
  151. package/dist/TabList/TabMenu.d.ts.map +1 -1
  152. package/dist/TabList/TabMenu.js +1 -1
  153. package/dist/Table/Table.d.ts +1 -1
  154. package/dist/Table/Table.js +1 -1
  155. package/dist/Table/index.d.ts +4 -0
  156. package/dist/Table/index.d.ts.map +1 -1
  157. package/dist/Table/index.js +2 -0
  158. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  159. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  160. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +24 -2
  161. package/dist/Table/plugins/rowStatus/index.d.ts +3 -0
  162. package/dist/Table/plugins/rowStatus/index.d.ts.map +1 -0
  163. package/dist/Table/plugins/rowStatus/index.js +3 -0
  164. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts +60 -0
  165. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -0
  166. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +145 -0
  167. package/dist/Table/plugins/selection/useTableSelection.d.ts +12 -0
  168. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  169. package/dist/Table/plugins/selection/useTableSelection.js +4 -1
  170. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  171. package/dist/Table/plugins/sortable/useTableSortable.js +18 -7
  172. package/dist/Table/plugins/tree/index.d.ts +5 -0
  173. package/dist/Table/plugins/tree/index.d.ts.map +1 -0
  174. package/dist/Table/plugins/tree/index.js +4 -0
  175. package/dist/Table/plugins/tree/useTableTreeData.d.ts +38 -0
  176. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -0
  177. package/dist/Table/plugins/tree/useTableTreeData.js +358 -0
  178. package/dist/Table/plugins/tree/useTableTreeState.d.ts +51 -0
  179. package/dist/Table/plugins/tree/useTableTreeState.d.ts.map +1 -0
  180. package/dist/Table/plugins/tree/useTableTreeState.js +175 -0
  181. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  182. package/dist/Table/useBaseTablePlugins.js +6 -3
  183. package/dist/Thumbnail/Thumbnail.d.ts +8 -1
  184. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  185. package/dist/Thumbnail/Thumbnail.js +46 -8
  186. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  187. package/dist/TimeInput/TimeInput.js +15 -4
  188. package/dist/Tokenizer/Tokenizer.d.ts +1 -1
  189. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  190. package/dist/Tokenizer/Tokenizer.js +16 -4
  191. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  192. package/dist/Toolbar/Toolbar.js +8 -1
  193. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  194. package/dist/TopNav/TopNavHeading.js +18 -8
  195. package/dist/TopNav/TopNavMegaMenu.js +10 -4
  196. package/dist/TopNav/TopNavMenu.d.ts +2 -1
  197. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  198. package/dist/TopNav/TopNavMenu.js +68 -6
  199. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  200. package/dist/TreeList/TreeListItem.js +5 -14
  201. package/dist/astryx.css +51 -16
  202. package/dist/astryx.umd.js +56 -56
  203. package/dist/astryx.umd.js.map +4 -4
  204. package/dist/authoring/doc.d.ts.map +1 -1
  205. package/dist/hooks/computeOverflow.d.ts +47 -0
  206. package/dist/hooks/computeOverflow.d.ts.map +1 -0
  207. package/dist/hooks/computeOverflow.js +213 -0
  208. package/dist/hooks/useOverflow.d.ts +19 -0
  209. package/dist/hooks/useOverflow.d.ts.map +1 -1
  210. package/dist/hooks/useOverflow.js +37 -25
  211. package/dist/theme/Theme.js +1 -1
  212. package/dist/theme/defineTheme.d.ts +1 -1
  213. package/dist/theme/defineTheme.js +1 -1
  214. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  215. package/dist/theme/generateThemeRules.js +51 -0
  216. package/dist/theme/hct.d.ts +0 -1
  217. package/dist/theme/hct.d.ts.map +1 -1
  218. package/dist/theme/hct.js +0 -3
  219. package/dist/utils/types.d.ts +13 -0
  220. package/dist/utils/types.d.ts.map +1 -1
  221. package/docs.mjs +32 -461
  222. package/locales/en.json +212 -172
  223. package/locales/fr-FR.json +10 -0
  224. package/locales/pseudo.json +30 -0
  225. package/package.json +3 -1
  226. package/scripts/postinstall.mjs +93 -0
  227. package/src/AppShell/AppShell.tsx +6 -0
  228. package/src/AppShell/AppShellMobileContext.tsx +8 -0
  229. package/src/AppShell/useAppShellMobile.doc.mjs +8 -0
  230. package/src/Avatar/Avatar.doc.mjs +6 -6
  231. package/src/Avatar/Avatar.tsx +16 -11
  232. package/src/Avatar/AvatarSizeContext.ts +1 -1
  233. package/src/Avatar/AvatarStatusDot.tsx +2 -2
  234. package/src/AvatarGroup/AvatarGroup.doc.mjs +1 -1
  235. package/src/AvatarGroup/AvatarGroup.test.tsx +17 -3
  236. package/src/AvatarGroup/AvatarGroup.tsx +3 -3
  237. package/src/AvatarGroup/AvatarGroupOverflow.doc.mjs +4 -4
  238. package/src/AvatarGroup/AvatarGroupOverflow.tsx +21 -5
  239. package/src/Banner/Banner.doc.mjs +9 -0
  240. package/src/Banner/Banner.test.tsx +30 -0
  241. package/src/Banner/Banner.tsx +47 -3
  242. package/src/Button/Button.doc.mjs +9 -0
  243. package/src/Button/Button.test.tsx +49 -0
  244. package/src/Button/Button.tsx +25 -1
  245. package/src/ButtonGroup/ButtonGroup.doc.mjs +3 -0
  246. package/src/ButtonGroup/ButtonGroup.test.tsx +37 -0
  247. package/src/ButtonGroup/ButtonGroup.tsx +32 -0
  248. package/src/Card/Card.doc.mjs +9 -0
  249. package/src/Card/Card.test.tsx +21 -3
  250. package/src/Card/Card.tsx +35 -2
  251. package/src/Carousel/Carousel.doc.mjs +1 -0
  252. package/src/Carousel/Carousel.test.tsx +149 -1
  253. package/src/Carousel/Carousel.tsx +62 -5
  254. package/src/Chat/Chat.doc.mjs +1 -1
  255. package/src/Chat/ChatComposer.doc.mjs +9 -0
  256. package/src/Chat/ChatComposer.test.tsx +35 -0
  257. package/src/Chat/ChatComposer.tsx +58 -9
  258. package/src/Chat/ChatComposerDrawer.test.tsx +61 -0
  259. package/src/Chat/ChatComposerDrawer.tsx +11 -1
  260. package/src/Chat/ChatComposerInput.doc.mjs +7 -0
  261. package/src/Chat/ChatComposerInput.test.tsx +87 -0
  262. package/src/Chat/ChatComposerInput.tsx +35 -3
  263. package/src/Chat/ChatLayout.tsx +3 -1
  264. package/src/Chat/ChatLayoutScrollButton.tsx +3 -1
  265. package/src/Chat/ChatMessage.tsx +4 -2
  266. package/src/Chat/ChatMessageMetadata.tsx +1 -2
  267. package/src/Chat/ChatSendButton.tsx +5 -1
  268. package/src/Chat/ChatToolCalls.test.tsx +64 -0
  269. package/src/Chat/ChatToolCalls.tsx +13 -1
  270. package/src/Chat/useTriggerMenu.tsx +3 -1
  271. package/src/CheckboxInput/CheckboxInput.doc.mjs +1 -1
  272. package/src/CheckboxInput/CheckboxInput.test.tsx +32 -1
  273. package/src/CheckboxList/CheckboxList.test.tsx +9 -1
  274. package/src/Citation/Citation.test.tsx +51 -0
  275. package/src/Citation/Citation.tsx +8 -2
  276. package/src/ClickableCard/ClickableCard.doc.mjs +2 -0
  277. package/src/ClickableCard/ClickableCard.test.tsx +34 -0
  278. package/src/ClickableCard/ClickableCard.tsx +11 -1
  279. package/src/CodeBlock/CodeBlock.test.tsx +56 -0
  280. package/src/CodeBlock/CodeBlock.tsx +9 -1
  281. package/src/Collapsible/Collapsible.doc.mjs +7 -0
  282. package/src/Collapsible/Collapsible.test.tsx +80 -0
  283. package/src/Collapsible/Collapsible.tsx +58 -5
  284. package/src/CommandPalette/CommandPaletteInput.doc.mjs +26 -9
  285. package/src/CommandPalette/CommandPaletteInput.test.tsx +34 -0
  286. package/src/CommandPalette/CommandPaletteInput.tsx +11 -0
  287. package/src/ContextMenu/ContextMenu.test.tsx +75 -1
  288. package/src/ContextMenu/ContextMenu.tsx +10 -5
  289. package/src/ContextMenu/index.ts +15 -0
  290. package/src/DateTimeInput/DateTimeInput.test.tsx +35 -0
  291. package/src/DateTimeInput/DateTimeInput.tsx +14 -2
  292. package/src/Dialog/Dialog.tsx +12 -2
  293. package/src/DropdownMenu/DropdownMenu.doc.mjs +23 -0
  294. package/src/DropdownMenu/DropdownMenu.tsx +11 -6
  295. package/src/DropdownMenu/DropdownMenuCheckboxItem.doc.mjs +95 -0
  296. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +236 -0
  297. package/src/DropdownMenu/DropdownMenuContext.tsx +29 -1
  298. package/src/DropdownMenu/DropdownMenuItem.doc.mjs +7 -1
  299. package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +80 -0
  300. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +118 -0
  301. package/src/DropdownMenu/DropdownMenuRadioItem.doc.mjs +80 -0
  302. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +232 -0
  303. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +163 -0
  304. package/src/DropdownMenu/index.ts +23 -3
  305. package/src/DropdownMenu/menuItemRoles.ts +22 -0
  306. package/src/Field/Field.doc.mjs +1 -1
  307. package/src/Field/Field.test.tsx +57 -12
  308. package/src/Field/FieldLabel.doc.mjs +1 -1
  309. package/src/FieldStatus/FieldStatus.doc.mjs +17 -5
  310. package/src/FieldStatus/FieldStatus.test.tsx +78 -40
  311. package/src/FieldStatus/FieldStatus.tsx +22 -4
  312. package/src/FileInput/FileInput.test.tsx +52 -3
  313. package/src/FileInput/FileInput.tsx +13 -2
  314. package/src/HoverCard/HoverCard.doc.mjs +13 -0
  315. package/src/HoverCard/HoverCard.test.tsx +21 -2
  316. package/src/HoverCard/HoverCard.tsx +11 -0
  317. package/src/HoverCard/useHoverCard.doc.mjs +2 -0
  318. package/src/HoverCard/useHoverCard.tsx +24 -2
  319. package/src/Icon/Icon.doc.mjs +12 -0
  320. package/src/Icon/Icon.test.tsx +142 -0
  321. package/src/Icon/Icon.tsx +98 -30
  322. package/src/IconButton/IconButton.doc.mjs +8 -0
  323. package/src/IconButton/IconButton.test.tsx +15 -18
  324. package/src/Item/Item.doc.mjs +1 -1
  325. package/src/Layer/useLayer.tsx +8 -0
  326. package/src/MobileNav/MobileNav.tsx +12 -1
  327. package/src/MobileNav/MobileNavToggle.test.tsx +116 -0
  328. package/src/MobileNav/MobileNavToggle.tsx +9 -1
  329. package/src/MultiSelector/MultiSelector.doc.mjs +4 -2
  330. package/src/MultiSelector/MultiSelector.test.tsx +162 -1
  331. package/src/MultiSelector/MultiSelector.tsx +55 -12
  332. package/src/MultiSelector/hooks.ts +17 -0
  333. package/src/NumberInput/NumberInput.doc.mjs +2 -2
  334. package/src/NumberInput/NumberInput.test.tsx +11 -7
  335. package/src/NumberInput/NumberInput.tsx +1 -2
  336. package/src/Outline/Outline.doc.mjs +123 -3
  337. package/src/Outline/Outline.test.tsx +735 -0
  338. package/src/Outline/Outline.tsx +183 -33
  339. package/src/Outline/useScrollSpy.ts +248 -72
  340. package/src/OverflowList/OverflowList.doc.mjs +39 -6
  341. package/src/OverflowList/OverflowList.test.tsx +111 -0
  342. package/src/OverflowList/OverflowList.tsx +48 -8
  343. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +67 -0
  344. package/src/PowerSearch/PowerSearchEditPopover.tsx +1 -1
  345. package/src/PowerSearch/usePowerSearchSource.ts +1 -2
  346. package/src/SelectableCard/SelectableCard.doc.mjs +2 -0
  347. package/src/SelectableCard/SelectableCard.test.tsx +46 -4
  348. package/src/SelectableCard/SelectableCard.tsx +27 -14
  349. package/src/Selector/Selector.test.tsx +69 -0
  350. package/src/Selector/Selector.tsx +5 -1
  351. package/src/Selector/SelectorOption.doc.mjs +1 -1
  352. package/src/Selector/hooks.ts +17 -0
  353. package/src/SideNav/SideNav.test.tsx +119 -3
  354. package/src/SideNav/SideNavCollapseButton.tsx +6 -1
  355. package/src/SideNav/SideNavHeading.tsx +24 -8
  356. package/src/SideNav/SideNavItem.doc.mjs +2 -2
  357. package/src/SideNav/SideNavItem.tsx +5 -1
  358. package/src/Slider/Slider.test.tsx +62 -2
  359. package/src/Slider/Slider.tsx +15 -1
  360. package/src/Switch/Switch.doc.mjs +1 -1
  361. package/src/Switch/Switch.test.tsx +32 -1
  362. package/src/TabList/Tab.tsx +5 -1
  363. package/src/TabList/TabList.doc.mjs +3 -3
  364. package/src/TabList/TabList.test.tsx +66 -0
  365. package/src/TabList/TabList.tsx +8 -0
  366. package/src/TabList/TabMenu.tsx +4 -1
  367. package/src/Table/Table.doc.mjs +2 -0
  368. package/src/Table/Table.tsx +1 -1
  369. package/src/Table/index.ts +12 -0
  370. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +6 -2
  371. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +90 -1
  372. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +37 -5
  373. package/src/Table/plugins/rowStatus/index.ts +7 -0
  374. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +138 -0
  375. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +189 -0
  376. package/src/Table/plugins/selection/useTableSelection.test.tsx +16 -0
  377. package/src/Table/plugins/selection/useTableSelection.tsx +18 -1
  378. package/src/Table/plugins/sortable/useTableSortable.test.tsx +95 -9
  379. package/src/Table/plugins/sortable/useTableSortable.tsx +23 -7
  380. package/src/Table/plugins/tree/index.ts +12 -0
  381. package/src/Table/plugins/tree/useTableTreeData-perf.test.tsx +163 -0
  382. package/src/Table/plugins/tree/useTableTreeData.test.tsx +594 -0
  383. package/src/Table/plugins/tree/useTableTreeData.tsx +495 -0
  384. package/src/Table/plugins/tree/useTableTreeState.test.tsx +636 -0
  385. package/src/Table/plugins/tree/useTableTreeState.tsx +253 -0
  386. package/src/Table/useBaseTablePlugins.ts +6 -2
  387. package/src/Table/useTableRowStatus.doc.mjs +30 -0
  388. package/src/Table/useTableSelection.doc.mjs +65 -14
  389. package/src/Table/useTableTreeData.doc.mjs +75 -0
  390. package/src/Table/useTableTreeState.doc.mjs +98 -0
  391. package/src/TextArea/TextArea.doc.mjs +1 -1
  392. package/src/TextArea/TextArea.test.tsx +9 -1
  393. package/src/TextInput/TextInput.doc.mjs +1 -1
  394. package/src/Thumbnail/Thumbnail.doc.mjs +9 -0
  395. package/src/Thumbnail/Thumbnail.test.tsx +38 -8
  396. package/src/Thumbnail/Thumbnail.tsx +28 -1
  397. package/src/TimeInput/TimeInput.test.tsx +76 -1
  398. package/src/TimeInput/TimeInput.tsx +19 -6
  399. package/src/Tokenizer/Tokenizer.doc.mjs +1 -1
  400. package/src/Tokenizer/Tokenizer.test.tsx +137 -2
  401. package/src/Tokenizer/Tokenizer.tsx +16 -4
  402. package/src/Toolbar/Toolbar.tsx +10 -3
  403. package/src/TopNav/TopNav.test.tsx +80 -1
  404. package/src/TopNav/TopNavHeading.tsx +24 -8
  405. package/src/TopNav/TopNavMegaMenu.test.tsx +149 -1
  406. package/src/TopNav/TopNavMegaMenu.tsx +10 -4
  407. package/src/TopNav/TopNavMenu.test.tsx +118 -4
  408. package/src/TopNav/TopNavMenu.tsx +83 -5
  409. package/src/TreeList/TreeList.test.tsx +32 -1
  410. package/src/TreeList/TreeListItem.tsx +19 -10
  411. package/src/authoring/doc.ts +1 -3
  412. package/src/docs-script.test.mjs +18 -13
  413. package/src/hooks/computeOverflow.test.ts +422 -0
  414. package/src/hooks/computeOverflow.ts +290 -0
  415. package/src/hooks/useOverflow.test.ts +96 -4
  416. package/src/hooks/useOverflow.ts +77 -33
  417. package/src/postinstall.test.mjs +71 -0
  418. package/src/theme/Theme.doc.mjs +4 -4
  419. package/src/theme/Theme.tsx +1 -1
  420. package/src/theme/defineTheme.ts +1 -1
  421. package/src/theme/generateThemeRules.test.ts +65 -0
  422. package/src/theme/generateThemeRules.ts +56 -0
  423. package/src/theme/hct.ts +0 -4
  424. package/src/utils/types.ts +14 -0
  425. package/dist/TreeList/treeListItem.markers.stylex.d.ts +0 -16
  426. package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +0 -1
  427. package/dist/TreeList/treeListItem.markers.stylex.js +0 -22
  428. package/src/TreeList/treeListItem.markers.stylex.ts +0 -20
@@ -1,7 +1,7 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
3
  import {describe, it, expect, vi} from 'vitest';
4
- import {render, screen} from '@testing-library/react';
4
+ import {render, screen, fireEvent} from '@testing-library/react';
5
5
  import {Carousel} from './Carousel';
6
6
 
7
7
  // Mock ResizeObserver (not available in jsdom)
@@ -92,6 +92,59 @@ describe('Carousel', () => {
92
92
  expect(screen.getByLabelText('Scroll right')).toBeInTheDocument();
93
93
  });
94
94
 
95
+ describe('slide semantics', () => {
96
+ it('exposes each slide as a group with aria-roledescription="slide" and a positional name', () => {
97
+ render(
98
+ <Carousel aria-label="Photos">
99
+ <div>One</div>
100
+ <div>Two</div>
101
+ <div>Three</div>
102
+ </Carousel>,
103
+ );
104
+ // APG carousel pattern: each slide container is role=group with
105
+ // aria-roledescription="slide" and an "N of M" accessible name.
106
+ const slides = screen.getAllByRole('group');
107
+ expect(slides).toHaveLength(3);
108
+ slides.forEach((slide, i) => {
109
+ expect(slide).toHaveAttribute('aria-roledescription', 'slide');
110
+ expect(slide).toHaveAccessibleName(`Slide ${i + 1} of 3`);
111
+ });
112
+ });
113
+
114
+ it('reflects the rendered child count, skipping null and boolean children', () => {
115
+ render(
116
+ <Carousel aria-label="Photos">
117
+ <div>One</div>
118
+ {null}
119
+ {false}
120
+ <div>Two</div>
121
+ </Carousel>,
122
+ );
123
+ const slides = screen.getAllByRole('group');
124
+ expect(slides).toHaveLength(2);
125
+ expect(
126
+ screen.getByRole('group', {name: 'Slide 1 of 2'}),
127
+ ).toBeInTheDocument();
128
+ expect(
129
+ screen.getByRole('group', {name: 'Slide 2 of 2'}),
130
+ ).toBeInTheDocument();
131
+ });
132
+
133
+ it('keeps the container region semantics unchanged around labelled slides', () => {
134
+ render(
135
+ <Carousel aria-label="Gallery">
136
+ <div>One</div>
137
+ <div>Two</div>
138
+ </Carousel>,
139
+ );
140
+ const region = screen.getByRole('region', {name: 'Gallery'});
141
+ expect(region).toHaveAttribute('aria-roledescription', 'carousel');
142
+ const slides = screen.getAllByRole('group');
143
+ expect(slides).toHaveLength(2);
144
+ slides.forEach(slide => expect(region).toContainElement(slide));
145
+ });
146
+ });
147
+
95
148
  it('disables edge scroll buttons instead of removing them from the tab order', () => {
96
149
  // In jsdom there is no measurable overflow, so both edges are at rest and
97
150
  // the scroll buttons are in their hidden/inert state. They must stay
@@ -111,4 +164,99 @@ describe('Carousel', () => {
111
164
  expect(left).toBeDisabled();
112
165
  expect(right).toBeDisabled();
113
166
  });
167
+
168
+ describe('Shift + wheel horizontal scroll', () => {
169
+ // jsdom doesn't lay out elements, so we fake an overflowing scroll
170
+ // container and capture scrollBy calls.
171
+ function getScroller() {
172
+ const region = screen.getByRole('region');
173
+ return region.firstElementChild as HTMLElement;
174
+ }
175
+
176
+ function makeOverflowing(el: HTMLElement) {
177
+ Object.defineProperty(el, 'scrollWidth', {
178
+ value: 500,
179
+ configurable: true,
180
+ });
181
+ Object.defineProperty(el, 'clientWidth', {
182
+ value: 200,
183
+ configurable: true,
184
+ });
185
+ }
186
+
187
+ it('maps Shift + vertical wheel to horizontal scroll', () => {
188
+ render(
189
+ <Carousel aria-label="Wheel">
190
+ <div>Item 1</div>
191
+ <div>Item 2</div>
192
+ </Carousel>,
193
+ );
194
+ const scroller = getScroller();
195
+ makeOverflowing(scroller);
196
+ const scrollBy = vi.fn();
197
+ scroller.scrollBy = scrollBy;
198
+
199
+ fireEvent.wheel(scroller, {shiftKey: true, deltaY: 120, deltaX: 0});
200
+
201
+ expect(scrollBy).toHaveBeenCalledWith({left: 120, behavior: 'auto'});
202
+ });
203
+
204
+ it('does not translate wheel without Shift held', () => {
205
+ render(
206
+ <Carousel aria-label="Wheel">
207
+ <div>Item 1</div>
208
+ <div>Item 2</div>
209
+ </Carousel>,
210
+ );
211
+ const scroller = getScroller();
212
+ makeOverflowing(scroller);
213
+ const scrollBy = vi.fn();
214
+ scroller.scrollBy = scrollBy;
215
+
216
+ fireEvent.wheel(scroller, {shiftKey: false, deltaY: 120, deltaX: 0});
217
+
218
+ expect(scrollBy).not.toHaveBeenCalled();
219
+ });
220
+
221
+ it('leaves native horizontal wheel deltas alone', () => {
222
+ // Trackpads emit deltaX directly — don't double-handle those.
223
+ render(
224
+ <Carousel aria-label="Wheel">
225
+ <div>Item 1</div>
226
+ <div>Item 2</div>
227
+ </Carousel>,
228
+ );
229
+ const scroller = getScroller();
230
+ makeOverflowing(scroller);
231
+ const scrollBy = vi.fn();
232
+ scroller.scrollBy = scrollBy;
233
+
234
+ fireEvent.wheel(scroller, {shiftKey: true, deltaY: 120, deltaX: 40});
235
+
236
+ expect(scrollBy).not.toHaveBeenCalled();
237
+ });
238
+
239
+ it('does not intercept when there is no horizontal overflow', () => {
240
+ render(
241
+ <Carousel aria-label="Wheel">
242
+ <div>Item 1</div>
243
+ </Carousel>,
244
+ );
245
+ const scroller = getScroller();
246
+ Object.defineProperty(scroller, 'scrollWidth', {
247
+ value: 200,
248
+ configurable: true,
249
+ });
250
+ Object.defineProperty(scroller, 'clientWidth', {
251
+ value: 200,
252
+ configurable: true,
253
+ });
254
+ const scrollBy = vi.fn();
255
+ scroller.scrollBy = scrollBy;
256
+
257
+ fireEvent.wheel(scroller, {shiftKey: true, deltaY: 120, deltaX: 0});
258
+
259
+ expect(scrollBy).not.toHaveBeenCalled();
260
+ });
261
+ });
114
262
  });
@@ -7,8 +7,12 @@
7
7
  * @input Uses React, StyleX, useScrollOverflow, useLayer, Button, Icon, theme tokens
8
8
  * @output Exports Carousel component
9
9
  * @position Horizontal scroll container with fade-edge overflow indication,
10
- * optional prev/next buttons on the top layer, scroll-snap, and a 1px
11
- * visual bleed allowance for child selection indicators.
10
+ * optional prev/next buttons on the top layer, scroll-snap, a 1px
11
+ * visual bleed allowance for child selection indicators, and Shift + wheel
12
+ * mapping so mouse users can scroll horizontally. Exposes APG
13
+ * carousel semantics: the root is a labelled region with
14
+ * aria-roledescription="carousel" and each item wrapper is a group with
15
+ * aria-roledescription="slide" named "Slide N of M".
12
16
  *
13
17
  * SYNC: When modified, update:
14
18
  * - /packages/core/src/Carousel/index.ts (exports)
@@ -16,7 +20,14 @@
16
20
  * - /packages/cli/templates/blocks/components/Carousel/ (showcase blocks)
17
21
  */
18
22
 
19
- import {type ReactNode, useRef, useCallback, useEffect, Children} from 'react';
23
+ import {
24
+ type ReactNode,
25
+ useRef,
26
+ useCallback,
27
+ useEffect,
28
+ Children,
29
+ isValidElement,
30
+ } from 'react';
20
31
  import * as stylex from '@stylexjs/stylex';
21
32
  import {
22
33
  spacingVars,
@@ -269,6 +280,11 @@ export function Carousel({
269
280
  const scrollElRef = useRef<HTMLElement | null>(null);
270
281
  const {scrollRef, overflowStart, overflowEnd} = useScrollOverflow();
271
282
 
283
+ // Children.toArray drops null/undefined/boolean children and assigns
284
+ // stable keys, so slide numbering ("Slide N of M") matches what actually
285
+ // renders even when some children are conditionally omitted.
286
+ const slides = Children.toArray(children);
287
+
272
288
  const layer = useLayer({
273
289
  mode: 'context',
274
290
  lightDismiss: false,
@@ -290,6 +306,31 @@ export function Carousel({
290
306
  [scrollRef],
291
307
  );
292
308
 
309
+ // Map Shift + vertical wheel to horizontal scroll. Trackpads emit
310
+ // horizontal deltas natively, but a standard mouse only produces deltaY —
311
+ // so mouse users can't wheel-scroll a horizontal container. Shift + wheel
312
+ // is the long-established convention for horizontal scroll containers; we
313
+ // honor it by translating the vertical delta into a horizontal scroll.
314
+ //
315
+ // Only kicks in when Shift is held and the wheel is purely vertical
316
+ // (deltaX === 0), so native trackpad horizontal scrolling is untouched.
317
+ const handleWheel = useCallback((event: React.WheelEvent<HTMLDivElement>) => {
318
+ if (!event.shiftKey || event.deltaY === 0 || event.deltaX !== 0) {
319
+ return;
320
+ }
321
+ const el = scrollElRef.current;
322
+ if (!el) {
323
+ return;
324
+ }
325
+ // Nothing to scroll horizontally — let the event fall through so the page
326
+ // can scroll as it normally would.
327
+ if (el.scrollWidth <= el.clientWidth) {
328
+ return;
329
+ }
330
+ event.preventDefault();
331
+ el.scrollBy({left: event.deltaY, behavior: 'auto'});
332
+ }, []);
333
+
293
334
  const scrollBy = useCallback((direction: -1 | 1) => {
294
335
  const el = scrollElRef.current;
295
336
  if (!el) {
@@ -346,6 +387,7 @@ export function Carousel({
346
387
  <div
347
388
  ref={composedRef}
348
389
  tabIndex={0}
390
+ onWheel={handleWheel}
349
391
  {...stylex.props(
350
392
  styles.scroller,
351
393
  gapStyles[gap],
@@ -353,8 +395,23 @@ export function Carousel({
353
395
  hasSnap && styles.snap,
354
396
  fadeStyle,
355
397
  )}>
356
- {Children.map(children, child => (
357
- <div {...stylex.props(styles.item)}>{child}</div>
398
+ {slides.map((child, index) => (
399
+ // APG carousel pattern: each slide container is a group with
400
+ // aria-roledescription="slide" and an "N of M" accessible name so
401
+ // ATs announce slide boundaries and position instead of anonymous
402
+ // generics.
403
+ <div
404
+ // eslint-disable-next-line @eslint-react/no-array-index-key -- index fallback only applies to text/number children, which are positional by definition; elements keep their Children.toArray keys
405
+ key={isValidElement(child) ? child.key : index}
406
+ role="group"
407
+ aria-roledescription="slide"
408
+ aria-label={t('@astryx.carousel.slideLabel', {
409
+ current: index + 1,
410
+ total: slides.length,
411
+ })}
412
+ {...stylex.props(styles.item)}>
413
+ {child}
414
+ </div>
358
415
  ))}
359
416
  </div>
360
417
 
@@ -80,7 +80,7 @@ export const docs = {
80
80
  { name: 'Scroll-to-bottom button', required: false, description: 'Appears when the user scrolls up or new messages arrive. Defaults to ChatLayoutScrollButton; pass null to hide or a custom element to override.' },
81
81
  { name: 'Composer', required: true, description: 'The input area for sending messages, typically ChatComposer. Docked at the bottom inside the frosted glass layer.' },
82
82
  { name: 'Empty state', required: false, description: 'Centered placeholder shown when no messages exist. Use EmptyState for a consistent look.' },
83
- { name: 'Avatar', required: false, description: 'A sender avatar rendered beside the message. Typically Avatar with size="small". Hidden for system messages.' },
83
+ { name: 'Avatar', required: false, description: 'A sender avatar rendered beside the message. Typically Avatar with size="md". Hidden for system messages.' },
84
84
  { name: 'Name', required: false, description: 'Sender name above the message body. Place on the bubble when using bubbles, or on the message wrapper for raw content.' },
85
85
  { name: 'Content', required: true, description: 'The message body: one or more ChatMessageBubble elements, or any free-form ReactNode like images or tool calls.' },
86
86
  { name: 'Metadata', required: false, description: 'Timestamp, delivery status, and footer actions below the message. Place on the last bubble or on the message wrapper.' },
@@ -56,6 +56,13 @@ export const docs = {
56
56
  description: 'Visual density.',
57
57
  default: "'balanced'",
58
58
  },
59
+ {
60
+ name: 'elevation',
61
+ type: "'none' | 'low'",
62
+ description:
63
+ "Resting elevation of the composer body. `low` (the default) keeps today's raised look — low at rest, bumping to med on hover / focus. `none` flattens it and draws a border with the same rest / hover / focus treatment as a text input (emphasized border → accent on focus, matching inset rings).",
64
+ default: "'low'",
65
+ },
59
66
  {
60
67
  name: 'drawer',
61
68
  type: 'ReactNode',
@@ -185,6 +192,7 @@ export const docsZh = {
185
192
  placeholder: '输入为空时显示的占位文本。',
186
193
  isDisabled: '禁用编写器。',
187
194
  density: '视觉密度。',
195
+ elevation: "编写器主体的静止高度。low(默认)保持当前的抬起外观——静止时 low,悬停/聚焦时升至 med。none 则扁平化。",
188
196
  drawer: '插槽:输入上方的可折叠抽屉——附件、上下文标签等。使用 ChatComposerDrawer。',
189
197
  headerActions: '插槽:标题左侧操作按钮(附件、提及按钮)。使用仅图标 size="sm" 按钮。',
190
198
  headerContext: '插槽:标题右侧上下文信息(上下文窗口使用情况、ProgressBar、辅助文本)。',
@@ -210,6 +218,7 @@ export const docsDense = {
210
218
  placeholder: 'placeholder when empty',
211
219
  isDisabled: 'disabled; use during streaming or unmet prereqs',
212
220
  density: 'visual density',
221
+ elevation: "resting elevation of composer body: low (default; low→med on hover/focus) | none (flat)",
213
222
  drawer: 'slot: collapsible drawer above input: attachments, context chips, etc.; use ChatComposerDrawer',
214
223
  headerActions: 'slot: left header actions (attach, mention); icon-only sm buttons',
215
224
  headerContext: 'slot: right header context info (window usage, ProgressBar, text)',
@@ -0,0 +1,35 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import {describe, it, expect} from 'vitest';
4
+ import {render} from '@testing-library/react';
5
+ import {ChatComposer} from './ChatComposer';
6
+
7
+ // The composer body — the elevated surface — is the div that wraps the input
8
+ // area (styles.body). A custom `input` slot renders inside the inputArea div,
9
+ // so the body is two ancestors up from the slot marker. Walking from a known
10
+ // marker keeps this independent of StyleX's generated class names.
11
+ function renderBodyClass(elevation?: 'none' | 'low'): string {
12
+ const {container} = render(
13
+ <ChatComposer
14
+ onSubmit={() => {}}
15
+ elevation={elevation}
16
+ input={<div data-testid="composer-input-marker" />}
17
+ />,
18
+ );
19
+ const marker = container.querySelector(
20
+ '[data-testid="composer-input-marker"]',
21
+ )!;
22
+ const inputArea = marker.parentElement!;
23
+ const body = inputArea.parentElement!;
24
+ return body.className;
25
+ }
26
+
27
+ describe('ChatComposer elevation', () => {
28
+ it('applies a distinct body class for each supported level', () => {
29
+ expect(renderBodyClass('none')).not.toBe(renderBodyClass('low'));
30
+ });
31
+
32
+ it("defaults to 'low' (preserves the raised look)", () => {
33
+ expect(renderBodyClass(undefined)).toBe(renderBodyClass('low'));
34
+ });
35
+ });
@@ -40,6 +40,7 @@ import {
40
40
  shadowVars,
41
41
  durationVars,
42
42
  easeVars,
43
+ borderVars,
43
44
  typeScaleVars,
44
45
  typographyVars,
45
46
  } from '../theme/tokens.stylex';
@@ -83,6 +84,14 @@ export interface ChatComposerProps extends Omit<
83
84
  isDisabled?: boolean;
84
85
  /** Density variant */
85
86
  density?: ChatComposerDensity;
87
+ /**
88
+ * Resting elevation of the composer body. `low` (the default) keeps today's
89
+ * raised look — low at rest, bumping to med on hover / focus. `none` flattens
90
+ * it and draws a border with the same rest / hover / focus treatment as a
91
+ * text input (emphasized border → accent on focus, with matching inset rings).
92
+ * @default 'low'
93
+ */
94
+ elevation?: 'none' | 'low';
86
95
 
87
96
  // --- Slot props ---
88
97
 
@@ -143,14 +152,7 @@ const styles = stylex.create({
143
152
  borderRadius: 'var(--_chat-composer-radius)',
144
153
  backgroundColor: colorVars['--color-background-popover'],
145
154
  cursor: 'text',
146
- boxShadow: {
147
- default: shadowVars['--shadow-low'],
148
- ':hover': {'@media (hover: hover)': shadowVars['--shadow-med']},
149
- },
150
- transition: `box-shadow ${durationVars['--duration-fast']} ${easeVars['--ease-standard']}`,
151
- ':focus-within': {
152
- boxShadow: shadowVars['--shadow-med'],
153
- },
155
+ transition: `box-shadow ${durationVars['--duration-fast']} ${easeVars['--ease-standard']}, border-color ${durationVars['--duration-fast']} ${easeVars['--ease-standard']}`,
154
156
  },
155
157
  header: {
156
158
  display: 'flex',
@@ -247,11 +249,56 @@ const styles = stylex.create({
247
249
  color: colorVars['--color-text-yellow'],
248
250
  },
249
251
  compact: {
250
- padding: spacingVars['--spacing-2'],
252
+ // Override the padding var (not the `padding` property) so both the base
253
+ // body padding and the border-inset calc in elevationStyles.none pick up
254
+ // the compact value. Scoped to the body element, so the status bar (which
255
+ // reads the var from root) keeps its own padding.
256
+ '--_chat-composer-padding': spacingVars['--spacing-2'],
251
257
  gap: spacingVars['--spacing-1'],
252
258
  },
253
259
  });
254
260
 
261
+ // Resting elevation for the composer body, narrowed to two steps.
262
+ // - `low` (default) preserves today's look: low at rest, bumping to med on
263
+ // hover / focus-within (a CSS-only interaction state the component owns).
264
+ // - `none` is flat; depth comes from a border + inset rings instead. The
265
+ // border is drawn *inside* the padding — its width is subtracted from every
266
+ // side (like Card) so total inset (border + padding) equals the elevated
267
+ // case and content geometry is unchanged. Border color, hover, and focus
268
+ // states mirror TextInput (shared `inputWrapperStyles.base` treatment):
269
+ // emphasized border at rest → accent on focus-within, with the same inset
270
+ // shadow rings on hover and focus.
271
+ const elevationStyles = stylex.create({
272
+ low: {
273
+ boxShadow: {
274
+ default: shadowVars['--shadow-low'],
275
+ ':hover': {'@media (hover: hover)': shadowVars['--shadow-med']},
276
+ },
277
+ ':focus-within': {
278
+ boxShadow: shadowVars['--shadow-med'],
279
+ },
280
+ },
281
+ none: {
282
+ borderWidth: borderVars['--border-width'],
283
+ borderStyle: 'solid',
284
+ borderColor: {
285
+ default: colorVars['--color-border-emphasized'],
286
+ ':focus-within': colorVars['--color-accent'],
287
+ },
288
+ // Draw the border *inside* the padding — subtract its width from the body
289
+ // padding (like Card's withBorder) so total inset (border + padding) equals
290
+ // the elevated case and content geometry is unchanged.
291
+ padding: `calc(var(--_chat-composer-padding) - ${borderVars['--border-width']})`,
292
+ boxShadow: {
293
+ default: 'none',
294
+ ':hover:not(:focus-within)': {
295
+ '@media (hover: hover)': `inset 0px 0px 0px 2px color-mix(in srgb, ${colorVars['--color-border-emphasized']} 30%, transparent)`,
296
+ },
297
+ ':focus-within': `inset 0px 0px 0px 2px ${colorVars['--color-accent-muted']}`,
298
+ },
299
+ },
300
+ });
301
+
255
302
  // =============================================================================
256
303
  // Component
257
304
  // =============================================================================
@@ -280,6 +327,7 @@ export function ChatComposer(props: ChatComposerProps) {
280
327
  placeholder: placeholderFromProps,
281
328
  isDisabled = false,
282
329
  density = 'balanced',
330
+ elevation = 'low',
283
331
  drawer,
284
332
  headerActions,
285
333
  headerContext,
@@ -402,6 +450,7 @@ export function ChatComposer(props: ChatComposerProps) {
402
450
  onClick={handleBodyClick}
403
451
  {...stylex.props(
404
452
  styles.body,
453
+ elevationStyles[elevation],
405
454
  density === 'compact' && styles.compact,
406
455
  xstyle,
407
456
  )}>
@@ -0,0 +1,61 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import {describe, it, expect} from 'vitest';
4
+ import {render, screen, fireEvent} from '@testing-library/react';
5
+ import {ChatComposerDrawer} from './ChatComposerDrawer';
6
+
7
+ describe('ChatComposerDrawer', () => {
8
+ it('links the toggle to the drawer content via aria-controls', () => {
9
+ render(
10
+ <ChatComposerDrawer count={2} label="Attachments">
11
+ <span>Drawer content</span>
12
+ </ChatComposerDrawer>,
13
+ );
14
+
15
+ const toggle = screen.getByRole('button', {name: /Attachments/});
16
+ const controlsId = toggle.getAttribute('aria-controls');
17
+ // aria-controls must be present and point at the real content region.
18
+ expect(controlsId).toBeTruthy();
19
+ const region = document.getElementById(controlsId as string);
20
+ expect(region).not.toBeNull();
21
+ expect(region).toContainElement(screen.getByText('Drawer content'));
22
+ });
23
+
24
+ it('keeps aria-controls resolvable while collapsed (content stays mounted)', () => {
25
+ render(
26
+ <ChatComposerDrawer count={2} label="Attachments" defaultIsCollapsed>
27
+ <span>Drawer content</span>
28
+ </ChatComposerDrawer>,
29
+ );
30
+
31
+ const toggle = screen.getByRole('button', {name: /Attachments/});
32
+ const controlsId = toggle.getAttribute('aria-controls');
33
+ expect(controlsId).toBeTruthy();
34
+ const region = document.getElementById(controlsId as string);
35
+ expect(region).not.toBeNull();
36
+ expect(region).toContainElement(screen.getByText('Drawer content'));
37
+ });
38
+
39
+ it('toggles aria-expanded when the toggle is activated', () => {
40
+ render(
41
+ <ChatComposerDrawer count={2} label="Attachments">
42
+ <span>Drawer content</span>
43
+ </ChatComposerDrawer>,
44
+ );
45
+
46
+ const toggle = screen.getByRole('button', {name: /Attachments/});
47
+ expect(toggle).toHaveAttribute('aria-expanded', 'true');
48
+
49
+ fireEvent.click(toggle);
50
+ expect(screen.getByRole('button', {name: /Attachments/})).toHaveAttribute(
51
+ 'aria-expanded',
52
+ 'false',
53
+ );
54
+
55
+ fireEvent.click(screen.getByRole('button', {name: /Attachments/}));
56
+ expect(screen.getByRole('button', {name: /Attachments/})).toHaveAttribute(
57
+ 'aria-expanded',
58
+ 'true',
59
+ );
60
+ });
61
+ });
@@ -9,13 +9,15 @@
9
9
  * @position Collapsible drawer panel for ChatComposer.
10
10
  * Supports expanded (full content) and collapsed (count + label) states
11
11
  * with fade animation and grid-template-rows height transition.
12
+ * The collapse toggle exposes aria-expanded + aria-controls linking it to
13
+ * the content region (disclosure pattern).
12
14
  *
13
15
  * SYNC: When modified, update:
14
16
  * - /packages/core/src/Chat/index.ts (exports)
15
17
  * - /packages/cli/templates/blocks/components/ChatComposerDrawer/ (block examples)
16
18
  */
17
19
 
18
- import {useState, type ReactNode} from 'react';
20
+ import {useId, useState, type ReactNode} from 'react';
19
21
  import type {StyleXStyles} from '@stylexjs/stylex';
20
22
  import * as stylex from '@stylexjs/stylex';
21
23
  import {
@@ -240,6 +242,12 @@ export function ChatComposerDrawer({
240
242
 
241
243
  const canCollapse = count != null;
242
244
 
245
+ // Links the toggle to the region it shows/hides so assistive tech can move
246
+ // from the button to its controlled content (disclosure pattern). The
247
+ // content stays mounted while collapsed (hidden via the grid-row collapse),
248
+ // so the reference always resolves.
249
+ const contentId = useId();
250
+
243
251
  const toggle = () => {
244
252
  const next = !isCollapsed;
245
253
  if (!isControlled) {
@@ -271,6 +279,7 @@ export function ChatComposerDrawer({
271
279
  role="button"
272
280
  tabIndex={0}
273
281
  aria-expanded={!isCollapsed}
282
+ aria-controls={contentId}
274
283
  aria-label={
275
284
  isCollapsed
276
285
  ? t('@astryx.chatComposerDrawer.expand', {label})
@@ -301,6 +310,7 @@ export function ChatComposerDrawer({
301
310
  )}
302
311
 
303
312
  <div
313
+ id={contentId}
304
314
  {...stylex.props(
305
315
  styles.contentGrid,
306
316
  canCollapse && isCollapsed && styles.contentGridCollapsed,
@@ -80,6 +80,11 @@ export const docs = {
80
80
  type: '(value: string) => void',
81
81
  description: 'Called when the user presses Enter without Shift. The serialized value includes token placeholders.',
82
82
  },
83
+ {
84
+ name: 'onKeyDown',
85
+ type: '(event) => void',
86
+ description: 'Key-down handler invoked before the built-in Enter/history behavior (after any open trigger menu). The seam for platform-specific keys: call event.preventDefault() to suppress the default submit (e.g. newline on a touch keyboard), or act on the event yourself to add behavior (e.g. submit on Cmd/Ctrl+Enter). IME composition is always respected — Enter never submits mid-composition.',
87
+ },
83
88
  ],
84
89
  };
85
90
 
@@ -102,6 +107,7 @@ export const docsZh = {
102
107
  onPaste: '粘贴文本时调用。用于拦截或转换粘贴内容。',
103
108
  onFiles: '粘贴或拖放文件到输入时调用。用于处理附件。',
104
109
  onSubmit: '用户不按 Shift 按 Enter 时调用。序列化值包含标记占位符。',
110
+ onKeyDown: '在内置 Enter/历史处理之前(在打开的触发菜单之后)调用的按键处理器。平台特定按键的接缝:调用 event.preventDefault() 可抑制默认提交(例如触控键盘上插入换行),或自行处理事件以添加行为(例如 Cmd/Ctrl+Enter 提交)。始终尊重输入法组字状态——组字过程中 Enter 永不提交。',
105
111
  },
106
112
  };
107
113
 
@@ -124,5 +130,6 @@ export const docsDense = {
124
130
  onPaste: 'paste handler; intercept/transform pasted content',
125
131
  onFiles: 'file paste/drop handler; use for attachments',
126
132
  onSubmit: 'Enter w/o Shift handler; serialized value includes token placeholders',
133
+ onKeyDown: 'keydown handler before built-in Enter/history (after trigger menu); preventDefault() to suppress default submit (touch newline) or act on event to add behavior (Cmd/Ctrl+Enter). IME composition always respected',
127
134
  },
128
135
  };