@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
@@ -21,6 +21,29 @@ function generateThemeTestCSS(theme: Parameters<typeof generateThemeCSS>[0]) {
21
21
  const {prose, component} = generateThemeCSS(theme);
22
22
  return [prose, component].filter(Boolean).join('\n\n');
23
23
  }
24
+
25
+ /**
26
+ * Concatenate all StyleX-injected CSS (both CSSOM sheets and <style> text) so
27
+ * tests can assert on compiled stylesheet rules — jsdom does not resolve the
28
+ * @layer cascade or compute layout, so structural CSS assertions read the
29
+ * generated declarations directly.
30
+ */
31
+ function collectCssText(): string {
32
+ let out = '';
33
+ for (const sheet of Array.from(document.styleSheets)) {
34
+ try {
35
+ for (const rule of Array.from(sheet.cssRules)) {
36
+ out += rule.cssText + '\n';
37
+ }
38
+ } catch {
39
+ // ignore cross-origin sheets
40
+ }
41
+ }
42
+ out += Array.from(document.querySelectorAll('style'))
43
+ .map(s => s.textContent || '')
44
+ .join('\n');
45
+ return out;
46
+ }
24
47
  const simpleItems: TreeListItemData[] = [
25
48
  {id: 'a', label: 'Item A'},
26
49
  {id: 'b', label: 'Item B'},
@@ -276,16 +299,16 @@ describe('TreeList', () => {
276
299
  expect(root).toHaveFocus();
277
300
 
278
301
  expect(
279
- getComputedStyle(root).getPropertyValue('--_tree-focus-outline'),
302
+ getComputedStyle(root).getPropertyValue('--_focus-outline'),
280
303
  ).toContain('solid');
281
304
  // Mid and Leaf are DOM descendants of Root's <li> (nested <ul role="group">
282
305
  // subtrees) — their own outline var must stay unset, not inherit Root's.
283
- expect(
284
- getComputedStyle(mid).getPropertyValue('--_tree-focus-outline'),
285
- ).toBe('none');
286
- expect(
287
- getComputedStyle(leaf).getPropertyValue('--_tree-focus-outline'),
288
- ).toBe('none');
306
+ expect(getComputedStyle(mid).getPropertyValue('--_focus-outline')).toBe(
307
+ 'none',
308
+ );
309
+ expect(getComputedStyle(leaf).getPropertyValue('--_focus-outline')).toBe(
310
+ 'none',
311
+ );
289
312
  });
290
313
 
291
314
  // ===========================================================================
@@ -548,6 +571,182 @@ describe('TreeList', () => {
548
571
  });
549
572
  });
550
573
 
