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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (428) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +30 -19
  3. package/dist/AppShell/AppShell.d.ts.map +1 -1
  4. package/dist/AppShell/AppShell.js +6 -2
  5. package/dist/AppShell/AppShellMobileContext.d.ts +8 -0
  6. package/dist/AppShell/AppShellMobileContext.d.ts.map +1 -1
  7. package/dist/Avatar/Avatar.d.ts +10 -5
  8. package/dist/Avatar/Avatar.d.ts.map +1 -1
  9. package/dist/Avatar/Avatar.js +12 -8
  10. package/dist/Avatar/AvatarSizeContext.d.ts +1 -1
  11. package/dist/Avatar/AvatarSizeContext.js +1 -1
  12. package/dist/Avatar/AvatarStatusDot.d.ts +2 -2
  13. package/dist/Avatar/AvatarStatusDot.js +2 -2
  14. package/dist/AvatarGroup/AvatarGroup.d.ts +2 -2
  15. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  16. package/dist/AvatarGroup/AvatarGroup.js +2 -2
  17. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  18. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  19. package/dist/AvatarGroup/AvatarGroupOverflow.js +16 -14
  20. package/dist/Banner/Banner.d.ts +9 -1
  21. package/dist/Banner/Banner.d.ts.map +1 -1
  22. package/dist/Banner/Banner.js +31 -2
  23. package/dist/Button/Button.d.ts +8 -2
  24. package/dist/Button/Button.d.ts.map +1 -1
  25. package/dist/Button/Button.js +31 -2
  26. package/dist/ButtonGroup/ButtonGroup.d.ts +9 -1
  27. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  28. package/dist/ButtonGroup/ButtonGroup.js +30 -1
  29. package/dist/Card/Card.d.ts +8 -2
  30. package/dist/Card/Card.d.ts.map +1 -1
  31. package/dist/Card/Card.js +28 -2
  32. package/dist/Carousel/Carousel.d.ts +6 -2
  33. package/dist/Carousel/Carousel.d.ts.map +1 -1
  34. package/dist/Carousel/Carousel.js +55 -5
  35. package/dist/Chat/ChatComposer.d.ts +8 -0
  36. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  37. package/dist/Chat/ChatComposer.js +31 -6
  38. package/dist/Chat/ChatComposerDrawer.d.ts +2 -0
  39. package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
  40. package/dist/Chat/ChatComposerDrawer.js +11 -1
  41. package/dist/Chat/ChatComposerInput.d.ts +20 -3
  42. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  43. package/dist/Chat/ChatComposerInput.js +19 -3
  44. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  45. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  46. package/dist/Chat/ChatMessage.d.ts +1 -1
  47. package/dist/Chat/ChatMessage.d.ts.map +1 -1
  48. package/dist/Chat/ChatMessage.js +1 -1
  49. package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
  50. package/dist/Chat/ChatSendButton.d.ts.map +1 -1
  51. package/dist/Chat/ChatToolCalls.d.ts +5 -1
  52. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  53. package/dist/Chat/ChatToolCalls.js +12 -3
  54. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  55. package/dist/Citation/Citation.d.ts.map +1 -1
  56. package/dist/Citation/Citation.js +10 -4
  57. package/dist/ClickableCard/ClickableCard.d.ts +9 -2
  58. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  59. package/dist/ClickableCard/ClickableCard.js +3 -0
  60. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  61. package/dist/CodeBlock/CodeBlock.js +8 -1
  62. package/dist/Collapsible/Collapsible.d.ts +11 -1
  63. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  64. package/dist/Collapsible/Collapsible.js +36 -4
  65. package/dist/CommandPalette/CommandPaletteInput.d.ts +7 -1
  66. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  67. package/dist/CommandPalette/CommandPaletteInput.js +6 -1
  68. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  69. package/dist/ContextMenu/ContextMenu.js +4 -3
  70. package/dist/ContextMenu/index.d.ts +3 -0
  71. package/dist/ContextMenu/index.d.ts.map +1 -1
  72. package/dist/ContextMenu/index.js +7 -1
  73. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  74. package/dist/DateTimeInput/DateTimeInput.js +10 -1
  75. package/dist/Dialog/Dialog.d.ts.map +1 -1
  76. package/dist/Dialog/Dialog.js +2 -1
  77. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  78. package/dist/DropdownMenu/DropdownMenu.js +8 -4
  79. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +88 -0
  80. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -0
  81. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +169 -0
  82. package/dist/DropdownMenu/DropdownMenuContext.d.ts +17 -1
  83. package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
  84. package/dist/DropdownMenu/DropdownMenuContext.js +18 -0
  85. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +68 -0
  86. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -0
  87. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +69 -0
  88. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +69 -0
  89. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -0
  90. package/dist/DropdownMenu/DropdownMenuRadioItem.js +178 -0
  91. package/dist/DropdownMenu/index.d.ts +5 -1
  92. package/dist/DropdownMenu/index.d.ts.map +1 -1
  93. package/dist/DropdownMenu/index.js +10 -1
  94. package/dist/DropdownMenu/menuItemRoles.d.ts +13 -0
  95. package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -0
  96. package/dist/DropdownMenu/menuItemRoles.js +15 -0
  97. package/dist/FieldStatus/FieldStatus.d.ts +8 -0
  98. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  99. package/dist/FieldStatus/FieldStatus.js +22 -4
  100. package/dist/FileInput/FileInput.d.ts.map +1 -1
  101. package/dist/FileInput/FileInput.js +10 -2
  102. package/dist/HoverCard/HoverCard.d.ts +9 -1
  103. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  104. package/dist/HoverCard/HoverCard.js +2 -0
  105. package/dist/HoverCard/useHoverCard.d.ts +8 -0
  106. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  107. package/dist/HoverCard/useHoverCard.js +7 -2
  108. package/dist/Icon/Icon.d.ts +43 -13
  109. package/dist/Icon/Icon.d.ts.map +1 -1
  110. package/dist/Icon/Icon.js +62 -27
  111. package/dist/Layer/useLayer.d.ts +6 -0
  112. package/dist/Layer/useLayer.d.ts.map +1 -1
  113. package/dist/Layer/useLayer.js +2 -0
  114. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  115. package/dist/MobileNav/MobileNav.js +6 -1
  116. package/dist/MobileNav/MobileNavToggle.d.ts.map +1 -1
  117. package/dist/MobileNav/MobileNavToggle.js +4 -0
  118. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  119. package/dist/MultiSelector/MultiSelector.js +39 -12
  120. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  121. package/dist/MultiSelector/hooks.js +16 -0
  122. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  123. package/dist/Outline/Outline.d.ts +68 -3
  124. package/dist/Outline/Outline.d.ts.map +1 -1
  125. package/dist/Outline/Outline.js +128 -26
  126. package/dist/Outline/useScrollSpy.d.ts +27 -7
  127. package/dist/Outline/useScrollSpy.d.ts.map +1 -1
  128. package/dist/Outline/useScrollSpy.js +202 -70
  129. package/dist/OverflowList/OverflowList.d.ts +20 -3
  130. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  131. package/dist/OverflowList/OverflowList.js +29 -4
  132. package/dist/PowerSearch/PowerSearchEditPopover.js +1 -1
  133. package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
  134. package/dist/SelectableCard/SelectableCard.d.ts +9 -2
  135. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  136. package/dist/SelectableCard/SelectableCard.js +14 -11
  137. package/dist/Selector/Selector.d.ts.map +1 -1
  138. package/dist/Selector/Selector.js +4 -2
  139. package/dist/Selector/hooks.d.ts.map +1 -1
  140. package/dist/Selector/hooks.js +16 -0
  141. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  142. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  143. package/dist/SideNav/SideNavHeading.js +21 -8
  144. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  145. package/dist/Slider/Slider.d.ts.map +1 -1
  146. package/dist/Slider/Slider.js +15 -1
  147. package/dist/TabList/Tab.d.ts.map +1 -1
  148. package/dist/TabList/Tab.js +2 -2
  149. package/dist/TabList/TabList.d.ts.map +1 -1
  150. package/dist/TabList/TabList.js +2 -0
  151. package/dist/TabList/TabMenu.d.ts.map +1 -1
  152. package/dist/TabList/TabMenu.js +1 -1
  153. package/dist/Table/Table.d.ts +1 -1
  154. package/dist/Table/Table.js +1 -1
  155. package/dist/Table/index.d.ts +4 -0
  156. package/dist/Table/index.d.ts.map +1 -1
  157. package/dist/Table/index.js +2 -0
  158. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  159. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  160. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +24 -2
  161. package/dist/Table/plugins/rowStatus/index.d.ts +3 -0
  162. package/dist/Table/plugins/rowStatus/index.d.ts.map +1 -0
  163. package/dist/Table/plugins/rowStatus/index.js +3 -0
  164. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts +60 -0
  165. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -0
  166. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +145 -0
  167. package/dist/Table/plugins/selection/useTableSelection.d.ts +12 -0
  168. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  169. package/dist/Table/plugins/selection/useTableSelection.js +4 -1
  170. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  171. package/dist/Table/plugins/sortable/useTableSortable.js +18 -7
  172. package/dist/Table/plugins/tree/index.d.ts +5 -0
  173. package/dist/Table/plugins/tree/index.d.ts.map +1 -0
  174. package/dist/Table/plugins/tree/index.js +4 -0
  175. package/dist/Table/plugins/tree/useTableTreeData.d.ts +38 -0
  176. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -0
  177. package/dist/Table/plugins/tree/useTableTreeData.js +358 -0
  178. package/dist/Table/plugins/tree/useTableTreeState.d.ts +51 -0
  179. package/dist/Table/plugins/tree/useTableTreeState.d.ts.map +1 -0
  180. package/dist/Table/plugins/tree/useTableTreeState.js +175 -0
  181. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  182. package/dist/Table/useBaseTablePlugins.js +6 -3
  183. package/dist/Thumbnail/Thumbnail.d.ts +8 -1
  184. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  185. package/dist/Thumbnail/Thumbnail.js +46 -8
  186. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  187. package/dist/TimeInput/TimeInput.js +15 -4
  188. package/dist/Tokenizer/Tokenizer.d.ts +1 -1
  189. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  190. package/dist/Tokenizer/Tokenizer.js +16 -4
  191. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  192. package/dist/Toolbar/Toolbar.js +8 -1
  193. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  194. package/dist/TopNav/TopNavHeading.js +18 -8
  195. package/dist/TopNav/TopNavMegaMenu.js +10 -4
  196. package/dist/TopNav/TopNavMenu.d.ts +2 -1
  197. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  198. package/dist/TopNav/TopNavMenu.js +68 -6
  199. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  200. package/dist/TreeList/TreeListItem.js +5 -14
  201. package/dist/astryx.css +51 -16
  202. package/dist/astryx.umd.js +56 -56
  203. package/dist/astryx.umd.js.map +4 -4
  204. package/dist/authoring/doc.d.ts.map +1 -1
  205. package/dist/hooks/computeOverflow.d.ts +47 -0
  206. package/dist/hooks/computeOverflow.d.ts.map +1 -0
  207. package/dist/hooks/computeOverflow.js +213 -0
  208. package/dist/hooks/useOverflow.d.ts +19 -0
  209. package/dist/hooks/useOverflow.d.ts.map +1 -1
  210. package/dist/hooks/useOverflow.js +37 -25
  211. package/dist/theme/Theme.js +1 -1
  212. package/dist/theme/defineTheme.d.ts +1 -1
  213. package/dist/theme/defineTheme.js +1 -1
  214. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  215. package/dist/theme/generateThemeRules.js +51 -0
  216. package/dist/theme/hct.d.ts +0 -1
  217. package/dist/theme/hct.d.ts.map +1 -1
  218. package/dist/theme/hct.js +0 -3
  219. package/dist/utils/types.d.ts +13 -0
  220. package/dist/utils/types.d.ts.map +1 -1
  221. package/docs.mjs +32 -461
  222. package/locales/en.json +212 -172
  223. package/locales/fr-FR.json +10 -0
  224. package/locales/pseudo.json +30 -0
  225. package/package.json +3 -1
  226. package/scripts/postinstall.mjs +93 -0
  227. package/src/AppShell/AppShell.tsx +6 -0
  228. package/src/AppShell/AppShellMobileContext.tsx +8 -0
  229. package/src/AppShell/useAppShellMobile.doc.mjs +8 -0
  230. package/src/Avatar/Avatar.doc.mjs +6 -6
  231. package/src/Avatar/Avatar.tsx +16 -11
  232. package/src/Avatar/AvatarSizeContext.ts +1 -1
  233. package/src/Avatar/AvatarStatusDot.tsx +2 -2
  234. package/src/AvatarGroup/AvatarGroup.doc.mjs +1 -1
  235. package/src/AvatarGroup/AvatarGroup.test.tsx +17 -3
  236. package/src/AvatarGroup/AvatarGroup.tsx +3 -3
  237. package/src/AvatarGroup/AvatarGroupOverflow.doc.mjs +4 -4
  238. package/src/AvatarGroup/AvatarGroupOverflow.tsx +21 -5
  239. package/src/Banner/Banner.doc.mjs +9 -0
  240. package/src/Banner/Banner.test.tsx +30 -0
  241. package/src/Banner/Banner.tsx +47 -3
  242. package/src/Button/Button.doc.mjs +9 -0
  243. package/src/Button/Button.test.tsx +49 -0
  244. package/src/Button/Button.tsx +25 -1
  245. package/src/ButtonGroup/ButtonGroup.doc.mjs +3 -0
  246. package/src/ButtonGroup/ButtonGroup.test.tsx +37 -0
  247. package/src/ButtonGroup/ButtonGroup.tsx +32 -0
  248. package/src/Card/Card.doc.mjs +9 -0
  249. package/src/Card/Card.test.tsx +21 -3
  250. package/src/Card/Card.tsx +35 -2
  251. package/src/Carousel/Carousel.doc.mjs +1 -0
  252. package/src/Carousel/Carousel.test.tsx +149 -1
  253. package/src/Carousel/Carousel.tsx +62 -5
  254. package/src/Chat/Chat.doc.mjs +1 -1
  255. package/src/Chat/ChatComposer.doc.mjs +9 -0
  256. package/src/Chat/ChatComposer.test.tsx +35 -0
  257. package/src/Chat/ChatComposer.tsx +58 -9
  258. package/src/Chat/ChatComposerDrawer.test.tsx +61 -0
  259. package/src/Chat/ChatComposerDrawer.tsx +11 -1
  260. package/src/Chat/ChatComposerInput.doc.mjs +7 -0
  261. package/src/Chat/ChatComposerInput.test.tsx +87 -0
  262. package/src/Chat/ChatComposerInput.tsx +35 -3
  263. package/src/Chat/ChatLayout.tsx +3 -1
  264. package/src/Chat/ChatLayoutScrollButton.tsx +3 -1
  265. package/src/Chat/ChatMessage.tsx +4 -2
  266. package/src/Chat/ChatMessageMetadata.tsx +1 -2
  267. package/src/Chat/ChatSendButton.tsx +5 -1
  268. package/src/Chat/ChatToolCalls.test.tsx +64 -0
  269. package/src/Chat/ChatToolCalls.tsx +13 -1
  270. package/src/Chat/useTriggerMenu.tsx +3 -1
  271. package/src/CheckboxInput/CheckboxInput.doc.mjs +1 -1
  272. package/src/CheckboxInput/CheckboxInput.test.tsx +32 -1
  273. package/src/CheckboxList/CheckboxList.test.tsx +9 -1
  274. package/src/Citation/Citation.test.tsx +51 -0
  275. package/src/Citation/Citation.tsx +8 -2
  276. package/src/ClickableCard/ClickableCard.doc.mjs +2 -0
  277. package/src/ClickableCard/ClickableCard.test.tsx +34 -0
  278. package/src/ClickableCard/ClickableCard.tsx +11 -1
  279. package/src/CodeBlock/CodeBlock.test.tsx +56 -0
  280. package/src/CodeBlock/CodeBlock.tsx +9 -1
  281. package/src/Collapsible/Collapsible.doc.mjs +7 -0
  282. package/src/Collapsible/Collapsible.test.tsx +80 -0
  283. package/src/Collapsible/Collapsible.tsx +58 -5
  284. package/src/CommandPalette/CommandPaletteInput.doc.mjs +26 -9
  285. package/src/CommandPalette/CommandPaletteInput.test.tsx +34 -0
  286. package/src/CommandPalette/CommandPaletteInput.tsx +11 -0
  287. package/src/ContextMenu/ContextMenu.test.tsx +75 -1
  288. package/src/ContextMenu/ContextMenu.tsx +10 -5
  289. package/src/ContextMenu/index.ts +15 -0
  290. package/src/DateTimeInput/DateTimeInput.test.tsx +35 -0
  291. package/src/DateTimeInput/DateTimeInput.tsx +14 -2
  292. package/src/Dialog/Dialog.tsx +12 -2
  293. package/src/DropdownMenu/DropdownMenu.doc.mjs +23 -0
  294. package/src/DropdownMenu/DropdownMenu.tsx +11 -6
  295. package/src/DropdownMenu/DropdownMenuCheckboxItem.doc.mjs +95 -0
  296. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +236 -0
  297. package/src/DropdownMenu/DropdownMenuContext.tsx +29 -1
  298. package/src/DropdownMenu/DropdownMenuItem.doc.mjs +7 -1
  299. package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +80 -0
  300. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +118 -0
  301. package/src/DropdownMenu/DropdownMenuRadioItem.doc.mjs +80 -0
  302. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +232 -0
  303. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +163 -0
  304. package/src/DropdownMenu/index.ts +23 -3
  305. package/src/DropdownMenu/menuItemRoles.ts +22 -0
  306. package/src/Field/Field.doc.mjs +1 -1
  307. package/src/Field/Field.test.tsx +57 -12
  308. package/src/Field/FieldLabel.doc.mjs +1 -1
  309. package/src/FieldStatus/FieldStatus.doc.mjs +17 -5
  310. package/src/FieldStatus/FieldStatus.test.tsx +78 -40
  311. package/src/FieldStatus/FieldStatus.tsx +22 -4
  312. package/src/FileInput/FileInput.test.tsx +52 -3
  313. package/src/FileInput/FileInput.tsx +13 -2
  314. package/src/HoverCard/HoverCard.doc.mjs +13 -0
  315. package/src/HoverCard/HoverCard.test.tsx +21 -2
  316. package/src/HoverCard/HoverCard.tsx +11 -0
  317. package/src/HoverCard/useHoverCard.doc.mjs +2 -0
  318. package/src/HoverCard/useHoverCard.tsx +24 -2
  319. package/src/Icon/Icon.doc.mjs +12 -0
  320. package/src/Icon/Icon.test.tsx +142 -0
  321. package/src/Icon/Icon.tsx +98 -30
  322. package/src/IconButton/IconButton.doc.mjs +8 -0
  323. package/src/IconButton/IconButton.test.tsx +15 -18
  324. package/src/Item/Item.doc.mjs +1 -1
  325. package/src/Layer/useLayer.tsx +8 -0
  326. package/src/MobileNav/MobileNav.tsx +12 -1
  327. package/src/MobileNav/MobileNavToggle.test.tsx +116 -0
  328. package/src/MobileNav/MobileNavToggle.tsx +9 -1
  329. package/src/MultiSelector/MultiSelector.doc.mjs +4 -2
  330. package/src/MultiSelector/MultiSelector.test.tsx +162 -1
  331. package/src/MultiSelector/MultiSelector.tsx +55 -12
  332. package/src/MultiSelector/hooks.ts +17 -0
  333. package/src/NumberInput/NumberInput.doc.mjs +2 -2
  334. package/src/NumberInput/NumberInput.test.tsx +11 -7
  335. package/src/NumberInput/NumberInput.tsx +1 -2
  336. package/src/Outline/Outline.doc.mjs +123 -3
  337. package/src/Outline/Outline.test.tsx +735 -0
  338. package/src/Outline/Outline.tsx +183 -33
  339. package/src/Outline/useScrollSpy.ts +248 -72
  340. package/src/OverflowList/OverflowList.doc.mjs +39 -6
  341. package/src/OverflowList/OverflowList.test.tsx +111 -0
  342. package/src/OverflowList/OverflowList.tsx +48 -8
  343. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +67 -0
  344. package/src/PowerSearch/PowerSearchEditPopover.tsx +1 -1
  345. package/src/PowerSearch/usePowerSearchSource.ts +1 -2
  346. package/src/SelectableCard/SelectableCard.doc.mjs +2 -0
  347. package/src/SelectableCard/SelectableCard.test.tsx +46 -4
  348. package/src/SelectableCard/SelectableCard.tsx +27 -14
  349. package/src/Selector/Selector.test.tsx +69 -0
  350. package/src/Selector/Selector.tsx +5 -1
  351. package/src/Selector/SelectorOption.doc.mjs +1 -1
  352. package/src/Selector/hooks.ts +17 -0
  353. package/src/SideNav/SideNav.test.tsx +119 -3
  354. package/src/SideNav/SideNavCollapseButton.tsx +6 -1
  355. package/src/SideNav/SideNavHeading.tsx +24 -8
  356. package/src/SideNav/SideNavItem.doc.mjs +2 -2
  357. package/src/SideNav/SideNavItem.tsx +5 -1
  358. package/src/Slider/Slider.test.tsx +62 -2
  359. package/src/Slider/Slider.tsx +15 -1
  360. package/src/Switch/Switch.doc.mjs +1 -1
  361. package/src/Switch/Switch.test.tsx +32 -1
  362. package/src/TabList/Tab.tsx +5 -1
  363. package/src/TabList/TabList.doc.mjs +3 -3
  364. package/src/TabList/TabList.test.tsx +66 -0
  365. package/src/TabList/TabList.tsx +8 -0
  366. package/src/TabList/TabMenu.tsx +4 -1
  367. package/src/Table/Table.doc.mjs +2 -0
  368. package/src/Table/Table.tsx +1 -1
  369. package/src/Table/index.ts +12 -0
  370. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +6 -2
  371. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +90 -1
  372. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +37 -5
  373. package/src/Table/plugins/rowStatus/index.ts +7 -0
  374. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +138 -0
  375. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +189 -0
  376. package/src/Table/plugins/selection/useTableSelection.test.tsx +16 -0
  377. package/src/Table/plugins/selection/useTableSelection.tsx +18 -1
  378. package/src/Table/plugins/sortable/useTableSortable.test.tsx +95 -9
  379. package/src/Table/plugins/sortable/useTableSortable.tsx +23 -7
  380. package/src/Table/plugins/tree/index.ts +12 -0
  381. package/src/Table/plugins/tree/useTableTreeData-perf.test.tsx +163 -0
  382. package/src/Table/plugins/tree/useTableTreeData.test.tsx +594 -0
  383. package/src/Table/plugins/tree/useTableTreeData.tsx +495 -0
  384. package/src/Table/plugins/tree/useTableTreeState.test.tsx +636 -0
  385. package/src/Table/plugins/tree/useTableTreeState.tsx +253 -0
  386. package/src/Table/useBaseTablePlugins.ts +6 -2
  387. package/src/Table/useTableRowStatus.doc.mjs +30 -0
  388. package/src/Table/useTableSelection.doc.mjs +65 -14
  389. package/src/Table/useTableTreeData.doc.mjs +75 -0
  390. package/src/Table/useTableTreeState.doc.mjs +98 -0
  391. package/src/TextArea/TextArea.doc.mjs +1 -1
  392. package/src/TextArea/TextArea.test.tsx +9 -1
  393. package/src/TextInput/TextInput.doc.mjs +1 -1
  394. package/src/Thumbnail/Thumbnail.doc.mjs +9 -0
  395. package/src/Thumbnail/Thumbnail.test.tsx +38 -8
  396. package/src/Thumbnail/Thumbnail.tsx +28 -1
  397. package/src/TimeInput/TimeInput.test.tsx +76 -1
  398. package/src/TimeInput/TimeInput.tsx +19 -6
  399. package/src/Tokenizer/Tokenizer.doc.mjs +1 -1
  400. package/src/Tokenizer/Tokenizer.test.tsx +137 -2
  401. package/src/Tokenizer/Tokenizer.tsx +16 -4
  402. package/src/Toolbar/Toolbar.tsx +10 -3
  403. package/src/TopNav/TopNav.test.tsx +80 -1
  404. package/src/TopNav/TopNavHeading.tsx +24 -8
  405. package/src/TopNav/TopNavMegaMenu.test.tsx +149 -1
  406. package/src/TopNav/TopNavMegaMenu.tsx +10 -4
  407. package/src/TopNav/TopNavMenu.test.tsx +118 -4
  408. package/src/TopNav/TopNavMenu.tsx +83 -5
  409. package/src/TreeList/TreeList.test.tsx +32 -1
  410. package/src/TreeList/TreeListItem.tsx +19 -10
  411. package/src/authoring/doc.ts +1 -3
  412. package/src/docs-script.test.mjs +18 -13
  413. package/src/hooks/computeOverflow.test.ts +422 -0
  414. package/src/hooks/computeOverflow.ts +290 -0
  415. package/src/hooks/useOverflow.test.ts +96 -4
  416. package/src/hooks/useOverflow.ts +77 -33
  417. package/src/postinstall.test.mjs +71 -0
  418. package/src/theme/Theme.doc.mjs +4 -4
  419. package/src/theme/Theme.tsx +1 -1
  420. package/src/theme/defineTheme.ts +1 -1
  421. package/src/theme/generateThemeRules.test.ts +65 -0
  422. package/src/theme/generateThemeRules.ts +56 -0
  423. package/src/theme/hct.ts +0 -4
  424. package/src/utils/types.ts +14 -0
  425. package/dist/TreeList/treeListItem.markers.stylex.d.ts +0 -16
  426. package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +0 -1
  427. package/dist/TreeList/treeListItem.markers.stylex.js +0 -22
  428. package/src/TreeList/treeListItem.markers.stylex.ts +0 -20
