@astryxdesign/core 0.3.0 → 0.4.0

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 (551) hide show
  1. package/CHANGELOG.md +156 -0
  2. package/dist/AlertDialog/AlertDialog.d.ts +10 -2
  3. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  4. package/dist/AlertDialog/AlertDialog.js +22 -3
  5. package/dist/AppShell/AppShell.d.ts +2 -2
  6. package/dist/AppShell/AppShell.d.ts.map +1 -1
  7. package/dist/AppShell/AppShell.js +40 -9
  8. package/dist/AspectRatio/AspectRatio.d.ts +13 -0
  9. package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
  10. package/dist/AspectRatio/AspectRatio.js +9 -0
  11. package/dist/Avatar/Avatar.d.ts.map +1 -1
  12. package/dist/Avatar/Avatar.js +33 -20
  13. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  14. package/dist/AvatarGroup/AvatarGroupOverflow.js +2 -3
  15. package/dist/Banner/Banner.d.ts.map +1 -1
  16. package/dist/Banner/Banner.js +15 -14
  17. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  18. package/dist/Breadcrumbs/BreadcrumbItem.js +21 -8
  19. package/dist/Breadcrumbs/index.d.ts +2 -1
  20. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  21. package/dist/Breadcrumbs/index.js +1 -0
  22. package/dist/Button/Button.d.ts.map +1 -1
  23. package/dist/Button/Button.js +11 -15
  24. package/dist/Calendar/Calendar.d.ts.map +1 -1
  25. package/dist/Calendar/Calendar.js +2 -1
  26. package/dist/Calendar/styles.d.ts +0 -2
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +0 -2
  29. package/dist/Carousel/Carousel.d.ts.map +1 -1
  30. package/dist/Carousel/Carousel.js +8 -12
  31. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  32. package/dist/Chat/ChatDictationButton.js +2 -1
  33. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  34. package/dist/Chat/ChatLayoutScrollButton.js +3 -1
  35. package/dist/Chat/ChatSendButton.d.ts.map +1 -1
  36. package/dist/Chat/ChatSendButton.js +4 -3
  37. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  38. package/dist/Chat/ChatToolCalls.js +17 -17
  39. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  40. package/dist/Chat/useTriggerMenu.js +2 -6
  41. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  42. package/dist/CheckboxInput/CheckboxInput.js +36 -125
  43. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  44. package/dist/ClickableCard/ClickableCard.js +3 -11
  45. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  46. package/dist/CodeBlock/CodeBlock.js +84 -67
  47. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  48. package/dist/Collapsible/Collapsible.js +35 -15
  49. package/dist/CommandPalette/CommandPaletteItem.d.ts +1 -1
  50. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  51. package/dist/CommandPalette/CommandPaletteItem.js +6 -3
  52. package/dist/CommandPalette/CommandPaletteList.js +1 -1
  53. package/dist/ComplexSelector/ComplexSelector.d.ts +1 -1
  54. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  55. package/dist/ComplexSelector/ComplexSelector.js +35 -33
  56. package/dist/ContextMenu/ContextMenu.d.ts +2 -2
  57. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  58. package/dist/ContextMenu/ContextMenu.js +1 -1
  59. package/dist/ContextMenu/index.d.ts +2 -1
  60. package/dist/ContextMenu/index.d.ts.map +1 -1
  61. package/dist/ContextMenu/index.js +1 -0
  62. package/dist/DateInput/DateInput.d.ts +9 -2
  63. package/dist/DateInput/DateInput.d.ts.map +1 -1
  64. package/dist/DateInput/DateInput.js +24 -23
  65. package/dist/DateRangeInput/DateRangeInput.d.ts +9 -2
  66. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  67. package/dist/DateRangeInput/DateRangeInput.js +11 -16
  68. package/dist/DateTimeInput/DateTimeInput.d.ts +9 -1
  69. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  70. package/dist/DateTimeInput/DateTimeInput.js +22 -17
  71. package/dist/Dialog/Dialog.d.ts.map +1 -1
  72. package/dist/Dialog/Dialog.js +4 -4
  73. package/dist/DropdownMenu/DropdownMenu.d.ts +33 -8
  74. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  75. package/dist/DropdownMenu/DropdownMenu.js +62 -20
  76. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +8 -11
  77. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  78. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +32 -31
  79. package/dist/DropdownMenu/DropdownMenuDivider.d.ts +25 -0
  80. package/dist/DropdownMenu/DropdownMenuDivider.d.ts.map +1 -0
  81. package/dist/DropdownMenu/DropdownMenuDivider.js +66 -0
  82. package/dist/DropdownMenu/DropdownMenuItem.d.ts +15 -2
  83. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  84. package/dist/DropdownMenu/DropdownMenuItem.js +20 -8
  85. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +5 -5
  86. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  87. package/dist/DropdownMenu/DropdownMenuRadioItem.js +24 -67
  88. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  89. package/dist/DropdownMenu/DropdownMenuSubMenu.js +2 -8
  90. package/dist/DropdownMenu/index.d.ts +2 -1
  91. package/dist/DropdownMenu/index.d.ts.map +1 -1
  92. package/dist/DropdownMenu/index.js +4 -0
  93. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  94. package/dist/DropdownMenu/renderDropdownItems.js +35 -27
  95. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  96. package/dist/EmptyState/EmptyState.js +9 -5
  97. package/dist/Field/InputClearButton.d.ts +16 -3
  98. package/dist/Field/InputClearButton.d.ts.map +1 -1
  99. package/dist/Field/InputClearButton.js +15 -4
  100. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  101. package/dist/HoverCard/useHoverCard.js +3 -19
  102. package/dist/Indicator/CheckIndicator.d.ts +25 -0
  103. package/dist/Indicator/CheckIndicator.d.ts.map +1 -0
  104. package/dist/Indicator/CheckIndicator.js +155 -0
  105. package/dist/Indicator/CheckboxIndicator.d.ts +22 -0
  106. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -0
  107. package/dist/Indicator/CheckboxIndicator.js +173 -0
  108. package/dist/Indicator/RadioIndicator.d.ts +23 -0
  109. package/dist/Indicator/RadioIndicator.d.ts.map +1 -0
  110. package/dist/Indicator/RadioIndicator.js +137 -0
  111. package/dist/Indicator/index.d.ts +24 -0
  112. package/dist/Indicator/index.d.ts.map +1 -0
  113. package/dist/Indicator/index.js +26 -0
  114. package/dist/Indicator/indicator.markers.stylex.d.ts +16 -0
  115. package/dist/Indicator/indicator.markers.stylex.d.ts.map +1 -0
  116. package/dist/Indicator/indicator.markers.stylex.js +21 -0
  117. package/dist/Indicator/indicatorRegistry.d.ts +71 -0
  118. package/dist/Indicator/indicatorRegistry.d.ts.map +1 -0
  119. package/dist/Indicator/indicatorRegistry.js +93 -0
  120. package/dist/Indicator/types.d.ts +167 -0
  121. package/dist/Indicator/types.d.ts.map +1 -0
  122. package/dist/Indicator/types.js +1 -0
  123. package/dist/Indicator/useIndicator.d.ts +18 -0
  124. package/dist/Indicator/useIndicator.d.ts.map +1 -0
  125. package/dist/Indicator/useIndicator.js +30 -0
  126. package/dist/Item/Item.d.ts.map +1 -1
  127. package/dist/Item/Item.js +4 -8
  128. package/dist/Layer/useLayer.d.ts +14 -0
  129. package/dist/Layer/useLayer.d.ts.map +1 -1
  130. package/dist/Layer/useLayer.js +25 -2
  131. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  132. package/dist/Lightbox/Lightbox.js +56 -53
  133. package/dist/Link/Link.d.ts.map +1 -1
  134. package/dist/Link/Link.js +4 -5
  135. package/dist/MobileNav/MobileNav.js +8 -8
  136. package/dist/MoreMenu/MoreMenu.d.ts +14 -1
  137. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  138. package/dist/MoreMenu/MoreMenu.js +4 -0
  139. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  140. package/dist/MultiSelector/MultiSelector.js +78 -75
  141. package/dist/NumberInput/NumberInput.d.ts +27 -1
  142. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  143. package/dist/NumberInput/NumberInput.js +230 -42
  144. package/dist/Outline/Outline.d.ts.map +1 -1
  145. package/dist/Outline/Outline.js +2 -3
  146. package/dist/Pagination/Pagination.d.ts.map +1 -1
  147. package/dist/Pagination/Pagination.js +61 -63
  148. package/dist/Popover/Popover.d.ts.map +1 -1
  149. package/dist/Popover/Popover.js +6 -8
  150. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  151. package/dist/PowerSearch/PowerSearch.js +1 -1
  152. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  153. package/dist/PowerSearch/PowerSearchEditPopover.js +7 -0
  154. package/dist/ProgressBar/ProgressBar.d.ts +11 -4
  155. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  156. package/dist/ProgressBar/ProgressBar.js +96 -6
  157. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  158. package/dist/RadioList/RadioListItem.js +29 -84
  159. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  160. package/dist/Resizable/ResizeHandle.js +2 -3
  161. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  162. package/dist/SegmentedControl/SegmentedControlItem.js +2 -3
  163. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  164. package/dist/SelectableCard/SelectableCard.js +3 -11
  165. package/dist/Selector/Selector.d.ts +2 -1
  166. package/dist/Selector/Selector.d.ts.map +1 -1
  167. package/dist/Selector/Selector.js +164 -94
  168. package/dist/Selector/hooks.d.ts +18 -6
  169. package/dist/Selector/hooks.d.ts.map +1 -1
  170. package/dist/Selector/hooks.js +57 -40
  171. package/dist/SideNav/SideNav.d.ts.map +1 -1
  172. package/dist/SideNav/SideNav.js +1 -1
  173. package/dist/SideNav/SideNavCollapseButton.d.ts +1 -1
  174. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  175. package/dist/SideNav/SideNavCollapseButton.js +29 -14
  176. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  177. package/dist/SideNav/SideNavHeading.js +56 -20
  178. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  179. package/dist/SideNav/SideNavItem.js +28 -22
  180. package/dist/Stack/stack.stylex.d.ts +3 -3
  181. package/dist/Switch/Switch.js +11 -10
  182. package/dist/TabList/Tab.d.ts.map +1 -1
  183. package/dist/TabList/Tab.js +2 -3
  184. package/dist/TabList/TabMenu.d.ts.map +1 -1
  185. package/dist/TabList/TabMenu.js +41 -19
  186. package/dist/Table/BaseTable.d.ts.map +1 -1
  187. package/dist/Table/BaseTable.js +8 -0
  188. package/dist/Table/index.d.ts +1 -1
  189. package/dist/Table/index.d.ts.map +1 -1
  190. package/dist/Table/index.js +1 -1
  191. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts +1 -1
  192. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  193. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +22 -18
  194. package/dist/Table/plugins/rowExpansion/index.d.ts +1 -1
  195. package/dist/Table/plugins/rowExpansion/index.d.ts.map +1 -1
  196. package/dist/Table/plugins/rowExpansion/index.js +1 -1
  197. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts +41 -76
  198. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  199. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +94 -345
  200. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  201. package/dist/Table/plugins/sortable/useTableSortable.js +23 -4
  202. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  203. package/dist/Table/plugins/tree/useTableTreeData.js +29 -33
  204. package/dist/Table/tableContextMenu.d.ts.map +1 -1
  205. package/dist/Table/tableContextMenu.js +2 -1
  206. package/dist/Table/types.d.ts +11 -0
  207. package/dist/Table/types.d.ts.map +1 -1
  208. package/dist/TextArea/TextArea.d.ts +10 -1
  209. package/dist/TextArea/TextArea.d.ts.map +1 -1
  210. package/dist/TextArea/TextArea.js +16 -6
  211. package/dist/TextInput/TextInput.d.ts +10 -1
  212. package/dist/TextInput/TextInput.d.ts.map +1 -1
  213. package/dist/TextInput/TextInput.js +13 -19
  214. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  215. package/dist/Thumbnail/Thumbnail.js +32 -26
  216. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  217. package/dist/TimeInput/TimeInput.js +7 -15
  218. package/dist/Timestamp/TimestampHoverCard.d.ts.map +1 -1
  219. package/dist/Timestamp/TimestampHoverCard.js +16 -34
  220. package/dist/Token/Token.d.ts.map +1 -1
  221. package/dist/Token/Token.js +20 -11
  222. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  223. package/dist/Tokenizer/Tokenizer.js +4 -2
  224. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  225. package/dist/Tooltip/useTooltip.js +3 -19
  226. package/dist/TopNav/TopNav.d.ts.map +1 -1
  227. package/dist/TopNav/TopNav.js +6 -5
  228. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  229. package/dist/TopNav/TopNavHeading.js +54 -16
  230. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  231. package/dist/TopNav/TopNavItem.js +3 -9
  232. package/dist/TopNav/TopNavMegaMenu.d.ts +7 -0
  233. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  234. package/dist/TopNav/TopNavMegaMenu.js +146 -60
  235. package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
  236. package/dist/TopNav/TopNavMegaMenuItem.js +22 -3
  237. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  238. package/dist/TopNav/TopNavMenu.js +83 -38
  239. package/dist/TreeList/TreeList.d.ts.map +1 -1
  240. package/dist/TreeList/TreeList.js +13 -0
  241. package/dist/TreeList/TreeListBranches.d.ts +1 -1
  242. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  243. package/dist/TreeList/TreeListBranches.js +14 -4
  244. package/dist/TreeList/TreeListItem.d.ts +11 -1
  245. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  246. package/dist/TreeList/TreeListItem.js +57 -30
  247. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  248. package/dist/Typeahead/BaseTypeahead.js +16 -8
  249. package/dist/astryx.css +74 -85
  250. package/dist/astryx.umd.js +56 -56
  251. package/dist/astryx.umd.js.map +4 -4
  252. package/dist/hooks/containerReveal.stylex.d.ts +61 -0
  253. package/dist/hooks/containerReveal.stylex.d.ts.map +1 -0
  254. package/dist/hooks/containerReveal.stylex.js +63 -0
  255. package/dist/hooks/index.d.ts +3 -0
  256. package/dist/hooks/index.d.ts.map +1 -1
  257. package/dist/hooks/index.js +2 -0
  258. package/dist/hooks/useClipboard.d.ts +59 -0
  259. package/dist/hooks/useClipboard.d.ts.map +1 -0
  260. package/dist/hooks/useClipboard.js +106 -0
  261. package/dist/hooks/useContainerReveal.d.ts +14 -8
  262. package/dist/hooks/useContainerReveal.d.ts.map +1 -1
  263. package/dist/hooks/useContainerReveal.js +28 -69
  264. package/dist/hooks/useIndicatorFocusRing.d.ts +28 -0
  265. package/dist/hooks/useIndicatorFocusRing.d.ts.map +1 -0
  266. package/dist/hooks/useIndicatorFocusRing.js +96 -0
  267. package/dist/hooks/useLongPress.d.ts.map +1 -1
  268. package/dist/hooks/useLongPress.js +12 -2
  269. package/dist/hooks/useScrollLock.d.ts.map +1 -1
  270. package/dist/hooks/useScrollLock.js +33 -18
  271. package/dist/hooks/useStreamingText.d.ts +18 -3
  272. package/dist/hooks/useStreamingText.d.ts.map +1 -1
  273. package/dist/hooks/useStreamingText.js +47 -7
  274. package/dist/hooks/useTreeFocus.d.ts.map +1 -1
  275. package/dist/hooks/useTreeFocus.js +9 -3
  276. package/dist/hooks/useTypeahead.d.ts +3 -2
  277. package/dist/hooks/useTypeahead.d.ts.map +1 -1
  278. package/dist/hooks/useTypeahead.js +19 -7
  279. package/dist/index.d.ts +1 -0
  280. package/dist/index.d.ts.map +1 -1
  281. package/dist/index.js +1 -0
  282. package/dist/theme/defineTheme.d.ts +16 -0
  283. package/dist/theme/defineTheme.d.ts.map +1 -1
  284. package/dist/theme/defineTheme.js +8 -0
  285. package/dist/theme/derivedVarRegistry.d.ts +8 -0
  286. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  287. package/dist/theme/derivedVarRegistry.js +14 -13
  288. package/dist/theme/expandColorScale.d.ts +3 -1
  289. package/dist/theme/expandColorScale.d.ts.map +1 -1
  290. package/dist/theme/expandColorScale.js +17 -7
  291. package/dist/theme/expandTypeScale.d.ts.map +1 -1
  292. package/dist/theme/expandTypeScale.js +17 -0
  293. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  294. package/dist/theme/generateThemeRules.js +11 -0
  295. package/dist/theme/index.d.ts +1 -0
  296. package/dist/theme/index.d.ts.map +1 -1
  297. package/dist/utils/focusOutline.stylex.d.ts +107 -0
  298. package/dist/utils/focusOutline.stylex.d.ts.map +1 -0
  299. package/dist/utils/focusOutline.stylex.js +130 -0
  300. package/dist/utils/focusReturn.d.ts +24 -0
  301. package/dist/utils/focusReturn.d.ts.map +1 -0
  302. package/dist/utils/focusReturn.js +29 -0
  303. package/dist/utils/index.d.ts +1 -0
  304. package/dist/utils/index.d.ts.map +1 -1
  305. package/dist/utils/index.js +1 -0
  306. package/dist/utils/mergeRefs.d.ts.map +1 -1
  307. package/dist/utils/mergeRefs.js +7 -5
  308. package/dist/utils/rtlStyles.d.ts +2 -2
  309. package/dist/utils/rtlStyles.js +2 -2
  310. package/dist/utils/themeProps.d.ts +20 -1
  311. package/dist/utils/themeProps.d.ts.map +1 -1
  312. package/dist/utils/themeProps.js +8 -2
  313. package/locales/en.json +12 -0
  314. package/locales/pseudo.json +9 -0
  315. package/package.json +7 -2
  316. package/src/AlertDialog/AlertDialog.doc.mjs +16 -3
  317. package/src/AlertDialog/AlertDialog.test.tsx +208 -1
  318. package/src/AlertDialog/AlertDialog.tsx +21 -3
  319. package/src/AppShell/AppShell.doc.mjs +11 -0
  320. package/src/AppShell/AppShell.test.tsx +139 -0
  321. package/src/AppShell/AppShell.tsx +32 -22
  322. package/src/AspectRatio/AspectRatio.doc.mjs +27 -3
  323. package/src/AspectRatio/AspectRatio.test.tsx +30 -0
  324. package/src/AspectRatio/AspectRatio.tsx +13 -0
  325. package/src/Avatar/Avatar.doc.mjs +2 -13
  326. package/src/Avatar/Avatar.test.tsx +33 -8
  327. package/src/Avatar/Avatar.tsx +52 -50
  328. package/src/AvatarGroup/AvatarGroupOverflow.tsx +2 -9
  329. package/src/Banner/Banner.tsx +9 -6
  330. package/src/Breadcrumbs/BreadcrumbItem.tsx +16 -6
  331. package/src/Breadcrumbs/index.ts +5 -1
  332. package/src/Button/Button.tsx +21 -41
  333. package/src/Calendar/Calendar.tsx +2 -1
  334. package/src/Calendar/styles.ts +0 -8
  335. package/src/Carousel/Carousel.doc.mjs +3 -0
  336. package/src/Carousel/Carousel.tsx +10 -6
  337. package/src/Chat/Chat.doc.mjs +2 -0
  338. package/src/Chat/ChatDictationButton.tsx +7 -1
  339. package/src/Chat/ChatLayoutScrollButton.test.tsx +22 -0
  340. package/src/Chat/ChatLayoutScrollButton.tsx +8 -1
  341. package/src/Chat/ChatSendButton.tsx +2 -3
  342. package/src/Chat/ChatToolCalls.test.tsx +35 -0
  343. package/src/Chat/ChatToolCalls.tsx +36 -16
  344. package/src/Chat/useTriggerMenu.tsx +2 -5
  345. package/src/CheckboxInput/CheckboxInput.doc.mjs +4 -2
  346. package/src/CheckboxInput/CheckboxInput.test.tsx +115 -0
  347. package/src/CheckboxInput/CheckboxInput.tsx +45 -202
  348. package/src/ClickableCard/ClickableCard.tsx +6 -11
  349. package/src/CodeBlock/CodeBlock.doc.mjs +3 -0
  350. package/src/CodeBlock/CodeBlock.test.tsx +91 -0
  351. package/src/CodeBlock/CodeBlock.tsx +57 -80
  352. package/src/Collapsible/Collapsible.tsx +22 -18
  353. package/src/CommandPalette/CommandPalette.test.tsx +29 -0
  354. package/src/CommandPalette/CommandPaletteItem.tsx +7 -4
  355. package/src/CommandPalette/CommandPaletteList.tsx +2 -2
  356. package/src/ComplexSelector/ComplexSelector.doc.mjs +42 -0
  357. package/src/ComplexSelector/ComplexSelector.test.tsx +64 -0
  358. package/src/ComplexSelector/ComplexSelector.tsx +46 -29
  359. package/src/ContextMenu/ContextMenu.doc.mjs +1 -1
  360. package/src/ContextMenu/ContextMenu.test.tsx +41 -0
  361. package/src/ContextMenu/ContextMenu.tsx +3 -3
  362. package/src/ContextMenu/index.ts +5 -1
  363. package/src/DateInput/DateInput.doc.mjs +18 -3
  364. package/src/DateInput/DateInput.test.tsx +93 -11
  365. package/src/DateInput/DateInput.tsx +41 -19
  366. package/src/DateRangeInput/DateRangeInput.doc.mjs +10 -2
  367. package/src/DateRangeInput/DateRangeInput.test.tsx +110 -14
  368. package/src/DateRangeInput/DateRangeInput.tsx +24 -12
  369. package/src/DateTimeInput/DateTimeInput.doc.mjs +16 -2
  370. package/src/DateTimeInput/DateTimeInput.test.tsx +75 -0
  371. package/src/DateTimeInput/DateTimeInput.tsx +35 -8
  372. package/src/Dialog/Dialog.tsx +2 -9
  373. package/src/DropdownMenu/DropdownMenu.doc.mjs +5 -9
  374. package/src/DropdownMenu/DropdownMenu.test.tsx +534 -4
  375. package/src/DropdownMenu/DropdownMenu.tsx +113 -52
  376. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +27 -30
  377. package/src/DropdownMenu/DropdownMenuDivider.doc.mjs +34 -0
  378. package/src/DropdownMenu/DropdownMenuDivider.tsx +81 -0
  379. package/src/DropdownMenu/DropdownMenuItem.doc.mjs +39 -4
  380. package/src/DropdownMenu/DropdownMenuItem.tsx +48 -10
  381. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +30 -76
  382. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +25 -19
  383. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +44 -14
  384. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +1 -5
  385. package/src/DropdownMenu/index.ts +8 -1
  386. package/src/DropdownMenu/renderDropdownItems.tsx +33 -34
  387. package/src/EmptyState/EmptyState.doc.mjs +4 -0
  388. package/src/EmptyState/EmptyState.test.tsx +62 -0
  389. package/src/EmptyState/EmptyState.tsx +16 -6
  390. package/src/Field/Field.doc.mjs +1 -0
  391. package/src/Field/InputClearButton.test.tsx +117 -0
  392. package/src/Field/InputClearButton.tsx +30 -3
  393. package/src/HoverCard/useHoverCard.tsx +2 -23
  394. package/src/Icon/Icon.doc.mjs +2 -1
  395. package/src/Indicator/CheckIndicator.tsx +174 -0
  396. package/src/Indicator/CheckboxIndicator.tsx +257 -0
  397. package/src/Indicator/Indicator.doc.mjs +344 -0
  398. package/src/Indicator/Indicator.test.tsx +393 -0
  399. package/src/Indicator/RadioIndicator.tsx +206 -0
  400. package/src/Indicator/index.ts +45 -0
  401. package/src/Indicator/indicator.markers.stylex.ts +19 -0
  402. package/src/Indicator/indicatorRegistry.test.tsx +106 -0
  403. package/src/Indicator/indicatorRegistry.ts +120 -0
  404. package/src/Indicator/types.ts +186 -0
  405. package/src/Indicator/useIndicator.ts +39 -0
  406. package/src/Item/Item.doc.mjs +1 -1
  407. package/src/Item/Item.tsx +7 -14
  408. package/src/Layer/useLayer.doc.mjs +2 -2
  409. package/src/Layer/useLayer.test.tsx +85 -0
  410. package/src/Layer/useLayer.tsx +39 -1
  411. package/src/Lightbox/Lightbox.doc.mjs +1 -1
  412. package/src/Lightbox/Lightbox.tsx +43 -38
  413. package/src/Link/Link.tsx +4 -11
  414. package/src/MobileNav/MobileNav.tsx +3 -3
  415. package/src/MoreMenu/MoreMenu.doc.mjs +31 -0
  416. package/src/MoreMenu/MoreMenu.test.tsx +68 -1
  417. package/src/MoreMenu/MoreMenu.tsx +19 -0
  418. package/src/MultiSelector/MultiSelector.doc.mjs +9 -1
  419. package/src/MultiSelector/MultiSelector.test.tsx +255 -16
  420. package/src/MultiSelector/MultiSelector.tsx +103 -99
  421. package/src/NumberInput/NumberInput.doc.mjs +265 -50
  422. package/src/NumberInput/NumberInput.test.tsx +726 -21
  423. package/src/NumberInput/NumberInput.tsx +353 -59
  424. package/src/Outline/Outline.tsx +2 -5
  425. package/src/Pagination/Pagination.doc.mjs +1 -1
  426. package/src/Pagination/Pagination.test.tsx +22 -7
  427. package/src/Pagination/Pagination.tsx +38 -35
  428. package/src/Popover/Popover.tsx +6 -7
  429. package/src/PowerSearch/PowerSearch.tsx +1 -1
  430. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +42 -0
  431. package/src/PowerSearch/PowerSearchEditPopover.tsx +7 -0
  432. package/src/ProgressBar/ProgressBar.doc.mjs +27 -5
  433. package/src/ProgressBar/ProgressBar.test.tsx +224 -1
  434. package/src/ProgressBar/ProgressBar.tsx +124 -22
  435. package/src/RadioList/RadioList.doc.mjs +4 -2
  436. package/src/RadioList/RadioListItem.tsx +31 -130
  437. package/src/Resizable/ResizeHandle.tsx +2 -9
  438. package/src/SegmentedControl/SegmentedControlItem.tsx +2 -9
  439. package/src/SelectableCard/SelectableCard.tsx +4 -9
  440. package/src/Selector/Selector.doc.mjs +7 -4
  441. package/src/Selector/Selector.test.tsx +935 -25
  442. package/src/Selector/Selector.tsx +189 -106
  443. package/src/Selector/hooks.ts +77 -50
  444. package/src/SideNav/SideNav.test.tsx +46 -0
  445. package/src/SideNav/SideNav.tsx +1 -0
  446. package/src/SideNav/SideNavCollapseButton.tsx +15 -10
  447. package/src/SideNav/SideNavHeading.tsx +48 -10
  448. package/src/SideNav/SideNavItem.tsx +20 -12
  449. package/src/Stack/Stack.doc.mjs +110 -0
  450. package/src/StatusDot/StatusDot.doc.mjs +12 -3
  451. package/src/Switch/Switch.test.tsx +32 -0
  452. package/src/Switch/Switch.tsx +8 -8
  453. package/src/TabList/Tab.tsx +2 -9
  454. package/src/TabList/TabMenu.tsx +14 -21
  455. package/src/Table/BaseTable.tsx +11 -0
  456. package/src/Table/index.ts +1 -4
  457. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +30 -14
  458. package/src/Table/plugins/rowExpansion/index.ts +1 -4
  459. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +125 -175
  460. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +115 -458
  461. package/src/Table/plugins/sortable/useTableSortable.tsx +2 -6
  462. package/src/Table/plugins/tree/useTableTreeData.tsx +43 -24
  463. package/src/Table/tableContextMenu.test.tsx +27 -0
  464. package/src/Table/tableContextMenu.tsx +1 -0
  465. package/src/Table/types.ts +11 -0
  466. package/src/Table/useTableGroupedRows.doc.mjs +1 -1
  467. package/src/Table/useTableRowExpansion.doc.mjs +43 -66
  468. package/src/TextArea/TextArea.doc.mjs +42 -2
  469. package/src/TextArea/TextArea.test.tsx +219 -0
  470. package/src/TextArea/TextArea.tsx +34 -7
  471. package/src/TextInput/TextInput.doc.mjs +18 -2
  472. package/src/TextInput/TextInput.test.tsx +189 -0
  473. package/src/TextInput/TextInput.tsx +25 -31
  474. package/src/Thumbnail/Thumbnail.tsx +2 -9
  475. package/src/TimeInput/TimeInput.doc.mjs +2 -2
  476. package/src/TimeInput/TimeInput.test.tsx +30 -3
  477. package/src/TimeInput/TimeInput.tsx +9 -26
  478. package/src/Timestamp/Timestamp.doc.mjs +4 -3
  479. package/src/Timestamp/TimestampHoverCard.tsx +13 -35
  480. package/src/Token/Token.tsx +5 -28
  481. package/src/Tokenizer/Tokenizer.doc.mjs +2 -2
  482. package/src/Tokenizer/Tokenizer.test.tsx +45 -3
  483. package/src/Tokenizer/Tokenizer.tsx +10 -2
  484. package/src/Tooltip/useTooltip.tsx +2 -23
  485. package/src/TopNav/TopNav.tsx +1 -3
  486. package/src/TopNav/TopNavHeading.tsx +42 -13
  487. package/src/TopNav/TopNavItem.tsx +3 -21
  488. package/src/TopNav/TopNavMegaMenu.test.tsx +433 -33
  489. package/src/TopNav/TopNavMegaMenu.tsx +118 -53
  490. package/src/TopNav/TopNavMegaMenuItem.tsx +2 -9
  491. package/src/TopNav/TopNavMenu.tsx +34 -37
  492. package/src/TreeList/TreeList.doc.mjs +5 -0
  493. package/src/TreeList/TreeList.test.tsx +206 -7
  494. package/src/TreeList/TreeList.tsx +22 -0
  495. package/src/TreeList/TreeListBranches.tsx +25 -2
  496. package/src/TreeList/TreeListItem.tsx +89 -52
  497. package/src/Typeahead/BaseTypeahead.tsx +19 -6
  498. package/src/Typeahead/Typeahead.doc.mjs +1 -0
  499. package/src/Typeahead/Typeahead.test.tsx +76 -3
  500. package/src/hooks/containerReveal.stylex.ts +102 -0
  501. package/src/hooks/index.ts +4 -0
  502. package/src/hooks/useClipboard.doc.mjs +87 -0
  503. package/src/hooks/useClipboard.test.tsx +151 -0
  504. package/src/hooks/useClipboard.ts +135 -0
  505. package/src/hooks/useContainerReveal.doc.mjs +7 -7
  506. package/src/hooks/useContainerReveal.test.tsx +42 -62
  507. package/src/hooks/useContainerReveal.ts +36 -91
  508. package/src/hooks/useImageMode.test.ts +1 -1
  509. package/src/hooks/useIndicatorFocusRing.tsx +120 -0
  510. package/src/hooks/useLongPress.test.tsx +18 -0
  511. package/src/hooks/useLongPress.ts +12 -2
  512. package/src/hooks/useScrollLock.test.ts +72 -0
  513. package/src/hooks/useScrollLock.ts +51 -19
  514. package/src/hooks/useStreamingText.test.ts +46 -1
  515. package/src/hooks/useStreamingText.ts +49 -7
  516. package/src/hooks/useTreeFocus.test.tsx +50 -0
  517. package/src/hooks/useTreeFocus.ts +14 -3
  518. package/src/hooks/useTypeahead.test.tsx +150 -0
  519. package/src/hooks/useTypeahead.ts +24 -9
  520. package/src/i18n/InternationalizationProvider.doc.mjs +7 -2
  521. package/src/index.ts +1 -0
  522. package/src/theme/defineTheme.ts +24 -0
  523. package/src/theme/derivedVarRegistry.test.ts +21 -1
  524. package/src/theme/derivedVarRegistry.ts +19 -6
  525. package/src/theme/expandColorScale.test.ts +14 -0
  526. package/src/theme/expandColorScale.ts +24 -9
  527. package/src/theme/expandTypeScale.test.ts +43 -0
  528. package/src/theme/expandTypeScale.ts +17 -0
  529. package/src/theme/generateThemeRules.test.ts +61 -20
  530. package/src/theme/generateThemeRules.ts +11 -0
  531. package/src/theme/index.ts +13 -0
  532. package/src/utils/focusOutline.stylex.ts +163 -0
  533. package/src/utils/focusReturn.test.ts +42 -0
  534. package/src/utils/focusReturn.ts +31 -0
  535. package/src/utils/index.ts +1 -0
  536. package/src/utils/mergeRefs.test.ts +48 -0
  537. package/src/utils/mergeRefs.ts +12 -6
  538. package/src/utils/rtlStyles.ts +2 -2
  539. package/src/utils/themeProps.ts +26 -4
  540. package/dist/CheckboxInput/checkbox.markers.stylex.d.ts +0 -9
  541. package/dist/CheckboxInput/checkbox.markers.stylex.d.ts.map +0 -1
  542. package/dist/CheckboxInput/checkbox.markers.stylex.js +0 -14
  543. package/dist/RadioList/radio.markers.stylex.d.ts +0 -7
  544. package/dist/RadioList/radio.markers.stylex.d.ts.map +0 -1
  545. package/dist/RadioList/radio.markers.stylex.js +0 -12
  546. package/dist/hooks/containerReveal.pool.stylex.d.ts +0 -303
  547. package/dist/hooks/containerReveal.pool.stylex.d.ts.map +0 -1
  548. package/dist/hooks/containerReveal.pool.stylex.js +0 -292
  549. package/src/CheckboxInput/checkbox.markers.stylex.ts +0 -12
  550. package/src/RadioList/radio.markers.stylex.ts +0 -10
  551. package/src/hooks/containerReveal.pool.stylex.ts +0 -571