574
+ // ===========================================================================
575
+ // Leaf chevron-column offset (group-expandable-sibling awareness)
576
+ // ===========================================================================
577
+
578
+ describe('leaf chevron-column offset', () => {
579
+ // A row publishes its indent as the inline `--_tree-indent` custom property.
580
+ // A leaf that reserves the chevron column adds a fixed offset
581
+ // (chevron width + gap) on top of its level indent; a flush leaf does not.
582
+ // The reserved column is expressed as the literal `+ <spacing-4> + <spacing-2>`
583
+ // suffix in the calc(), so its presence is what we assert (jsdom does not
584
+ // resolve the token values, so we check the structure, not pixels).
585
+ const indentStyleOf = (text: string): string => {
586
+ const li = screen.getByText(text).closest('li')!;
587
+ const styled = li.querySelector('[style*="--_tree-indent"]');
588
+ return styled?.getAttribute('style') ?? '';
589
+ };
590
+ // A reserved chevron column adds two extra terms to the indent calc()
591
+ // beyond the single `level * var(--tree-list-indent)` term.
592
+ const reservesColumn = (text: string): boolean => {
593
+ const style = indentStyleOf(text);
594
+ // count the `var(` occurrences inside --_tree-indent: a flush row has
595
+ // exactly one (the indent step); a reserving leaf adds the chevron
596
+ // width + gap tokens, so it has more.
597
+ const m = /--_tree-indent:\s*calc\(([^;]*)\)/.exec(style);
598
+ const body = m?.[1] ?? '';
599
+ return (body.match(/\bvar\(/g)?.length ?? 0) > 1 || body.includes('+');
600
+ };
601
+
602
+ it('a leaf in a mixed group reserves the chevron column (aligns under its expandable sibling)', () => {
603
+ // Group: [Parent (has children), Sibling (leaf)] → Sibling must line up
604
+ // under Parent's caret, so it keeps the chevron-column offset.
605
+ render(<TreeList items={nestedItems} />);
606
+ expect(reservesColumn('Sibling')).toBe(true);
607
+ });
608
+
609
+ it('a leaf under an expandable ancestor reserves the chevron column even when its own group is all leaves', () => {
610
+ // 'Root' → 'Mid' → 'Leaf'; 'Leaf' is the only item in its immediate
611
+ // group, but the tree has carets (Root, Mid), so Leaf must reserve the
612
+ // chevron column to stay indented past its parent's label. Flushing it
613
+ // here would push it left of Mid's label — the all-leaf-group bug.
614
+ render(<TreeList items={deepItems} variant="noGuides" />);
615
+ expect(reservesColumn('Leaf')).toBe(true);
616
+ });
617
+
618
+ it("an expanded parent's all-leaf children reserve the chevron column (do not flush left of the parent label)", () => {
619
+ // Regression: Parent (caret) → [Child 1, Child 2] is an all-leaf group
620
+ // nested under an expandable parent. Per-group flushing wrongly dropped
621
+ // these children's chevron column, pushing them LEFT of Parent's own
622
+ // label. Because the tree has a caret, they must reserve the column and
623
+ // stay indented past Parent.
624
+ render(<TreeList items={nestedItemsExpanded} />);
625
+ expect(reservesColumn('Child 1')).toBe(true);
626
+ expect(reservesColumn('Child 2')).toBe(true);
627
+ });
628
+
629
+ it('every row in a flat (all-leaf) tree sits flush — no chevron column reserved', () => {
630
+ render(<TreeList items={flatItems} />);
631
+ expect(reservesColumn('Apple')).toBe(false);
632
+ expect(reservesColumn('Banana')).toBe(false);
633
+ expect(reservesColumn('Cherry')).toBe(false);
634
+ });
635
+
636
+ it('leaves flush in an all-leaf group have the same indent structure as a parent at that level', () => {
637
+ // In a mixed group the reserving leaf indent has the extra terms; in an
638
+ // all-leaf group the leaf indent is the bare level step, exactly like a
639
+ // parent row's indent.
640
+ render(<TreeList items={flatItems} />);
641
+ const flush = indentStyleOf('Apple');
642
+ // bare level step: one var(--tree-list-indent), no additive chevron terms
643
+ expect(flush).toContain('var(--tree-list-indent)');
644
+ expect(/--_tree-indent:\s*calc\([^;]*\+[^;]*\)/.test(flush)).toBe(false);
645
+ });
646
+ });
647
+
648
+ // ===========================================================================
649
+ // Inter-row gap lever (--tree-list-row-gap) + guide spanning
650
+ // ===========================================================================
651
+
652
+ describe('row gap lever', () => {
653
+ it('defaults the row gap to a subtle 2px separation', () => {
654
+ // The lever is published on the tree-list root; its default is
655
+ // --spacing-0-5 (2px) so rows have a light separation out of the box. A
656
+ // theme can widen or close it via the tree-list target. jsdom does not
657
+ // resolve token vars, so the declared value is the --spacing-0-5 token.
658
+ render(<TreeList items={simpleItems} data-testid="tree" />);
659
+ const root = screen.getByTestId('tree');
660
+ expect(
661
+ getComputedStyle(root).getPropertyValue('--tree-list-row-gap').trim(),
662
+ ).toBe('var(--spacing-0-5)');
663
+ });
664
+
665
+ it('lets a theme open a row gap via the tree-list target', () => {
666
+ // jsdom cannot resolve the @layer cascade, so the generated CSS is what
667
+ // proves the override reaches the lever.
668
+ const theme = defineTheme({
669
+ name: 'tree-list-row-gap-test',
670
+ components: {
671
+ 'tree-list': {
672
+ base: {'--tree-list-row-gap': 'var(--spacing-1)'},
673
+ },
674
+ },
675
+ });
676
+ const css = generateThemeTestCSS(theme);
677
+ expect(css).toContain('.astryx-tree-list {');
678
+ expect(css).toContain('--tree-list-row-gap: var(--spacing-1)');
679
+ });
680
+
681
+ it('the guide of a row with a sibling below spans into it (verticalFull)', () => {
682
+ // A row that is NOT last in its group bridges the 1px hairline into the
683
+ // next contiguous sibling so the connector reads as one continuous line.
684
+ // Assert the APPLIED class on that row's own connector — not a global
685
+ // stylesheet regex — so the isLast ? verticalLast : verticalFull branch
686
+ // is actually exercised (Child 1 has Child 2 below it).
687
+ render(<TreeList items={nestedItemsExpanded} />);
688
+ const child1 = screen.getByText('Child 1').closest('li')!;
689
+ const guide = child1.firstElementChild!.querySelector(
690
+ '.astryx-tree-list-guide',
691
+ );
692
+ expect(guide).not.toBeNull();
693
+ expect(guide!.className).toContain('verticalFull');
694
+ expect(guide!.className).not.toContain('verticalLast');
695
+ });
696
+
697
+ it('clamps the last-in-group guide so it does not overhang the gap (verticalLast)', () => {
698
+ // The last row in a group has nothing below it, so its connector must NOT
699
+ // run through the row wrapper's bottom padding into empty space — it uses
700
+ // verticalLast, which subtracts half the row gap. Assert the applied class
701
+ // on the last child's own connector (Child 2 is last in its group), so a
702
+ // reverted branch is caught.
703
+ render(<TreeList items={nestedItemsExpanded} />);
704
+ const child2 = screen.getByText('Child 2').closest('li')!;
705
+ const guide = child2.firstElementChild!.querySelector(
706
+ '.astryx-tree-list-guide',
707
+ );
708
+ expect(guide).not.toBeNull();
709
+ expect(guide!.className).toContain('verticalLast');
710
+ expect(guide!.className).not.toContain('verticalFull');
711
+ });
712
+
713
+ it('carries the inter-row gap as collapse-proof padding on the row wrapper, not the paint target', () => {
714
+ // Finding from review: the gap must be `padding-block` (which cannot
715
+ // collapse) on the row WRAPPER, not `margin-block` on the row box — a
716
+ // margin there collapses through the position:relative wrapper and the
717
+ // <li>, delivering only half the configured gap. It also must not sit on
718
+ // `tree-list-item`, which is a paint seam (per the theming-target
719
+ // guidelines): layout longhands do not belong on a paintable target.
720
+ const {container} = render(<TreeList items={simpleItems} />);
721
+ const item = container.querySelector<HTMLElement>(
722
+ '.astryx-tree-list-item',
723
+ );
724
+ expect(item).not.toBeNull();
725
+ const rowWrapper = item!.parentElement!;
726
+
727
+ const rules = collectCssText();
728
+ // Map the rowWrapper's compiled class to its declaration block and assert
729
+ // it declares padding-block from the row-gap lever (half above/below).
730
+ const rowWrapperClass = Array.from(rowWrapper.classList).find(c =>
731
+ c.includes('rowWrapper'),
732
+ );
733
+ expect(rowWrapperClass).toBeDefined();
734
+ // The gap rides padding-block (collapse-proof), keyed off the lever.
735
+ expect(rules).toMatch(
736
+ /padding-block:\s*calc\(\s*var\(--tree-list-row-gap[^)]*\)\s*\/\s*2\s*\)/,
737
+ );
738
+ // And the paint target (tree-list-item / contentWrapper) must NOT carry a
739
+ // margin-block gap — the layout seam has moved off the paintable element.
740
+ const itemClass = Array.from(item!.classList).find(c =>
741
+ c.includes('contentWrapper'),
742
+ );
743
+ expect(itemClass).toBeDefined();
744
+ expect(rules).not.toMatch(
745
+ /margin-block:\s*calc\([^;]*var\(--tree-list-row-gap/,
746
+ );
747
+ });
748
+ });
749
+
551
750
  // ===========================================================================
552
751
  // xds class name
553
752
  // ===========================================================================
@@ -88,6 +88,13 @@ const styles = stylex.create({
88
88
  // on the `tree-list` target, and both the row margins (TreeListItem) and
89
89
  // the guide-line offsets (TreeListBranches) read it so they stay aligned.
90
90
  '--tree-list-indent': spacingVars['--spacing-4'],
91
+ // Vertical gap between adjacent rows. Public, themeable lever (default
92
+ // `--spacing-0-5` = 2px for a subtle separation between rows). A theme sets
93
+ // it on the `tree-list` target to widen or close the gap; the guide
94
+ // connector spans the gap natively (see TreeListBranches) so the line stays
95
+ // continuous and does not overhang the last row — no consumer-side guide
96
+ // tuning needed.
97
+ '--tree-list-row-gap': spacingVars['--spacing-0-5'],
91
98
  },
92
99
  list: {
93
100
  margin: 0,
@@ -250,11 +257,25 @@ export function TreeList({
250
257
  hasRovingTabIndex: true,
251
258
  });
252
259
 
260
+ const hasExpandableItems = items.some(
261
+ item => item.children != null && item.children.length > 0,
262
+ );
263
+
253
264
  function renderItems(
254
265
  items: TreeListItemData[],
255
266
  nestedLevel: number,
256
267
  ancestorsIsLast: ReadonlyArray<boolean>,
257
268
  ): ReactNode {
269
+ // A leaf reserves the chevron column (so its label lines up under a parent's
270
+ // caret) whenever the tree contains ANY expandable item — i.e. whenever a
271
+ // caret exists somewhere to align under. This is a whole-tree property, not
272
+ // a per-group one: a leaf nested under an expandable ancestor must still
273
+ // reserve the column so it stays indented past its parent's label, even if
274
+ // its own immediate group happens to be all leaves. Only a fully flat tree
275
+ // (no expandable items anywhere) has no caret to align under, so its rows
276
+ // sit flush. Because any expandable descendant forces its whole ancestor
277
+ // chain to be expandable, "the tree has an expandable item" is equivalent to
278
+ // "the root group has an expandable item" — checked once at the root.
258
279
  return items.map((item, index) => {
259
280
  const isLast = index === items.length - 1;
260
281
  const isExpanded = expandedKeysOverride.has(item.id)
@@ -284,6 +305,7 @@ export function TreeList({
284
305
  startContent={item.startContent}
285
306
  endContent={item.endContent}
286
307
  hasChildren={hasChildren}
308
+ hasExpandableItems={hasExpandableItems}
287
309
  onClick={item.onClick}
288
310
  href={item.href}
289
311
  target={item.target}
@@ -46,9 +46,24 @@ const styles = stylex.create({
46
46
  width: LINE_WIDTH,
47
47
  backgroundColor: colorVars['--color-border-emphasized'],
48
48
  },
49
+ // Guide segment spanning the full <li>. The row box's inter-row gap now lives
50
+ // INSIDE the <li> (as `padding-block` on the row wrapper), so `height: 100%`
51
+ // already covers it — the segment only needs the original `1px` to bridge the
52
+ // hairline into the next contiguous sibling so the connector reads as one
53
+ // continuous line. Independent of `--tree-list-row-gap`: the gap is absorbed
54
+ // by the <li> height, not added on top here.
49
55
  verticalFull: {
50
56
  height: 'calc(100% + 1px)',
51
57
  },
58
+ // Last-in-group connector: nothing sits below, so the segment must not run
59
+ // through the row wrapper's bottom `padding-block` (`--tree-list-row-gap` / 2)
60
+ // into empty space. Clamp it back by that half-gap so it ends exactly at the
61
+ // row box's bottom edge. At the default `--spacing-0-5` gap this trims the
62
+ // 1px of bottom padding; at `0px` it is exactly `100%` — no overhang at any
63
+ // gap.
64
+ verticalLast: {
65
+ height: 'calc(100% - var(--tree-list-row-gap, 0px) / 2)',
66
+ },
52
67
  });
53
68
 
54
69
  // =============================================================================
@@ -76,7 +91,7 @@ interface TreeListBranchesProps {
76
91
  */
77
92
  export function TreeListBranches({
78
93
  ancestorsIsLast,
79
- isLast: _isLast,
94
+ isLast,
80
95
  nestedLevel,
81
96
  }: TreeListBranchesProps) {
82
97
  return (
@@ -117,7 +132,15 @@ export function TreeListBranches({
117
132
  <div
118
133
  {...mergeProps(
119
134
  themeProps('tree-list-guide'),
120
- stylex.props(styles.verticalLine, styles.verticalFull),
135
+ // The last item in a group has no sibling below, so its connector
136
+ // is clamped to the row box's bottom edge (verticalLast) instead
137
+ // of bridging into the inter-row gap — no overhang into empty
138
+ // space. Every other row bridges the gap (verticalFull) so the
139
+ // line stays continuous down to the next sibling.
140
+ stylex.props(
141
+ styles.verticalLine,
142
+ isLast ? styles.verticalLast : styles.verticalFull,
143
+ ),
121
144
  )}
122
145
  />
123
146
  </div>
@@ -24,8 +24,9 @@ import {
24
24
  easeVars,
25
25
  typeScaleVars,
26
26
  } from '../theme/tokens.stylex';
27
- import {useIcon} from '../Icon';
28
- import {mergeProps, rtlStyles} from '../utils';
27
+ import {focusOutlineProps} from '../utils/focusOutline.stylex';
28
+ import {Icon} from '../Icon';
29
+ import {mergeProps} from '../utils';
29
30
  import {useLinkComponent} from '../Link/useLinkComponent';
30
31
  import {TreeListBranches} from './TreeListBranches';
31
32
  import type {TreeListDensity, TreeListVariant} from './TreeListTypes';
@@ -51,14 +52,6 @@ const styles = stylex.create({
51
52
  // redeclares these vars (default: 'none' / '0'), so a descendant row's
52
53
  // default shadows an ancestor's active value — the ring can never leak
53
54
  // past the nearest containing treeitem, however deep the tree nests.
54
- '--_tree-focus-outline': {
55
- default: 'none',
56
- ':focus-visible': `2px solid ${colorVars['--color-accent']}`,
57
- },
58
- '--_tree-focus-outline-offset': {
59
- default: '0',
60
- ':focus-visible': '2px',
61
- },
62
55
  },
63
56
  childGroup: {
64
57
  margin: 0,
@@ -70,6 +63,17 @@ const styles = stylex.create({
70
63
  },
71
64
  rowWrapper: {
72
65
  position: 'relative',
66
+ // Inter-row gap. Half the public `--tree-list-row-gap` lever sits above and
67
+ // half below the row box; because this is PADDING (not margin) it cannot
68
+ // collapse, so adjacent rows end up a full gap apart — and it rides the
69
+ // `rowWrapper`, which carries no theme target, so the paintable
70
+ // `tree-list-item` stays a pure paint seam (layout lives off it). The <li>s
71
+ // stay contiguous — the gap is padding INSIDE each <li>, not space between
72
+ // them — so the per-<li> connector guide can still span it and read as a
73
+ // continuous line (see TreeListBranches). The lever's default is a subtle
74
+ // `--spacing-0-5` (2px, set on the tree-list root); a theme widens or closes
75
+ // it via the `tree-list` target.
76
+ paddingBlock: 'calc(var(--tree-list-row-gap, 0px) / 2)',
73
77
  },
74
78
  contentWrapper: {
75
79
  borderRadius: radiusVars['--radius-element'],
@@ -77,7 +81,8 @@ const styles = stylex.create({
77
81
  alignItems: 'center',
78
82
  gap: spacingVars['--spacing-2'],
79
83
  paddingInline: spacingVars['--spacing-2'],
80
- outline: 'none',
84
+ // No `outline: 'none'` here: this element receives the shared focus ring,
85
+ // which already defaults to none, and the shorthand would erase it.
81
86
  overflow: 'hidden',
82
87
  position: 'relative',
83
88
  boxSizing: 'border-box',
@@ -102,19 +107,6 @@ const styles = stylex.create({
102
107
  ':active': `linear-gradient(${colorVars['--color-overlay-pressed']}, ${colorVars['--color-overlay-pressed']})`,
103
108
  },
104
109
  },
105
- focusVisibleOutline: {
106
- outline: {
107
- // Reads the row's own --_tree-focus-outline (published on the <li> in
108
- // `wrapper`), which resolves to the nearest containing treeitem only.
109
- default: 'var(--_tree-focus-outline, none)',
110
- // Also support inner focusable actions.
111
- ':has(:focus-visible)': `2px solid ${colorVars['--color-accent']}`,
112
- },
113
- outlineOffset: {
114
- default: 'var(--_tree-focus-outline-offset, 0)',
115
- ':has(:focus-visible)': '2px',
116
- },
117
- },
118
110
  disabled: {
119
111
  cursor: 'not-allowed',
120
112
  opacity: 0.5,
@@ -207,15 +199,34 @@ const styles = stylex.create({
207
199
  },
208
200
  chevronSvg: {
209
201
  display: 'flex',
202
+ // The chevron column is sized in spacing tokens by the button/container
203
+ // around it (--spacing-4 = 16px), not on Icon's rem scale, so the glyph's
204
+ // box is pinned to that same token. Icon's `sm` (1rem) only coincides with
205
+ // 16px at a 16px root font-size; drifting off the token would knock the
206
+ // glyph out of its 16px column.
207
+ width: spacingVars['--spacing-4'],
208
+ height: spacingVars['--spacing-4'],
209
+ fontSize: spacingVars['--spacing-4'],
210
210
  transitionProperty: 'transform',
211
211
  transitionDuration: durationVars['--duration-fast'],
212
212
  transitionTimingFunction: easeVars['--ease-standard'],
213
213
  },
214
+ // The RTL mirror is folded into each state's transform rather than living on
215
+ // a parent span. Both are `transform`, so on one element the later value
216
+ // would win — spelling out `scaleX(-1) rotate(...)` per state composes them
217
+ // exactly as the nested elements did, while leaving a single element to
218
+ // carry the glyph's theme target.
214
219
  chevronExpanded: {
215
- transform: 'rotate(90deg)',
220
+ transform: {
221
+ default: 'rotate(90deg)',
222
+ ':is([dir="rtl"] *)': 'scaleX(-1) rotate(90deg)',
223
+ },
216
224
  },
217
225
  chevronCollapsed: {
218
- transform: 'rotate(0deg)',
226
+ transform: {
227
+ default: 'rotate(0deg)',
228
+ ':is([dir="rtl"] *)': 'scaleX(-1) rotate(0deg)',
229
+ },
219
230
  },
220
231
  });
221
232
 
@@ -273,6 +284,16 @@ export interface TreeListItemInternalProps {
273
284
  isDisabled?: boolean;
274
285
  isSelected?: boolean;
275
286
  hasChildren: boolean;
287
+ /**
288
+ * Whether the tree contains at least one expandable item anywhere (i.e. a
289
+ * caret exists somewhere to align labels under). A leaf reserves the chevron
290
+ * column — the extra offset that lines its label up past an expandable
291
+ * ancestor/sibling's caret — whenever this is true, so it stays indented
292
+ * beyond its parent's label. Only a fully flat tree (no expandable items at
293
+ * all) has no caret to align under, so its rows sit flush (no chevron-column
294
+ * offset). Computed once for the whole tree by TreeList.
295
+ */
296
+ hasExpandableItems: boolean;
276
297
  nestedLevel: number;
277
298
  isLast: boolean;
278
299
  ancestorsIsLast: ReadonlyArray<boolean>;
@@ -315,6 +336,7 @@ export function TreeListItem({
315
336
  isDisabled = false,
316
337
  isSelected = false,
317
338
  hasChildren,
339
+ hasExpandableItems,
318
340
  nestedLevel,
319
341
  isLast,
320
342
  ancestorsIsLast,
@@ -328,7 +350,6 @@ export function TreeListItem({
328
350
  isTabbable,
329
351
  }: TreeListItemInternalProps) {
330
352
  const t = useTranslator();
331
- const chevronRightIcon = useIcon('chevronRight');
332
353
  const labelId = useId();
333
354
  const descriptionId = useId();
334
355
  const LinkComponent = useLinkComponent();
@@ -367,15 +388,19 @@ export function TreeListItem({
367
388
 
368
389
  // Per-level indent distance. The per-level step is the public, themeable
369
390
  // `--tree-list-indent` lever (default `--spacing-4`, set on the tree-list
370
- // root). Leaves add a fixed chevron-column offset (chevron width + gap) so
371
- // their labels line up with sibling parents' labels; that offset is tied to
391
+ // root). A leaf adds a fixed chevron-column offset (chevron width + gap) so
392
+ // its label lines up past an expandable ancestor/sibling's caret but ONLY
393
+ // when the tree actually contains an expandable item somewhere. In a fully
394
+ // flat tree there is no caret to align under, so the offset is pointless and
395
+ // every row sits flush, like a parent at that level. That offset is tied to
372
396
  // the chevron's own dimensions, not the indent step, so it does not scale
373
397
  // with the lever. Published as the private `--_tree-indent` and consumed by
374
398
  // `contentWrapper`'s stylesheet `margin-inline-start` (kept out of the inline
375
399
  // style so the theme layer can override it — see #4308).
376
- const indentDistance = hasChildren
377
- ? `calc(${nestedLevel} * var(--tree-list-indent))`
378
- : `calc(${nestedLevel} * var(--tree-list-indent) + ${spacingVars['--spacing-4']} + ${spacingVars['--spacing-2']})`;
400
+ const reservesChevronColumn = !hasChildren && hasExpandableItems;
401
+ const indentDistance = reservesChevronColumn
402
+ ? `calc(${nestedLevel} * var(--tree-list-indent) + ${spacingVars['--spacing-4']} + ${spacingVars['--spacing-2']})`
403
+ : `calc(${nestedLevel} * var(--tree-list-indent))`;
379
404
  const indentStyle: IndentStyle = {'--_tree-indent': indentDistance};
380
405
 
381
406
  const labelAndDescription = (
@@ -406,16 +431,24 @@ export function TreeListItem({
406
431
  </>
407
432
  );
408
433
 
434
+ // <Icon> renders the glyph's span itself — carrying the pre-existing
435
+ // astryx-icon target — so the rotation rides on that same element instead of
436
+ // an extra wrapper: a theme can still restyle the mark and its open/closed
437
+ // transform through one selector.
409
438
  const chevronIcon = (
410
- <span {...stylex.props(rtlStyles.mirror)}>
411
- <span
412
- {...stylex.props(
413
- styles.chevronSvg,
414
- isExpanded ? styles.chevronExpanded : styles.chevronCollapsed,
415
- )}>
416
- {chevronRightIcon}
417
- </span>
418
- </span>
439
+ <Icon
440
+ icon="chevronRight"
441
+ // Nearest size to the 16px chevron column; `chevronSvg` re-pins the exact
442
+ // box because the column is spacing-token-sized, not rem-sized.
443
+ size="sm"
444
+ // The button/container owns the chevron color (--color-icon-secondary);
445
+ // inheriting keeps that as the single source.
446
+ color="inherit"
447
+ xstyle={[
448
+ styles.chevronSvg,
449
+ isExpanded ? styles.chevronExpanded : styles.chevronCollapsed,
450
+ ]}
451
+ />
419
452
  );
420
453
 
421
454
  const chevron = hasChildren ? (
@@ -521,7 +554,7 @@ export function TreeListItem({
521
554
  data-tree-id={id}
522
555
  data-tree-level={nestedLevel + 1}
523
556
  data-tree-disabled={isDisabled || undefined}
524
- {...stylex.props(styles.wrapper)}>
557
+ {...focusOutlineProps.publishFocusVisibleVars(styles.wrapper)}>
525
558
  {variant !== 'noGuides' && (
526
559
  <div {...stylex.props(styles.treeBranches)}>
527
560
  <TreeListBranches
@@ -539,16 +572,20 @@ export function TreeListItem({
539
572
  selected: isSelected ? 'selected' : null,
540
573
  disabled: isDisabled ? 'disabled' : null,
541
574
  }),
542
- stylex.props(
543
- styles.contentWrapper,
544
- densityStyles[density],
545
- (isInteractive || (hasChildren && onClick == null)) &&
546
- styles.interactive,
547
- (isInteractive || (hasChildren && onClick == null)) &&
548
- styles.focusVisibleOutline,
549
- isDisabled && styles.disabled,
550
- isSelected && styles.selected,
551
- ),
575
+ isInteractive || (hasChildren && onClick == null)
576
+ ? focusOutlineProps.focusWithinOrPublished(
577
+ styles.contentWrapper,
578
+ densityStyles[density],
579
+ styles.interactive,
580
+ isDisabled && styles.disabled,
581
+ isSelected && styles.selected,
582
+ )
583
+ : stylex.props(
584
+ styles.contentWrapper,
585
+ densityStyles[density],
586
+ isDisabled && styles.disabled,
587
+ isSelected && styles.selected,
588
+ ),
552
589
  )}
553
590
  style={indentStyle}
554
591
  onClick={handleClick}>
@@ -30,6 +30,7 @@ import * as stylex from '@stylexjs/stylex';
30
30
  import type {StyleXStyles} from '@stylexjs/stylex';
31
31
  import {usePopover} from '../Popover/usePopover';
32
32
  import {useAnnounce} from '../hooks/useAnnounce';
33
+ import {isImeKeyEvent} from '../hooks/useFocusTrap';
33
34
  import {TypeaheadItem} from './TypeaheadItem';
34
35
  import {Icon} from '../Icon';
35
36
  import {
@@ -225,10 +226,6 @@ const styles = stylex.create({
225
226
  popover: {
226
227
  minWidth: 'anchor-size(width)',
227
228
  },
228
- popoverGap: {
229
- marginBlockStart: spacingVars['--spacing-1'],
230
- marginBlockEnd: spacingVars['--spacing-1'],
231
- },
232
229
  item: {
233
230
  boxSizing: 'border-box',
234
231
  display: 'flex',
@@ -633,6 +630,17 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
633
630
  return;
634
631
  }
635
632
 
633
+ // An IME candidate window uses Enter to commit the composition and
634
+ // Escape/ArrowUp/ArrowDown/Home/End to navigate its own candidates.
635
+ // Without this guard, a composing Enter both fires handleSelect AND
636
+ // clears the input via handleSelect's setQuery(''), so the IME's
637
+ // subsequent compositionend then writes the still-pending syllable
638
+ // into the freshly-cleared field -- producing a second, spurious
639
+ // selection on the next real Enter.
640
+ if (isImeKeyEvent(e.nativeEvent)) {
641
+ return;
642
+ }
643
+
636
644
  if (!popover.isOpen) {
637
645
  if (e.key === 'ArrowDown' && (hasEntriesOnFocus || query.length > 0)) {
638
646
  e.preventDefault();
@@ -809,7 +817,11 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
809
817
  stylex.props(styles.dropdown),
810
818
  )}>
811
819
  {results.length === 0 && hasSearched ? (
812
- <div {...stylex.props(styles.emptyState)}>
820
+ <div
821
+ {...mergeProps(
822
+ themeProps('typeahead-empty-state'),
823
+ stylex.props(styles.emptyState),
824
+ )}>
813
825
  {emptySearchResultsText}
814
826
  </div>
815
827
  ) : (
@@ -849,7 +861,8 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
849
861
  {
850
862
  placement: 'below',
851
863
  alignment: 'start',
852
- xstyle: [styles.popover, styles.popoverGap],
864
+ offset: spacingVars['--spacing-1'],
865
+ xstyle: styles.popover,
853
866
  },
854
867
  )}
855
868
  </>
@@ -183,6 +183,7 @@ export const docs = {
183
183
  targets: [
184
184
  {className: 'astryx-typeahead', visualProps: ['status', 'size']},
185
185
  {className: 'astryx-typeahead-dropdown'},
186
+ {className: 'astryx-typeahead-empty-state'},
186
187
  {className: 'astryx-typeahead-item'},
187
188
  ],
188
189
  },