@@ -29,7 +29,6 @@ import {mergeProps} from '../utils';
29
29
  import {useLinkComponent} from '../Link/useLinkComponent';
30
30
  import {TreeListBranches} from './TreeListBranches';
31
31
  import type {TreeListDensity} from './TreeListTypes';
32
- import {treeItemScope} from './treeListItem.markers.stylex';
33
32
  import {themeProps} from '../utils/themeProps';
34
33
  import {useTranslator} from '../i18n';
35
34
 
@@ -47,6 +46,19 @@ const styles = stylex.create({
47
46
  // The treeitem row is the roving-tabindex focus owner; suppress the
48
47
  // native focus ring in favor of the row's :focus-visible outline below.
49
48
  outline: 'none',
49
+ // Publish this row's own focus state as an inheritable CSS variable
50
+ // instead of matching it via an ancestor selector. Every nested <li>
51
+ // redeclares these vars (default: 'none' / '0'), so a descendant row's
52
+ // default shadows an ancestor's active value — the ring can never leak
53
+ // 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
+ },
50
62
  },
51
63
  childGroup: {
52
64
  margin: 0,
@@ -86,17 +98,14 @@ const styles = stylex.create({
86
98
  },
87
99
  focusVisibleOutline: {
88
100
  outline: {
89
- default: 'none',
90
- // Focus lives on the treeitem row (the <li>) via roving tabindex.
91
- // Scoped to the row's OWN treeitem so focusing a parent does not leak
92
- // the ring onto descendant rows. Also support inner focusable actions.
93
- [stylex.when.ancestor(':focus-visible', treeItemScope)]:
94
- `2px solid ${colorVars['--color-accent']}`,
101
+ // Reads the row's own --_tree-focus-outline (published on the <li> in
102
+ // `wrapper`), which resolves to the nearest containing treeitem only.
103
+ default: 'var(--_tree-focus-outline, none)',
104
+ // Also support inner focusable actions.
95
105
  ':has(:focus-visible)': `2px solid ${colorVars['--color-accent']}`,
96
106
  },
97
107
  outlineOffset: {
98
- default: '0',
99
- [stylex.when.ancestor(':focus-visible', treeItemScope)]: '2px',
108
+ default: 'var(--_tree-focus-outline-offset, 0)',
100
109
  ':has(:focus-visible)': '2px',
101
110
  },
102
111
  },
@@ -451,7 +460,7 @@ export function TreeListItem({
451
460
  data-tree-id={id}
452
461
  data-tree-level={nestedLevel + 1}
453
462
  data-tree-disabled={isDisabled || undefined}
454
- {...stylex.props(styles.wrapper, treeItemScope)}>
463
+ {...stylex.props(styles.wrapper)}>
455
464
  <div {...stylex.props(styles.treeBranches)}>
456
465
  <TreeListBranches
457
466
  ancestorsIsLast={ancestorsIsLast}
@@ -130,9 +130,7 @@ export type AstryxGenericDocInput = AstryxBaseDocInput;
130
130
  * any code referring to the old name.
131
131
  */
132
132
  export type AstryxComponentDoc =
133
- | AstryxComponentDocInput
134
- | AstryxFunctionDocInput
135
- | AstryxGenericDocInput;
133
+ AstryxComponentDocInput | AstryxFunctionDocInput | AstryxGenericDocInput;
136
134
 
137
135
  /**
138
136
  * Author a component doc. Stamp-only: returns the def with `type: 'component'`
@@ -3,12 +3,13 @@
3
3
  /**
4
4
  * @file Subprocess smoke tests for the zero-dependency docs script.
5
5
  *
6
- * `docs.mjs` resolves the package directory from `import.meta.url`. Using
7
- * `new URL(...).pathname` there breaks on Windows (drive-letter paths come
8
- * back as `/D:/...`), which made `--list` print nothing and component
9
- * lookups crash with ENOENT. These tests spawn the script as a real
10
- * subprocess from a neutral cwd, so resolution can only come from the
11
- * module URL, and assert it finds the component catalog on every platform.
6
+ * `docs.mjs` no longer prints component docs itself — it intentionally
7
+ * redirects to the Astryx CLI (`npx @astryxdesign/cli init`) so agents and
8
+ * humans converge on the CLI + `init` rather than a stale bundled snapshot.
9
+ * These tests spawn the script as a real subprocess from a neutral cwd and
10
+ * assert that, regardless of the arguments passed, it prints the redirect
11
+ * banner and exits 0 instead of rendering a component catalog or per-component
12
+ * docs.
12
13
  */
13
14
 
14
15
  import {describe, it, expect} from 'vitest';
@@ -29,22 +30,26 @@ function runDocs(args) {
29
30
  });
30
31
  }
31
32
 
32
- describe('docs.mjs component discovery', () => {
33
- it('--list discovers the component catalog', () => {
33
+ describe('docs.mjs redirects to the Astryx CLI', () => {
34
+ it('redirects to the CLI instead of printing a component catalog for --list', () => {
34
35
  const r = runDocs(['--list']);
35
36
  expect(r.error).toBeUndefined();
36
37
  expect(r.signal).toBeNull();
37
38
  expect(r.status).toBe(0);
38
- expect(r.stdout).toMatch(/\bButton\b/);
39
- expect(r.stdout).toMatch(/\bDialog\b/);
39
+ // Prints the redirect banner, not a component list.
40
+ expect(r.stdout).toMatch(/docs are served by the Astryx CLI/);
41
+ expect(r.stdout).toMatch(/npx @astryxdesign\/cli init/);
42
+ expect(r.stdout).toMatch(/npx @astryxdesign\/cli component --list/);
40
43
  });
41
44
 
42
- it('renders docs for a single component', () => {
45
+ it('redirects to the CLI instead of rendering docs for a single component', () => {
43
46
  const r = runDocs(['Button']);
44
47
  expect(r.error).toBeUndefined();
45
48
  expect(r.signal).toBeNull();
46
49
  expect(r.status).toBe(0);
47
- expect(r.stdout).toMatch(/^# Button$/m);
48
- expect(r.stdout).toMatch(/## Props/);
50
+ // A pure redirect: it must not render the component's docs.
51
+ expect(r.stdout).not.toMatch(/^# Button$/m);
52
+ expect(r.stdout).not.toMatch(/## Props/);
53
+ expect(r.stdout).toMatch(/npx @astryxdesign\/cli component <Name>/);
49
54
  });
50
55
  });
@@ -0,0 +1,422 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import {describe, it, expect} from 'vitest';
4
+ import {computeOverflow, type ComputeOverflowInput} from './computeOverflow';
5
+
6
+ // Convenience wrapper with sensible defaults so each test states only what it
7
+ // exercises. All widths/gaps are plain numbers — no DOM involved.
8
+ function compute(overrides: Partial<ComputeOverflowInput>) {
9
+ return computeOverflow({
10
+ widths: [],
11
+ gap: 0,
12
+ availableWidth: 0,
13
+ indicatorWidth: 0,
14
+ minVisibleItems: 0,
15
+ collapseFrom: 'end',
16
+ ...overrides,
17
+ });
18
+ }
19
+
20
+ describe('computeOverflow — single line, no limits (parity with legacy loop)', () => {
21
+ it('shows all items when they fit', () => {
22
+ // 3×50, gap 10 → 170 ≤ 200
23
+ expect(
24
+ compute({widths: [50, 50, 50], gap: 10, availableWidth: 200}),
25
+ ).toEqual({visibleCount: 3, rows: 1});
26
+ });
27
+
28
+ it('hides items that do not fit and reserves indicator space', () => {
29
+ // 4×50, gap 10, indicator 30, container 150 → 2 fit
30
+ expect(
31
+ compute({
32
+ widths: [50, 50, 50, 50],
33
+ gap: 10,
34
+ indicatorWidth: 30,
35
+ availableWidth: 150,
36
+ }),
37
+ ).toEqual({visibleCount: 2, rows: 1});
38
+ });
39
+
40
+ it('shows zero when nothing fits and floor is 0', () => {
41
+ expect(
42
+ compute({
43
+ widths: [100, 100, 100],
44
+ indicatorWidth: 30,
45
+ availableWidth: 50,
46
+ }),
47
+ ).toEqual({visibleCount: 0, rows: 0});
48
+ });
49
+
50
+ it('exact fit with indicator not needed shows all', () => {
51
+ // 3×50, gap 10 → 170 == 170
52
+ expect(
53
+ compute({
54
+ widths: [50, 50, 50],
55
+ gap: 10,
56
+ indicatorWidth: 30,
57
+ availableWidth: 170,
58
+ }),
59
+ ).toEqual({visibleCount: 3, rows: 1});
60
+ });
61
+
62
+ it('off-by-one: one pixel short drops the last item', () => {
63
+ expect(
64
+ compute({
65
+ widths: [50, 50, 50],
66
+ gap: 10,
67
+ indicatorWidth: 30,
68
+ availableWidth: 169,
69
+ }),
70
+ ).toEqual({visibleCount: 2, rows: 1});
71
+ });
72
+
73
+ it('works without an indicator', () => {
74
+ // 3×50, gap 10, container 130 → 2 fit
75
+ expect(
76
+ compute({widths: [50, 50, 50], gap: 10, availableWidth: 130}),
77
+ ).toEqual({visibleCount: 2, rows: 1});
78
+ });
79
+
80
+ it('varied widths', () => {
81
+ expect(
82
+ compute({
83
+ widths: [30, 80, 40, 60],
84
+ gap: 10,
85
+ indicatorWidth: 25,
86
+ availableWidth: 200,
87
+ }),
88
+ ).toEqual({visibleCount: 2, rows: 1});
89
+ });
90
+ });
91
+
92
+ describe('computeOverflow — floor (minVisibleItems)', () => {
93
+ it('respects the floor even when items do not fit', () => {
94
+ expect(
95
+ compute({
96
+ widths: [100, 100, 100],
97
+ indicatorWidth: 30,
98
+ availableWidth: 50,
99
+ minVisibleItems: 2,
100
+ }),
101
+ ).toEqual({visibleCount: 2, rows: 1});
102
+ });
103
+
104
+ it('floor larger than itemCount is clamped to itemCount', () => {
105
+ expect(
106
+ compute({
107
+ widths: [100, 100],
108
+ availableWidth: 10,
109
+ minVisibleItems: 5,
110
+ }),
111
+ ).toEqual({visibleCount: 2, rows: 1});
112
+ });
113
+ });
114
+
115
+ describe('computeOverflow — cap (maxVisibleItems)', () => {
116
+ it('caps below fit even when everything fits', () => {
117
+ // all 5 fit in a wide container, cap at 2
118
+ expect(
119
+ compute({
120
+ widths: [40, 40, 40, 40, 40],
121
+ gap: 8,
122
+ availableWidth: 10000,
123
+ maxVisibleItems: 2,
124
+ }),
125
+ ).toEqual({visibleCount: 2, rows: 1});
126
+ });
127
+
128
+ it('cap of 0 shows nothing', () => {
129
+ expect(
130
+ compute({
131
+ widths: [40, 40, 40],
132
+ availableWidth: 10000,
133
+ maxVisibleItems: 0,
134
+ }),
135
+ ).toEqual({visibleCount: 0, rows: 0});
136
+ });
137
+
138
+ it('cap greater than itemCount is a no-op', () => {
139
+ expect(
140
+ compute({
141
+ widths: [40, 40, 40],
142
+ gap: 8,
143
+ availableWidth: 10000,
144
+ maxVisibleItems: 99,
145
+ }),
146
+ ).toEqual({visibleCount: 3, rows: 1});
147
+ });
148
+
149
+ it('cap does not raise a fit-limited count', () => {
150
+ // container only fits 2, cap is 4 → still 2
151
+ expect(
152
+ compute({
153
+ widths: [50, 50, 50, 50],
154
+ gap: 10,
155
+ indicatorWidth: 30,
156
+ availableWidth: 150,
157
+ maxVisibleItems: 4,
158
+ }),
159
+ ).toEqual({visibleCount: 2, rows: 1});
160
+ });
161
+ });
162
+
163
+ describe('computeOverflow — cap + floor composition', () => {
164
+ it('both honored when min <= max', () => {
165
+ // fit would be 2; floor 1, cap 3 → 2
166
+ expect(
167
+ compute({
168
+ widths: [50, 50, 50, 50],
169
+ gap: 10,
170
+ indicatorWidth: 30,
171
+ availableWidth: 150,
172
+ minVisibleItems: 1,
173
+ maxVisibleItems: 3,
174
+ }),
175
+ ).toEqual({visibleCount: 2, rows: 1});
176
+ });
177
+
178
+ it('D1: when max < min, min wins', () => {
179
+ // wide container fits all 5; floor 3, cap 1 → floor wins → 3
180
+ expect(
181
+ compute({
182
+ widths: [40, 40, 40, 40, 40],
183
+ gap: 8,
184
+ availableWidth: 10000,
185
+ minVisibleItems: 3,
186
+ maxVisibleItems: 1,
187
+ }),
188
+ ).toEqual({visibleCount: 3, rows: 1});
189
+ });
190
+ });
191
+
192
+ describe('computeOverflow — collapseFrom start vs end + cap', () => {
193
+ it('single-line collapseFrom start keeps the tail', () => {
194
+ // Items: 30, 80, 40. gap 10, indicator 25, container 100.
195
+ // reversed: [40, 80, 30] → only 40 fits → visibleCount 1
196
+ expect(
197
+ compute({
198
+ widths: [30, 80, 40],
199
+ gap: 10,
200
+ indicatorWidth: 25,
201
+ availableWidth: 100,
202
+ collapseFrom: 'start',
203
+ }),
204
+ ).toEqual({visibleCount: 1, rows: 1});
205
+ });
206
+
207
+ it('collapseFrom start + cap trims from the correct end', () => {
208
+ expect(
209
+ compute({
210
+ widths: [40, 40, 40, 40],
211
+ gap: 8,
212
+ availableWidth: 10000,
213
+ collapseFrom: 'start',
214
+ maxVisibleItems: 2,
215
+ }),
216
+ ).toEqual({visibleCount: 2, rows: 1});
217
+ });
218
+ });
219
+
220
+ describe('computeOverflow — multi-row (maxRows)', () => {
221
+ it('maxRows 1 is equivalent to single line', () => {
222
+ const single = compute({
223
+ widths: [50, 50, 50, 50],
224
+ gap: 10,
225
+ indicatorWidth: 30,
226
+ availableWidth: 150,
227
+ });
228
+ const withMaxRows1 = compute({
229
+ widths: [50, 50, 50, 50],
230
+ gap: 10,
231
+ indicatorWidth: 30,
232
+ availableWidth: 150,
233
+ maxRows: 1,
234
+ });
235
+ expect(withMaxRows1).toEqual(single);
236
+ expect(withMaxRows1).toEqual({visibleCount: 2, rows: 1});
237
+ });
238
+
239
+ it('packs items onto exactly maxRows rows when all fit', () => {
240
+ // 6×50, gap 10 → each row of width 170 holds 3 (50+10+50+10+50=170).
241
+ // availableWidth 170, maxRows 2 → all 6 fit on 2 rows.
242
+ expect(
243
+ compute({
244
+ widths: [50, 50, 50, 50, 50, 50],
245
+ gap: 10,
246
+ availableWidth: 170,
247
+ maxRows: 2,
248
+ }),
249
+ ).toEqual({visibleCount: 6, rows: 2});
250
+ });
251
+
252
+ it('collapses overflow past maxRows into the indicator (reserved on last row)', () => {
253
+ // 8×50, gap 10, width 170 → 3 per row. maxRows 2.
254
+ // Row 1: 3 items (170). Row 2 (last): reserve indicator 40 + gap 10 = 50.
255
+ // available for items on row 2 = 170; place 50 (ok, rowWidth 50),
256
+ // next 50 → 50+10+50=110 + reserve 50 = 160 ≤ 170 ok (2 items),
257
+ // next 50 → 110+10+50=170 + 50 = 220 > 170 stop. Row 2 holds 2.
258
+ // → 3 + 2 = 5 visible, 2 rows.
259
+ expect(
260
+ compute({
261
+ widths: [50, 50, 50, 50, 50, 50, 50, 50],
262
+ gap: 10,
263
+ indicatorWidth: 40,
264
+ availableWidth: 170,
265
+ maxRows: 2,
266
+ }),
267
+ ).toEqual({visibleCount: 5, rows: 2});
268
+ });
269
+
270
+ it('all items on a single row still reports rows: 1 under maxRows 2', () => {
271
+ expect(
272
+ compute({
273
+ widths: [30, 30, 30],
274
+ gap: 5,
275
+ availableWidth: 500,
276
+ maxRows: 2,
277
+ }),
278
+ ).toEqual({visibleCount: 3, rows: 1});
279
+ });
280
+
281
+ it('cap wins when it is stricter than the rows allow', () => {
282
+ // 6 items fit on 2 rows, but cap at 4 → 4 visible.
283
+ expect(
284
+ compute({
285
+ widths: [50, 50, 50, 50, 50, 50],
286
+ gap: 10,
287
+ indicatorWidth: 40,
288
+ availableWidth: 170,
289
+ maxRows: 2,
290
+ maxVisibleItems: 4,
291
+ }),
292
+ ).toEqual({visibleCount: 4, rows: 2});
293
+ });
294
+
295
+ it('rows win when they are stricter than the cap', () => {
296
+ // maxRows 2 admits 5 (see above); cap 8 is looser → rows win → 5.
297
+ expect(
298
+ compute({
299
+ widths: [50, 50, 50, 50, 50, 50, 50, 50],
300
+ gap: 10,
301
+ indicatorWidth: 40,
302
+ availableWidth: 170,
303
+ maxRows: 2,
304
+ maxVisibleItems: 8,
305
+ }),
306
+ ).toEqual({visibleCount: 5, rows: 2});
307
+ });
308
+
309
+ it('multi-row collapseFrom start keeps the tail items', () => {
310
+ // 8 items, reversed packing; same geometry as the end case → 5 visible.
311
+ const res = compute({
312
+ widths: [50, 50, 50, 50, 50, 50, 50, 50],
313
+ gap: 10,
314
+ indicatorWidth: 40,
315
+ availableWidth: 170,
316
+ maxRows: 2,
317
+ collapseFrom: 'start',
318
+ });
319
+ expect(res).toEqual({visibleCount: 5, rows: 2});
320
+ });
321
+
322
+ it('maxRows taller than content does not add phantom rows', () => {
323
+ // 3 items fit on one row; maxRows 5 → rows 1, all visible.
324
+ expect(
325
+ compute({
326
+ widths: [40, 40, 40],
327
+ gap: 10,
328
+ availableWidth: 500,
329
+ maxRows: 5,
330
+ }),
331
+ ).toEqual({visibleCount: 3, rows: 1});
332
+ });
333
+
334
+ it('floor is honored in multi-row even when nothing fits', () => {
335
+ // Very narrow container, floor 2.
336
+ expect(
337
+ compute({
338
+ widths: [500, 500, 500],
339
+ gap: 10,
340
+ indicatorWidth: 40,
341
+ availableWidth: 100,
342
+ maxRows: 2,
343
+ minVisibleItems: 2,
344
+ }),
345
+ ).toEqual({visibleCount: 2, rows: 2});
346
+ });
347
+ });
348
+
349
+ describe('computeOverflow — resize behavior (shrink/grow re-clamps, never exceeds cap)', () => {
350
+ const base: Partial<ComputeOverflowInput> = {
351
+ widths: [50, 50, 50, 50, 50],
352
+ gap: 10,
353
+ indicatorWidth: 30,
354
+ maxVisibleItems: 3,
355
+ };
356
+
357
+ it('grows back only up to the cap', () => {
358
+ // Huge container would fit all 5, cap holds at 3.
359
+ expect(compute({...base, availableWidth: 10000})).toEqual({
360
+ visibleCount: 3,
361
+ rows: 1,
362
+ });
363
+ });
364
+
365
+ it('shrinks below the cap when space is tight', () => {
366
+ // Narrow container fits fewer than the cap.
367
+ expect(compute({...base, availableWidth: 150})).toEqual({
368
+ visibleCount: 2,
369
+ rows: 1,
370
+ });
371
+ });
372
+
373
+ it('never exceeds the cap across a shrink→grow cycle', () => {
374
+ const shrunk = compute({...base, availableWidth: 120});
375
+ const grown = compute({...base, availableWidth: 10000});
376
+ expect(grown.visibleCount).toBeLessThanOrEqual(3);
377
+ expect(shrunk.visibleCount).toBeLessThanOrEqual(grown.visibleCount);
378
+ });
379
+ });
380
+
381
+ describe('computeOverflow — degenerate inputs', () => {
382
+ it('empty list', () => {
383
+ expect(compute({widths: []})).toEqual({visibleCount: 0, rows: 0});
384
+ });
385
+
386
+ it('single item that fits', () => {
387
+ expect(compute({widths: [50], gap: 10, availableWidth: 100})).toEqual({
388
+ visibleCount: 1,
389
+ rows: 1,
390
+ });
391
+ });
392
+
393
+ it('single item that does not fit, no floor', () => {
394
+ expect(
395
+ compute({widths: [200], indicatorWidth: 30, availableWidth: 100}),
396
+ ).toEqual({visibleCount: 0, rows: 0});
397
+ });
398
+
399
+ it('maxRows 0 falls back to single-line semantics', () => {
400
+ // maxRows 0 is degenerate; treated as single line (not multi-row).
401
+ expect(
402
+ compute({
403
+ widths: [50, 50, 50],
404
+ gap: 10,
405
+ availableWidth: 130,
406
+ maxRows: 0,
407
+ }),
408
+ ).toEqual({visibleCount: 2, rows: 1});
409
+ });
410
+
411
+ it('cap 0 with floor 0 and multi-row shows nothing', () => {
412
+ expect(
413
+ compute({
414
+ widths: [50, 50],
415
+ gap: 10,
416
+ availableWidth: 500,
417
+ maxRows: 2,
418
+ maxVisibleItems: 0,
419
+ }),
420
+ ).toEqual({visibleCount: 0, rows: 0});
421
+ });
422
+ });