@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
@@ -562,6 +562,23 @@ export interface MultiSelectorProps<
562
562
  'data-testid'?: string;
563
563
  }
564
564
 
565
+ // Case-insensitive substring filter over the selectable options. Shared by the
566
+ // `filteredItems` memo (rendering) and the search-change handler, which needs
567
+ // the count for the *next* query synchronously to announce it exactly once per
568
+ // keystroke rather than reacting to state in an effect.
569
+ function filterOptionsByQuery(
570
+ items: MultiSelectorOptionData[],
571
+ query: string,
572
+ ): MultiSelectorOptionData[] {
573
+ if (!query) {
574
+ return items;
575
+ }
576
+ const q = query.toLowerCase();
577
+ return items.filter(item =>
578
+ (item.label ?? item.value).toLowerCase().includes(q),
579
+ );
580
+ }
581
+
565
582
  /**
566
583
  * A multi-select dropdown component with checkboxes for choosing
567
584
  * multiple items from a list of options.
@@ -692,15 +709,10 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
692
709
  );
693
710
 
694
711
  // Filter items by search query
695
- const filteredItems = useMemo(() => {
696
- if (!searchQuery) {
697
- return selectableItems;
698
- }
699
- const query = searchQuery.toLowerCase();
700
- return selectableItems.filter(item =>
701
- (item.label ?? item.value).toLowerCase().includes(query),
702
- );
703
- }, [selectableItems, searchQuery]);
712
+ const filteredItems = useMemo(
713
+ () => filterOptionsByQuery(selectableItems, searchQuery),
714
+ [selectableItems, searchQuery],
715
+ );
704
716
 
705
717
  // Single source of truth for item order. Both the hook (keyboard navigation)
706
718
  // and renderOptions (DOM rendering) consume this list — no independent sorting.
@@ -772,8 +784,11 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
772
784
  const handleLayerHide = useCallback(() => {
773
785
  setSearchQuery('');
774
786
  setSelectedAtOpen(null);
787
+ // Clear any lingering result count when the popover closes so stale status
788
+ // text does not linger in the a11y tree.
789
+ announce('');
775
790
  triggerRef.current?.focus();
776
- }, []);
791
+ }, [announce]);
777
792
 
778
793
  const popover = usePopover({
779
794
  hasLightDismiss: true,
@@ -793,6 +808,30 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
793
808
  // eslint-disable-next-line @eslint-react/exhaustive-deps -- mount-only: isDefaultOpen is not reactive
794
809
  }, []);
795
810
 
811
+ // Announce the filtered result count from the query-change handler (matching
812
+ // BaseTypeahead) rather than a reactive effect: computing the count for the
813
+ // next query here fires the announcement exactly once per keystroke and does
814
+ // not re-speak on unrelated re-renders. Reuses the announce instance shared
815
+ // with the selection-count announcements above.
816
+ const handleSearchChange = useCallback(
817
+ (event: React.ChangeEvent<HTMLInputElement>) => {
818
+ const nextQuery = event.target.value;
819
+ setSearchQuery(nextQuery);
820
+ if (nextQuery.length === 0) {
821
+ // Emptying the query clears the region rather than announcing a count.
822
+ announce('');
823
+ return;
824
+ }
825
+ const count = filterOptionsByQuery(selectableItems, nextQuery).length;
826
+ announce(
827
+ count === 0
828
+ ? 'No results found'
829
+ : `${count} result${count === 1 ? '' : 's'}`,
830
+ );
831
+ },
832
+ [announce, selectableItems],
833
+ );
834
+
796
835
  // Handle toggle
797
836
  // Clear all selected values. Shared by the clear button and the keyboard
798
837
  // Delete/Backspace path so clearing is reachable without a mouse.
@@ -1041,14 +1080,17 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1041
1080
  aria-label={t('@astryx.multiSelector.searchOptions')}
1042
1081
  type="text"
1043
1082
  value={searchQuery}
1044
- onChange={e => setSearchQuery(e.target.value)}
1083
+ onChange={handleSearchChange}
1045
1084
  onKeyDown={e => {
1046
1085
  // Arrow keys navigate options; Enter toggles; Escape/Tab close.
1047
1086
  // Space and Home/End are left to the input (type a space / move
1048
- // the caret), not forwarded to option navigation.
1087
+ // the caret) per the APG editable combobox; PageUp/PageDown are
1088
+ // the sanctioned substitute for jumping to the first/last option.
1049
1089
  if (
1050
1090
  e.key === 'ArrowDown' ||
1051
1091
  e.key === 'ArrowUp' ||
1092
+ e.key === 'PageUp' ||
1093
+ e.key === 'PageDown' ||
1052
1094
  e.key === 'Enter' ||
1053
1095
  e.key === 'Escape' ||
1054
1096
  e.key === 'Tab'
@@ -1067,6 +1109,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1067
1109
  listboxId,
1068
1110
  searchQuery,
1069
1111
  searchPlaceholder,
1112
+ handleSearchChange,
1070
1113
  onKeyDown,
1071
1114
  popover.isOpen,
1072
1115
  highlightedIndex,
@@ -192,6 +192,23 @@ export function useMultiCombobox({
192
192
  }
193
193
  break;
194
194
 
195
+ // PageUp/PageDown mirror Home/End. In search mode Home/End stay on
196
+ // the input for caret movement (APG editable combobox), so these are
197
+ // the sanctioned substitute for jumping to the first/last option.
198
+ case 'PageUp':
199
+ e.preventDefault();
200
+ if (isOpen && enabledIndices.length > 0) {
201
+ setHighlightedIndex(enabledIndices[0]);
202
+ }
203
+ break;
204
+
205
+ case 'PageDown':
206
+ e.preventDefault();
207
+ if (isOpen && enabledIndices.length > 0) {
208
+ setHighlightedIndex(enabledIndices[enabledIndices.length - 1]);
209
+ }
210
+ break;
211
+
195
212
  case 'Delete':
196
213
  case 'Backspace':
197
214
  // Keyboard equivalent of the clear button (comboboxes-2): clear all
@@ -9,11 +9,19 @@
9
9
  * SYNC: When NumberInput.tsx changes, update tests to match new behavior
10
10
  */
