@astryxdesign/core 0.1.9 → 0.2.0-canary.068bda9

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 (601) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/dist/Avatar/Avatar.d.ts +42 -3
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +190 -59
  5. package/dist/Avatar/AvatarStatusDot.d.ts +6 -4
  6. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  7. package/dist/AvatarGroup/AvatarGroup.d.ts +7 -1
  8. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  9. package/dist/AvatarGroup/AvatarGroup.js +58 -6
  10. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  11. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  12. package/dist/AvatarGroup/AvatarGroupOverflow.js +7 -2
  13. package/dist/Banner/Banner.js +2 -2
  14. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  15. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -4
  16. package/dist/Breadcrumbs/index.d.ts +1 -0
  17. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  18. package/dist/Breadcrumbs/index.js +2 -1
  19. package/dist/Button/Button.d.ts +1 -1
  20. package/dist/Button/Button.d.ts.map +1 -1
  21. package/dist/Button/Button.js +6 -4
  22. package/dist/Calendar/Calendar.d.ts.map +1 -1
  23. package/dist/Calendar/Calendar.js +50 -9
  24. package/dist/Calendar/dayCellUtils.d.ts +4 -4
  25. package/dist/Calendar/dayCellUtils.js +2 -2
  26. package/dist/Calendar/styles.d.ts +30 -31
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +30 -31
  29. package/dist/Card/Card.js +1 -1
  30. package/dist/Carousel/Carousel.d.ts.map +1 -1
  31. package/dist/Carousel/Carousel.js +22 -12
  32. package/dist/Chat/ChatComposerInput.js +1 -1
  33. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  34. package/dist/Chat/ChatDictationButton.js +3 -1
  35. package/dist/Chat/ChatLayout.js +4 -4
  36. package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
  37. package/dist/Chat/ChatMessageBubble.js +8 -8
  38. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  39. package/dist/Chat/ChatToolCalls.js +10 -3
  40. package/dist/Chat/useTriggerMenu.js +2 -2
  41. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  42. package/dist/CheckboxInput/CheckboxInput.js +6 -6
  43. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  44. package/dist/CheckboxList/CheckboxListItem.js +19 -4
  45. package/dist/Citation/Citation.d.ts +22 -1
  46. package/dist/Citation/Citation.d.ts.map +1 -1
  47. package/dist/Citation/Citation.js +20 -6
  48. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  49. package/dist/CodeBlock/CodeBlock.js +16 -10
  50. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  51. package/dist/Collapsible/Collapsible.js +3 -1
  52. package/dist/CommandPalette/CommandPalette.d.ts +1 -1
  53. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  54. package/dist/CommandPalette/CommandPalette.js +42 -3
  55. package/dist/CommandPalette/CommandPaletteItem.js +1 -1
  56. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  57. package/dist/ContextMenu/ContextMenu.js +23 -5
  58. package/dist/ContextMenu/index.d.ts +1 -0
  59. package/dist/ContextMenu/index.d.ts.map +1 -1
  60. package/dist/ContextMenu/index.js +3 -1
  61. package/dist/DateInput/DateInput.d.ts +10 -2
  62. package/dist/DateInput/DateInput.d.ts.map +1 -1
  63. package/dist/DateInput/DateInput.js +33 -19
  64. package/dist/DateRangeInput/DateRangeInput.d.ts +10 -2
  65. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  66. package/dist/DateRangeInput/DateRangeInput.js +23 -20
  67. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  68. package/dist/DateTimeInput/DateTimeInput.js +23 -18
  69. package/dist/Divider/Divider.d.ts +2 -2
  70. package/dist/Divider/Divider.d.ts.map +1 -1
  71. package/dist/Divider/Divider.js +13 -0
  72. package/dist/DropdownMenu/DropdownMenu.d.ts +6 -0
  73. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  74. package/dist/DropdownMenu/DropdownMenu.js +22 -5
  75. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  76. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +4 -1
  77. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  78. package/dist/DropdownMenu/DropdownMenuItem.js +5 -2
  79. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +8 -11
  80. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
  81. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +3 -1
  82. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +1 -1
  83. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  84. package/dist/DropdownMenu/DropdownMenuRadioItem.js +5 -2
  85. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +104 -0
  86. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -0
  87. package/dist/DropdownMenu/DropdownMenuSubMenu.js +435 -0
  88. package/dist/DropdownMenu/index.d.ts +10 -0
  89. package/dist/DropdownMenu/index.d.ts.map +1 -1
  90. package/dist/DropdownMenu/index.js +13 -0
  91. package/dist/DropdownMenu/menuItemHover.d.ts +24 -0
  92. package/dist/DropdownMenu/menuItemHover.d.ts.map +1 -0
  93. package/dist/DropdownMenu/menuItemHover.js +34 -0
  94. package/dist/DropdownMenu/menuItemRoles.d.ts +8 -0
  95. package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -1
  96. package/dist/DropdownMenu/menuItemRoles.js +10 -1
  97. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  98. package/dist/DropdownMenu/renderDropdownItems.js +21 -6
  99. package/dist/Field/Field.d.ts +3 -1
  100. package/dist/Field/Field.d.ts.map +1 -1
  101. package/dist/Field/Field.js +4 -1
  102. package/dist/Field/FieldLabel.d.ts +2 -1
  103. package/dist/Field/FieldLabel.d.ts.map +1 -1
  104. package/dist/Field/FieldLabel.js +9 -5
  105. package/dist/Field/inputStyles.stylex.d.ts +1 -0
  106. package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
  107. package/dist/Field/inputStyles.stylex.js +1 -0
  108. package/dist/FieldStatus/FieldStatus.d.ts +10 -0
  109. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  110. package/dist/FieldStatus/FieldStatus.js +51 -5
  111. package/dist/FileInput/FileInput.d.ts +10 -2
  112. package/dist/FileInput/FileInput.d.ts.map +1 -1
  113. package/dist/FileInput/FileInput.js +93 -65
  114. package/dist/Heading/Heading.d.ts.map +1 -1
  115. package/dist/Heading/Heading.js +3 -2
  116. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  117. package/dist/HoverCard/useHoverCard.js +10 -4
  118. package/dist/Icon/Icon.d.ts +15 -1
  119. package/dist/Icon/Icon.d.ts.map +1 -1
  120. package/dist/Icon/Icon.js +19 -3
  121. package/dist/Item/Item.d.ts +16 -3
  122. package/dist/Item/Item.d.ts.map +1 -1
  123. package/dist/Item/Item.js +31 -6
  124. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  125. package/dist/Layer/layerAnimations.stylex.js +10 -2
  126. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  127. package/dist/Lightbox/Lightbox.js +34 -15
  128. package/dist/List/List.d.ts.map +1 -1
  129. package/dist/List/List.js +6 -3
  130. package/dist/List/ListItem.d.ts +11 -1
  131. package/dist/List/ListItem.d.ts.map +1 -1
  132. package/dist/List/ListItem.js +2 -0
  133. package/dist/Markdown/Markdown.d.ts.map +1 -1
  134. package/dist/Markdown/Markdown.js +7 -0
  135. package/dist/MetadataList/MetadataList.d.ts +1 -1
  136. package/dist/MetadataList/MetadataList.d.ts.map +1 -1
  137. package/dist/MetadataList/MetadataList.js +4 -2
  138. package/dist/MultiSelector/MultiSelector.d.ts +9 -1
  139. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  140. package/dist/MultiSelector/MultiSelector.js +81 -43
  141. package/dist/NavMenu/NavHeadingMenuItem.js +1 -1
  142. package/dist/NumberInput/NumberInput.d.ts +10 -2
  143. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  144. package/dist/NumberInput/NumberInput.js +22 -17
  145. package/dist/Outline/Outline.js +2 -2
  146. package/dist/Pagination/Pagination.d.ts.map +1 -1
  147. package/dist/Pagination/Pagination.js +11 -3
  148. package/dist/PowerSearch/PowerSearch.d.ts +9 -2
  149. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  150. package/dist/PowerSearch/PowerSearch.js +2 -0
  151. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  152. package/dist/ProgressBar/ProgressBar.js +6 -1
  153. package/dist/RadioList/RadioList.d.ts +2 -2
  154. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  155. package/dist/RadioList/RadioListItem.js +5 -5
  156. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  157. package/dist/Resizable/ResizeHandle.js +49 -19
  158. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
  159. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  160. package/dist/SegmentedControl/SegmentedControlItem.js +5 -3
  161. package/dist/Selector/Selector.d.ts +8 -0
  162. package/dist/Selector/Selector.d.ts.map +1 -1
  163. package/dist/Selector/Selector.js +66 -17
  164. package/dist/SideNav/SideNav.d.ts.map +1 -1
  165. package/dist/SideNav/SideNav.js +5 -5
  166. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  167. package/dist/SideNav/SideNavCollapseButton.js +13 -10
  168. package/dist/SideNav/SideNavItem.d.ts +1 -1
  169. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  170. package/dist/SideNav/SideNavItem.js +12 -10
  171. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  172. package/dist/Skeleton/Skeleton.js +2 -2
  173. package/dist/Slider/Slider.d.ts.map +1 -1
  174. package/dist/Slider/Slider.js +87 -34
  175. package/dist/Spinner/Spinner.d.ts +3 -2
  176. package/dist/Spinner/Spinner.d.ts.map +1 -1
  177. package/dist/Spinner/Spinner.js +11 -2
  178. package/dist/Switch/Switch.d.ts +8 -1
  179. package/dist/Switch/Switch.d.ts.map +1 -1
  180. package/dist/Switch/Switch.js +171 -91
  181. package/dist/TabList/Tab.js +2 -2
  182. package/dist/TabList/TabList.d.ts +2 -11
  183. package/dist/TabList/TabList.d.ts.map +1 -1
  184. package/dist/TabList/TabList.js +5 -9
  185. package/dist/TabList/TabListContext.d.ts +0 -5
  186. package/dist/TabList/TabListContext.d.ts.map +1 -1
  187. package/dist/TabList/TabListContext.js +0 -5
  188. package/dist/TabList/TabMenu.js +1 -1
  189. package/dist/Table/BaseTable.d.ts.map +1 -1
  190. package/dist/Table/BaseTable.js +30 -4
  191. package/dist/Table/TableCell.js +3 -3
  192. package/dist/Table/TableHeaderCell.js +3 -3
  193. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  194. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +15 -11
  195. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  196. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +37 -27
  197. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  198. package/dist/Table/plugins/selection/useTableSelection.js +3 -0
  199. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  200. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +15 -7
  201. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  202. package/dist/Table/plugins/tree/useTableTreeData.js +29 -23
  203. package/dist/Table/types.d.ts +32 -0
  204. package/dist/Table/types.d.ts.map +1 -1
  205. package/dist/Text/Text.d.ts +10 -1
  206. package/dist/Text/Text.d.ts.map +1 -1
  207. package/dist/Text/Text.js +25 -2
  208. package/dist/Text/index.d.ts +2 -2
  209. package/dist/Text/index.d.ts.map +1 -1
  210. package/dist/TextArea/TextArea.d.ts +10 -1
  211. package/dist/TextArea/TextArea.d.ts.map +1 -1
  212. package/dist/TextArea/TextArea.js +119 -52
  213. package/dist/TextInput/TextInput.d.ts +10 -2
  214. package/dist/TextInput/TextInput.d.ts.map +1 -1
  215. package/dist/TextInput/TextInput.js +20 -17
  216. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  217. package/dist/Thumbnail/Thumbnail.js +51 -45
  218. package/dist/TimeInput/TimeInput.d.ts +10 -2
  219. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  220. package/dist/TimeInput/TimeInput.js +17 -17
  221. package/dist/Timestamp/Timestamp.d.ts +31 -3
  222. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  223. package/dist/Timestamp/Timestamp.js +48 -77
  224. package/dist/Timestamp/formatInstant.d.ts +41 -0
  225. package/dist/Timestamp/formatInstant.d.ts.map +1 -0
  226. package/dist/Timestamp/formatInstant.js +177 -0
  227. package/dist/Timestamp/index.d.ts +1 -0
  228. package/dist/Timestamp/index.d.ts.map +1 -1
  229. package/dist/Timestamp/tooltipEntries.d.ts +56 -0
  230. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -0
  231. package/dist/Timestamp/tooltipEntries.js +163 -0
  232. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  233. package/dist/Toast/ToastViewport.js +61 -9
  234. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  235. package/dist/ToggleButton/ToggleButton.js +3 -1
  236. package/dist/Token/Token.d.ts +34 -4
  237. package/dist/Token/Token.d.ts.map +1 -1
  238. package/dist/Token/Token.js +85 -45
  239. package/dist/Token/TokenLink.d.ts +62 -0
  240. package/dist/Token/TokenLink.d.ts.map +1 -0
  241. package/dist/Token/TokenLink.js +76 -0
  242. package/dist/Token/index.d.ts +1 -1
  243. package/dist/Token/index.d.ts.map +1 -1
  244. package/dist/Tokenizer/Tokenizer.d.ts +9 -2
  245. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  246. package/dist/Tokenizer/Tokenizer.js +4 -2
  247. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  248. package/dist/Tooltip/useTooltip.js +17 -1
  249. package/dist/TopNav/TopNavItem.d.ts +3 -1
  250. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  251. package/dist/TopNav/TopNavItem.js +30 -3
  252. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  253. package/dist/TopNav/TopNavMegaMenu.js +9 -3
  254. package/dist/TreeList/TreeList.d.ts.map +1 -1
  255. package/dist/TreeList/TreeList.js +1 -0
  256. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  257. package/dist/TreeList/TreeListBranches.js +8 -6
  258. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  259. package/dist/TreeList/TreeListItem.js +32 -14
  260. package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
  261. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  262. package/dist/Typeahead/BaseTypeahead.js +22 -11
  263. package/dist/Typeahead/Typeahead.d.ts +9 -2
  264. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  265. package/dist/Typeahead/Typeahead.js +11 -2
  266. package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
  267. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  268. package/dist/Typeahead/TypeaheadItem.js +20 -10
  269. package/dist/astryx.css +114 -35
  270. package/dist/astryx.umd.js +50 -50
  271. package/dist/astryx.umd.js.map +4 -4
  272. package/dist/hooks/containerReveal.pool.stylex.d.ts +281 -0
  273. package/dist/hooks/containerReveal.pool.stylex.d.ts.map +1 -0
  274. package/dist/hooks/containerReveal.pool.stylex.js +281 -0
  275. package/dist/hooks/index.d.ts +4 -0
  276. package/dist/hooks/index.d.ts.map +1 -1
  277. package/dist/hooks/index.js +3 -1
  278. package/dist/hooks/isRtlElement.d.ts +18 -0
  279. package/dist/hooks/isRtlElement.d.ts.map +1 -0
  280. package/dist/hooks/isRtlElement.js +27 -0
  281. package/dist/hooks/useAnnounce.d.ts +3 -0
  282. package/dist/hooks/useAnnounce.d.ts.map +1 -1
  283. package/dist/hooks/useAnnounce.js +39 -1
  284. package/dist/hooks/useContainerReveal.d.ts +83 -0
  285. package/dist/hooks/useContainerReveal.d.ts.map +1 -0
  286. package/dist/hooks/useContainerReveal.js +123 -0
  287. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  288. package/dist/hooks/useFocusTrap.js +52 -11
  289. package/dist/hooks/useGridFocus.d.ts +6 -1
  290. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  291. package/dist/hooks/useGridFocus.js +12 -9
  292. package/dist/hooks/useHotkeys.d.ts +9 -0
  293. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  294. package/dist/hooks/useHotkeys.js +9 -0
  295. package/dist/hooks/useInputStatusIcon.d.ts +38 -0
  296. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -0
  297. package/dist/hooks/useInputStatusIcon.js +169 -0
  298. package/dist/hooks/useKeyboardHint.d.ts +1 -1
  299. package/dist/hooks/useKeyboardHint.d.ts.map +1 -1
  300. package/dist/hooks/useKeyboardHint.js +4 -2
  301. package/dist/hooks/useListFocus.d.ts +39 -1
  302. package/dist/hooks/useListFocus.d.ts.map +1 -1
  303. package/dist/hooks/useListFocus.js +54 -9
  304. package/dist/i18n/InternationalizationContext.d.ts +1 -0
  305. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  306. package/dist/i18n/InternationalizationContext.js +3 -0
  307. package/dist/i18n/InternationalizationProvider.d.ts +8 -1
  308. package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
  309. package/dist/i18n/InternationalizationProvider.js +5 -1
  310. package/dist/i18n/getLocaleDirection.d.ts +18 -0
  311. package/dist/i18n/getLocaleDirection.d.ts.map +1 -0
  312. package/dist/i18n/getLocaleDirection.js +29 -0
  313. package/dist/i18n/index.d.ts +6 -0
  314. package/dist/i18n/index.d.ts.map +1 -1
  315. package/dist/i18n/index.js +7 -1
  316. package/dist/i18n/useDirection.d.ts +2 -0
  317. package/dist/i18n/useDirection.d.ts.map +1 -0
  318. package/dist/i18n/useDirection.js +27 -0
  319. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  320. package/dist/theme/derivedVarRegistry.js +13 -0
  321. package/dist/theme/index.d.ts +3 -3
  322. package/dist/theme/index.d.ts.map +1 -1
  323. package/dist/theme/types.d.ts +39 -2
  324. package/dist/theme/types.d.ts.map +1 -1
  325. package/dist/utils/index.d.ts +1 -0
  326. package/dist/utils/index.d.ts.map +1 -1
  327. package/dist/utils/index.js +2 -1
  328. package/dist/utils/plainDate.d.ts +17 -0
  329. package/dist/utils/plainDate.d.ts.map +1 -1
  330. package/dist/utils/plainDate.js +23 -4
  331. package/dist/utils/rtlStyles.d.ts +24 -0
  332. package/dist/utils/rtlStyles.d.ts.map +1 -0
  333. package/dist/utils/rtlStyles.js +27 -0
  334. package/locales/en.json +120 -0
  335. package/locales/pseudo.json +90 -0
  336. package/package.json +7 -2
  337. package/src/Avatar/Avatar.doc.mjs +50 -3
  338. package/src/Avatar/Avatar.test.tsx +218 -0
  339. package/src/Avatar/Avatar.tsx +285 -79
  340. package/src/Avatar/AvatarStatusDot.doc.mjs +4 -4
  341. package/src/Avatar/AvatarStatusDot.tsx +6 -4
  342. package/src/AvatarGroup/AvatarGroup.doc.mjs +2 -0
  343. package/src/AvatarGroup/AvatarGroup.test.tsx +162 -2
  344. package/src/AvatarGroup/AvatarGroup.tsx +58 -3
  345. package/src/AvatarGroup/AvatarGroupOverflow.tsx +5 -2
  346. package/src/Banner/Banner.tsx +6 -6
  347. package/src/Breadcrumbs/BreadcrumbItem.tsx +10 -10
  348. package/src/Breadcrumbs/Breadcrumbs.test.tsx +52 -1
  349. package/src/Breadcrumbs/index.ts +4 -0
  350. package/src/Button/Button.doc.mjs +0 -4
  351. package/src/Button/Button.test.tsx +16 -0
  352. package/src/Button/Button.tsx +6 -4
  353. package/src/Calendar/Calendar.test.tsx +170 -20
  354. package/src/Calendar/Calendar.tsx +73 -16
  355. package/src/Calendar/dayCellUtils.test.ts +18 -18
  356. package/src/Calendar/dayCellUtils.ts +2 -2
  357. package/src/Calendar/styles.ts +33 -39
  358. package/src/Card/Card.tsx +1 -1
  359. package/src/Carousel/Carousel.tsx +25 -6
  360. package/src/Chat/ChatComposer.doc.mjs +2 -2
  361. package/src/Chat/ChatComposerInput.doc.mjs +1 -1
  362. package/src/Chat/ChatComposerInput.tsx +2 -2
  363. package/src/Chat/ChatDictationButton.tsx +7 -1
  364. package/src/Chat/ChatLayout.tsx +4 -4
  365. package/src/Chat/ChatMessageBubble.tsx +15 -10
  366. package/src/Chat/ChatSystemMessage.test.tsx +5 -0
  367. package/src/Chat/ChatToolCalls.tsx +9 -2
  368. package/src/Chat/useTriggerMenu.tsx +1 -1
  369. package/src/CheckboxInput/CheckboxInput.doc.mjs +6 -0
  370. package/src/CheckboxInput/CheckboxInput.test.tsx +23 -0
  371. package/src/CheckboxInput/CheckboxInput.tsx +21 -6
  372. package/src/CheckboxList/CheckboxList.doc.mjs +6 -0
  373. package/src/CheckboxList/CheckboxList.test.tsx +94 -3
  374. package/src/CheckboxList/CheckboxListItem.tsx +17 -10
  375. package/src/Citation/Citation.doc.mjs +4 -3
  376. package/src/Citation/Citation.test.tsx +97 -0
  377. package/src/Citation/Citation.tsx +43 -11
  378. package/src/CodeBlock/CodeBlock.test.tsx +17 -0
  379. package/src/CodeBlock/CodeBlock.tsx +38 -11
  380. package/src/Collapsible/Collapsible.doc.mjs +1 -0
  381. package/src/Collapsible/Collapsible.test.tsx +7 -0
  382. package/src/Collapsible/Collapsible.tsx +16 -6
  383. package/src/CommandPalette/CommandPalette.test.tsx +95 -1
  384. package/src/CommandPalette/CommandPalette.tsx +41 -2
  385. package/src/CommandPalette/CommandPaletteItem.tsx +1 -1
  386. package/src/ContextMenu/ContextMenu.test.tsx +59 -2
  387. package/src/ContextMenu/ContextMenu.tsx +22 -11
  388. package/src/ContextMenu/index.ts +5 -0
  389. package/src/DateInput/DateInput.doc.mjs +25 -0
  390. package/src/DateInput/DateInput.test.tsx +230 -0
  391. package/src/DateInput/DateInput.tsx +51 -29
  392. package/src/DateRangeInput/DateRangeInput.doc.mjs +16 -0
  393. package/src/DateRangeInput/DateRangeInput.test.tsx +140 -0
  394. package/src/DateRangeInput/DateRangeInput.tsx +42 -30
  395. package/src/DateTimeInput/DateTimeInput.doc.mjs +22 -1
  396. package/src/DateTimeInput/DateTimeInput.test.tsx +112 -0
  397. package/src/DateTimeInput/DateTimeInput.tsx +44 -39
  398. package/src/Divider/Divider.test.tsx +32 -8
  399. package/src/Divider/Divider.tsx +14 -1
  400. package/src/DropdownMenu/DropdownMenu.test.tsx +60 -0
  401. package/src/DropdownMenu/DropdownMenu.tsx +31 -12
  402. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +8 -2
  403. package/src/DropdownMenu/DropdownMenuItem.tsx +9 -3
  404. package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +4 -12
  405. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +9 -10
  406. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +9 -3
  407. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +20 -2
  408. package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +88 -0
  409. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +663 -0
  410. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +561 -0
  411. package/src/DropdownMenu/index.ts +16 -0
  412. package/src/DropdownMenu/menuItemHover.ts +40 -0
  413. package/src/DropdownMenu/menuItemRoles.ts +9 -0
  414. package/src/DropdownMenu/renderDropdownItems.tsx +29 -15
  415. package/src/Field/Field.doc.mjs +4 -0
  416. package/src/Field/Field.tsx +14 -9
  417. package/src/Field/FieldLabel.test.tsx +25 -0
  418. package/src/Field/FieldLabel.tsx +12 -3
  419. package/src/Field/inputStyles.stylex.ts +2 -0
  420. package/src/FieldStatus/FieldStatus.doc.mjs +1 -0
  421. package/src/FieldStatus/FieldStatus.test.tsx +133 -0
  422. package/src/FieldStatus/FieldStatus.tsx +61 -4
  423. package/src/FileInput/FileInput.doc.mjs +23 -2
  424. package/src/FileInput/FileInput.test.tsx +104 -13
  425. package/src/FileInput/FileInput.tsx +106 -79
  426. package/src/Heading/Heading.tsx +2 -1
  427. package/src/HoverCard/useHoverCard.tsx +15 -3
  428. package/src/Icon/Icon.doc.mjs +13 -0
  429. package/src/Icon/Icon.test.tsx +89 -0
  430. package/src/Icon/Icon.tsx +51 -3
  431. package/src/Item/Item.doc.mjs +5 -0
  432. package/src/Item/Item.test.tsx +88 -0
  433. package/src/Item/Item.tsx +57 -11
  434. package/src/Layer/layerAnimations.stylex.ts +23 -0
  435. package/src/Lightbox/Lightbox.test.tsx +33 -0
  436. package/src/Lightbox/Lightbox.tsx +30 -10
  437. package/src/List/List.test.tsx +17 -29
  438. package/src/List/List.tsx +6 -1
  439. package/src/List/ListItem.doc.mjs +12 -0
  440. package/src/List/ListItem.tsx +13 -0
  441. package/src/Markdown/Markdown.test.tsx +21 -0
  442. package/src/Markdown/Markdown.tsx +4 -0
  443. package/src/MetadataList/MetadataList.test.tsx +24 -0
  444. package/src/MetadataList/MetadataList.tsx +6 -2
  445. package/src/MultiSelector/MultiSelector.doc.mjs +20 -0
  446. package/src/MultiSelector/MultiSelector.test.tsx +402 -0
  447. package/src/MultiSelector/MultiSelector.tsx +126 -64
  448. package/src/NavMenu/NavHeadingMenuItem.tsx +1 -1
  449. package/src/NumberInput/NumberInput.doc.mjs +21 -0
  450. package/src/NumberInput/NumberInput.test.tsx +71 -0
  451. package/src/NumberInput/NumberInput.tsx +35 -26
  452. package/src/Outline/Outline.test.tsx +12 -12
  453. package/src/Outline/Outline.tsx +2 -2
  454. package/src/Pagination/Pagination.tsx +11 -3
  455. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  456. package/src/PowerSearch/PowerSearch.test.tsx +23 -0
  457. package/src/PowerSearch/PowerSearch.tsx +10 -1
  458. package/src/ProgressBar/ProgressBar.test.tsx +34 -0
  459. package/src/ProgressBar/ProgressBar.tsx +16 -1
  460. package/src/RadioList/RadioList.doc.mjs +6 -0
  461. package/src/RadioList/RadioList.test.tsx +26 -4
  462. package/src/RadioList/RadioList.tsx +2 -2
  463. package/src/RadioList/RadioListItem.tsx +12 -5
  464. package/src/Resizable/ResizeHandle.test.tsx +94 -0
  465. package/src/Resizable/ResizeHandle.tsx +70 -20
  466. package/src/SegmentedControl/SegmentedControl.test.tsx +28 -0
  467. package/src/SegmentedControl/SegmentedControlItem.tsx +22 -2
  468. package/src/Selector/Selector.doc.mjs +15 -0
  469. package/src/Selector/Selector.test.tsx +370 -0
  470. package/src/Selector/Selector.tsx +88 -20
  471. package/src/SideNav/SideNav.tsx +1 -8
  472. package/src/SideNav/SideNavCollapseButton.tsx +9 -7
  473. package/src/SideNav/SideNavItem.tsx +5 -3
  474. package/src/Skeleton/Skeleton.test.tsx +16 -0
  475. package/src/Skeleton/Skeleton.tsx +13 -1
  476. package/src/Slider/Slider.doc.mjs +6 -0
  477. package/src/Slider/Slider.test.tsx +240 -12
  478. package/src/Slider/Slider.tsx +96 -23
  479. package/src/Spinner/Spinner.test.tsx +10 -8
  480. package/src/Spinner/Spinner.tsx +15 -5
  481. package/src/Switch/Switch.doc.mjs +142 -42
  482. package/src/Switch/Switch.test.tsx +111 -0
  483. package/src/Switch/Switch.tsx +149 -32
  484. package/src/TabList/Tab.tsx +2 -2
  485. package/src/TabList/TabList.doc.mjs +0 -6
  486. package/src/TabList/TabList.test.tsx +3 -15
  487. package/src/TabList/TabList.tsx +6 -17
  488. package/src/TabList/TabListContext.ts +0 -6
  489. package/src/TabList/TabMenu.tsx +2 -2
  490. package/src/Table/BaseTable.tsx +34 -2
  491. package/src/Table/Table.doc.mjs +11 -0
  492. package/src/Table/Table.test.tsx +85 -0
  493. package/src/Table/TableCell.tsx +3 -3
  494. package/src/Table/TableHeaderCell.tsx +3 -3
  495. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +9 -6
  496. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +19 -0
  497. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +27 -18
  498. package/src/Table/plugins/selection/useTableSelection.test.tsx +61 -3
  499. package/src/Table/plugins/selection/useTableSelection.tsx +3 -0
  500. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +38 -9
  501. package/src/Table/plugins/tree/useTableTreeData.tsx +17 -13
  502. package/src/Table/types.ts +33 -0
  503. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  504. package/src/Text/Heading.doc.mjs +3 -3
  505. package/src/Text/Text.doc.mjs +1 -1
  506. package/src/Text/Text.test.tsx +42 -0
  507. package/src/Text/Text.tsx +25 -1
  508. package/src/Text/index.ts +2 -6
  509. package/src/TextArea/TextArea.doc.mjs +23 -2
  510. package/src/TextArea/TextArea.test.tsx +132 -4
  511. package/src/TextArea/TextArea.tsx +161 -67
  512. package/src/TextInput/TextInput.doc.mjs +21 -0
  513. package/src/TextInput/TextInput.test.tsx +150 -0
  514. package/src/TextInput/TextInput.tsx +28 -25
  515. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  516. package/src/Thumbnail/Thumbnail.test.tsx +24 -1
  517. package/src/Thumbnail/Thumbnail.tsx +28 -35
  518. package/src/TimeInput/TimeInput.doc.mjs +21 -0
  519. package/src/TimeInput/TimeInput.test.tsx +23 -0
  520. package/src/TimeInput/TimeInput.tsx +24 -25
  521. package/src/Timestamp/Timestamp.doc.mjs +32 -6
  522. package/src/Timestamp/Timestamp.test.tsx +371 -0
  523. package/src/Timestamp/Timestamp.tsx +96 -93
  524. package/src/Timestamp/formatInstant.ts +218 -0
  525. package/src/Timestamp/index.ts +4 -0
  526. package/src/Timestamp/tooltipEntries.test.ts +552 -0
  527. package/src/Timestamp/tooltipEntries.ts +210 -0
  528. package/src/Toast/ToastViewport.test.tsx +218 -8
  529. package/src/Toast/ToastViewport.tsx +71 -12
  530. package/src/ToggleButton/ToggleButton.test.tsx +24 -0
  531. package/src/ToggleButton/ToggleButton.tsx +19 -1
  532. package/src/Token/Token.test.tsx +166 -15
  533. package/src/Token/Token.tsx +104 -47
  534. package/src/Token/TokenLink.tsx +107 -0
  535. package/src/Token/index.ts +1 -1
  536. package/src/Tokenizer/Tokenizer.doc.mjs +23 -1
  537. package/src/Tokenizer/Tokenizer.test.tsx +23 -0
  538. package/src/Tokenizer/Tokenizer.tsx +14 -2
  539. package/src/Tooltip/Tooltip.test.tsx +46 -0
  540. package/src/Tooltip/useTooltip.tsx +20 -0
  541. package/src/TopNav/TopNav.test.tsx +93 -2
  542. package/src/TopNav/TopNavItem.doc.mjs +3 -3
  543. package/src/TopNav/TopNavItem.tsx +35 -6
  544. package/src/TopNav/TopNavMegaMenu.tsx +26 -2
  545. package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +1 -1
  546. package/src/TreeList/TreeList.doc.mjs +6 -0
  547. package/src/TreeList/TreeList.test.tsx +50 -10
  548. package/src/TreeList/TreeList.tsx +5 -0
  549. package/src/TreeList/TreeListBranches.tsx +8 -6
  550. package/src/TreeList/TreeListItem.tsx +33 -11
  551. package/src/Typeahead/BaseTypeahead.tsx +42 -15
  552. package/src/Typeahead/Typeahead.doc.mjs +13 -0
  553. package/src/Typeahead/Typeahead.test.tsx +112 -0
  554. package/src/Typeahead/Typeahead.tsx +17 -1
  555. package/src/Typeahead/TypeaheadItem.tsx +2 -1
  556. package/src/__tests__/babelPluginAddExtensions.test.ts +190 -0
  557. package/src/__tests__/forcedColors.ts +60 -0
  558. package/src/__tests__/inputWidthContract.test.tsx +62 -0
  559. package/src/doc-mjs.d.ts +19 -0
  560. package/src/hooks/containerReveal.pool.stylex.ts +464 -0
  561. package/src/hooks/index.ts +13 -0
  562. package/src/hooks/isRtlElement.ts +27 -0
  563. package/src/hooks/useAnnounce.test.tsx +75 -2
  564. package/src/hooks/useAnnounce.ts +47 -1
  565. package/src/hooks/useContainerReveal.test.tsx +116 -0
  566. package/src/hooks/useContainerReveal.ts +180 -0
  567. package/src/hooks/useFocusTrap.test.tsx +104 -0
  568. package/src/hooks/useFocusTrap.ts +61 -12
  569. package/src/hooks/useGridFocus.doc.mjs +3 -3
  570. package/src/hooks/useGridFocus.test.tsx +41 -0
  571. package/src/hooks/useGridFocus.ts +21 -10
  572. package/src/hooks/useHotkeys.ts +9 -0
  573. package/src/hooks/useInputStatusIcon.test.tsx +221 -0
  574. package/src/hooks/useInputStatusIcon.tsx +235 -0
  575. package/src/hooks/useKeyboardHint.test.tsx +22 -0
  576. package/src/hooks/useKeyboardHint.tsx +6 -2
  577. package/src/hooks/useListFocus.doc.mjs +25 -3
  578. package/src/hooks/useListFocus.test.tsx +153 -1
  579. package/src/hooks/useListFocus.ts +102 -9
  580. package/src/i18n/InternationalizationContext.ts +4 -0
  581. package/src/i18n/InternationalizationProvider.tsx +12 -2
  582. package/src/i18n/__tests__/getLocaleDirection.test.ts +40 -0
  583. package/src/i18n/__tests__/useDirection.test.tsx +66 -0
  584. package/src/i18n/getLocaleDirection.ts +29 -0
  585. package/src/i18n/index.ts +6 -0
  586. package/src/i18n/useDirection.ts +29 -0
  587. package/src/reset.css +0 -26
  588. package/src/reset.test.ts +60 -0
  589. package/src/theme/derivedVarRegistry.test.ts +1 -0
  590. package/src/theme/derivedVarRegistry.ts +6 -0
  591. package/src/theme/generateThemeRules.test.ts +42 -0
  592. package/src/theme/index.ts +4 -8
  593. package/src/theme/types.ts +43 -13
  594. package/src/utils/index.ts +2 -0
  595. package/src/utils/plainDate.test.ts +24 -6
  596. package/src/utils/plainDate.ts +18 -4
  597. package/src/utils/rtlStyles.ts +27 -0
  598. package/dist/Thumbnail/thumbnail.markers.stylex.d.ts +0 -9
  599. package/dist/Thumbnail/thumbnail.markers.stylex.d.ts.map +0 -1
  600. package/dist/Thumbnail/thumbnail.markers.stylex.js +0 -14
  601. package/src/Thumbnail/thumbnail.markers.stylex.ts +0 -12
