@astryxdesign/core 0.1.8 → 0.1.9

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 (487) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/dist/AppShell/AppShell.d.ts.map +1 -1
  3. package/dist/AppShell/AppShell.js +6 -2
  4. package/dist/AppShell/AppShellMobileContext.d.ts +8 -0
  5. package/dist/AppShell/AppShellMobileContext.d.ts.map +1 -1
  6. package/dist/Avatar/Avatar.d.ts +18 -2
  7. package/dist/Avatar/Avatar.d.ts.map +1 -1
  8. package/dist/Avatar/Avatar.js +63 -7
  9. package/dist/Avatar/AvatarStatusDot.d.ts +33 -5
  10. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  11. package/dist/Avatar/AvatarStatusDot.js +144 -19
  12. package/dist/Banner/Banner.d.ts +9 -1
  13. package/dist/Banner/Banner.d.ts.map +1 -1
  14. package/dist/Banner/Banner.js +31 -2
  15. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +31 -2
  16. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  17. package/dist/Breadcrumbs/BreadcrumbItem.js +208 -6
  18. package/dist/Breadcrumbs/index.d.ts +5 -0
  19. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  20. package/dist/Breadcrumbs/index.js +9 -1
  21. package/dist/Button/Button.d.ts +8 -2
  22. package/dist/Button/Button.d.ts.map +1 -1
  23. package/dist/Button/Button.js +31 -2
  24. package/dist/ButtonGroup/ButtonGroup.d.ts +9 -1
  25. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  26. package/dist/ButtonGroup/ButtonGroup.js +30 -1
  27. package/dist/Calendar/Calendar.d.ts.map +1 -1
  28. package/dist/Calendar/Calendar.js +30 -2
  29. package/dist/Calendar/dayCellUtils.js +1 -1
  30. package/dist/Card/Card.d.ts +8 -2
  31. package/dist/Card/Card.d.ts.map +1 -1
  32. package/dist/Card/Card.js +28 -2
  33. package/dist/Carousel/Carousel.d.ts +4 -1
  34. package/dist/Carousel/Carousel.d.ts.map +1 -1
  35. package/dist/Carousel/Carousel.js +24 -4
  36. package/dist/Chat/ChatComposer.d.ts +8 -0
  37. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  38. package/dist/Chat/ChatComposer.js +46 -7
  39. package/dist/Chat/ChatComposerDrawer.d.ts +2 -0
  40. package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
  41. package/dist/Chat/ChatComposerDrawer.js +11 -1
  42. package/dist/Chat/ChatComposerInput.d.ts +20 -3
  43. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  44. package/dist/Chat/ChatComposerInput.js +35 -3
  45. package/dist/Chat/ChatContext.d.ts +33 -0
  46. package/dist/Chat/ChatContext.d.ts.map +1 -1
  47. package/dist/Chat/ChatContext.js +16 -2
  48. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  49. package/dist/Chat/ChatDictationButton.js +1 -1
  50. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  51. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  52. package/dist/Chat/ChatLayoutScrollButton.js +4 -4
  53. package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
  54. package/dist/Chat/ChatSendButton.d.ts.map +1 -1
  55. package/dist/Chat/ChatSendButton.js +7 -1
  56. package/dist/Chat/ChatToolCalls.d.ts +5 -1
  57. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  58. package/dist/Chat/ChatToolCalls.js +18 -9
  59. package/dist/Chat/index.d.ts +2 -1
  60. package/dist/Chat/index.d.ts.map +1 -1
  61. package/dist/Chat/index.js +1 -1
  62. package/dist/Chat/useChatStreamScroll.d.ts +2 -1
  63. package/dist/Chat/useChatStreamScroll.d.ts.map +1 -1
  64. package/dist/Chat/useChatStreamScroll.js +22 -8
  65. package/dist/Chat/useSpeechRecognition.d.ts +0 -1
  66. package/dist/Chat/useSpeechRecognition.d.ts.map +1 -1
  67. package/dist/Chat/useSpeechRecognition.js +1 -1
  68. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  69. package/dist/CheckboxInput/CheckboxInput.js +1 -1
  70. package/dist/CheckboxList/CheckboxListItem.d.ts +20 -1
  71. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  72. package/dist/CheckboxList/CheckboxListItem.js +11 -1
  73. package/dist/ClickableCard/ClickableCard.d.ts +9 -2
  74. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  75. package/dist/ClickableCard/ClickableCard.js +5 -2
  76. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  77. package/dist/CodeBlock/CodeBlock.js +8 -1
  78. package/dist/CommandPalette/CommandPalette.d.ts +1 -1
  79. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  80. package/dist/CommandPalette/CommandPalette.js +3 -1
  81. package/dist/CommandPalette/CommandPaletteGroup.d.ts.map +1 -1
  82. package/dist/CommandPalette/CommandPaletteGroup.js +9 -2
  83. package/dist/CommandPalette/CommandPaletteInput.d.ts +7 -1
  84. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  85. package/dist/CommandPalette/CommandPaletteInput.js +6 -1
  86. package/dist/DateInput/DateInput.d.ts +47 -1
  87. package/dist/DateInput/DateInput.d.ts.map +1 -1
  88. package/dist/DateInput/DateInput.js +26 -2
  89. package/dist/DateInput/index.d.ts +1 -1
  90. package/dist/DateInput/index.d.ts.map +1 -1
  91. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  92. package/dist/DateTimeInput/DateTimeInput.js +10 -1
  93. package/dist/Dialog/Dialog.d.ts +3 -0
  94. package/dist/Dialog/Dialog.d.ts.map +1 -1
  95. package/dist/Dialog/Dialog.js +57 -6
  96. package/dist/Dialog/DialogContext.d.ts +7 -0
  97. package/dist/Dialog/DialogContext.d.ts.map +1 -1
  98. package/dist/Dialog/DialogContext.js +2 -2
  99. package/dist/Dialog/DialogHeader.d.ts +5 -2
  100. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  101. package/dist/Dialog/DialogHeader.js +6 -1
  102. package/dist/Divider/Divider.js +1 -1
  103. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +1 -2
  104. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  105. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +1 -2
  106. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +1 -2
  107. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
  108. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +1 -2
  109. package/dist/Field/Field.d.ts.map +1 -1
  110. package/dist/Field/Field.js +2 -3
  111. package/dist/Field/FieldLabel.d.ts +1 -1
  112. package/dist/Field/FieldLabel.d.ts.map +1 -1
  113. package/dist/Field/FieldLabel.js +42 -15
  114. package/dist/FieldStatus/FieldStatus.d.ts +8 -0
  115. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  116. package/dist/FieldStatus/FieldStatus.js +22 -4
  117. package/dist/FileInput/FileInput.d.ts.map +1 -1
  118. package/dist/FileInput/FileInput.js +10 -2
  119. package/dist/HoverCard/HoverCard.d.ts +9 -1
  120. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  121. package/dist/HoverCard/HoverCard.js +2 -0
  122. package/dist/HoverCard/useHoverCard.d.ts +8 -0
  123. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  124. package/dist/HoverCard/useHoverCard.js +7 -2
  125. package/dist/Icon/Icon.d.ts +7 -2
  126. package/dist/Icon/Icon.d.ts.map +1 -1
  127. package/dist/Item/Item.d.ts +6 -1
  128. package/dist/Item/Item.d.ts.map +1 -1
  129. package/dist/Item/Item.js +22 -1
  130. package/dist/Layer/useLayer.d.ts +6 -0
  131. package/dist/Layer/useLayer.d.ts.map +1 -1
  132. package/dist/Layer/useLayer.js +2 -0
  133. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  134. package/dist/MobileNav/MobileNav.js +6 -1
  135. package/dist/MobileNav/MobileNavToggle.d.ts.map +1 -1
  136. package/dist/MobileNav/MobileNavToggle.js +4 -0
  137. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  138. package/dist/MultiSelector/MultiSelector.js +39 -12
  139. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  140. package/dist/MultiSelector/hooks.js +16 -0
  141. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  142. package/dist/Outline/Outline.d.ts +0 -1
  143. package/dist/Outline/Outline.d.ts.map +1 -1
  144. package/dist/Outline/Outline.js +0 -1
  145. package/dist/OverflowList/OverflowList.d.ts +20 -3
  146. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  147. package/dist/OverflowList/OverflowList.js +29 -4
  148. package/dist/Popover/Popover.d.ts.map +1 -1
  149. package/dist/Popover/Popover.js +3 -2
  150. package/dist/Popover/usePopover.d.ts.map +1 -1
  151. package/dist/Popover/usePopover.js +3 -9
  152. package/dist/PowerSearch/PowerSearchEditPopover.js +1 -1
  153. package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
  154. package/dist/SelectableCard/SelectableCard.d.ts +9 -2
  155. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  156. package/dist/SelectableCard/SelectableCard.js +16 -13
  157. package/dist/Selector/Selector.d.ts.map +1 -1
  158. package/dist/Selector/Selector.js +4 -2
  159. package/dist/Selector/hooks.d.ts.map +1 -1
  160. package/dist/Selector/hooks.js +16 -0
  161. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  162. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  163. package/dist/SideNav/SideNavHeading.js +21 -8
  164. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  165. package/dist/Slider/Slider.d.ts.map +1 -1
  166. package/dist/Slider/Slider.js +19 -2
  167. package/dist/Stack/stack.stylex.d.ts +9 -9
  168. package/dist/Table/BaseTable.d.ts.map +1 -1
  169. package/dist/Table/BaseTable.js +3 -2
  170. package/dist/Table/index.d.ts +2 -0
  171. package/dist/Table/index.d.ts.map +1 -1
  172. package/dist/Table/index.js +1 -0
  173. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  174. package/dist/Table/plugins/rowStatus/index.d.ts +3 -0
  175. package/dist/Table/plugins/rowStatus/index.d.ts.map +1 -0
  176. package/dist/Table/plugins/rowStatus/index.js +3 -0
  177. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts +60 -0
  178. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -0
  179. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +145 -0
  180. package/dist/Table/plugins/selection/useTableSelection.d.ts +13 -0
  181. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  182. package/dist/Table/plugins/selection/useTableSelection.js +4 -1
  183. package/dist/Table/plugins/tree/useTableTreeData.d.ts +16 -0
  184. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  185. package/dist/Table/plugins/tree/useTableTreeData.js +87 -0
  186. package/dist/Table/plugins/tree/useTableTreeState.d.ts +6 -0
  187. package/dist/Table/plugins/tree/useTableTreeState.d.ts.map +1 -1
  188. package/dist/Table/plugins/tree/useTableTreeState.js +22 -1
  189. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  190. package/dist/Table/useBaseTablePlugins.js +5 -3
  191. package/dist/Thumbnail/Thumbnail.d.ts +26 -5
  192. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  193. package/dist/Thumbnail/Thumbnail.js +97 -54
  194. package/dist/Thumbnail/thumbnail.markers.stylex.d.ts +9 -0
  195. package/dist/Thumbnail/thumbnail.markers.stylex.d.ts.map +1 -0
  196. package/dist/Thumbnail/thumbnail.markers.stylex.js +14 -0
  197. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  198. package/dist/TimeInput/TimeInput.js +15 -4
  199. package/dist/Timestamp/Timestamp.d.ts +3 -1
  200. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  201. package/dist/Timestamp/Timestamp.js +8 -6
  202. package/dist/Toast/useToast.d.ts.map +1 -1
  203. package/dist/Toast/useToast.js +2 -5
  204. package/dist/ToggleButton/ToggleButtonGroup.d.ts +0 -1
  205. package/dist/ToggleButton/ToggleButtonGroup.d.ts.map +1 -1
  206. package/dist/ToggleButton/ToggleButtonGroup.js +1 -1
  207. package/dist/Tokenizer/Tokenizer.d.ts +1 -1
  208. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  209. package/dist/Tokenizer/Tokenizer.js +16 -4
  210. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  211. package/dist/TopNav/TopNavHeading.js +18 -8
  212. package/dist/TopNav/TopNavMegaMenu.js +10 -4
  213. package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts +9 -1
  214. package/dist/TopNav/TopNavMegaMenuFeaturedCard.d.ts.map +1 -1
  215. package/dist/TopNav/TopNavMegaMenuFeaturedCard.js +8 -1
  216. package/dist/TopNav/TopNavMenu.d.ts +2 -1
  217. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  218. package/dist/TopNav/TopNavMenu.js +68 -6
  219. package/dist/TreeList/TreeList.d.ts +12 -3
  220. package/dist/TreeList/TreeList.d.ts.map +1 -1
  221. package/dist/TreeList/TreeList.js +2 -0
  222. package/dist/TreeList/TreeListBranches.d.ts +5 -0
  223. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  224. package/dist/TreeList/TreeListBranches.js +10 -4
  225. package/dist/TreeList/TreeListItem.d.ts +8 -2
  226. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  227. package/dist/TreeList/TreeListItem.js +27 -7
  228. package/dist/TreeList/TreeListTypes.d.ts +25 -1
  229. package/dist/TreeList/TreeListTypes.d.ts.map +1 -1
  230. package/dist/TreeList/index.d.ts +2 -2
  231. package/dist/TreeList/index.d.ts.map +1 -1
  232. package/dist/astryx.css +28 -18
  233. package/dist/astryx.umd.js +51 -51
  234. package/dist/astryx.umd.js.map +4 -4
  235. package/dist/authoring/doc.d.ts.map +1 -1
  236. package/dist/docs-types.d.ts +8 -0
  237. package/dist/docs-types.d.ts.map +1 -1
  238. package/dist/hooks/computeOverflow.d.ts +47 -0
  239. package/dist/hooks/computeOverflow.d.ts.map +1 -0
  240. package/dist/hooks/computeOverflow.js +213 -0
  241. package/dist/hooks/index.d.ts +1 -0
  242. package/dist/hooks/index.d.ts.map +1 -1
  243. package/dist/hooks/index.js +2 -1
  244. package/dist/hooks/useDevWarning.d.ts +18 -0
  245. package/dist/hooks/useDevWarning.d.ts.map +1 -0
  246. package/dist/hooks/useDevWarning.js +49 -0
  247. package/dist/hooks/useOverflow.d.ts +19 -0
  248. package/dist/hooks/useOverflow.d.ts.map +1 -1
  249. package/dist/hooks/useOverflow.js +36 -25
  250. package/dist/i18n/resolve.d.ts.map +1 -1
  251. package/dist/i18n/resolve.js +7 -18
  252. package/dist/theme/Theme.d.ts.map +1 -1
  253. package/dist/theme/Theme.js +2 -7
  254. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  255. package/dist/theme/generateThemeRules.js +51 -0
  256. package/dist/theme/hct.d.ts +0 -1
  257. package/dist/theme/hct.d.ts.map +1 -1
  258. package/dist/theme/hct.js +1 -1
  259. package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
  260. package/dist/theme/syntax/defineSyntaxTheme.js +2 -1
  261. package/dist/utils/devWarning.d.ts +42 -0
  262. package/dist/utils/devWarning.d.ts.map +1 -0
  263. package/dist/utils/devWarning.js +88 -0
  264. package/dist/utils/index.d.ts +3 -1
  265. package/dist/utils/index.d.ts.map +1 -1
  266. package/dist/utils/index.js +3 -2
  267. package/dist/utils/plainDate.d.ts +30 -0
  268. package/dist/utils/plainDate.d.ts.map +1 -1
  269. package/dist/utils/plainDate.js +53 -0
  270. package/dist/utils/types.d.ts +13 -0
  271. package/dist/utils/types.d.ts.map +1 -1
  272. package/locales/en.json +24 -0
  273. package/locales/fr-FR.json +14 -0
  274. package/locales/pseudo.json +18 -0
  275. package/package.json +1 -1
  276. package/src/AppShell/AppShell.tsx +6 -0
  277. package/src/AppShell/AppShellMobileContext.tsx +8 -0
  278. package/src/AppShell/useAppShellMobile.doc.mjs +8 -0
  279. package/src/Avatar/Avatar.doc.mjs +15 -3
  280. package/src/Avatar/Avatar.test.tsx +156 -1
  281. package/src/Avatar/Avatar.tsx +96 -4
  282. package/src/Avatar/AvatarStatusDot.doc.mjs +23 -12
  283. package/src/Avatar/AvatarStatusDot.test.tsx +284 -0
  284. package/src/Avatar/AvatarStatusDot.tsx +178 -21
  285. package/src/Banner/Banner.doc.mjs +9 -0
  286. package/src/Banner/Banner.test.tsx +30 -0
  287. package/src/Banner/Banner.tsx +47 -3
  288. package/src/Breadcrumbs/BreadcrumbItem.doc.mjs +26 -0
  289. package/src/Breadcrumbs/BreadcrumbItem.tsx +333 -14
  290. package/src/Breadcrumbs/Breadcrumbs.doc.mjs +2 -0
  291. package/src/Breadcrumbs/Breadcrumbs.test.tsx +284 -2
  292. package/src/Breadcrumbs/index.ts +27 -0
  293. package/src/Button/Button.doc.mjs +9 -0
  294. package/src/Button/Button.test.tsx +49 -0
  295. package/src/Button/Button.tsx +25 -1
  296. package/src/ButtonGroup/ButtonGroup.doc.mjs +3 -0
  297. package/src/ButtonGroup/ButtonGroup.test.tsx +37 -0
  298. package/src/ButtonGroup/ButtonGroup.tsx +32 -0
  299. package/src/Calendar/Calendar.doc.mjs +12 -1
  300. package/src/Calendar/Calendar.test.tsx +193 -0
  301. package/src/Calendar/Calendar.tsx +38 -16
  302. package/src/Calendar/dayCellUtils.test.ts +5 -0
  303. package/src/Calendar/dayCellUtils.ts +1 -1
  304. package/src/Card/Card.doc.mjs +9 -0
  305. package/src/Card/Card.test.tsx +21 -3
  306. package/src/Card/Card.tsx +35 -2
  307. package/src/Carousel/Carousel.doc.mjs +1 -0
  308. package/src/Carousel/Carousel.test.tsx +53 -0
  309. package/src/Carousel/Carousel.tsx +34 -4
  310. package/src/Chat/ChatComposer.doc.mjs +12 -3
  311. package/src/Chat/ChatComposer.test.tsx +115 -0
  312. package/src/Chat/ChatComposer.tsx +73 -9
  313. package/src/Chat/ChatComposerDrawer.test.tsx +61 -0
  314. package/src/Chat/ChatComposerDrawer.tsx +11 -1
  315. package/src/Chat/ChatComposerInput.doc.mjs +7 -0
  316. package/src/Chat/ChatComposerInput.test.tsx +103 -0
  317. package/src/Chat/ChatComposerInput.tsx +49 -3
  318. package/src/Chat/ChatContext.tsx +37 -10
  319. package/src/Chat/ChatDictationButton.tsx +4 -1
  320. package/src/Chat/ChatLayout.tsx +3 -1
  321. package/src/Chat/ChatLayoutScrollButton.tsx +7 -2
  322. package/src/Chat/ChatMessageMetadata.tsx +1 -2
  323. package/src/Chat/ChatSendButton.tsx +11 -1
  324. package/src/Chat/ChatToolCalls.test.tsx +64 -0
  325. package/src/Chat/ChatToolCalls.tsx +25 -4
  326. package/src/Chat/index.ts +5 -1
  327. package/src/Chat/useChatStreamScroll.test.tsx +49 -0
  328. package/src/Chat/useChatStreamScroll.ts +27 -10
  329. package/src/Chat/useSpeechRecognition.ts +1 -1
  330. package/src/Chat/useTriggerMenu.tsx +3 -1
  331. package/src/CheckboxInput/CheckboxInput.test.tsx +32 -1
  332. package/src/CheckboxInput/CheckboxInput.tsx +1 -1
  333. package/src/CheckboxList/CheckboxList.test.tsx +81 -3
  334. package/src/CheckboxList/CheckboxListItem.doc.mjs +33 -5
  335. package/src/CheckboxList/CheckboxListItem.tsx +41 -5
  336. package/src/ClickableCard/ClickableCard.doc.mjs +2 -0
  337. package/src/ClickableCard/ClickableCard.test.tsx +34 -0
  338. package/src/ClickableCard/ClickableCard.tsx +13 -3
  339. package/src/CodeBlock/CodeBlock.test.tsx +56 -0
  340. package/src/CodeBlock/CodeBlock.tsx +9 -1
  341. package/src/CommandPalette/CommandPalette.doc.mjs +1 -0
  342. package/src/CommandPalette/CommandPalette.tsx +3 -1
  343. package/src/CommandPalette/CommandPaletteGroup.test.tsx +71 -0
  344. package/src/CommandPalette/CommandPaletteGroup.tsx +12 -1
  345. package/src/CommandPalette/CommandPaletteInput.doc.mjs +26 -9
  346. package/src/CommandPalette/CommandPaletteInput.test.tsx +34 -0
  347. package/src/CommandPalette/CommandPaletteInput.tsx +11 -0
  348. package/src/ContextMenu/ContextMenu.test.tsx +193 -0
  349. package/src/DateInput/DateInput.doc.mjs +16 -0
  350. package/src/DateInput/DateInput.test.tsx +108 -0
  351. package/src/DateInput/DateInput.tsx +68 -3
  352. package/src/DateInput/index.ts +1 -0
  353. package/src/DateTimeInput/DateTimeInput.test.tsx +35 -0
  354. package/src/DateTimeInput/DateTimeInput.tsx +14 -2
  355. package/src/Dialog/Dialog.doc.mjs +2 -2
  356. package/src/Dialog/Dialog.test.tsx +77 -4
  357. package/src/Dialog/Dialog.tsx +89 -5
  358. package/src/Dialog/DialogContext.ts +9 -2
  359. package/src/Dialog/DialogHeader.doc.mjs +2 -2
  360. package/src/Dialog/DialogHeader.tsx +9 -2
  361. package/src/Divider/Divider.tsx +2 -2
  362. package/src/DropdownMenu/DropdownMenu.test.tsx +169 -0
  363. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +1 -2
  364. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +1 -2
  365. package/src/Field/Field.test.tsx +57 -12
  366. package/src/Field/Field.tsx +6 -5
  367. package/src/Field/FieldLabel.tsx +8 -0
  368. package/src/FieldStatus/FieldStatus.doc.mjs +17 -5
  369. package/src/FieldStatus/FieldStatus.test.tsx +78 -40
  370. package/src/FieldStatus/FieldStatus.tsx +22 -4
  371. package/src/FileInput/FileInput.test.tsx +52 -3
  372. package/src/FileInput/FileInput.tsx +13 -2
  373. package/src/HoverCard/HoverCard.doc.mjs +13 -0
  374. package/src/HoverCard/HoverCard.test.tsx +21 -2
  375. package/src/HoverCard/HoverCard.tsx +11 -0
  376. package/src/HoverCard/useHoverCard.doc.mjs +2 -0
  377. package/src/HoverCard/useHoverCard.tsx +24 -2
  378. package/src/Icon/Icon.doc.mjs +3 -2
  379. package/src/Icon/Icon.tsx +7 -2
  380. package/src/IconButton/IconButton.doc.mjs +8 -0
  381. package/src/IconButton/IconButton.test.tsx +15 -18
  382. package/src/Item/Item.test.tsx +40 -18
  383. package/src/Item/Item.tsx +38 -2
  384. package/src/Layer/useLayer.tsx +8 -0
  385. package/src/List/List.test.tsx +9 -8
  386. package/src/MobileNav/MobileNav.tsx +12 -1
  387. package/src/MobileNav/MobileNavToggle.test.tsx +116 -0
  388. package/src/MobileNav/MobileNavToggle.tsx +9 -1
  389. package/src/MultiSelector/MultiSelector.doc.mjs +4 -2
  390. package/src/MultiSelector/MultiSelector.test.tsx +172 -3
  391. package/src/MultiSelector/MultiSelector.tsx +55 -12
  392. package/src/MultiSelector/hooks.ts +17 -0
  393. package/src/NumberInput/NumberInput.test.tsx +11 -7
  394. package/src/NumberInput/NumberInput.tsx +1 -2
  395. package/src/Outline/Outline.tsx +0 -1
  396. package/src/OverflowList/OverflowList.doc.mjs +39 -6
  397. package/src/OverflowList/OverflowList.test.tsx +111 -0
  398. package/src/OverflowList/OverflowList.tsx +48 -8
  399. package/src/Popover/Popover.tsx +7 -4
  400. package/src/Popover/usePopover.tsx +9 -13
  401. package/src/PowerSearch/PowerSearch.doc.mjs +14 -0
  402. package/src/PowerSearch/PowerSearch.test.tsx +20 -0
  403. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +67 -0
  404. package/src/PowerSearch/PowerSearchEditPopover.tsx +1 -1
  405. package/src/PowerSearch/usePowerSearchSource.ts +1 -2
  406. package/src/SelectableCard/SelectableCard.doc.mjs +2 -0
  407. package/src/SelectableCard/SelectableCard.test.tsx +46 -4
  408. package/src/SelectableCard/SelectableCard.tsx +29 -16
  409. package/src/Selector/Selector.test.tsx +69 -0
  410. package/src/Selector/Selector.tsx +5 -1
  411. package/src/Selector/hooks.ts +17 -0
  412. package/src/SideNav/SideNav.test.tsx +119 -3
  413. package/src/SideNav/SideNavCollapseButton.tsx +6 -1
  414. package/src/SideNav/SideNavHeading.tsx +24 -8
  415. package/src/SideNav/SideNavItem.tsx +5 -1
  416. package/src/Slider/Slider.test.tsx +75 -2
  417. package/src/Slider/Slider.tsx +21 -5
  418. package/src/Switch/Switch.test.tsx +32 -1
  419. package/src/Table/BaseTable.tsx +3 -5
  420. package/src/Table/index.ts +5 -0
  421. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +6 -2
  422. package/src/Table/plugins/rowStatus/index.ts +7 -0
  423. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +138 -0
  424. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +189 -0
  425. package/src/Table/plugins/selection/useTableSelection.test.tsx +16 -0
  426. package/src/Table/plugins/selection/useTableSelection.tsx +19 -1
  427. package/src/Table/plugins/tree/useTableTreeData.test.tsx +135 -3
  428. package/src/Table/plugins/tree/useTableTreeData.tsx +128 -1
  429. package/src/Table/plugins/tree/useTableTreeState.test.tsx +104 -0
  430. package/src/Table/plugins/tree/useTableTreeState.tsx +46 -2
  431. package/src/Table/useBaseTablePlugins.ts +10 -6
  432. package/src/Table/useTableRowStatus.doc.mjs +30 -0
  433. package/src/Table/useTableSelection.doc.mjs +65 -14
  434. package/src/Table/useTableTreeData.doc.mjs +29 -0
  435. package/src/Table/useTableTreeState.doc.mjs +2 -2
  436. package/src/TextArea/TextArea.test.tsx +9 -1
  437. package/src/Thumbnail/Thumbnail.doc.mjs +19 -10
  438. package/src/Thumbnail/Thumbnail.test.tsx +98 -7
  439. package/src/Thumbnail/Thumbnail.tsx +141 -41
  440. package/src/Thumbnail/thumbnail.markers.stylex.ts +12 -0
  441. package/src/TimeInput/TimeInput.test.tsx +76 -1
  442. package/src/TimeInput/TimeInput.tsx +19 -6
  443. package/src/Timestamp/Timestamp.doc.mjs +4 -4
  444. package/src/Timestamp/Timestamp.test.tsx +32 -0
  445. package/src/Timestamp/Timestamp.tsx +28 -8
  446. package/src/Toast/useToast.tsx +7 -8
  447. package/src/ToggleButton/ToggleButtonGroup.tsx +1 -1
  448. package/src/Tokenizer/Tokenizer.doc.mjs +15 -1
  449. package/src/Tokenizer/Tokenizer.test.tsx +183 -2
  450. package/src/Tokenizer/Tokenizer.tsx +16 -4
  451. package/src/TopNav/TopNav.test.tsx +125 -1
  452. package/src/TopNav/TopNavHeading.tsx +24 -8
  453. package/src/TopNav/TopNavMegaMenu.test.tsx +149 -1
  454. package/src/TopNav/TopNavMegaMenu.tsx +10 -4
  455. package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +3 -3
  456. package/src/TopNav/TopNavMegaMenuFeaturedCard.tsx +19 -2
  457. package/src/TopNav/TopNavMenu.test.tsx +118 -4
  458. package/src/TopNav/TopNavMenu.tsx +83 -5
  459. package/src/TreeList/TreeList.doc.mjs +22 -0
  460. package/src/TreeList/TreeList.test.tsx +232 -0
  461. package/src/TreeList/TreeList.tsx +18 -2
  462. package/src/TreeList/TreeListBranches.tsx +16 -2
  463. package/src/TreeList/TreeListItem.tsx +51 -11
  464. package/src/TreeList/TreeListTypes.ts +27 -1
  465. package/src/TreeList/index.ts +2 -2
  466. package/src/authoring/doc.ts +1 -3
  467. package/src/docs-types.ts +5 -0
  468. package/src/hooks/computeOverflow.test.ts +422 -0
  469. package/src/hooks/computeOverflow.ts +290 -0
  470. package/src/hooks/index.ts +2 -0
  471. package/src/hooks/useDevWarning.test.tsx +45 -0
  472. package/src/hooks/useDevWarning.ts +54 -0
  473. package/src/hooks/useOverflow.test.ts +96 -4
  474. package/src/hooks/useOverflow.ts +74 -33
  475. package/src/i18n/InternationalizationProvider.doc.mjs +99 -2
  476. package/src/i18n/resolve.ts +9 -21
  477. package/src/theme/Theme.tsx +11 -14
  478. package/src/theme/generateThemeRules.test.ts +65 -0
  479. package/src/theme/generateThemeRules.ts +56 -0
  480. package/src/theme/hct.ts +1 -1
  481. package/src/theme/syntax/defineSyntaxTheme.ts +5 -6
  482. package/src/utils/devWarning.test.ts +56 -0
  483. package/src/utils/devWarning.ts +102 -0
  484. package/src/utils/index.ts +6 -0
  485. package/src/utils/plainDate.test.ts +22 -0
  486. package/src/utils/plainDate.ts +61 -0
  487. package/src/utils/types.ts +14 -0