@@ -14,26 +14,21 @@
14
14
  * state + onChange come from DropdownMenuRadioGroupContext. Keyboard nav +
15
15
  * Enter/Space activation come from the parent DropdownMenu.
16
16
  *
17
- * The round radio visual (circle + center dot) is decorative (aria-hidden) —
18
- * the row owns the checked state. Its size is derived from the menu's item size
19
- * and it swaps to the inline-end of the row on coarse-pointer (touch) devices
20
- * via CSS `order`. The dot is a shaped element, not an icon (a filled circle
21
- * has no registry glyph).
17
+ * The round radio visual is the shared radio indicator, decorative
18
+ * (aria-hidden) — the row owns the checked state, and menu radios pick up the
19
+ * same `radio` theming (and any theme replacement) as RadioList. This row keeps
20
+ * the marker box: its size is derived from the menu's item size and it swaps to
21
+ * the inline-end of the row on coarse-pointer (touch) devices via CSS `order`.
22
22
  */
23
23
 
24
24
  import {useCallback, type PointerEvent, type ReactNode} from 'react';
25
25
  import * as stylex from '@stylexjs/stylex';
26
26
  import {renderIconSlot, type IconType} from '../Icon';
27
+ import {useIndicator} from '../Indicator';
27
28
  import {Item} from '../Item';
