@astryxdesign/core 0.1.9 → 0.2.0-canary.136a00f

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 (560) 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 +427 -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 +23 -0
  92. package/dist/DropdownMenu/menuItemHover.d.ts.map +1 -0
  93. package/dist/DropdownMenu/menuItemHover.js +33 -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 +39 -3
  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/HoverCard/useHoverCard.d.ts.map +1 -1
  115. package/dist/HoverCard/useHoverCard.js +10 -4
  116. package/dist/Icon/Icon.d.ts +15 -1
  117. package/dist/Icon/Icon.d.ts.map +1 -1
  118. package/dist/Icon/Icon.js +19 -3
  119. package/dist/Item/Item.d.ts +16 -3
  120. package/dist/Item/Item.d.ts.map +1 -1
  121. package/dist/Item/Item.js +31 -6
  122. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  123. package/dist/Layer/layerAnimations.stylex.js +10 -2
  124. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  125. package/dist/Lightbox/Lightbox.js +19 -13
  126. package/dist/List/List.d.ts.map +1 -1
  127. package/dist/List/List.js +6 -3
  128. package/dist/List/ListItem.d.ts +11 -1
  129. package/dist/List/ListItem.d.ts.map +1 -1
  130. package/dist/List/ListItem.js +2 -0
  131. package/dist/Markdown/Markdown.d.ts.map +1 -1
  132. package/dist/Markdown/Markdown.js +7 -0
  133. package/dist/MetadataList/MetadataList.d.ts +1 -1
  134. package/dist/MetadataList/MetadataList.d.ts.map +1 -1
  135. package/dist/MetadataList/MetadataList.js +4 -2
  136. package/dist/MultiSelector/MultiSelector.d.ts +9 -1
  137. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  138. package/dist/MultiSelector/MultiSelector.js +80 -42
  139. package/dist/NavMenu/NavHeadingMenuItem.js +1 -1
  140. package/dist/NumberInput/NumberInput.d.ts +10 -2
  141. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  142. package/dist/NumberInput/NumberInput.js +22 -17
  143. package/dist/Outline/Outline.js +2 -2
  144. package/dist/Pagination/Pagination.d.ts.map +1 -1
  145. package/dist/Pagination/Pagination.js +11 -3
  146. package/dist/PowerSearch/PowerSearch.d.ts +9 -2
  147. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  148. package/dist/PowerSearch/PowerSearch.js +2 -0
  149. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  150. package/dist/ProgressBar/ProgressBar.js +6 -1
  151. package/dist/RadioList/RadioList.d.ts +2 -2
  152. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  153. package/dist/RadioList/RadioListItem.js +5 -5
  154. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  155. package/dist/Resizable/ResizeHandle.js +49 -19
  156. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
  157. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  158. package/dist/SegmentedControl/SegmentedControlItem.js +5 -3
  159. package/dist/Selector/Selector.d.ts +8 -0
  160. package/dist/Selector/Selector.d.ts.map +1 -1
  161. package/dist/Selector/Selector.js +66 -17
  162. package/dist/SideNav/SideNav.d.ts.map +1 -1
  163. package/dist/SideNav/SideNav.js +5 -5
  164. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  165. package/dist/SideNav/SideNavCollapseButton.js +13 -10
  166. package/dist/SideNav/SideNavItem.d.ts +1 -1
  167. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  168. package/dist/SideNav/SideNavItem.js +12 -10
  169. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  170. package/dist/Skeleton/Skeleton.js +2 -2
  171. package/dist/Slider/Slider.d.ts.map +1 -1
  172. package/dist/Slider/Slider.js +87 -34
  173. package/dist/Spinner/Spinner.d.ts +3 -2
  174. package/dist/Spinner/Spinner.d.ts.map +1 -1
  175. package/dist/Spinner/Spinner.js +11 -2
  176. package/dist/Switch/Switch.d.ts +8 -1
  177. package/dist/Switch/Switch.d.ts.map +1 -1
  178. package/dist/Switch/Switch.js +171 -91
  179. package/dist/TabList/Tab.js +2 -2
  180. package/dist/TabList/TabList.d.ts +2 -11
  181. package/dist/TabList/TabList.d.ts.map +1 -1
  182. package/dist/TabList/TabList.js +5 -9
  183. package/dist/TabList/TabListContext.d.ts +0 -5
  184. package/dist/TabList/TabListContext.d.ts.map +1 -1
  185. package/dist/TabList/TabListContext.js +0 -5
  186. package/dist/TabList/TabMenu.js +1 -1
  187. package/dist/Table/BaseTable.d.ts.map +1 -1
  188. package/dist/Table/BaseTable.js +30 -4
  189. package/dist/Table/TableCell.js +3 -3
  190. package/dist/Table/TableHeaderCell.js +3 -3
  191. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  192. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +15 -11
  193. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  194. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +37 -27
  195. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  196. package/dist/Table/plugins/selection/useTableSelection.js +3 -0
  197. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  198. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +15 -7
  199. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  200. package/dist/Table/plugins/tree/useTableTreeData.js +29 -23
  201. package/dist/Table/types.d.ts +32 -0
  202. package/dist/Table/types.d.ts.map +1 -1
  203. package/dist/TextArea/TextArea.d.ts +10 -1
  204. package/dist/TextArea/TextArea.d.ts.map +1 -1
  205. package/dist/TextArea/TextArea.js +18 -19
  206. package/dist/TextInput/TextInput.d.ts +10 -2
  207. package/dist/TextInput/TextInput.d.ts.map +1 -1
  208. package/dist/TextInput/TextInput.js +20 -17
  209. package/dist/Thumbnail/Thumbnail.js +2 -2
  210. package/dist/TimeInput/TimeInput.d.ts +10 -2
  211. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  212. package/dist/TimeInput/TimeInput.js +17 -17
  213. package/dist/Timestamp/Timestamp.d.ts +31 -3
  214. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  215. package/dist/Timestamp/Timestamp.js +47 -76
  216. package/dist/Timestamp/formatInstant.d.ts +41 -0
  217. package/dist/Timestamp/formatInstant.d.ts.map +1 -0
  218. package/dist/Timestamp/formatInstant.js +177 -0
  219. package/dist/Timestamp/index.d.ts +1 -0
  220. package/dist/Timestamp/index.d.ts.map +1 -1
  221. package/dist/Timestamp/tooltipEntries.d.ts +56 -0
  222. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -0
  223. package/dist/Timestamp/tooltipEntries.js +163 -0
  224. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  225. package/dist/Toast/ToastViewport.js +61 -9
  226. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  227. package/dist/ToggleButton/ToggleButton.js +3 -1
  228. package/dist/Token/Token.d.ts +34 -4
  229. package/dist/Token/Token.d.ts.map +1 -1
  230. package/dist/Token/Token.js +85 -45
  231. package/dist/Token/TokenLink.d.ts +62 -0
  232. package/dist/Token/TokenLink.d.ts.map +1 -0
  233. package/dist/Token/TokenLink.js +76 -0
  234. package/dist/Token/index.d.ts +1 -1
  235. package/dist/Token/index.d.ts.map +1 -1
  236. package/dist/Tokenizer/Tokenizer.d.ts +9 -2
  237. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  238. package/dist/Tokenizer/Tokenizer.js +4 -2
  239. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  240. package/dist/Tooltip/useTooltip.js +17 -1
  241. package/dist/TopNav/TopNavItem.d.ts +3 -1
  242. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  243. package/dist/TopNav/TopNavItem.js +30 -3
  244. package/dist/TreeList/TreeList.d.ts.map +1 -1
  245. package/dist/TreeList/TreeList.js +1 -0
  246. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  247. package/dist/TreeList/TreeListBranches.js +8 -6
  248. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  249. package/dist/TreeList/TreeListItem.js +32 -14
  250. package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
  251. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  252. package/dist/Typeahead/BaseTypeahead.js +22 -11
  253. package/dist/Typeahead/Typeahead.d.ts +9 -2
  254. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  255. package/dist/Typeahead/Typeahead.js +11 -2
  256. package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
  257. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  258. package/dist/Typeahead/TypeaheadItem.js +20 -10
  259. package/dist/astryx.css +70 -30
  260. package/dist/astryx.umd.js +51 -51
  261. package/dist/astryx.umd.js.map +4 -4
  262. package/dist/hooks/index.d.ts +2 -0
  263. package/dist/hooks/index.d.ts.map +1 -1
  264. package/dist/hooks/index.js +1 -0
  265. package/dist/hooks/isRtlElement.d.ts +18 -0
  266. package/dist/hooks/isRtlElement.d.ts.map +1 -0
  267. package/dist/hooks/isRtlElement.js +27 -0
  268. package/dist/hooks/useAnnounce.d.ts +3 -0
  269. package/dist/hooks/useAnnounce.d.ts.map +1 -1
  270. package/dist/hooks/useAnnounce.js +39 -1
  271. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  272. package/dist/hooks/useFocusTrap.js +52 -11
  273. package/dist/hooks/useGridFocus.d.ts +6 -1
  274. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  275. package/dist/hooks/useGridFocus.js +12 -9
  276. package/dist/hooks/useHotkeys.d.ts +9 -0
  277. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  278. package/dist/hooks/useHotkeys.js +9 -0
  279. package/dist/hooks/useInputStatusIcon.d.ts +38 -0
  280. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -0
  281. package/dist/hooks/useInputStatusIcon.js +158 -0
  282. package/dist/hooks/useKeyboardHint.d.ts +1 -1
  283. package/dist/hooks/useKeyboardHint.d.ts.map +1 -1
  284. package/dist/hooks/useKeyboardHint.js +4 -2
  285. package/dist/hooks/useListFocus.d.ts +39 -1
  286. package/dist/hooks/useListFocus.d.ts.map +1 -1
  287. package/dist/hooks/useListFocus.js +54 -9
  288. package/dist/i18n/InternationalizationContext.d.ts +1 -0
  289. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  290. package/dist/i18n/InternationalizationContext.js +3 -0
  291. package/dist/i18n/InternationalizationProvider.d.ts +8 -1
  292. package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
  293. package/dist/i18n/InternationalizationProvider.js +5 -1
  294. package/dist/i18n/getLocaleDirection.d.ts +18 -0
  295. package/dist/i18n/getLocaleDirection.d.ts.map +1 -0
  296. package/dist/i18n/getLocaleDirection.js +29 -0
  297. package/dist/i18n/index.d.ts +6 -0
  298. package/dist/i18n/index.d.ts.map +1 -1
  299. package/dist/i18n/index.js +7 -1
  300. package/dist/i18n/useDirection.d.ts +2 -0
  301. package/dist/i18n/useDirection.d.ts.map +1 -0
  302. package/dist/i18n/useDirection.js +27 -0
  303. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  304. package/dist/theme/derivedVarRegistry.js +13 -0
  305. package/dist/utils/index.d.ts +1 -0
  306. package/dist/utils/index.d.ts.map +1 -1
  307. package/dist/utils/index.js +2 -1
  308. package/dist/utils/plainDate.d.ts +17 -0
  309. package/dist/utils/plainDate.d.ts.map +1 -1
  310. package/dist/utils/plainDate.js +23 -4
  311. package/dist/utils/rtlStyles.d.ts +24 -0
  312. package/dist/utils/rtlStyles.d.ts.map +1 -0
  313. package/dist/utils/rtlStyles.js +27 -0
  314. package/locales/en.json +112 -0
  315. package/locales/pseudo.json +84 -0
  316. package/package.json +6 -1
  317. package/src/Avatar/Avatar.doc.mjs +50 -3
  318. package/src/Avatar/Avatar.test.tsx +218 -0
  319. package/src/Avatar/Avatar.tsx +285 -79
  320. package/src/Avatar/AvatarStatusDot.doc.mjs +4 -4
  321. package/src/Avatar/AvatarStatusDot.tsx +6 -4
  322. package/src/AvatarGroup/AvatarGroup.doc.mjs +2 -0
  323. package/src/AvatarGroup/AvatarGroup.test.tsx +162 -2
  324. package/src/AvatarGroup/AvatarGroup.tsx +58 -3
  325. package/src/AvatarGroup/AvatarGroupOverflow.tsx +5 -2
  326. package/src/Banner/Banner.tsx +6 -6
  327. package/src/Breadcrumbs/BreadcrumbItem.tsx +10 -10
  328. package/src/Breadcrumbs/Breadcrumbs.test.tsx +52 -1
  329. package/src/Breadcrumbs/index.ts +4 -0
  330. package/src/Button/Button.doc.mjs +0 -4
  331. package/src/Button/Button.test.tsx +16 -0
  332. package/src/Button/Button.tsx +6 -4
  333. package/src/Calendar/Calendar.test.tsx +170 -20
  334. package/src/Calendar/Calendar.tsx +73 -16
  335. package/src/Calendar/dayCellUtils.test.ts +18 -18
  336. package/src/Calendar/dayCellUtils.ts +2 -2
  337. package/src/Calendar/styles.ts +33 -39
  338. package/src/Card/Card.tsx +1 -1
  339. package/src/Carousel/Carousel.tsx +25 -6
  340. package/src/Chat/ChatComposer.doc.mjs +2 -2
  341. package/src/Chat/ChatComposerInput.doc.mjs +1 -1
  342. package/src/Chat/ChatComposerInput.tsx +2 -2
  343. package/src/Chat/ChatDictationButton.tsx +7 -1
  344. package/src/Chat/ChatLayout.tsx +4 -4
  345. package/src/Chat/ChatMessageBubble.tsx +15 -10
  346. package/src/Chat/ChatSystemMessage.test.tsx +5 -0
  347. package/src/Chat/ChatToolCalls.tsx +9 -2
  348. package/src/Chat/useTriggerMenu.tsx +1 -1
  349. package/src/CheckboxInput/CheckboxInput.doc.mjs +6 -0
  350. package/src/CheckboxInput/CheckboxInput.test.tsx +23 -0
  351. package/src/CheckboxInput/CheckboxInput.tsx +21 -6
  352. package/src/CheckboxList/CheckboxList.doc.mjs +6 -0
  353. package/src/CheckboxList/CheckboxList.test.tsx +94 -3
  354. package/src/CheckboxList/CheckboxListItem.tsx +17 -10
  355. package/src/Citation/Citation.doc.mjs +4 -3
  356. package/src/Citation/Citation.test.tsx +97 -0
  357. package/src/Citation/Citation.tsx +43 -11
  358. package/src/CodeBlock/CodeBlock.test.tsx +17 -0
  359. package/src/CodeBlock/CodeBlock.tsx +38 -11
  360. package/src/Collapsible/Collapsible.doc.mjs +1 -0
  361. package/src/Collapsible/Collapsible.test.tsx +7 -0
  362. package/src/Collapsible/Collapsible.tsx +16 -6
  363. package/src/CommandPalette/CommandPalette.test.tsx +95 -1
  364. package/src/CommandPalette/CommandPalette.tsx +41 -2
  365. package/src/CommandPalette/CommandPaletteItem.tsx +1 -1
  366. package/src/ContextMenu/ContextMenu.test.tsx +59 -2
  367. package/src/ContextMenu/ContextMenu.tsx +22 -11
  368. package/src/ContextMenu/index.ts +5 -0
  369. package/src/DateInput/DateInput.doc.mjs +25 -0
  370. package/src/DateInput/DateInput.test.tsx +230 -0
  371. package/src/DateInput/DateInput.tsx +51 -29
  372. package/src/DateRangeInput/DateRangeInput.doc.mjs +16 -0
  373. package/src/DateRangeInput/DateRangeInput.test.tsx +140 -0
  374. package/src/DateRangeInput/DateRangeInput.tsx +42 -30
  375. package/src/DateTimeInput/DateTimeInput.doc.mjs +22 -1
  376. package/src/DateTimeInput/DateTimeInput.test.tsx +112 -0
  377. package/src/DateTimeInput/DateTimeInput.tsx +44 -39
  378. package/src/Divider/Divider.test.tsx +32 -8
  379. package/src/Divider/Divider.tsx +14 -1
  380. package/src/DropdownMenu/DropdownMenu.test.tsx +60 -0
  381. package/src/DropdownMenu/DropdownMenu.tsx +31 -12
  382. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +8 -2
  383. package/src/DropdownMenu/DropdownMenuItem.tsx +9 -3
  384. package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +4 -12
  385. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +9 -10
  386. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +9 -3
  387. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +20 -2
  388. package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +88 -0
  389. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +635 -0
  390. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +553 -0
  391. package/src/DropdownMenu/index.ts +16 -0
  392. package/src/DropdownMenu/menuItemHover.ts +39 -0
  393. package/src/DropdownMenu/menuItemRoles.ts +9 -0
  394. package/src/DropdownMenu/renderDropdownItems.tsx +29 -15
  395. package/src/Field/Field.tsx +14 -9
  396. package/src/Field/FieldLabel.test.tsx +25 -0
  397. package/src/Field/FieldLabel.tsx +12 -3
  398. package/src/Field/inputStyles.stylex.ts +2 -0
  399. package/src/FieldStatus/FieldStatus.test.tsx +72 -0
  400. package/src/FieldStatus/FieldStatus.tsx +48 -2
  401. package/src/FileInput/FileInput.doc.mjs +23 -2
  402. package/src/FileInput/FileInput.test.tsx +104 -13
  403. package/src/FileInput/FileInput.tsx +106 -79
  404. package/src/HoverCard/useHoverCard.tsx +15 -3
  405. package/src/Icon/Icon.doc.mjs +13 -0
  406. package/src/Icon/Icon.test.tsx +89 -0
  407. package/src/Icon/Icon.tsx +51 -3
  408. package/src/Item/Item.doc.mjs +5 -0
  409. package/src/Item/Item.test.tsx +88 -0
  410. package/src/Item/Item.tsx +57 -11
  411. package/src/Layer/layerAnimations.stylex.ts +23 -0
  412. package/src/Lightbox/Lightbox.tsx +15 -7
  413. package/src/List/List.test.tsx +17 -29
  414. package/src/List/List.tsx +6 -1
  415. package/src/List/ListItem.doc.mjs +12 -0
  416. package/src/List/ListItem.tsx +13 -0
  417. package/src/Markdown/Markdown.test.tsx +21 -0
  418. package/src/Markdown/Markdown.tsx +4 -0
  419. package/src/MetadataList/MetadataList.test.tsx +24 -0
  420. package/src/MetadataList/MetadataList.tsx +6 -2
  421. package/src/MultiSelector/MultiSelector.doc.mjs +20 -0
  422. package/src/MultiSelector/MultiSelector.test.tsx +402 -0
  423. package/src/MultiSelector/MultiSelector.tsx +120 -59
  424. package/src/NavMenu/NavHeadingMenuItem.tsx +1 -1
  425. package/src/NumberInput/NumberInput.doc.mjs +21 -0
  426. package/src/NumberInput/NumberInput.test.tsx +71 -0
  427. package/src/NumberInput/NumberInput.tsx +35 -26
  428. package/src/Outline/Outline.test.tsx +12 -12
  429. package/src/Outline/Outline.tsx +2 -2
  430. package/src/Pagination/Pagination.tsx +11 -3
  431. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  432. package/src/PowerSearch/PowerSearch.test.tsx +23 -0
  433. package/src/PowerSearch/PowerSearch.tsx +10 -1
  434. package/src/ProgressBar/ProgressBar.test.tsx +34 -0
  435. package/src/ProgressBar/ProgressBar.tsx +16 -1
  436. package/src/RadioList/RadioList.doc.mjs +6 -0
  437. package/src/RadioList/RadioList.test.tsx +26 -4
  438. package/src/RadioList/RadioList.tsx +2 -2
  439. package/src/RadioList/RadioListItem.tsx +12 -5
  440. package/src/Resizable/ResizeHandle.test.tsx +94 -0
  441. package/src/Resizable/ResizeHandle.tsx +70 -20
  442. package/src/SegmentedControl/SegmentedControl.test.tsx +28 -0
  443. package/src/SegmentedControl/SegmentedControlItem.tsx +22 -2
  444. package/src/Selector/Selector.doc.mjs +15 -0
  445. package/src/Selector/Selector.test.tsx +370 -0
  446. package/src/Selector/Selector.tsx +88 -20
  447. package/src/SideNav/SideNav.tsx +1 -8
  448. package/src/SideNav/SideNavCollapseButton.tsx +9 -7
  449. package/src/SideNav/SideNavItem.tsx +5 -3
  450. package/src/Skeleton/Skeleton.test.tsx +16 -0
  451. package/src/Skeleton/Skeleton.tsx +13 -1
  452. package/src/Slider/Slider.doc.mjs +6 -0
  453. package/src/Slider/Slider.test.tsx +240 -12
  454. package/src/Slider/Slider.tsx +96 -23
  455. package/src/Spinner/Spinner.test.tsx +10 -8
  456. package/src/Spinner/Spinner.tsx +15 -5
  457. package/src/Switch/Switch.doc.mjs +142 -42
  458. package/src/Switch/Switch.test.tsx +111 -0
  459. package/src/Switch/Switch.tsx +149 -32
  460. package/src/TabList/Tab.tsx +2 -2
  461. package/src/TabList/TabList.doc.mjs +0 -6
  462. package/src/TabList/TabList.test.tsx +3 -15
  463. package/src/TabList/TabList.tsx +6 -17
  464. package/src/TabList/TabListContext.ts +0 -6
  465. package/src/TabList/TabMenu.tsx +2 -2
  466. package/src/Table/BaseTable.tsx +34 -2
  467. package/src/Table/Table.doc.mjs +11 -0
  468. package/src/Table/Table.test.tsx +85 -0
  469. package/src/Table/TableCell.tsx +3 -3
  470. package/src/Table/TableHeaderCell.tsx +3 -3
  471. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +9 -6
  472. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +19 -0
  473. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +27 -18
  474. package/src/Table/plugins/selection/useTableSelection.test.tsx +61 -3
  475. package/src/Table/plugins/selection/useTableSelection.tsx +3 -0
  476. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +38 -9
  477. package/src/Table/plugins/tree/useTableTreeData.tsx +17 -13
  478. package/src/Table/types.ts +33 -0
  479. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  480. package/src/TextArea/TextArea.doc.mjs +21 -0
  481. package/src/TextArea/TextArea.test.tsx +23 -0
  482. package/src/TextArea/TextArea.tsx +27 -26
  483. package/src/TextInput/TextInput.doc.mjs +21 -0
  484. package/src/TextInput/TextInput.test.tsx +150 -0
  485. package/src/TextInput/TextInput.tsx +28 -25
  486. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  487. package/src/Thumbnail/Thumbnail.tsx +1 -1
  488. package/src/TimeInput/TimeInput.doc.mjs +21 -0
  489. package/src/TimeInput/TimeInput.test.tsx +23 -0
  490. package/src/TimeInput/TimeInput.tsx +24 -25
  491. package/src/Timestamp/Timestamp.doc.mjs +32 -6
  492. package/src/Timestamp/Timestamp.test.tsx +371 -0
  493. package/src/Timestamp/Timestamp.tsx +96 -93
  494. package/src/Timestamp/formatInstant.ts +218 -0
  495. package/src/Timestamp/index.ts +4 -0
  496. package/src/Timestamp/tooltipEntries.test.ts +552 -0
  497. package/src/Timestamp/tooltipEntries.ts +210 -0
  498. package/src/Toast/ToastViewport.test.tsx +218 -8
  499. package/src/Toast/ToastViewport.tsx +71 -12
  500. package/src/ToggleButton/ToggleButton.test.tsx +24 -0
  501. package/src/ToggleButton/ToggleButton.tsx +19 -1
  502. package/src/Token/Token.test.tsx +166 -15
  503. package/src/Token/Token.tsx +104 -47
  504. package/src/Token/TokenLink.tsx +107 -0
  505. package/src/Token/index.ts +1 -1
  506. package/src/Tokenizer/Tokenizer.doc.mjs +23 -1
  507. package/src/Tokenizer/Tokenizer.test.tsx +23 -0
  508. package/src/Tokenizer/Tokenizer.tsx +14 -2
  509. package/src/Tooltip/Tooltip.test.tsx +46 -0
  510. package/src/Tooltip/useTooltip.tsx +20 -0
  511. package/src/TopNav/TopNav.test.tsx +93 -2
  512. package/src/TopNav/TopNavItem.doc.mjs +3 -3
  513. package/src/TopNav/TopNavItem.tsx +35 -6
  514. package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +1 -1
  515. package/src/TreeList/TreeList.doc.mjs +6 -0
  516. package/src/TreeList/TreeList.test.tsx +50 -10
  517. package/src/TreeList/TreeList.tsx +5 -0
  518. package/src/TreeList/TreeListBranches.tsx +8 -6
  519. package/src/TreeList/TreeListItem.tsx +33 -11
  520. package/src/Typeahead/BaseTypeahead.tsx +42 -15
  521. package/src/Typeahead/Typeahead.doc.mjs +13 -0
  522. package/src/Typeahead/Typeahead.test.tsx +112 -0
  523. package/src/Typeahead/Typeahead.tsx +17 -1
  524. package/src/Typeahead/TypeaheadItem.tsx +2 -1
  525. package/src/__tests__/forcedColors.ts +60 -0
  526. package/src/__tests__/inputWidthContract.test.tsx +62 -0
  527. package/src/doc-mjs.d.ts +19 -0
  528. package/src/hooks/index.ts +6 -0
  529. package/src/hooks/isRtlElement.ts +27 -0
  530. package/src/hooks/useAnnounce.test.tsx +75 -2
  531. package/src/hooks/useAnnounce.ts +47 -1
  532. package/src/hooks/useFocusTrap.test.tsx +104 -0
  533. package/src/hooks/useFocusTrap.ts +61 -12
  534. package/src/hooks/useGridFocus.doc.mjs +3 -3
  535. package/src/hooks/useGridFocus.test.tsx +41 -0
  536. package/src/hooks/useGridFocus.ts +21 -10
  537. package/src/hooks/useHotkeys.ts +9 -0
  538. package/src/hooks/useInputStatusIcon.test.tsx +162 -0
  539. package/src/hooks/useInputStatusIcon.tsx +224 -0
  540. package/src/hooks/useKeyboardHint.test.tsx +22 -0
  541. package/src/hooks/useKeyboardHint.tsx +6 -2
  542. package/src/hooks/useListFocus.doc.mjs +25 -3
  543. package/src/hooks/useListFocus.test.tsx +153 -1
  544. package/src/hooks/useListFocus.ts +102 -9
  545. package/src/i18n/InternationalizationContext.ts +4 -0
  546. package/src/i18n/InternationalizationProvider.tsx +12 -2
  547. package/src/i18n/__tests__/getLocaleDirection.test.ts +40 -0
  548. package/src/i18n/__tests__/useDirection.test.tsx +66 -0
  549. package/src/i18n/getLocaleDirection.ts +29 -0
  550. package/src/i18n/index.ts +6 -0
  551. package/src/i18n/useDirection.ts +29 -0
  552. package/src/reset.css +0 -26
  553. package/src/reset.test.ts +60 -0
  554. package/src/theme/derivedVarRegistry.test.ts +1 -0
  555. package/src/theme/derivedVarRegistry.ts +6 -0
  556. package/src/theme/generateThemeRules.test.ts +42 -0
  557. package/src/utils/index.ts +2 -0
  558. package/src/utils/plainDate.test.ts +24 -6
  559. package/src/utils/plainDate.ts +18 -4
  560. package/src/utils/rtlStyles.ts +27 -0