11
11
 
12
- import {describe, it, expect, vi, beforeEach} from 'vitest';
12
+ import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
13
13
  import {render, screen, fireEvent, waitFor} from '@testing-library/react';
14
14
  import userEvent from '@testing-library/user-event';
15
15
  import {TestIcon} from '../__tests__/TestIcon';
16
16
  import {NumberInput} from './NumberInput';
17
+ import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
18
+
19
+ // FieldStatus announces status messages through the persistent useAnnounce
20
+ // singletons; remove them between tests so role/aria-live queries in this
21
+ // file never match a leftover region.
22
+ afterEach(() => {
23
+ __resetLiveRegionsForTest();
24
+ });
17
25
 
18
26
  // Mock showPopover/hidePopover since jsdom does not implement them. Used by the
19
27
  // disabledMessage tooltip.
@@ -871,9 +879,7 @@ describe('NumberInput', () => {
871
879
  describe('keyboard clearing with hasClear (#3599)', () => {
872
880
  it('commits null when the input is emptied and blurred', () => {
873
881
  const onChange = vi.fn();
874
- render(
875
- <NumberInput label="Qty" hasClear value={42} onChange={onChange} />,
876
- );
882
+ render(<NumberInput label="Qty" hasClear value={42} onChange={onChange} />);
877
883
  const input = screen.getByLabelText('Qty');
878
884
  fireEvent.change(input, {target: {value: ''}});
879
885
  fireEvent.blur(input);
@@ -882,9 +888,7 @@ describe('keyboard clearing with hasClear (#3599)', () => {
882
888
 
883
889
  it('commits null when the input is emptied and Enter is pressed', () => {
884
890
  const onChange = vi.fn();
885
- render(
886
- <NumberInput label="Qty" hasClear value={42} onChange={onChange} />,
887
- );
891
+ render(<NumberInput label="Qty" hasClear value={42} onChange={onChange} />);
888
892
  const input = screen.getByLabelText('Qty');
889
893
  fireEvent.change(input, {target: {value: ''}});
890
894
  fireEvent.keyDown(input, {key: 'Enter'});
@@ -311,8 +311,7 @@ type NumberInputPropsClearable = NumberInputPropsBase & {
311
311
  };
312
312
 
313
313
  export type NumberInputProps =
314
- | NumberInputPropsNonClearable
315
- | NumberInputPropsClearable;
314
+ NumberInputPropsNonClearable | NumberInputPropsClearable;
316
315
 
317
316
  /**
318
317
  * Parse and validate a string input as a number.
@@ -299,7 +299,6 @@ function getIndentStyle(level: number) {
299
299
  * @example
300
300
  * ```
301
301
  * const scrollContainerRef = useRef<HTMLDivElement>(null);
302
- *
303
302
  * <div ref={scrollContainerRef} style={{overflowY: 'auto'}}>...</div>
304
303
  * <Outline
305
304
  * items={items}
@@ -61,6 +61,20 @@ export const docs = {
61
61
  description: 'Minimum number of items to always show, even when overflowing.',
62
62
  default: '0',
63
63
  },
64
+ {
65
+ name: 'maxVisibleItems',
66
+ type: 'number',
67
+ description:
68
+ 'Maximum number of items to ever show, even when they all fit. The ceiling partner to minVisibleItems; extra items collapse into the overflow indicator. If less than minVisibleItems, the floor wins.',
69
+ default: 'undefined (no cap)',
70
+ },
71
+ {
72
+ name: 'maxRows',
73
+ type: 'number',
74
+ description:
75
+ 'Wrap items across up to this many rows before collapsing the rest into the overflow indicator. A number, not a boolean. Leave undefined (or set 1) for single-line behavior. Assumes uniform row height.',
76
+ default: 'undefined (single line)',
77
+ },
64
78
  {
65
79
  name: 'collapseFrom',
66
80
  type: "'start' | 'end'",
@@ -91,8 +105,9 @@ export const docs = {
91
105
  'A horizontal list that automatically hides items when they exceed the available width. Use OverflowList for breadcrumbs, toolbars, tag lists, or any row that needs to collapse gracefully at smaller sizes.',
92
106
  bestPractices: [
93
107
  { guidance: true, description: 'Provide a meaningful overflowRenderer: a "+N more" badge, a dropdown, or a count indicator.' },
94
- { guidance: true, description: 'Set minVisibleItems to keep key items visible regardless of container size.' },
95
- { guidance: false, description: 'Use OverflowList for vertical layouts; it only works with horizontal rows.' },
108
+ { guidance: true, description: 'Set minVisibleItems to keep key items visible, and maxVisibleItems to cap the row at a fixed count regardless of available width.' },
109
+ { guidance: true, description: 'Use maxRows to let items wrap onto a bounded number of rows (e.g. a two-row tag cloud) before collapsing the rest into the indicator.' },
110
+ { guidance: false, description: 'Use OverflowList for a vertical stack; horizontal multi-row wrap is supported via maxRows, but items still flow left-to-right, not top-to-bottom.' },
96
111
  ],
97
112
  },
98
113
  };
@@ -127,6 +142,20 @@ export const docsZh = {
127
142
  description: '溢出时始终显示的最小项目数。',
128
143
  default: '0',
129
144
  },
145
+ {
146
+ name: 'maxVisibleItems',
147
+ type: 'number',
148
+ description:
149
+ '始终显示的最大项目数(即使全部都能放下)。它是 minVisibleItems 的上限伙伴;多余项目会折叠进溢出指示器。若小于 minVisibleItems,则以下限为准。',
150
+ default: 'undefined(无上限)',
151
+ },
152
+ {
153
+ name: 'maxRows',
154
+ type: 'number',
155
+ description:
156
+ '在将其余项目折叠进溢出指示器之前,允许项目换行到的最大行数。是数字而非布尔值。留空(或设为 1)表示单行。假定行高一致。',
157
+ default: 'undefined(单行)',
158
+ },
130
159
  {
131
160
  name: 'collapseFrom',
132
161
  type: "'start' | 'end'",
@@ -152,8 +181,9 @@ export const docsZh = {
152
181
  'A horizontal list that automatically hides items when they exceed the available width. Use OverflowList for breadcrumbs, toolbars, tag lists, or any row that needs to collapse gracefully at smaller sizes.',
153
182
  bestPractices: [
154
183
  { guidance: true, description: 'Provide a meaningful overflowRenderer: a "+N more" badge, a dropdown, or a count indicator.' },
155
- { guidance: true, description: 'Set minVisibleItems to keep key items visible regardless of container size.' },
156
- { guidance: false, description: 'Use OverflowList for vertical layouts; it only works with horizontal rows.' },
184
+ { guidance: true, description: 'Set minVisibleItems to keep key items visible, and maxVisibleItems to cap the row at a fixed count regardless of available width.' },
185
+ { guidance: true, description: 'Use maxRows to let items wrap onto a bounded number of rows (e.g. a two-row tag cloud) before collapsing the rest into the indicator.' },
186
+ { guidance: false, description: 'Use OverflowList for a vertical stack; horizontal multi-row wrap is supported via maxRows, but items still flow left-to-right, not top-to-bottom.' },
157
187
  ],
158
188
  },
159
189
  };
@@ -166,8 +196,9 @@ export const docsDense = {
166
196
  'A horizontal list that automatically hides items when they exceed the available width. Use OverflowList for breadcrumbs, toolbars, tag lists, or any row that needs to collapse gracefully at smaller sizes.',
167
197
  bestPractices: [
168
198
  { guidance: true, description: 'Provide a meaningful overflowRenderer: a "+N more" badge, a dropdown, or a count indicator.' },
169
- { guidance: true, description: 'Set minVisibleItems to keep key items visible regardless of container size.' },
170
- { guidance: false, description: 'Use OverflowList for vertical layouts; it only works with horizontal rows.' },
199
+ { guidance: true, description: 'Set minVisibleItems to keep key items visible, and maxVisibleItems to cap the row at a fixed count regardless of available width.' },
200
+ { guidance: true, description: 'Use maxRows to let items wrap onto a bounded number of rows (e.g. a two-row tag cloud) before collapsing the rest into the indicator.' },
201
+ { guidance: false, description: 'Use OverflowList for a vertical stack; horizontal multi-row wrap is supported via maxRows, but items still flow left-to-right, not top-to-bottom.' },
171
202
  ],
172
203
  },
173
204
  propDescriptions: {
@@ -175,6 +206,8 @@ export const docsDense = {
175
206
  overflowRenderer: 'renders overflow indicator, receives hidden items w/ index',
176
207
  gap: 'item gap as spacing step',
177
208
  minVisibleItems: 'min items always shown even when overflowing',
209
+ maxVisibleItems: 'max items ever shown (cap); extra items collapse to indicator; min wins if smaller',
210
+ maxRows: 'wrap items across up to N rows then collapse rest (number, not boolean); undefined = single line',
178
211
  collapseFrom: 'which end to collapse from',
179
212
  behavior: 'observeSelf (default) or observeParent for content-sized containers',
180
213
  xstyle: 'StyleX styles, use stylex.create() values only',
@@ -413,4 +413,115 @@ describe('OverflowList', () => {
413
413
  expect(OverflowList.displayName).toBe('OverflowList');
414
414
  });
415
415
  });
416
+
417
+ describe('maxVisibleItems (cap)', () => {
418
+ it('caps visible items even when they all fit', () => {
419
+ render(
420
+ <OverflowList
421
+ gap={0}
422
+ data-w="1000"
423
+ data-testid="ov"
424
+ maxVisibleItems={2}
425
+ overflowRenderer={indicator('more:')}>
426
+ <button type="button" data-w="40">
427
+ A
428
+ </button>
429
+ <button type="button" data-w="40">
430
+ B
431
+ </button>
432
+ <button type="button" data-w="40">
433
+ C
434
+ </button>
435
+ <button type="button" data-w="40">
436
+ D
437
+ </button>
438
+ </OverflowList>,
439
+ );
440
+ const vis = visibleContainer();
441
+ expect(within(vis).getByText('A')).toBeInTheDocument();
442
+ expect(within(vis).getByText('B')).toBeInTheDocument();
443
+ expect(within(vis).queryByText('C')).not.toBeInTheDocument();
444
+ expect(within(vis).getByText('more:2,3')).toBeInTheDocument();
445
+ });
446
+
447
+ it('min wins over a smaller cap (D1)', () => {
448
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
449
+ render(
450
+ <OverflowList
451
+ gap={0}
452
+ data-w="1000"
453
+ data-testid="ov"
454
+ minVisibleItems={3}
455
+ maxVisibleItems={1}
456
+ overflowRenderer={indicator('more:')}>
457
+ <button type="button" data-w="40">
458
+ A
459
+ </button>
460
+ <button type="button" data-w="40">
461
+ B
462
+ </button>
463
+ <button type="button" data-w="40">
464
+ C
465
+ </button>
466
+ <button type="button" data-w="40">
467
+ D
468
+ </button>
469
+ </OverflowList>,
470
+ );
471
+ const vis = visibleContainer();
472
+ expect(within(vis).getByText('A')).toBeInTheDocument();
473
+ expect(within(vis).getByText('B')).toBeInTheDocument();
474
+ expect(within(vis).getByText('C')).toBeInTheDocument();
475
+ expect(within(vis).queryByText('D')).not.toBeInTheDocument();
476
+ expect(warn).toHaveBeenCalled();
477
+ warn.mockRestore();
478
+ });
479
+ });
480
+
481
+ describe('maxRows (multi-row)', () => {
482
+ it('applies a different container class when maxRows enables wrapping', () => {
483
+ const {rerender} = render(
484
+ <OverflowList gap={0} data-w="1000" data-testid="ov">
485
+ <button type="button" data-w="40">
486
+ A
487
+ </button>
488
+ </OverflowList>,
489
+ );
490
+ const singleLine = visibleContainer().getAttribute('class');
491
+
492
+ rerender(
493
+ <OverflowList gap={0} data-w="1000" data-testid="ov" maxRows={2}>
494
+ <button type="button" data-w="40">
495
+ A
496
+ </button>
497
+ </OverflowList>,
498
+ );
499
+ const multiRow = visibleContainer().getAttribute('class');
500
+ expect(multiRow).not.toEqual(singleLine);
501
+ });
502
+
503
+ it('keeps single-line behavior with maxRows={1}', () => {
504
+ render(
505
+ <OverflowList
506
+ gap={0}
507
+ data-w="100"
508
+ data-testid="ov"
509
+ maxRows={1}
510
+ overflowRenderer={indicator('more:')}>
511
+ <button type="button" data-w="40">
512
+ A
513
+ </button>
514
+ <button type="button" data-w="40">
515
+ B
516
+ </button>
517
+ <button type="button" data-w="40">
518
+ C
519
+ </button>
520
+ </OverflowList>,
521
+ );
522
+ const vis = visibleContainer();
523
+ expect(within(vis).getByText('A')).toBeInTheDocument();
524
+ expect(within(vis).queryByText('B')).not.toBeInTheDocument();
525
+ });
526
+ });
416
527
  });
@@ -9,8 +9,9 @@
9
9
  * @position Core implementation; consumed by index.ts
10
10
  *
11
11
  * Renders a horizontal list of items, hiding those that don't fit in the
12
- * available width and optionally showing an overflow indicator.
13
- * Uses a hidden measurement container to avoid flickering.
12
+ * available width and optionally showing an overflow indicator. Supports an
13
+ * optional item cap (`maxVisibleItems`) and bounded multi-row wrapping
14
+ * (`maxRows`). Uses a hidden measurement container to avoid flickering.
14
15
  *
15
16
  * SYNC: When modified, update these files to stay in sync:
16
17
  * - /packages/core/src/OverflowList/index.ts (exports if types change)
@@ -34,6 +35,14 @@ const styles = stylex.create({
34
35
  whiteSpace: 'nowrap',
35
36
  minWidth: 0,
36
37
  },
38
+ containerMultiRow: {
39
+ display: 'flex',
40
+ flexWrap: 'wrap',
41
+ alignContent: 'flex-start',
42
+ overflow: 'hidden',
43
+ whiteSpace: 'normal',
44
+ minWidth: 0,
45
+ },
37
46
  fillParent: {
38
47
  width: '100%',
39
48
  },
@@ -52,6 +61,12 @@ const styles = stylex.create({
52
61
  },
53
62
  });
54
63
 
64
+ const multiRowHeight = stylex.create({
65
+ height: (maxRows: number, rowHeight: number, gapPx: number) => ({
66
+ maxHeight: `calc(${rowHeight}px * ${maxRows} + ${gapPx}px * ${maxRows - 1})`,
67
+ }),
68
+ });
69
+
55
70
  const gapStyles = stylex.create({
56
71
  0: {gap: spacingVars['--spacing-0']},
57
72
  0.5: {gap: spacingVars['--spacing-0-5']},
@@ -112,6 +127,24 @@ export interface OverflowListProps extends BaseProps<HTMLDivElement> {
112
127
  */
113
128
  minVisibleItems?: number;
114
129
 
130
+ /**
131
+ * Maximum number of items to ever show, even when they all fit. The ceiling
132
+ * partner to `minVisibleItems`; extra items collapse into the overflow
133
+ * indicator. Leave undefined for no cap. If it is less than
134
+ * `minVisibleItems`, the floor wins (and a dev-only warning is logged).
135
+ * @default undefined
136
+ */
137
+ maxVisibleItems?: number;
138
+
139
+ /**
140
+ * Wrap items across up to this many rows before collapsing the remainder
141
+ * into the overflow indicator. Leave undefined (or set `1`) for the default
142
+ * single-line behavior. A number, not a boolean — unbounded wrapping is a
143
+ * plain flex-wrap layout, not overflow collapse. Assumes uniform row height.
144
+ * @default undefined
145
+ */
146
+ maxRows?: number;
147
+
115
148
  /**
116
149
  * Which end to collapse items from.
117
150
  * @default 'end'
@@ -180,6 +213,8 @@ export function OverflowList({
180
213
  children,
181
214
  gap = 2,
182
215
  minVisibleItems = 0,
216
+ maxVisibleItems,
217
+ maxRows,
183
218
  collapseFrom = 'end',
184
219
  behavior = 'observeSelf',
185
220
  overflowRenderer,
@@ -195,16 +230,17 @@ export function OverflowList({
195
230
  const gapPx = spacingToPx[gap];
196
231
 
197
232
  const observeParent = behavior === 'observeParent';
233
+ const isMultiRow = maxRows != null && maxRows > 1;
198
234
 
199
- const {containerRef, measureRef, visibleCount, hasOverflow} = useOverflow(
200
- itemCount,
201
- {
235
+ const {containerRef, measureRef, visibleCount, hasOverflow, rowHeight} =
236
+ useOverflow(itemCount, {
202
237
  gap: gapPx,
203
238
  minVisibleItems,
239
+ maxVisibleItems,
240
+ maxRows,
204
241
  collapseFrom,
205
242
  behavior,
206
- },
207
- );
243
+ });
208
244
 
209
245
  const allItems: OverflowItem[] = childArray.map((child, index) => ({
210
246
  child,
@@ -248,8 +284,12 @@ export function OverflowList({
248
284
  {...mergeProps(
249
285
  themeProps('overflow-list'),
250
286
  stylex.props(
251
- styles.container,
287
+ isMultiRow ? styles.containerMultiRow : styles.container,
252
288
  gapStyles[gap],
289
+ isMultiRow &&
290
+ rowHeight > 0 &&
291
+ maxRows != null &&
292
+ multiRowHeight.height(maxRows, rowHeight, gapPx),
253
293
  observeParent && hasOverflow && styles.fillParent,
254
294
  xstyle,
255
295
  ),
@@ -26,6 +26,7 @@ import React, {
26
26
  import {useIsomorphicLayoutEffect} from '../hooks/useIsomorphicLayoutEffect';
27
27
  import * as stylex from '@stylexjs/stylex';
28
28
  import {mergeProps} from '../utils';
29
+ import {devWarn} from '../utils/devWarning';
29
30
  import type {BaseProps} from '../BaseProps';
30
31
  import {usePopover} from './usePopover';
31
32
  import type {LayerAlignment, LayerPlacement} from '../Layer/useLayer';
@@ -410,8 +411,9 @@ export function Popover({
410
411
 
411
412
  const button = findTriggerButton(el);
412
413
  if (!button) {
413
- console.warn(
414
- 'Popover: anchorRef must reference a <button> or [role="button"] element. ' +
414
+ devWarn(
415
+ 'Popover',
416
+ 'anchorRef must reference a <button> or [role="button"] element. ' +
415
417
  'The popover trigger implements the button + dialog ARIA pattern.',
416
418
  );
417
419
  }
@@ -453,8 +455,9 @@ export function Popover({
453
455
  // Find the button inside the wrapper
454
456
  const button = findTriggerButton(wrapper);
455
457
  if (!button) {
456
- console.warn(
457
- 'Popover: children must contain a <button> or [role="button"] element. ' +
458
+ devWarn(
459
+ 'Popover',
460
+ 'children must contain a <button> or [role="button"] element. ' +
458
461
  'The popover trigger implements the button + dialog ARIA pattern.',
459
462
  );
460
463
  }
@@ -28,6 +28,7 @@ import {
28
28
  } from '../theme/tokens.stylex';
29
29
  import {Button} from '../Button';
30
30
  import {useTranslator} from '../i18n';
31
+ import {useDevWarning} from '../hooks/useDevWarning';
31
32
 
32
33
  const styles = stylex.create({
33
34
  // Default popover surface — background, radius, shadow.
@@ -391,19 +392,14 @@ export function usePopover(options: UsePopoverOptions = {}): UsePopoverReturn {
391
392
  'aria-controls': layer.id,
392
393
  };
393
394
 
394
- // Dev-time guardrail: a dialog popover should always be labeled. Warn once
395
- // per hook instance (in an effect) rather than on every render.
396
- const warnedUnnamedDialogRef = useRef(false);
397
- useEffect(() => {
398
- if (role === 'dialog' && !dialogLabel && !warnedUnnamedDialogRef.current) {
399
- warnedUnnamedDialogRef.current = true;
400
- console.warn(
401
- 'usePopover: role="dialog" without a `dialogLabel` renders an unnamed ' +
402
- 'dialog. Pass `dialogLabel`, or use `role: "none"` for listbox/menu ' +
403
- 'popups whose content already carries its own role.',
404
- );
405
- }
406
- }, [role, dialogLabel]);
395
+ // Dev-time guardrail: a dialog popover should always be labeled.
396
+ useDevWarning(
397
+ 'usePopover',
398
+ 'role="dialog" without a `dialogLabel` renders an unnamed ' +
399
+ 'dialog. Pass `dialogLabel`, or use `role: "none"` for listbox/menu ' +
400
+ 'popups whose content already carries its own role.',
401
+ role === 'dialog' && !dialogLabel,
402
+ );
407
403
 
408
404
  // Wrapped render function that includes surface styles and optional hidden close button
409
405
  const render = useCallback(
@@ -83,6 +83,13 @@ export const docs = {
83
83
  description:
84
84
  'Validation status object with type and optional message.',
85
85
  },
86
+ {
87
+ name: 'startIcon',
88
+ type: 'ReactNode | IconType',
89
+ description:
90
+ 'Icon to display at the start of the input, before any filter tokens. Forwarded to the internal Tokenizer. Accepts a semantic icon name, an SVG icon component, or a ReactNode directly.',
91
+ slotElements: [{__element: 'Icon', props: {icon: 'search', size: 'sm'}}],
92
+ },
86
93
  {
87
94
  name: 'maxTokenLength',
88
95
  type: 'number',
@@ -235,6 +242,12 @@ export const docsZh = {
235
242
  type: "{type: 'warning' | 'error' | 'success', message?: string}",
236
243
  description: '带有类型和可选消息的验证状态对象。',
237
244
  },
245
+ {
246
+ name: 'startIcon',
247
+ type: 'ReactNode | IconType',
248
+ description:
249
+ '在输入框开头(筛选 token 之前)显示的图标,转发给内部的 Tokenizer。接受语义图标名称、SVG 图标组件或直接传入 ReactNode。',
250
+ },
238
251
  {
239
252
  name: 'maxTokenLength',
240
253
  type: 'number',
@@ -318,6 +331,7 @@ export const docsDense = {
318
331
  isReadOnly: 'Prevent adding, editing, or removing filters.',
319
332
  isDisabled: 'Disables entire component.',
320
333
  status: 'Validation status object w/ type + optional message.',
334
+ startIcon: 'Icon at input start, before filter tokens. Forwarded to internal Tokenizer.',
321
335
  maxTokenLength: 'Max char length for filter value display in tokens.',
322
336
  popoverSaveButtonLabel: 'Label for save button in edit popover.',
323
337
  timezoneID: 'Timezone ID for date formatting (e.g. "America/New_York").',
@@ -16,6 +16,7 @@ import userEvent from '@testing-library/user-event';
16
16
  import {PowerSearch} from './PowerSearch';
17
17
  import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
18
18
  import type {PowerSearchConfig, PowerSearchFilter} from './types';
19
+ import {TestIcon} from '../__tests__/TestIcon';
19
20
 
20
21
  // =============================================================================
21
22
  // Test infrastructure
@@ -157,6 +158,25 @@ describe('PowerSearch', () => {
157
158
  expect(screen.getByRole('combobox')).toHaveFocus();
158
159
  });
159
160
 
161
+ describe('startIcon', () => {
162
+ it('does not render a start icon when omitted', () => {
163
+ render(<PowerSearch config={config} filters={[]} onChange={() => {}} />);
164
+ expect(document.querySelector('svg')).not.toBeInTheDocument();
165
+ });
166
+
167
+ it('forwards startIcon to the internal Tokenizer', () => {
168
+ render(
169
+ <PowerSearch
170
+ config={config}
171
+ filters={[]}
172
+ onChange={() => {}}
173
+ startIcon={<TestIcon data-testid="start-icon" />}
174
+ />,
175
+ );
176
+ expect(screen.getByTestId('start-icon')).toBeInTheDocument();
177
+ });
178
+ });
179
+
160
180
  describe('paste behavior', () => {
161
181
  it('pasting a field name shows matching field suggestions', async () => {
162
182
  const user = userEvent.setup();