28
29
  import {useDropdownMenuContext} from './DropdownMenuContext';
29
30
  import {focusMenuItemOnHover} from './menuItemHover';
30
- import {
31
- colorVars,
32
- spacingVars,
33
- durationVars,
34
- easeVars,
35
- borderVars,
36
- } from '../theme/tokens.stylex';
31
+ import {colorVars, spacingVars} from '../theme/tokens.stylex';
37
32
  import {mergeProps, themeProps} from '../utils';
38
33
  import type {BaseProps} from '../BaseProps';
39
34
  import {useDropdownMenuRadioGroupContext} from './DropdownMenuContext';
@@ -56,21 +51,13 @@ const styles = stylex.create({
56
51
  },
57
52
  // Rendered in Item's `marker` slot as a raw flex child. On touch it moves to
58
53
  // the inline-end of the row via `order`.
59
- circle: {
60
- display: 'flex',
61
- alignItems: 'center',
62
- justifyContent: 'center',
63
- flexShrink: 0,
64
- boxSizing: 'border-box',
65
- borderWidth: borderVars['--border-width'],
66
- borderStyle: 'solid',
67
- borderRadius: '50%',
68
- transitionProperty: 'background-color, border-color',
69
- transitionDuration: {
70
- default: durationVars['--duration-fast'],
71
- '@media (prefers-reduced-motion: reduce)': '0s',
72
- },
73
- transitionTimingFunction: easeVars['--ease-standard'],
54
+ // Layout box for the decorative marker. The painted circle is the radio
55
+ // indicator inside it, which carries the shared `radio` theme target.
56
+ // Placement of the marker within the row. The indicator draws its own box
57
+ // (size, fill, border) — these are only the rules the MENU owns: where the
58
+ // marker sits in the row, and that it never takes the pointer.
59
+ marker: {
60
+ pointerEvents: 'none',
74
61
  order: {
75
62
  default: 0,
76
63
  '@media (pointer: coarse)': 1,
@@ -80,30 +67,10 @@ const styles = stylex.create({
80
67
  '@media (pointer: coarse)': 'auto',
81
68
  },
82
69
  },
83
- unchecked: {
84
- borderColor: colorVars['--color-border-emphasized'],
85
- backgroundColor: colorVars['--color-background-surface'],
86
- },
87
- checked: {
88
- borderColor: colorVars['--color-accent'],
89
- backgroundColor: colorVars['--color-accent'],
90
- },
91
- dot: {
92
- borderRadius: '50%',
93
- backgroundColor: colorVars['--color-on-accent'],
94
- },
95
- });
96
-
97
- const circleSizeStyles = stylex.create({
98
- sm: {width: 18, height: 18},
99
- md: {width: 22, height: 22},
100
- });
101
-
102
- const dotSizeStyles = stylex.create({
103
- sm: {width: 6, height: 6},
104
- md: {width: 8, height: 8},
105
70
  });
106
71
 
72
+ // Matches the control sizes RadioList uses, so a radio reads the same in a
73
+ // menu row as it does in a radio group.
107
74
  export interface DropdownMenuRadioItemProps extends Omit<
108
75
  BaseProps,
109
76
  'role' | 'aria-checked' | 'tabIndex'
@@ -173,6 +140,7 @@ export function DropdownMenuRadioItem({
173
140
  const menuSize = menuCtx?.menuSize ?? 'md';
174
141
  const controlSize = menuSize === 'sm' ? 'sm' : 'md';
175
142
  const isChecked = groupCtx.value === value;
143
+ const RadioControl = useIndicator('radio');
176
144
 
177
145
  const handleClick = useCallback(() => {
178
146
  if (isDisabled) {
@@ -197,33 +165,19 @@ export function DropdownMenuRadioItem({
197
165
  tabIndex={isDisabled ? undefined : -1}
198
166
  onPointerMove={handlePointerMove}
199
167
  marker={
200
- <span
201
- aria-hidden="true"
202
- {...mergeProps(
203
- themeProps('dropdown-menu-radio', {
204
- size: controlSize,
205
- checked: isChecked ? 'checked' : null,
206
- disabled: isDisabled ? 'disabled' : null,
207
- }),
208
- stylex.props(
209
- styles.circle,
210
- circleSizeStyles[controlSize],
211
- isChecked ? styles.checked : styles.unchecked,
212
- ),
213
- )}>
214
- {isChecked && (
215
- <span
216
- {...mergeProps(
217
- themeProps('dropdown-menu-radio-dot', {
218
- size: controlSize,
219
- checked: 'checked',
220
- disabled: isDisabled ? 'disabled' : null,
221
- }),
222
- stylex.props(styles.dot, dotSizeStyles[controlSize]),
223
- )}
224
- />
225
- )}
226
- </span>
168
+ // No wrapper — see DropdownMenuCheckboxItem: the target belongs on the
169
+ // visible circle, and the indicator already owns its control size.
170
+ <RadioControl
171
+ state={isChecked ? 'checked' : 'unchecked'}
172
+ size={controlSize}
173
+ isDisabled={isDisabled}
174
+ xstyle={styles.marker}
175
+ {...themeProps('dropdown-menu-radio', {
176
+ size: controlSize,
177
+ checked: isChecked ? 'checked' : null,
178
+ disabled: isDisabled ? 'disabled' : null,
179
+ })}
180
+ />
227
181
  }
228
182
  startContent={
229
183
  icon
@@ -78,7 +78,7 @@ describe('DropdownMenuCheckboxItem', () => {
78
78
  expect(onChangeSpy).toHaveBeenCalledWith(true);
79
79
  });
80
80
 
81
- it('keeps the composed checkbox decorative (row is the only announced control)', async () => {
81
+ it('keeps the checkbox indicator decorative (row is the only announced control)', async () => {
82
82
  const user = userEvent.setup();
83
83
  render(
84
84
  <DropdownMenu button={{label: 'View'}}>
@@ -92,21 +92,20 @@ describe('DropdownMenuCheckboxItem', () => {
92
92
  screen.getAllByRole('menuitemcheckbox', {hidden: true}),
93
93
  ).toHaveLength(1);
94
94
 
95
- // The composed CheckboxInput is present in the DOM but sits inside an
96
- // `aria-hidden` + `inert` subtree: it contributes nothing to the row's
97
- // accessible name and its native <input> is out of the tab order and the
98
- // accessibility tree, so it is not a second announced/focusable control.
99
- // (Browsers enforce inert; jsdom does not model its a11y removal, so this
100
- // asserts the aria-hidden/inert boundary directly rather than via role.)
95
+ // The visual is the shared checkbox indicator: aria-hidden, with no nested
96
+ // native <input>, so the row is the only announced/focusable control.
101
97
  const row = screen.getByRole('menuitemcheckbox', {
102
98
  name: /Show archived/,
103
99
  hidden: true,
104
100
  });
105
- const input = row.querySelector('input[type="checkbox"]');
106
- expect(input).not.toBeNull();
107
- const marker = input?.closest('[inert]');
108
- expect(marker).not.toBeNull();
101
+ expect(row.querySelector('input[type="checkbox"]')).toBeNull();
102
+ // The shared checkbox target, directly on the row — no wrapper, and no
103
+ // menu-specific target added for it (main reached this element through
104
+ // `astryx-checkbox` too).
105
+ const marker = row.querySelector('.astryx-checkbox');
106
+ expect(marker).toBeInTheDocument();
109
107
  expect(marker).toHaveAttribute('aria-hidden', 'true');
108
+ expect(marker).toHaveAttribute('data-checked', 'checked');
110
109
  });
111
110
 
112
111
  it('does not toggle when disabled', async () => {
@@ -159,7 +158,7 @@ describe('DropdownMenuRadioGroup / RadioItem', () => {
159
158
  ).toBeInTheDocument();
160
159
  });
161
160
 
162
- it('exposes a themeable slot on the checked radio dot', async () => {
161
+ it('renders the shared radio indicator in the menu marker', async () => {
163
162
  const user = userEvent.setup();
164
163
  render(
165
164
  <DropdownMenu button={{label: 'Sort'}}>
@@ -177,18 +176,25 @@ describe('DropdownMenuRadioGroup / RadioItem', () => {
177
176
  name: 'Newest',
178
177
  hidden: true,
179
178
  });
180
- const dot = checked.querySelector('.astryx-dropdown-menu-radio-dot');
181
- expect(dot).toBeInTheDocument();
182
- // Mirrors the radio container's visual props/states for consistent theming.
183
- expect(dot).toHaveAttribute('data-size', 'md');
184
- expect(dot).toHaveAttribute('data-checked', 'checked');
185
- // The unchecked radio has no dot, so no dot slot either.
179
+ // The menu's target and the shared radio target land on the SAME painted
180
+ // circle, so menu radios and RadioList radios theme together and a theme
181
+ // never has to reach through a wrapper.
182
+ const box = checked.querySelector('.astryx-dropdown-menu-radio');
183
+ expect(box).toHaveClass('astryx-radio');
184
+ expect(box).toHaveAttribute('data-size', 'md');
185
+ expect(box).toHaveAttribute('data-checked', 'checked');
186
+ expect(box?.querySelector('.astryx-radio-dot')).toBeInTheDocument();
187
+
188
+ // The unchecked radio still draws its circle, without the dot.
186
189
  const unchecked = screen.getByRole('menuitemradio', {
187
190
  name: 'Oldest',
188
191
  hidden: true,
189
192
  });
193
+ const uncheckedIndicator = unchecked.querySelector('.astryx-radio');
194
+ expect(uncheckedIndicator).toBeInTheDocument();
195
+ expect(uncheckedIndicator).not.toHaveAttribute('data-checked');
190
196
  expect(
191
- unchecked.querySelector('.astryx-dropdown-menu-radio-dot'),
197
+ uncheckedIndicator?.querySelector('.astryx-radio-dot'),
192
198
  ).not.toBeInTheDocument();
193
199
  });
194
200
 
@@ -89,12 +89,12 @@ describe('DropdownMenuSubMenu', () => {
89
89
  name: /Move to/,
90
90
  hidden: true,
91
91
  });
92
- // The menu focuses its first item (Rename) on open via rAF; wait for that
93
- // to settle before moving focus to the submenu trigger, so the deferred
94
- // focus can't steal it back mid-test.
92
+ // A pointer open focuses the menu container via rAF (#4477); wait for
93
+ // that to settle before moving focus to the submenu trigger, so the
94
+ // deferred focus can't steal it back mid-test.
95
95
  await waitFor(() => {
96
96
  expect(
97
- screen.getByRole('menuitem', {name: 'Rename', hidden: true}),
97
+ screen.getByRole('menu', {name: 'Actions', hidden: true}),
98
98
  ).toHaveFocus();
99
99
  });
100
100
  trigger.focus();
@@ -176,11 +176,12 @@ describe('DropdownMenuSubMenu', () => {
176
176
  name: /Move to/,
177
177
  hidden: true,
178
178
  });
179
- // Let the root menu's open-focus (Rename, via rAF) settle before moving
180
- // focus to the submenu trigger, so it can't steal focus back mid-test.
179
+ // Let the root menu's pointer-open focus (the container, via rAF) settle
180
+ // before moving focus to the submenu trigger, so it can't steal focus
181
+ // back mid-test.
181
182
  await waitFor(() => {
182
183
  expect(
183
- screen.getByRole('menuitem', {name: 'Rename', hidden: true}),
184
+ screen.getByRole('menu', {name: 'Actions', hidden: true}),
184
185
  ).toHaveFocus();
185
186
  });
186
187
  trigger.focus();
@@ -235,6 +236,13 @@ describe('DropdownMenuSubMenu', () => {
235
236
  name: /Move to/,
236
237
  hidden: true,
237
238
  });
239
+ // Let the pointer-open container focus (rAF, #4477) settle so it can't
240
+ // steal focus from the manually focused trigger mid-test.
241
+ await waitFor(() => {
242
+ expect(
243
+ screen.getByRole('menu', {name: 'Actions', hidden: true}),
244
+ ).toHaveFocus();
245
+ });
238
246
  trigger.focus();
239
247
  await user.keyboard('{ArrowRight}');
240
248
  // Opens and focuses the first item.
@@ -334,13 +342,20 @@ describe('DropdownMenu data-driven submenus', () => {
334
342
  />,
335
343
  );
336
344
  await user.click(screen.getByRole('button', {name: /Actions/}));
345
+ // Pointer open focuses the container (#4477); the first ArrowDown then
346
+ // moves onto Rename.
347
+ await waitFor(() => {
348
+ expect(
349
+ screen.getByRole('menu', {name: 'Actions', hidden: true}),
350
+ ).toHaveFocus();
351
+ });
352
+ await user.keyboard('{ArrowDown}');
337
353
  await waitFor(() => {
338
354
  expect(
339
355
  screen.getByRole('menuitem', {name: 'Rename', hidden: true}),
340
356
  ).toHaveFocus();
341
357
  });
342
- // Rename → Move to → Delete (three ArrowDowns, no stalling on hidden
343
- // flyout items).
358
+ // Rename → Move to → Delete (no stalling on hidden flyout items).
344
359
  await user.keyboard('{ArrowDown}');
345
360
  await waitFor(() => {
346
361
  expect(
@@ -372,7 +387,7 @@ describe('DropdownMenuSubMenu accessibility (WCAG 2.2 / APG)', () => {
372
387
  });
373
388
  await waitFor(() => {
374
389
  expect(
375
- screen.getByRole('menuitem', {name: 'Rename', hidden: true}),
390
+ screen.getByRole('menu', {name: 'Actions', hidden: true}),
376
391
  ).toHaveFocus();
377
392
  });
378
393
  trigger.focus();
@@ -409,6 +424,13 @@ describe('DropdownMenuSubMenu accessibility (WCAG 2.2 / APG)', () => {
409
424
  name: /Move to/,
410
425
  hidden: true,
411
426
  });
427
+ // Let the pointer-open container focus (rAF, #4477) settle so it can't
428
+ // steal focus from the manually focused trigger mid-test.
429
+ await waitFor(() => {
430
+ expect(
431
+ screen.getByRole('menu', {name: 'Actions', hidden: true}),
432
+ ).toHaveFocus();
433
+ });
412
434
  trigger.focus();
413
435
  await user.keyboard('{ArrowRight}');
414
436
  await waitFor(() => {
@@ -463,11 +485,12 @@ describe('DropdownMenuSubMenu accessibility (WCAG 2.2 / APG)', () => {
463
485
  name: /Share to/,
464
486
  hidden: true,
465
487
  });
466
- // Let the root menu's open-focus (Copy link, via rAF) settle first so it
467
- // can't steal focus back after we move to the submenu trigger.
488
+ // Let the root menu's pointer-open focus (the container, via rAF, #4477)
489
+ // settle first so it can't steal focus back after we move to the submenu
490
+ // trigger.
468
491
  await waitFor(() => {
469
492
  expect(
470
- screen.getByRole('menuitem', {name: 'Copy link', hidden: true}),
493
+ screen.getByRole('menu', {name: 'Share', hidden: true}),
471
494
  ).toHaveFocus();
472
495
  });
473
496
  shareTo.focus();
@@ -584,7 +607,7 @@ describe('DropdownMenuSubMenu accessibility (WCAG 2.2 / APG)', () => {
584
607
  });
585
608
  await waitFor(() => {
586
609
  expect(
587
- screen.getByRole('menuitem', {name: 'Rename', hidden: true}),
610
+ screen.getByRole('menu', {name: 'Actions', hidden: true}),
588
611
  ).toHaveFocus();
589
612
  });
590
613
  trigger.focus();
@@ -650,6 +673,13 @@ describe('DropdownMenuSubMenu accessibility (WCAG 2.2 / APG)', () => {
650
673
  name: /Move to/,
651
674
  hidden: true,
652
675
  });
676
+ // Let the pointer-open container focus (rAF, #4477) settle so it can't
677
+ // steal focus from the manually focused trigger mid-test.
678
+ await waitFor(() => {
679
+ expect(
680
+ screen.getByRole('menu', {name: 'Actions', hidden: true}),
681
+ ).toHaveFocus();
682
+ });
653
683
  trigger.focus();
654
684
  await user.keyboard('{ArrowRight}');
655
685
  // Children resolve on open; ArrowDown moves from the container to Folder A.
@@ -147,14 +147,9 @@ const flyoutStyles = stylex.create({
147
147
  },
148
148
  popover: {
149
149
  minWidth: '160px',
150
- // Small inline gap so the flyout doesn't sit flush against the parent menu.
151
- marginInlineStart: spacingVars['--spacing-1'],
152
- marginInlineEnd: spacingVars['--spacing-1'],
153
150
  },
154
151
  popoverCustomWidth: (width: string | number) => ({
155
152
  minWidth: typeof width === 'number' ? `${width}px` : width,
156
- marginInlineStart: spacingVars['--spacing-1'],
157
- marginInlineEnd: spacingVars['--spacing-1'],
158
153
  }),
159
154
  });
160
155
 
@@ -559,6 +554,7 @@ export function DropdownMenuSubMenu(
559
554
  {
560
555
  placement: 'end',
561
556
  alignment: 'start',
557
+ offset: spacingVars['--spacing-1'],
562
558
  xstyle: [popoverXstyle, layerAnimations.end],
563
559
  },
564
560
  )}
@@ -22,13 +22,20 @@ export {
22
22
  type DropdownMenuProps,
23
23
  type DropdownMenuButtonProps,
24
24
  type DropdownMenuItemData,
25
- type DropdownMenuDivider,
25
+ type DropdownMenuDividerData,
26
26
  type DropdownMenuSection,
27
27
  type DropdownMenuOption,
28
28
  } from './DropdownMenu';
29
29
 
30
30
  export {DropdownMenuItem, type DropdownMenuItemProps} from './DropdownMenuItem';
31
31
 
32
+ // Divider — the compound peer of the data API's `{type: 'divider'}`. Both
33
+ // modes render this component, so they cannot drift.
34
+ export {
35
+ DropdownMenuDivider,
36
+ type DropdownMenuDividerProps,
37
+ } from './DropdownMenuDivider';
38
+
32
39
  // Selectable items — checkbox (independent) and radio (single-select group).
33
40
  export {
34
41
  DropdownMenuCheckboxItem,
@@ -6,10 +6,10 @@
6
6
  * @position Utility; used by DropdownMenu to unify data-driven and compound paths
7
7
  */
8
8
 
9
- import type {ReactNode} from 'react';
9
+ import type {ReactElement, ReactNode} from 'react';
10
10
  import * as stylex from '@stylexjs/stylex';
11
- import {Divider} from '../Divider';
12
11
  import {DropdownMenuItem} from './DropdownMenuItem';
12
+ import {DropdownMenuDivider} from './DropdownMenuDivider';
13
13
  import {DropdownMenuSubMenu} from './DropdownMenuSubMenu';
14
14
  import {
15
15
  spacingVars,
@@ -34,17 +34,38 @@ const styles = stylex.create({
34
34
  color: colorVars['--color-text-secondary'],
35
35
  userSelect: 'none',
36
36
  },
37
- divider: {
38
- marginBlock: spacingVars['--spacing-1'],
39
- },
40
37
  });
41
38
 
42
- function getItemKey(item: DropdownMenuItemData): string {
43
- return `item-${item.label}`;
39
+ function getItemKey(item: DropdownMenuItemData, index: number): string {
40
+ return `item-${item.id ?? index}`;
44
41
  }
45
42
 
46
43
  function getSectionKey(section: DropdownMenuSection, index: number): string {
47
- return `section-${section.title ?? index}`;
44
+ return `section-${section.id ?? index}`;
45
+ }
46
+
47
+ /**
48
+ * Renders one leaf row as a `DropdownMenuItem`.
49
+ *
50
+ * Keyed by `item.id` when the caller supplies one, else by position. NOT by
51
+ * label: an item that reports its own result (a copy row swapping to "Copied")
52
+ * would change key mid-interaction, remounting the row and dropping keyboard
53
+ * focus. Position is the safe default because a menu's rows are usually fixed;
54
+ * a menu whose items reorder or filter needs `id` for the same reason.
55
+ *
56
+ * `items` selects the submenu shape rather than being an item prop, and `id` is
57
+ * identity for React rather than something `DropdownMenuItem` renders, so both
58
+ * are stripped. Every remaining field of `DropdownMenuItemData` is a
59
+ * `DropdownMenuItem` prop by construction (the type is `Pick`ed from
60
+ * `DropdownMenuItemProps`), so the data path forwards them wholesale and can't
61
+ * silently drop a field the data API advertises.
62
+ */
63
+ function renderLeafItem(
64
+ item: DropdownMenuItemData,
65
+ index: number,
66
+ ): ReactElement {
67
+ const {items: _submenuItems, id: _id, ...itemProps} = item;
68
+ return <DropdownMenuItem key={getItemKey(item, index)} {...itemProps} />;
48
69
  }
49
70
 
50
71
  /**
@@ -58,13 +79,7 @@ export function renderDropdownItems(items: DropdownMenuOption[]): ReactNode {
58
79
  const option = items[i];
59
80
 
60
81
  if ('type' in option && option.type === 'divider') {
61
- elements.push(
62
- <Divider
63
- key={`divider-${i}`}
64
- xstyle={styles.divider}
65
- {...themeProps('dropdown-menu-divider')}
66
- />,
67
- );
82
+ elements.push(<DropdownMenuDivider key={`divider-${i}`} />);
68
83
  } else if ('type' in option && option.type === 'section') {
69
84
  elements.push(
70
85
  <div
@@ -81,15 +96,7 @@ export function renderDropdownItems(items: DropdownMenuOption[]): ReactNode {
81
96
  {option.title}
82
97
  </div>
83
98
  )}
84
- {option.items.map(item => (
85
- <DropdownMenuItem
86
- key={getItemKey(item)}
87
- icon={item.icon}
88
- label={item.label}
89
- onClick={item.onClick}
90
- isDisabled={item.isDisabled}
91
- />
92
- ))}
99
+ {option.items.map(renderLeafItem)}
93
100
  </div>,
94
101
  );
95
102
  } else if (!('type' in option)) {
@@ -101,7 +108,7 @@ export function renderDropdownItems(items: DropdownMenuOption[]): ReactNode {
101
108
  if (option.items && option.items.length > 0) {
102
109
  elements.push(
103
110
  <DropdownMenuSubMenu
104
- key={getItemKey(option)}
111
+ key={getItemKey(option, i)}
105
112
  icon={option.icon}
106
113
  label={option.label}
107
114
  isDisabled={option.isDisabled}>
@@ -109,15 +116,7 @@ export function renderDropdownItems(items: DropdownMenuOption[]): ReactNode {
109
116
  </DropdownMenuSubMenu>,
110
117
  );
111
118
  } else {
112
- elements.push(
113
- <DropdownMenuItem
114
- key={getItemKey(option)}
115
- icon={option.icon}
116
- label={option.label}
117
- onClick={option.onClick}
118
- isDisabled={option.isDisabled}
119
- />,
120
- );
119
+ elements.push(renderLeafItem(option, i));
121
120
  }
122
121
  }
123
122
  }
@@ -66,6 +66,8 @@ export const docs = {
66
66
  theming: {
67
67
  targets: [
68
68
  {className: 'astryx-empty-state', visualProps: ['variant']},
69
+ {className: 'astryx-empty-state-title', visualProps: ['variant']},
70
+ {className: 'astryx-empty-state-description', visualProps: ['variant']},
69
71
  ],
70
72
  },
71
73
  usage: {
@@ -141,6 +143,8 @@ export const docsZh = {
141
143
  theming: {
142
144
  targets: [
143
145
  {className: 'astryx-empty-state', visualProps: ['variant']},
146
+ {className: 'astryx-empty-state-title', visualProps: ['variant']},
147
+ {className: 'astryx-empty-state-description', visualProps: ['variant']},
144
148
  ],
145
149
  },
146
150
  usage: {
@@ -3,6 +3,13 @@
3
3
  import {describe, it, expect} from 'vitest';
4
4
  import {render, screen} from '@testing-library/react';
5
5
  import {EmptyState} from './EmptyState';
6
+ import {defineTheme} from '../theme/defineTheme';
7
+ import {generateThemeCSS} from '../theme/generateThemeRules';
8
+
9
+ function generateThemeTestCSS(theme: Parameters<typeof generateThemeCSS>[0]) {
10
+ const {prose, component} = generateThemeCSS(theme);
11
+ return [prose, component].filter(Boolean).join('\n\n');
12
+ }
6
13
 
7
14
  describe('EmptyState', () => {
8
15
  it('renders with title', () => {
@@ -148,4 +155,59 @@ describe('EmptyState', () => {
148
155
  expect(screen.getByText('Clear filters')).toBeInTheDocument();
149
156
  expect(screen.getByText('Go back')).toBeInTheDocument();
150
157
  });
158
+
159
+ describe('theming targets', () => {
160
+ it('puts astryx-empty-state-title on the title heading', () => {
161
+ render(<EmptyState title="No results" />);
162
+ const heading = screen.getByRole('heading', {name: 'No results'});
163
+ expect(heading).toHaveClass('astryx-empty-state-title');
164
+ });
165
+
166
+ it('puts astryx-empty-state-description on the description', () => {
167
+ render(
168
+ <EmptyState title="No results" description="Try another search." />,
169
+ );
170
+ const description = screen.getByText('Try another search.');
171
+ expect(description).toHaveClass('astryx-empty-state-description');
172
+ });
173
+
174
+ it('reflects the compact variant on the title and description targets', () => {
175
+ render(
176
+ <EmptyState
177
+ title="No results"
178
+ description="Try another search."
179
+ isCompact
180
+ />,
181
+ );
182
+ expect(screen.getByRole('heading', {name: 'No results'})).toHaveAttribute(
183
+ 'data-variant',
184
+ 'compact',
185
+ );
186
+ expect(screen.getByText('Try another search.')).toHaveAttribute(
187
+ 'data-variant',
188
+ 'compact',
189
+ );
190
+ });
191
+
192
+ it('exposes the title and description as themeable defineTheme targets', () => {
193
+ // jsdom can't resolve the @layer cascade, so this asserts the targets are
194
+ // reachable by a theme via the sanctioned defineTheme channel — replacing
195
+ // the structural `> div:has(> :is(h1..h6))` heading-detection selectors a
196
+ // consumer would otherwise need to restyle the title and description.
197
+ const theme = defineTheme({
198
+ name: 'empty-state-target-test',
199
+ components: {
200
+ 'empty-state-title': {
201
+ base: {fontSize: 'var(--text-large-size)'},
202
+ },
203
+ 'empty-state-description': {
204
+ base: {color: 'var(--color-text-secondary)'},
205
+ },
206
+ },
207
+ });
208
+ const css = generateThemeTestCSS(theme);
209
+ expect(css).toContain('.astryx-empty-state-title');
210
+ expect(css).toContain('.astryx-empty-state-description');
211
+ });
212
+ });
151
213
  });
@@ -157,6 +157,7 @@ export function EmptyState({
157
157
  return (
158
158
  <div
159
159
  ref={ref}
160
+ {...props}
160
161
  role="status"
161
162
  {...mergeProps(
162
163
  themeProps('empty-state', {variant: isCompact ? 'compact' : null}),
@@ -167,13 +168,17 @@ export function EmptyState({
167
168
  ),
168
169
  className,
169
170
  style,
170
- )}
171
- {...props}>
171
+ )}>
172
172
  {icon != null && <div aria-hidden="true">{icon}</div>}
173
173
  <div {...stylex.props(styles.textGroup)}>
174
174
  {createElement(
175
175
  HeadingTag,
176
- stylex.props(styles.title, isCompact && styles.titleCompact),
176
+ mergeProps(
177
+ themeProps('empty-state-title', {
178
+ variant: isCompact ? 'compact' : null,
179
+ }),
180
+ stylex.props(styles.title, isCompact && styles.titleCompact),
181
+ ),
177
182
  title,
178
183
  )}
179
184
  {description != null && (
@@ -182,9 +187,14 @@ export function EmptyState({
182
187
  // mismatches. The StyleX style sets margin: 0, so appearance is
183
188
  // unchanged.
184
189
  <div
185
- {...stylex.props(
186
- styles.description,
187
- isCompact && styles.descriptionCompact,
190
+ {...mergeProps(
191
+ themeProps('empty-state-description', {
192
+ variant: isCompact ? 'compact' : null,
193
+ }),
194
+ stylex.props(
195
+ styles.description,
196
+ isCompact && styles.descriptionCompact,
197
+ ),
188
198
  )}>
189
199
  {description}
190
200
  </div>