@@ -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';
@@ -479,6 +480,13 @@ export interface MultiSelectorProps<
479
480
  * Status indicator for the selector.
480
481
  */
481
482
  status?: MultiSelectorStatus;
483
+ /**
484
+ * How the status message is placed relative to the input.
485
+ * - 'attached': message overlaps directly below the input (bordered treatment)
486
+ * - 'detached': message floats below as a separate element with spacing
487
+ * @default 'attached'
488
+ */
489
+ statusVariant?: FieldStatusVariant;
482
490
 
483
491
  /**
484
492
  * Width of the field. Numbers are treated as pixels, strings are used as-is
@@ -562,6 +570,21 @@ export interface MultiSelectorProps<
562
570
  'data-testid'?: string;
563
571
  }
564
572
 
573
+ // Case-insensitive substring match for a single option. The one predicate used
574
+ // by both the flat filter (count + keyboard nav) and the grouped renderer, so
575
+ // what is shown while searching stays in lockstep with the announced count.
576
+ function optionMatchesQuery(
577
+ option: MultiSelectorOptionData,
578
+ query: string,
579
+ ): boolean {
580
+ if (!query) {
581
+ return true;
582
+ }
583
+ return (option.label ?? option.value)
584
+ .toLowerCase()
585
+ .includes(query.toLowerCase());
586
+ }
587
+
565
588
  // Case-insensitive substring filter over the selectable options. Shared by the
566
589
  // `filteredItems` memo (rendering) and the search-change handler, which needs
567
590
  // the count for the *next* query synchronously to announce it exactly once per
@@ -573,10 +596,7 @@ function filterOptionsByQuery(
573
596
  if (!query) {
574
597
  return items;
575
598
  }
576
- const q = query.toLowerCase();
577
- return items.filter(item =>
578
- (item.label ?? item.value).toLowerCase().includes(q),
579
- );
599
+ return items.filter(item => optionMatchesQuery(item, query));
580
600
  }
581
601
 
582
602
  /**
@@ -610,6 +630,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
610
630
  placeholder: placeholderFromProps,
611
631
  size: sizeProp,
612
632
  status,
633
+ statusVariant = 'attached',
613
634
  labelTooltip,
614
635
  startIcon,
615
636
  hasClear = false,
@@ -716,35 +737,25 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
716
737
 
717
738
  // Single source of truth for item order. Both the hook (keyboard navigation)
718
739
  // and renderOptions (DOM rendering) consume this list — no independent sorting.
719
- // Selected-at-open items are placed first within each group/section.
740
+ // Selected-at-open items are placed first within each group/section, and the
741
+ // same walk applies while searching so group structure survives filtering
742
+ // (only matching items are kept; the query is empty in non-search mode).
720
743
  const sortedItems = useMemo(() => {
721
744
  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
745
  const result: MultiSelectorOptionData[] = [];
737
746
  let pendingFlat: MultiSelectorOptionData[] = [];
738
747
 
748
+ const orderSelectedFirst = (items: MultiSelectorOptionData[]) => {
749
+ const selected = items.filter(item => selectedSet.has(item.value));
750
+ const unselected = items.filter(item => !selectedSet.has(item.value));
751
+ return [...selected, ...unselected];
752
+ };
753
+
739
754
  const flushFlat = () => {
740
755
  if (pendingFlat.length === 0) {
741
756
  return;
742
757
  }
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);
758
+ result.push(...orderSelectedFirst(pendingFlat));
748
759
  pendingFlat = [];
749
760
  };
750
761
 
@@ -753,16 +764,15 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
753
764
  flushFlat();
754
765
  } else if (isSection(option)) {
755
766
  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);
767
+ const sectionOptions = option.options
768
+ .map(opt => normalizeOption(opt))
769
+ .filter(opt => optionMatchesQuery(opt, searchQuery));
770
+ result.push(...orderSelectedFirst(sectionOptions));
764
771
  } else if (isOptionData(option)) {
765
- pendingFlat.push(normalizeOption(option));
772
+ const normalized = normalizeOption(option);
773
+ if (optionMatchesQuery(normalized, searchQuery)) {
774
+ pendingFlat.push(normalized);
775
+ }
766
776
  }
767
777
  }
768
778
  flushFlat();
@@ -771,14 +781,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
771
781
  return [{value: SELECT_ALL_VALUE, label: selectAllLabel}, ...result];
772
782
  }
773
783
  return result;
774
- }, [
775
- filteredItems,
776
- searchQuery,
777
- options,
778
- selectedAtOpen,
779
- hasSelectAll,
780
- selectAllLabel,
781
- ]);
784
+ }, [searchQuery, options, selectedAtOpen, hasSelectAll, selectAllLabel]);
782
785
 
783
786
  // Layer for dropdown positioning
784
787
  const handleLayerHide = useCallback(() => {
@@ -1126,6 +1129,11 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1126
1129
  ? allEnabledSelected
1127
1130
  : optimisticValue.includes(item.value);
1128
1131
  const checkboxValue = isSelectAll ? selectAllState : isSelected;
1132
+ // aria-selected="mixed" is invalid on role="option", and the tri-state
1133
+ // checkbox is inert/decorative, so the indeterminate state must be
1134
+ // conveyed through the option's accessible name (WCAG 4.1.2).
1135
+ const isPartiallySelected =
1136
+ isSelectAll && selectAllState === 'indeterminate';
1129
1137
 
1130
1138
  return (
1131
1139
  <div
@@ -1133,6 +1141,13 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1133
1141
  id={getItemId(flatIndex)}
1134
1142
  role="option"
1135
1143
  aria-selected={isSelected}
1144
+ aria-label={
1145
+ isPartiallySelected
1146
+ ? t('@astryx.multiSelector.selectAllPartiallySelected', {
1147
+ label: selectAllLabel,
1148
+ })
1149
+ : undefined
1150
+ }
1136
1151
  aria-disabled={item.disabled}
1137
1152
  onClick={() => {
1138
1153
  if (!item.disabled) {
@@ -1177,10 +1192,12 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1177
1192
  optimisticValue,
1178
1193
  allEnabledSelected,
1179
1194
  selectAllState,
1195
+ selectAllLabel,
1180
1196
  getItemId,
1181
1197
  handleNavigableToggle,
1182
1198
  onItemMouseEnter,
1183
1199
  size,
1200
+ t,
1184
1201
  ],
1185
1202
  );
1186
1203
 
@@ -1208,25 +1225,28 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1208
1225
  cursor = 1;
1209
1226
  }
1210
1227
 
1211
- // Empty state — no real items to show
1228
+ // Empty state — no real items to show. role="presentation" keeps the
1229
+ // message out of the listbox's accessibility tree (role="listbox" only
1230
+ // permits option/group children); the no-results outcome is announced
1231
+ // via the result-count live region instead.
1212
1232
  if (realItemCount === 0) {
1213
1233
  elements.push(
1214
- <div key="empty" {...stylex.props(styles.emptyState)}>
1234
+ <div
1235
+ key="empty"
1236
+ role="presentation"
1237
+ {...stylex.props(styles.emptyState)}>
1215
1238
  No results found
1216
1239
  </div>,
1217
1240
  );
1218
1241
  return elements;
1219
1242
  }
1220
1243
 
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.
1244
+ // Consume items from sortedItems in order, interleaving structural elements
1245
+ // (dividers, section headers) from the options prop. While searching, only
1246
+ // matching items are present in sortedItems, so a section consumes just its
1247
+ // matches and is skipped entirely when none match — no header left standing
1248
+ // over nothing, and the cursor stays aligned with the combobox indices.
1249
+ const isSearching = Boolean(searchQuery);
1230
1250
  let pendingCount = 0;
1231
1251
 
1232
1252
  const flushPending = () => {
@@ -1242,12 +1262,25 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1242
1262
 
1243
1263
  if (isDivider(option)) {
1244
1264
  flushPending();
1245
- elements.push(<Divider key={`divider-${i}`} xstyle={styles.divider} />);
1265
+ // A standalone divider between groups would orphan itself once its
1266
+ // neighbors are filtered out, so skip it while searching.
1267
+ if (!isSearching) {
1268
+ elements.push(
1269
+ <Divider key={`divider-${i}`} xstyle={styles.divider} />,
1270
+ );
1271
+ }
1246
1272
  } else if (isSection(option)) {
1247
1273
  flushPending();
1248
- const count = option.options.length;
1274
+ const matchCount = isSearching
1275
+ ? option.options.filter(opt =>
1276
+ optionMatchesQuery(normalizeOption(opt), searchQuery),
1277
+ ).length
1278
+ : option.options.length;
1279
+ if (matchCount === 0) {
1280
+ continue;
1281
+ }
1249
1282
  const sectionItems: ReactNode[] = [];
1250
- for (let j = 0; j < count; j++) {
1283
+ for (let j = 0; j < matchCount; j++) {
1251
1284
  sectionItems.push(renderItem(sortedItems[cursor], cursor));
1252
1285
  cursor++;
1253
1286
  }
@@ -1266,7 +1299,12 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1266
1299
  </div>,
1267
1300
  );
1268
1301
  } else if (isOptionData(option)) {
1269
- pendingCount++;
1302
+ if (
1303
+ !isSearching ||
1304
+ optionMatchesQuery(normalizeOption(option), searchQuery)
1305
+ ) {
1306
+ pendingCount++;
1307
+ }
1270
1308
  }
1271
1309
  }
1272
1310
  flushPending();
@@ -1295,7 +1333,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1295
1333
  isDisabled && inputWrapperStyles.disabled,
1296
1334
  optimisticValue.length === 0 && styles.triggerPlaceholder,
1297
1335
  status && inputStatusBorderStyles[status.type],
1298
- status && inputStatusHoverShadowStyles[status.type],
1336
+ status && !isDisabled && inputStatusHoverShadowStyles[status.type],
1299
1337
  inputGroup && groupStyles.inGroup,
1300
1338
  xstyle,
1301
1339
  ),
@@ -1359,7 +1397,17 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1359
1397
  onClick={handleClear}
1360
1398
  aria-label={t('@astryx.multiSelector.clearAll', {label})}
1361
1399
  {...stylex.props(styles.clearButton)}>
1362
- <Icon icon="close" size="sm" color="secondary" />
1400
+ <Icon
1401
+ icon="close"
1402
+ size="sm"
1403
+ color="secondary"
1404
+ // Stable theme target on the clear glyph itself, so a theme can
1405
+ // restyle just this icon (color, size, hover) via `defineTheme`.
1406
+ // Same-element rules in @layer astryx-theme win over the icon's
1407
+ // own base color/size, which a button-level target could not
1408
+ // reach.
1409
+ {...themeProps('multi-selector-clear-icon')}
1410
+ />
1363
1411
  </button>
1364
1412
  )}
1365
1413
  <span
@@ -1375,7 +1423,19 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1375
1423
  color={STATUS_ICON_COLOR_MAP[status.type]}
1376
1424
  />
1377
1425
  ) : (
1378
- <Icon icon="chevronDown" size="sm" color="inherit" />
1426
+ <Icon
1427
+ icon="chevronDown"
1428
+ size="sm"
1429
+ color="inherit"
1430
+ // Stable theme target on the chevron glyph itself, so a theme can
1431
+ // restyle just this icon (color, size, hover) — and its
1432
+ // open/closed state — via `defineTheme`. Same-element rules in
1433
+ // @layer astryx-theme win over the icon's own base color/size,
1434
+ // which a button-level target could not reach.
1435
+ {...themeProps('multi-selector-indicator-icon', {
1436
+ state: popover.isOpen ? 'expanded' : 'collapsed',
1437
+ })}
1438
+ />
1379
1439
  )}
1380
1440
  </span>
1381
1441
  </div>
@@ -1426,6 +1486,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1426
1486
  }
1427
1487
  : undefined
1428
1488
  }
1489
+ statusVariant={statusVariant}
1429
1490
  labelTooltip={labelTooltip}
1430
1491
  width={width}>
1431
1492
  {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}