@@ -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
+ });
@@ -0,0 +1,290 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file computeOverflow.ts
5
+ * @input Plain measurements (item widths, gap, available width, indicator width, limits)
6
+ * @output Pure overflow computation: how many items are visible and their row layout
7
+ * @position Pure helper consumed by useOverflow; contains no DOM or React code so the
8
+ * fit/clamp math and the multi-row packing can be unit-tested in isolation.
9
+ *
10
+ * There is a single source of truth for `visibleCount`:
11
+ * visibleCount = clamp(fitCount, minVisibleItems, maxVisibleItems ?? itemCount)
12
+ *
13
+ * SYNC: When modified, update:
14
+ * - /packages/core/src/hooks/useOverflow.ts (the only caller)
15
+ */
16
+
17
+ export interface ComputeOverflowInput {
18
+ /** Measured widths of each item, in original DOM order. */
19
+ widths: number[];
20
+ /** Gap between items, in pixels. */
21
+ gap: number;
22
+ /** Width available to lay items out, in pixels. */
23
+ availableWidth: number;
24
+ /** Measured width of the overflow indicator (0 if none). */
25
+ indicatorWidth: number;
26
+ /** Floor — always show at least this many items. */
27
+ minVisibleItems: number;
28
+ /** Ceiling — never show more than this many items. `undefined` = no cap. */
29
+ maxVisibleItems?: number;
30
+ /**
31
+ * Bounded multi-row: wrap items across up to this many rows, then collapse
32
+ * the rest into the overflow indicator. `undefined` (or `1`) = single line.
33
+ */
34
+ maxRows?: number;
35
+ /** Which end items collapse from. */
36
+ collapseFrom: 'start' | 'end';
37
+ }
38
+
39
+ export interface ComputeOverflowResult {
40
+ /** Number of items that should be rendered in the visible container. */
41
+ visibleCount: number;
42
+ /** Number of rows the visible items occupy (always 1 for the single-line path). */
43
+ rows: number;
44
+ }
45
+
46
+ function clamp(value: number, min: number, max: number): number {
47
+ return Math.max(Math.min(value, max), min);
48
+ }
49
+
50
+ /**
51
+ * Resolve the ceiling and floor into a single [floor, ceiling] pair.
52
+ *
53
+ * `maxVisibleItems` is clamped to `itemCount` (a cap larger than the list is a
54
+ * no-op). When `maxVisibleItems < minVisibleItems`, the floor wins (D1) via the
55
+ * final clamp — the hook is responsible for the dev-only warning.
56
+ */
57
+ function resolveBounds(
58
+ itemCount: number,
59
+ minVisibleItems: number,
60
+ maxVisibleItems: number | undefined,
61
+ ): {floor: number; ceiling: number} {
62
+ const floor = Math.max(0, Math.min(minVisibleItems, itemCount));
63
+ const rawCeiling = maxVisibleItems == null ? itemCount : maxVisibleItems;
64
+ const ceiling = Math.max(0, Math.min(rawCeiling, itemCount));
65
+ return {floor, ceiling};
66
+ }
67
+
68
+ /**
69
+ * Single-line greedy fit: how many items fit in one row of `availableWidth`,
70
+ * reserving indicator space for every non-final admitted item. Mirrors the
71
+ * original in-hook loop so single-line behavior is unchanged.
72
+ */
73
+ function computeSingleLineFit(
74
+ orderedWidths: number[],
75
+ gap: number,
76
+ availableWidth: number,
77
+ indicatorWidth: number,
78
+ floor: number,
79
+ ceiling: number,
80
+ ): number {
81
+ let totalWidth = 0;
82
+ let count = 0;
83
+
84
+ for (let i = 0; i < orderedWidths.length; i++) {
85
+ if (count >= ceiling) {
86
+ break;
87
+ }
88
+
89
+ const itemWidth = orderedWidths[i];
90
+ const gapWidth = i > 0 ? gap : 0;
91
+ const candidateWidth = totalWidth + itemWidth + gapWidth;
92
+
93
+ const isLastItem = i === orderedWidths.length - 1;
94
+ const reservedWidth = isLastItem
95
+ ? 0
96
+ : indicatorWidth + (count > 0 || indicatorWidth > 0 ? gap : 0);
97
+
98
+ if (candidateWidth + reservedWidth > availableWidth && count >= floor) {
99
+ break;
100
+ }
101
+
102
+ totalWidth = candidateWidth;
103
+ count++;
104
+ }
105
+
106
+ return count;
107
+ }
108
+
109
+ /**
110
+ * Pack items into rows, wrapping when the next item would exceed
111
+ * `availableWidth`. On the last allowed row, keep `indicatorReserve` px free so
112
+ * the overflow indicator fits. Stops admitting once no further item can be
113
+ * placed within `maxRows`.
114
+ */
115
+ function packRows(
116
+ orderedWidths: number[],
117
+ gap: number,
118
+ availableWidth: number,
119
+ indicatorReserve: number,
120
+ maxRows: number,
121
+ ): {placed: number; rows: number} {
122
+ let placed = 0;
123
+ let row = 1;
124
+ let rowWidth = 0;
125
+
126
+ for (let i = 0; i < orderedWidths.length; i++) {
127
+ const w = orderedWidths[i];
128
+ const isFirstInRow = rowWidth === 0;
129
+ const candidate = isFirstInRow ? w : rowWidth + gap + w;
130
+
131
+ const onLastRow = row === maxRows;
132
+ const reserve =
133
+ onLastRow && indicatorReserve > 0 ? indicatorReserve + gap : 0;
134
+
135
+ if (candidate + reserve <= availableWidth) {
136
+ rowWidth = candidate;
137
+ placed++;
138
+ continue;
139
+ }
140
+
141
+ if (isFirstInRow) {
142
+ // A single item wider than the row occupies this row alone (it will be
143
+ // clipped visually). But if it can't coexist with the reserved indicator
144
+ // on the last row, stop here.
145
+ if (onLastRow && reserve > 0) {
146
+ break;
147
+ }
148
+ rowWidth = candidate;
149
+ placed++;
150
+ continue;
151
+ }
152
+
153
+ if (row >= maxRows) {
154
+ break;
155
+ }
156
+ row++;
157
+ rowWidth = 0;
158
+ i--; // re-attempt this item as the first on the new row
159
+ }
160
+
161
+ return {placed, rows: row};
162
+ }
163
+
164
+ /** Count how many rows a set of items occupies when wrapped at availableWidth. */
165
+ function countRows(
166
+ orderedWidths: number[],
167
+ gap: number,
168
+ availableWidth: number,
169
+ ): number {
170
+ if (orderedWidths.length === 0) {
171
+ return 0;
172
+ }
173
+ let rows = 1;
174
+ let rowWidth = 0;
175
+ for (let i = 0; i < orderedWidths.length; i++) {
176
+ const w = orderedWidths[i];
177
+ const isFirstInRow = rowWidth === 0;
178
+ const candidate = isFirstInRow ? w : rowWidth + gap + w;
179
+ if (candidate <= availableWidth || isFirstInRow) {
180
+ rowWidth = candidate;
181
+ } else {
182
+ rows++;
183
+ rowWidth = w;
184
+ }
185
+ }
186
+ return rows;
187
+ }
188
+
189
+ /**
190
+ * Multi-row packing (Strategy A): simulate wrapping items into rows of
191
+ * `availableWidth`, admitting items until a new item would require a row beyond
192
+ * `maxRows`, reserving indicator space on the final row when items overflow.
193
+ * Returns the admitted count and how many rows those items occupy. Assumes
194
+ * uniform row height.
195
+ */
196
+ function computeMultiRowFit(
197
+ orderedWidths: number[],
198
+ gap: number,
199
+ availableWidth: number,
200
+ indicatorWidth: number,
201
+ maxRows: number,
202
+ ): {count: number; rows: number} {
203
+ const n = orderedWidths.length;
204
+ if (n === 0) {
205
+ return {count: 0, rows: 0};
206
+ }
207
+
208
+ // If everything fits within maxRows without reserving indicator space, there
209
+ // is no overflow and no indicator is needed.
210
+ const packAll = packRows(orderedWidths, gap, availableWidth, 0, maxRows);
211
+ if (packAll.placed === n) {
212
+ return {count: n, rows: packAll.rows};
213
+ }
214
+
215
+ // Overflow → reserve indicator space on the final row and re-pack.
216
+ const packWithIndicator = packRows(
217
+ orderedWidths,
218
+ gap,
219
+ availableWidth,
220
+ indicatorWidth,
221
+ maxRows,
222
+ );
223
+
224
+ const count = packWithIndicator.placed;
225
+ const rows = countRows(orderedWidths.slice(0, count), gap, availableWidth);
226
+
227
+ return {count, rows: Math.max(count > 0 ? 1 : 0, rows)};
228
+ }
229
+
230
+ /**
231
+ * Pure overflow computation. Given plain measurements and the configured
232
+ * limits, return how many items should be visible and how many rows they
233
+ * occupy. No DOM, no React — safe to unit-test directly.
234
+ */
235
+ export function computeOverflow(
236
+ input: ComputeOverflowInput,
237
+ ): ComputeOverflowResult {
238
+ const {
239
+ widths,
240
+ gap,
241
+ availableWidth,
242
+ indicatorWidth,
243
+ minVisibleItems,
244
+ maxVisibleItems,
245
+ maxRows,
246
+ collapseFrom,
247
+ } = input;
248
+
249
+ const itemCount = widths.length;
250
+ if (itemCount === 0) {
251
+ return {visibleCount: 0, rows: 0};
252
+ }
253
+
254
+ const {floor, ceiling} = resolveBounds(
255
+ itemCount,
256
+ minVisibleItems,
257
+ maxVisibleItems,
258
+ );
259
+
260
+ const orderedWidths = collapseFrom === 'end' ? widths : [...widths].reverse();
261
+
262
+ const multiRow = maxRows != null && maxRows > 1;
263
+
264
+ if (!multiRow) {
265
+ const fitCount = computeSingleLineFit(
266
+ orderedWidths,
267
+ gap,
268
+ availableWidth,
269
+ indicatorWidth,
270
+ floor,
271
+ ceiling,
272
+ );
273
+ const visibleCount = clamp(fitCount, floor, ceiling);
274
+ return {visibleCount, rows: visibleCount > 0 ? 1 : 0};
275
+ }
276
+
277
+ const {count, rows} = computeMultiRowFit(
278
+ orderedWidths,
279
+ gap,
280
+ availableWidth,
281
+ indicatorWidth,
282
+ maxRows,
283
+ );
284
+ const visibleCount = clamp(count, floor, ceiling);
285
+ const resolvedRows =
286
+ visibleCount === count
287
+ ? rows
288
+ : countRows(orderedWidths.slice(0, visibleCount), gap, availableWidth);
289
+ return {visibleCount, rows: visibleCount > 0 ? Math.max(1, resolvedRows) : 0};
290
+ }
@@ -84,3 +84,5 @@ export type {
84
84
 
85
85
  export {useLongPress} from './useLongPress';
86
86
  export type {UseLongPressOptions, UseLongPressHandlers} from './useLongPress';
87
+
88
+ export {useDevWarning} from './useDevWarning';