@@ -37,6 +37,7 @@ import {
37
37
  inputStatusBorderStyles,
38
38
  inputStatusHoverShadowStyles,
39
39
  inputWrapperStyles,
40
+ type FieldStatusVariant,
40
41
  } from '../Field';
41
42
  import {Divider} from '../Divider';
42
43
  import {Spinner} from '../Spinner';
@@ -118,11 +119,12 @@ const styles = stylex.create({
118
119
  lineHeight: 'inherit',
119
120
  color: 'inherit',
120
121
  cursor: 'pointer',
121
- outline: {
122
- default: 'none',
123
- ':focus-visible': `${borderVars['--border-width']} solid ${colorVars['--color-accent']}`,
124
- },
125
- outlineOffset: '0',
122
+ // The wrapper (inputWrapperStyles.base) renders the focus ring via
123
+ // :focus-within when this button is focused, matching
124
+ // TextInput/NumberInput/Selector. The button must not draw its own
125
+ // :focus-visible outline or the two stack into a doubled ring over the
126
+ // trigger.
127
+ outline: 'none',
126
128
  borderRadius: radiusVars['--radius-element'],
127
129
  },
128
130
  triggerPlaceholder: {
@@ -479,6 +481,13 @@ export interface MultiSelectorProps<
479
481
  * Status indicator for the selector.
480
482
  */
481
483
  status?: MultiSelectorStatus;
484
+ /**
485
+ * How the status message is placed relative to the input.
486
+ * - 'attached': message overlaps directly below the input (bordered treatment)
487
+ * - 'detached': message floats below as a separate element with spacing
488
+ * @default 'attached'
489
+ */
490
+ statusVariant?: FieldStatusVariant;
482
491
 
483
492
  /**
484
493
  * Width of the field. Numbers are treated as pixels, strings are used as-is
@@ -562,6 +571,21 @@ export interface MultiSelectorProps<
562
571
  'data-testid'?: string;
563
572
  }
564
573
 
574
+ // Case-insensitive substring match for a single option. The one predicate used
575
+ // by both the flat filter (count + keyboard nav) and the grouped renderer, so
576
+ // what is shown while searching stays in lockstep with the announced count.
577
+ function optionMatchesQuery(
578
+ option: MultiSelectorOptionData,
579
+ query: string,
580
+ ): boolean {
581
+ if (!query) {
582
+ return true;
583
+ }
584
+ return (option.label ?? option.value)
585
+ .toLowerCase()
586
+ .includes(query.toLowerCase());
587
+ }
588
+
565
589
  // Case-insensitive substring filter over the selectable options. Shared by the
566
590
  // `filteredItems` memo (rendering) and the search-change handler, which needs
567
591
  // the count for the *next* query synchronously to announce it exactly once per
@@ -573,10 +597,7 @@ function filterOptionsByQuery(
573
597
  if (!query) {
574
598
  return items;
575
599
  }
576
- const q = query.toLowerCase();
577
- return items.filter(item =>
578
- (item.label ?? item.value).toLowerCase().includes(q),
579
- );
600
+ return items.filter(item => optionMatchesQuery(item, query));
580
601
  }
581
602
 
582
603
  /**
@@ -610,6 +631,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
610
631
  placeholder: placeholderFromProps,
611
632
  size: sizeProp,
612
633
  status,
634
+ statusVariant = 'attached',
613
635
  labelTooltip,
614
636
  startIcon,
615
637
  hasClear = false,
@@ -716,35 +738,25 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
716
738
 
717
739
  // Single source of truth for item order. Both the hook (keyboard navigation)
718
740
  // and renderOptions (DOM rendering) consume this list — no independent sorting.
719
- // Selected-at-open items are placed first within each group/section.
741
+ // Selected-at-open items are placed first within each group/section, and the
742
+ // same walk applies while searching so group structure survives filtering
743
+ // (only matching items are kept; the query is empty in non-search mode).
720
744
  const sortedItems = useMemo(() => {
721
745
  const selectedSet = selectedAtOpen ?? new Set<string>();
722
- if (searchQuery) {
723
- const selected = filteredItems.filter(item =>
724
- selectedSet.has(item.value),
725
- );
726
- const unselected = filteredItems.filter(
727
- item => !selectedSet.has(item.value),
728
- );
729
- const items = [...selected, ...unselected];
730
- if (hasSelectAll) {
731
- return [{value: SELECT_ALL_VALUE, label: selectAllLabel}, ...items];
732
- }
733
- return items;
734
- }
735
- // For non-search mode, flatten options in the same order as renderOptions
736
746
  const result: MultiSelectorOptionData[] = [];
737
747
  let pendingFlat: MultiSelectorOptionData[] = [];
738
748
 
749
+ const orderSelectedFirst = (items: MultiSelectorOptionData[]) => {
750
+ const selected = items.filter(item => selectedSet.has(item.value));
751
+ const unselected = items.filter(item => !selectedSet.has(item.value));
752
+ return [...selected, ...unselected];
753
+ };
754
+
739
755
  const flushFlat = () => {
740
756
  if (pendingFlat.length === 0) {
741
757
  return;
742
758
  }
743
- const selected = pendingFlat.filter(item => selectedSet.has(item.value));
744
- const unselected = pendingFlat.filter(
745
- item => !selectedSet.has(item.value),
746
- );
747
- result.push(...selected, ...unselected);
759
+ result.push(...orderSelectedFirst(pendingFlat));
748
760
  pendingFlat = [];
749
761
  };
750
762
 
@@ -753,16 +765,15 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
753
765
  flushFlat();
754
766
  } else if (isSection(option)) {
755
767
  flushFlat();
756
- const sectionOptions = option.options.map(opt => normalizeOption(opt));
757
- const selected = sectionOptions.filter(item =>
758
- selectedSet.has(item.value),
759
- );
760
- const unselected = sectionOptions.filter(
761
- item => !selectedSet.has(item.value),
762
- );
763
- result.push(...selected, ...unselected);
768
+ const sectionOptions = option.options
769
+ .map(opt => normalizeOption(opt))
770
+ .filter(opt => optionMatchesQuery(opt, searchQuery));
771
+ result.push(...orderSelectedFirst(sectionOptions));
764
772
  } else if (isOptionData(option)) {
765
- pendingFlat.push(normalizeOption(option));
773
+ const normalized = normalizeOption(option);
774
+ if (optionMatchesQuery(normalized, searchQuery)) {
775
+ pendingFlat.push(normalized);
776
+ }
766
777
  }
767
778
  }
768
779
  flushFlat();
@@ -771,14 +782,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
771
782
  return [{value: SELECT_ALL_VALUE, label: selectAllLabel}, ...result];
772
783
  }
773
784
  return result;
774
- }, [
775
- filteredItems,
776
- searchQuery,
777
- options,
778
- selectedAtOpen,
779
- hasSelectAll,
780
- selectAllLabel,
781
- ]);
785
+ }, [searchQuery, options, selectedAtOpen, hasSelectAll, selectAllLabel]);
782
786
 
783
787
  // Layer for dropdown positioning
784
788
  const handleLayerHide = useCallback(() => {
@@ -1126,6 +1130,11 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1126
1130
  ? allEnabledSelected
1127
1131
  : optimisticValue.includes(item.value);
1128
1132
  const checkboxValue = isSelectAll ? selectAllState : isSelected;
1133
+ // aria-selected="mixed" is invalid on role="option", and the tri-state
1134
+ // checkbox is inert/decorative, so the indeterminate state must be
1135
+ // conveyed through the option's accessible name (WCAG 4.1.2).
1136
+ const isPartiallySelected =
1137
+ isSelectAll && selectAllState === 'indeterminate';
1129
1138
 
1130
1139
  return (
1131
1140
  <div
@@ -1133,6 +1142,13 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1133
1142
  id={getItemId(flatIndex)}
1134
1143
  role="option"
1135
1144
  aria-selected={isSelected}
1145
+ aria-label={
1146
+ isPartiallySelected
1147
+ ? t('@astryx.multiSelector.selectAllPartiallySelected', {
1148
+ label: selectAllLabel,
1149
+ })
1150
+ : undefined
1151
+ }
1136
1152
  aria-disabled={item.disabled}
1137
1153
  onClick={() => {
1138
1154
  if (!item.disabled) {
@@ -1177,10 +1193,12 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1177
1193
  optimisticValue,
1178
1194
  allEnabledSelected,
1179
1195
  selectAllState,
1196
+ selectAllLabel,
1180
1197
  getItemId,
1181
1198
  handleNavigableToggle,
1182
1199
  onItemMouseEnter,
1183
1200
  size,
1201
+ t,
1184
1202
  ],
1185
1203
  );
1186
1204
 
@@ -1208,25 +1226,28 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1208
1226
  cursor = 1;
1209
1227
  }
1210
1228
 
1211
- // Empty state — no real items to show
1229
+ // Empty state — no real items to show. role="presentation" keeps the
1230
+ // message out of the listbox's accessibility tree (role="listbox" only
1231
+ // permits option/group children); the no-results outcome is announced
1232
+ // via the result-count live region instead.
1212
1233
  if (realItemCount === 0) {
1213
1234
  elements.push(
1214
- <div key="empty" {...stylex.props(styles.emptyState)}>
1235
+ <div
1236
+ key="empty"
1237
+ role="presentation"
1238
+ {...stylex.props(styles.emptyState)}>
1215
1239
  No results found
1216
1240
  </div>,
1217
1241
  );
1218
1242
  return elements;
1219
1243
  }
1220
1244
 
1221
- if (searchQuery) {
1222
- for (let i = cursor; i < sortedItems.length; i++) {
1223
- elements.push(renderItem(sortedItems[i], i));
1224
- }
1225
- return elements;
1226
- }
1227
-
1228
- // Non-search: consume items from sortedItems in order, interleaving
1229
- // structural elements (dividers, section headers) from the options prop.
1245
+ // Consume items from sortedItems in order, interleaving structural elements
1246
+ // (dividers, section headers) from the options prop. While searching, only
1247
+ // matching items are present in sortedItems, so a section consumes just its
1248
+ // matches and is skipped entirely when none match — no header left standing
1249
+ // over nothing, and the cursor stays aligned with the combobox indices.
1250
+ const isSearching = Boolean(searchQuery);
1230
1251
  let pendingCount = 0;
1231
1252
 
1232
1253
  const flushPending = () => {
@@ -1242,12 +1263,25 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1242
1263
 
1243
1264
  if (isDivider(option)) {
1244
1265
  flushPending();
1245
- elements.push(<Divider key={`divider-${i}`} xstyle={styles.divider} />);
1266
+ // A standalone divider between groups would orphan itself once its
1267
+ // neighbors are filtered out, so skip it while searching.
1268
+ if (!isSearching) {
1269
+ elements.push(
1270
+ <Divider key={`divider-${i}`} xstyle={styles.divider} />,
1271
+ );
1272
+ }
1246
1273
  } else if (isSection(option)) {
1247
1274
  flushPending();
1248
- const count = option.options.length;
1275
+ const matchCount = isSearching
1276
+ ? option.options.filter(opt =>
1277
+ optionMatchesQuery(normalizeOption(opt), searchQuery),
1278
+ ).length
1279
+ : option.options.length;
1280
+ if (matchCount === 0) {
1281
+ continue;
1282
+ }
1249
1283
  const sectionItems: ReactNode[] = [];
1250
- for (let j = 0; j < count; j++) {
1284
+ for (let j = 0; j < matchCount; j++) {
1251
1285
  sectionItems.push(renderItem(sortedItems[cursor], cursor));
1252
1286
  cursor++;
1253
1287
  }
@@ -1266,7 +1300,12 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1266
1300
  </div>,
1267
1301
  );
1268
1302
  } else if (isOptionData(option)) {
1269
- pendingCount++;
1303
+ if (
1304
+ !isSearching ||
1305
+ optionMatchesQuery(normalizeOption(option), searchQuery)
1306
+ ) {
1307
+ pendingCount++;
1308
+ }
1270
1309
  }
1271
1310
  }
1272
1311
  flushPending();
@@ -1295,7 +1334,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1295
1334
  isDisabled && inputWrapperStyles.disabled,
1296
1335
  optimisticValue.length === 0 && styles.triggerPlaceholder,
1297
1336
  status && inputStatusBorderStyles[status.type],
1298
- status && inputStatusHoverShadowStyles[status.type],
1337
+ status && !isDisabled && inputStatusHoverShadowStyles[status.type],
1299
1338
  inputGroup && groupStyles.inGroup,
1300
1339
  xstyle,
1301
1340
  ),
@@ -1359,7 +1398,17 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1359
1398
  onClick={handleClear}
1360
1399
  aria-label={t('@astryx.multiSelector.clearAll', {label})}
1361
1400
  {...stylex.props(styles.clearButton)}>
1362
- <Icon icon="close" size="sm" color="secondary" />
1401
+ <Icon
1402
+ icon="close"
1403
+ size="sm"
1404
+ color="secondary"
1405
+ // Stable theme target on the clear glyph itself, so a theme can
1406
+ // restyle just this icon (color, size, hover) via `defineTheme`.
1407
+ // Same-element rules in @layer astryx-theme win over the icon's
1408
+ // own base color/size, which a button-level target could not
1409
+ // reach.
1410
+ {...themeProps('multi-selector-clear-icon')}
1411
+ />
1363
1412
  </button>
1364
1413
  )}
1365
1414
  <span
@@ -1375,7 +1424,19 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1375
1424
  color={STATUS_ICON_COLOR_MAP[status.type]}
1376
1425
  />
1377
1426
  ) : (
1378
- <Icon icon="chevronDown" size="sm" color="inherit" />
1427
+ <Icon
1428
+ icon="chevronDown"
1429
+ size="sm"
1430
+ color="inherit"
1431
+ // Stable theme target on the chevron glyph itself, so a theme can
1432
+ // restyle just this icon (color, size, hover) — and its
1433
+ // open/closed state — via `defineTheme`. Same-element rules in
1434
+ // @layer astryx-theme win over the icon's own base color/size,
1435
+ // which a button-level target could not reach.
1436
+ {...themeProps('multi-selector-indicator-icon', {
1437
+ state: popover.isOpen ? 'expanded' : 'collapsed',
1438
+ })}
1439
+ />
1379
1440
  )}
1380
1441
  </span>
1381
1442
  </div>
@@ -1426,6 +1487,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1426
1487
  }
1427
1488
  : undefined
1428
1489
  }
1490
+ statusVariant={statusVariant}
1429
1491
  labelTooltip={labelTooltip}
1430
1492
  width={width}>
1431
1493
  {multiSelectorContent}
@@ -49,7 +49,7 @@ const styles = stylex.create({
49
49
  },
50
50
  border: 'none',
51
51
  cursor: 'pointer',
52
- textAlign: 'left',
52
+ textAlign: 'start',
53
53
  outline: 'none',
54
54
  textDecoration: 'none',
55
55
  },
@@ -94,6 +94,13 @@ export const docs = {
94
94
  type: "{type: 'error' | 'warning' | 'success', message?: string}",
95
95
  description: 'Validation status with optional message.',
96
96
  },
97
+ {
98
+ name: 'statusVariant',
99
+ type: "'attached' | 'detached' | 'tooltip'",
100
+ description:
101
+ 'How the status message is placed relative to the input. attached overlaps directly below the input (bordered treatment); detached floats below as a separate element with spacing; tooltip hides the message box and surfaces it in a tooltip on the status icon.',
102
+ default: "'attached'",
103
+ },
97
104
  {
98
105
  name: 'min',
99
106
  type: 'number | null',
@@ -138,6 +145,12 @@ export const docs = {
138
145
  type: 'string',
139
146
  description: 'HTML autocomplete attribute.',
140
147
  },
148
+ {
149
+ name: 'width',
150
+ type: 'SizeValue',
151
+ description:
152
+ 'Width of the field (number = pixels, string used as-is, e.g. "100%"). Sizes the whole field (label, control, and status) so they stay aligned.',
153
+ },
141
154
  {
142
155
  name: 'hasAutoFocus',
143
156
  type: 'boolean',
@@ -275,6 +288,13 @@ export const docsZh = {
275
288
  type: "{type: 'error' | 'warning' | 'success', message?: string}",
276
289
  description: '带可选消息的验证状态。',
277
290
  },
291
+ {
292
+ name: 'statusVariant',
293
+ type: "'attached' | 'detached' | 'tooltip'",
294
+ description:
295
+ '状态消息相对于输入框的放置方式。attached 直接叠加在输入框下方(带边框处理);detached 作为独立元素浮于下方并留有间距;tooltip 隐藏消息框,并在状态图标上以提示气泡形式显示。',
296
+ default: "'attached'",
297
+ },
278
298
  {
279
299
  name: 'min',
280
300
  type: 'number | null',
@@ -402,6 +422,7 @@ export const docsDense = {
402
422
  startIcon: 'Icon at input start.',
403
423
  labelIcon: 'Icon before label text.',
404
424
  status: 'Validation status w/ optional message.',
425
+ statusVariant: 'How status message is placed: attached overlaps below input; detached floats below w/ spacing; tooltip hides the box and shows it on the status icon.',
405
426
  min: 'Minimum value allowed.',
406
427
  max: 'Maximum value allowed.',
407
428
  step: 'Step increment.',
@@ -13,6 +13,7 @@ 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
+ import {InputGroup} from '../InputGroup';
16
17
  import {NumberInput} from './NumberInput';
17
18
  import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
18
19
 
@@ -336,6 +337,33 @@ describe('NumberInput', () => {
336
337
  expect(screen.queryByText('%')).not.toBeInTheDocument();
337
338
  expect(screen.queryByText('GB')).not.toBeInTheDocument();
338
339
  });
340
+
341
+ it('includes the units text in the accessible description (WCAG 1.3.1)', () => {
342
+ render(
343
+ <NumberInput
344
+ label="Storage"
345
+ value={50}
346
+ onChange={() => {}}
347
+ units="GB"
348
+ />,
349
+ );
350
+ expect(screen.getByRole('spinbutton')).toHaveAccessibleDescription(/GB/);
351
+ });
352
+
353
+ it('combines units with the description in the accessible description', () => {
354
+ render(
355
+ <NumberInput
356
+ label="Discount"
357
+ value={10}
358
+ onChange={() => {}}
359
+ description="Applied at checkout"
360
+ units="%"
361
+ />,
362
+ );
363
+ const input = screen.getByRole('spinbutton');
364
+ expect(input).toHaveAccessibleDescription(/Applied at checkout/);
365
+ expect(input).toHaveAccessibleDescription(/%/);
366
+ });
339
367
  });
340
368
 
341
369
  describe('event callbacks', () => {
@@ -463,6 +491,26 @@ describe('NumberInput', () => {
463
491
  expect(screen.getByText('Value must be positive')).toBeInTheDocument();
464
492
  });
465
493
 
494
+ it('has no dangling aria-describedby ids inside InputGroup (WCAG 1.3.1)', () => {
495
+ // Inside an InputGroup no Field renders, so the status message element
496
+ // does not exist; aria-describedby must not reference its id.
497
+ render(
498
+ <InputGroup label="Price">
499
+ <NumberInput
500
+ label="Amount"
501
+ value={null}
502
+ onChange={() => {}}
503
+ status={{type: 'error', message: 'Value must be positive'}}
504
+ />
505
+ </InputGroup>,
506
+ );
507
+ const input = screen.getByRole('spinbutton');
508
+ const describedBy = input.getAttribute('aria-describedby') ?? '';
509
+ for (const idToken of describedBy.split(/\s+/).filter(Boolean)) {
510
+ expect(document.getElementById(idToken)).not.toBeNull();
511
+ }
512
+ });
513
+
466
514
  it('does not render status message when not provided', () => {
467
515
  render(
468
516
  <NumberInput
@@ -916,3 +964,26 @@ describe('keyboard clearing with hasClear (#3599)', () => {
916
964
  expect((input as HTMLInputElement).value).toBe('42');
917
965
  });
918
966
  });
967
+
968
+
969
+ describe('NumberInput statusVariant forwarding', () => {
970
+ it('defaults to attached (status renders with data-variant="attached")', () => {
971
+ const {container} = render(
972
+ <NumberInput label="Amount" value={null} onChange={() => {}} status={{type: 'error', message: 'Must be positive'}} />,
973
+ );
974
+ expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
975
+ 'data-variant',
976
+ 'attached',
977
+ );
978
+ });
979
+
980
+ it('forwards statusVariant="detached" to the underlying Field status', () => {
981
+ const {container} = render(
982
+ <NumberInput label="Amount" value={null} onChange={() => {}} status={{type: 'error', message: 'Must be positive'}} statusVariant="detached" />,
983
+ );
984
+ expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
985
+ 'data-variant',
986
+ 'detached',
987
+ );
988
+ });
989
+ });
@@ -27,7 +27,6 @@ import {
27
27
  type ReactNode,
28
28
  } from 'react';
29
29
  import * as stylex from '@stylexjs/stylex';
30
- import type {IconName} from '../Icon';
31
30
  import {
32
31
  colorVars,
33
32
  sizeVars,
@@ -39,11 +38,11 @@ import {
39
38
  import {
40
39
  Field,
41
40
  type InputStatus,
42
- type InputStatusType,
43
41
  inputWrapperStyles,
44
42
  inputStatusBorderStyles,
45
43
  inputStatusHoverShadowStyles,
46
44
  inputStatusFocusWithinStyles,
45
+ type FieldStatusVariant,
47
46
  } from '../Field';
48
47
  import {Icon, renderIconSlot, type IconType} from '../Icon';
49
48
  import {VisuallyHidden} from '../VisuallyHidden';
@@ -51,6 +50,7 @@ import {useTooltip} from '../Tooltip';
51
50
  import {getInputARIA} from '../utils';
52
51
  import {useSize} from '../SizeContext/SizeContext';
53
52
  import {useInputContainer} from '../hooks/useInputContainer';
53
+ import {useInputStatusIcon} from '../hooks/useInputStatusIcon';
54
54
  import {useInputGroup} from '../InputGroup/InputGroupContext';
55
55
 
56
56
  const styles = stylex.create({
@@ -207,6 +207,14 @@ interface NumberInputPropsBase extends Omit<
207
207
  * If message is provided, displays a floating message box below the input.
208
208
  */
209
209
  status?: InputStatus;
210
+ /**
211
+ * How the status message is placed relative to the input.
212
+ * - 'attached': message overlaps directly below the input (bordered treatment)
213
+ * - 'detached': message floats below as a separate element with spacing
214
+ * - 'tooltip': no message box; the status icon becomes a focusable info-tip button that reveals the message on hover, keyboard focus, or tap
215
+ * @default 'attached'
216
+ */
217
+ statusVariant?: FieldStatusVariant;
210
218
  /**
211
219
  * The size of the input.
212
220
  * - 'sm': Compact size (28px height)
@@ -374,6 +382,7 @@ export function NumberInput({
374
382
  startIcon,
375
383
  labelIcon,
376
384
  status,
385
+ statusVariant = 'attached',
377
386
  size: sizeProp,
378
387
  onChange,
379
388
  value,
@@ -405,6 +414,7 @@ export function NumberInput({
405
414
  const inputLabelID = useId();
406
415
  const descriptionID = useId();
407
416
  const statusMessageID = useId();
417
+ const unitsID = useId();
408
418
  const inputRef = useRef<HTMLInputElement | null>(null);
409
419
  const containerRef = useRef<HTMLDivElement | null>(null);
410
420
  const inputGroup = useInputGroup();
@@ -426,26 +436,26 @@ export function NumberInput({
426
436
  isEnabled: showsDisabledMessage,
427
437
  });
428
438
 
429
- const statusIconMap: Record<InputStatusType, IconName> = {
430
- warning: 'warning',
431
- error: 'error',
432
- success: 'success',
433
- };
434
-
435
- const statusIconColorMap: Record<
436
- InputStatusType,
437
- 'warning' | 'error' | 'success'
438
- > = {
439
- warning: 'warning',
440
- error: 'error',
441
- success: 'success',
442
- };
439
+ const {statusIcon, describedBy: statusTooltipDescribedBy} =
440
+ useInputStatusIcon({
441
+ status,
442
+ statusVariant,
443
+ isInGroup: !!inputGroup,
444
+ });
443
445
 
444
446
  const {ariaLabelledBy, ariaDescribedBy} = getInputARIA(
445
447
  inputLabelID,
446
448
  [
447
449
  description ? descriptionID : null,
448
- status?.message ? statusMessageID : null,
450
+ // The status message element is rendered by Field, which is skipped
451
+ // inside an InputGroup — only reference it when it actually exists.
452
+ !inputGroup && statusVariant !== 'tooltip' && status?.message
453
+ ? statusMessageID
454
+ : null,
455
+ // The tooltip variant renders no message box; describe the input by the
456
+ // tooltip's content instead so the status is still announced.
457
+ statusTooltipDescribedBy,
458
+ units ? unitsID : null,
449
459
  showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
450
460
  ],
451
461
  inputGroup,
@@ -605,7 +615,7 @@ export function NumberInput({
605
615
  sizeStyles[size],
606
616
  isDisabled && inputWrapperStyles.disabled,
607
617
  status && inputStatusBorderStyles[status.type],
608
- status && inputStatusHoverShadowStyles[status.type],
618
+ status && !isDisabled && inputStatusHoverShadowStyles[status.type],
609
619
  status && inputStatusFocusWithinStyles[status.type],
610
620
  inputGroup && groupStyles.inGroup,
611
621
  xstyle,
@@ -651,7 +661,11 @@ export function NumberInput({
651
661
  !isInputValid && styles.inputInvalid,
652
662
  )}
653
663
  />
654
- {units && <span {...stylex.props(styles.units)}>{units}</span>}
664
+ {units && (
665
+ <span id={unitsID} {...stylex.props(styles.units)}>
666
+ {units}
667
+ </span>
668
+ )}
655
669
  {/*
656
670
  Live region announcing invalid typed input to assistive technology.
657
671
  The value silently reverts on blur, so without this a screen-reader
@@ -669,13 +683,7 @@ export function NumberInput({
669
683
  <Icon icon="close" size="sm" color="secondary" />
670
684
  </button>
671
685
  )}
672
- {status && !inputGroup && (
673
- <Icon
674
- icon={statusIconMap[status.type]}
675
- size="md"
676
- color={statusIconColorMap[status.type]}
677
- />
678
- )}
686
+ {statusIcon}
679
687
  </div>
680
688
  );
681
689
 
@@ -709,6 +717,7 @@ export function NumberInput({
709
717
  }
710
718
  : undefined
711
719
  }
720
+ statusVariant={statusVariant}
712
721
  labelTooltip={labelTooltip}
713
722
  width={width}>
714
723
  {inputWrapper}