@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
@@ -136,6 +136,21 @@ describe('ContextMenu', () => {
136
136
  expect(HTMLElement.prototype.hidePopover).toHaveBeenCalled();
137
137
  });
138
138
 
139
+ it('closes the menu when Tab is pressed inside it (APG menu pattern)', () => {
140
+ render(
141
+ <ContextMenu items={[{label: 'Item 1'}]}>
142
+ <div>Right-click me</div>
143
+ </ContextMenu>,
144
+ );
145
+
146
+ fireEvent.contextMenu(screen.getByText('Right-click me'));
147
+ expect(HTMLElement.prototype.showPopover).toHaveBeenCalled();
148
+
149
+ const menu = screen.getByRole('menu', {hidden: true});
150
+ fireEvent.keyDown(menu, {key: 'Tab'});
151
+ expect(HTMLElement.prototype.hidePopover).toHaveBeenCalled();
152
+ });
153
+
139
154
  it('ignores Escape during IME composition', () => {
140
155
  render(
141
156
  <ContextMenu items={[{label: 'Item 1'}]}>
@@ -552,7 +567,7 @@ describe('ContextMenu selectable items', () => {
552
567
  <ContextMenuRadioGroup
553
568
  value="name"
554
569
  onChange={() => {}}
555
- aria-label="Sort by">
570
+ label="Sort by">
556
571
  <ContextMenuRadioItem value="name" label="Sort by name" />
557
572
  <ContextMenuRadioItem value="date" label="Sort by date" />
558
573
  </ContextMenuRadioGroup>
@@ -587,7 +602,7 @@ describe('ContextMenu selectable items', () => {
587
602
  <ContextMenuRadioGroup
588
603
  value="name"
589
604
  onChange={onSort}
590
- aria-label="Sort by">
605
+ label="Sort by">
591
606
  <ContextMenuRadioItem value="name" label="Sort by name" />
592
607
  <ContextMenuRadioItem value="date" label="Sort by date" />
593
608
  </ContextMenuRadioGroup>
@@ -803,4 +818,46 @@ describe('ContextMenu keyboard access for menuitemradio/menuitemcheckbox (#3829)
803
818
  screen.getByRole('menuitemradio', {name: 'Oldest', hidden: true}),
804
819
  ).toHaveFocus();
805
820
  });
821
+
822
+ it('renders a submenu from a nested items array and keyboard-reaches an item after it', () => {
823
+ // Same inline-flyout scoping the DropdownMenu fix covers: the submenu's
824
+ // items must not pollute the ContextMenu's roving order, and an item after
825
+ // the submenu row must stay keyboard-reachable.
826
+ const onDelete = vi.fn();
827
+ render(
828
+ <ContextMenu
829
+ items={[
830
+ {label: 'Cut', onClick: () => {}},
831
+ {
832
+ label: 'Move to',
833
+ items: [
834
+ {label: 'Folder A', onClick: () => {}},
835
+ {label: 'Folder B', onClick: () => {}},
836
+ ],
837
+ },
838
+ {type: 'divider'},
839
+ {label: 'Delete', onClick: onDelete},
840
+ ]}>
841
+ <div>Right-click me</div>
842
+ </ContextMenu>,
843
+ );
844
+ fireEvent.contextMenu(screen.getByText('Right-click me'));
845
+ // Two role="menu" exist (the context menu + the inline submenu flyout);
846
+ // the context menu is the first in DOM order.
847
+ const menu = screen.getAllByRole('menu', {hidden: true})[0];
848
+ // The submenu trigger renders as a menuitem with aria-haspopup.
849
+ const submenuTrigger = screen.getByRole('menuitem', {
850
+ name: /Move to/,
851
+ hidden: true,
852
+ });
853
+ expect(submenuTrigger).toHaveAttribute('aria-haspopup', 'menu');
854
+ // Cut → Move to → Delete, one step per press (submenu items not swept in).
855
+ screen.getByRole('menuitem', {name: 'Cut', hidden: true}).focus();
856
+ fireEvent.keyDown(menu, {key: 'ArrowDown'});
857
+ expect(submenuTrigger).toHaveFocus();
858
+ fireEvent.keyDown(menu, {key: 'ArrowDown'});
859
+ expect(
860
+ screen.getByRole('menuitem', {name: 'Delete', hidden: true}),
861
+ ).toHaveFocus();
862
+ });
806
863
  });
@@ -49,6 +49,7 @@ import {
49
49
  import {
50
50
  MENU_ITEM_ROLES,
51
51
  MENU_ITEM_SELECTOR,
52
+ MENU_BOUNDARY_SELECTOR,
52
53
  } from '../DropdownMenu/menuItemRoles';
53
54
  import {useListFocus} from '../hooks/useListFocus';
54
55
  import {useTypeahead} from '../hooks/useTypeahead';
@@ -268,22 +269,18 @@ export function ContextMenu({
268
269
  handleKeyDown: listNavKeyDown,
269
270
  focusFirst,
270
271
  focusItem,
272
+ ownsEvent,
273
+ getItems: getMenuItems,
271
274
  } = useListFocus<HTMLDivElement>({
272
275
  itemSelector: MENU_ITEM_SELECTOR,
276
+ boundarySelector: MENU_BOUNDARY_SELECTOR,
273
277
  wrap: false,
274
278
  onEscape: closeMenu,
275
279
  });
276
280
 
277
- // First-character typeahead over the enabled menu items (menus-11).
278
- const getMenuItems = useCallback(
279
- (): HTMLElement[] =>
280
- listRef.current
281
- ? Array.from(
282
- listRef.current.querySelectorAll<HTMLElement>(MENU_ITEM_SELECTOR),
283
- )
284
- : [],
285
- [listRef],
286
- );
281
+ // First-character typeahead over the enabled menu items (menus-11). Reuses
282
+ // the hook's scoped item collection so an inline submenu flyout's items
283
+ // aren't swept in.
287
284
  const typeahead = useTypeahead({
288
285
  getItemLabels: () => getMenuItems().map(el => el.textContent),
289
286
  onMatch: focusItem,
@@ -340,6 +337,11 @@ export function ContextMenu({
340
337
 
341
338
  const listKeyDown = useCallback(
342
339
  (e: React.KeyboardEvent) => {
340
+ // A submenu flyout renders inline inside this menu; its key events bubble
341
+ // up here. Let that level own them — only handle events from this level.
342
+ if (!ownsEvent(e)) {
343
+ return;
344
+ }
343
345
  if (e.key === 'Enter' || e.key === ' ') {
344
346
  e.preventDefault();
345
347
  const focused = document.activeElement as HTMLElement | null;
@@ -351,13 +353,22 @@ export function ContextMenu({
351
353
  }
352
354
  return;
353
355
  }
356
+ // APG menu pattern: Tab closes the menu. Menu items are tabIndex={-1}
357
+ // so Tab would otherwise leak focus into the page while the menu stayed
358
+ // open (menus-5). Do NOT preventDefault — closing restores focus to the
359
+ // previously focused element, and the browser's default Tab then
360
+ // continues from there to the next element.
361
+ if (e.key === 'Tab') {
362
+ closeMenu();
363
+ return;
364
+ }
354
365
  if (typeahead.onKeyDown(e)) {
355
366
  e.preventDefault();
356
367
  return;
357
368
  }
358
369
  listNavKeyDown(e);
359
370
  },
360
- [listNavKeyDown, typeahead],
371
+ [listNavKeyDown, closeMenu, typeahead, ownsEvent],
361
372
  );
362
373
 
363
374
  // Place the zero-size cursor anchor at a point in the trigger's local
@@ -35,3 +35,8 @@ export {
35
35
  DropdownMenuRadioItem as ContextMenuRadioItem,
36
36
  type DropdownMenuRadioItemProps as ContextMenuRadioItemProps,
37
37
  } from '../DropdownMenu/DropdownMenuRadioItem';
38
+ // Submenus work inside a ContextMenu too (nested flyout of items).
39
+ export {
40
+ DropdownMenuSubMenu as ContextMenuSubMenu,
41
+ type DropdownMenuSubMenuProps as ContextMenuSubMenuProps,
42
+ } from '../DropdownMenu/DropdownMenuSubMenu';
@@ -115,6 +115,13 @@ export const docs = {
115
115
  description:
116
116
  'Status indicator object for error, warning, or success states with a message.',
117
117
  },
118
+ {
119
+ name: 'statusVariant',
120
+ type: "'attached' | 'detached' | 'tooltip'",
121
+ description:
122
+ '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.',
123
+ default: "'attached'",
124
+ },
118
125
  {
119
126
  name: 'labelTooltip',
120
127
  type: 'string',
@@ -142,6 +149,12 @@ export const docs = {
142
149
  "How the committed date value is displayed. Named values are reused from Timestamp's format vocabulary: 'date' shows 'Mar 21, 2026', 'date_long' shows 'March 21, 2026', 'date_weekday' shows 'Wed, Mar 21, 2026', 'system_date' shows '2026-03-21'. A function receives the ISO value and returns a custom string. Applies only to the committed value, never to text being typed.",
143
150
  default: "'date_long'",
144
151
  },
152
+ {
153
+ name: 'width',
154
+ type: 'SizeValue',
155
+ description:
156
+ '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.',
157
+ },
145
158
  {
146
159
  name: 'xstyle',
147
160
  type: 'StyleXStyles',
@@ -152,6 +165,8 @@ export const docs = {
152
165
  theming: {
153
166
  targets: [
154
167
  {className: 'astryx-date-input', visualProps: ['size', 'status']},
168
+ {className: 'astryx-date-input-toggle-icon', states: ['state']},
169
+ {className: 'astryx-date-input-clear-icon'},
155
170
  ],
156
171
  },
157
172
  usage: {
@@ -384,6 +399,13 @@ export const docsZh = {
384
399
  type: "{type: 'warning' | 'error' | 'success', message?: string}",
385
400
  description: '错误、警告或成功状态的状态指示对象,附带消息。',
386
401
  },
402
+ {
403
+ name: 'statusVariant',
404
+ type: "'attached' | 'detached' | 'tooltip'",
405
+ description:
406
+ '状态消息相对于输入框的放置方式。attached 直接叠加在输入框下方(带边框处理);detached 作为独立元素浮于下方并留有间距;tooltip 隐藏消息框,并在状态图标上以提示气泡形式显示。',
407
+ default: "'attached'",
408
+ },
387
409
  {
388
410
  name: 'labelTooltip',
389
411
  type: 'string',
@@ -415,6 +437,8 @@ export const docsZh = {
415
437
  className: 'astryx-date-input',
416
438
  visualProps: ['size', 'status'],
417
439
  },
440
+ {className: 'astryx-date-input-toggle-icon', states: ['state']},
441
+ {className: 'astryx-date-input-clear-icon'},
418
442
  ],
419
443
  },
420
444
  };
@@ -492,6 +516,7 @@ export const docsDense = {
492
516
  placeholder: 'placeholder text in input',
493
517
  size: 'input control size',
494
518
  status: 'error/warning/success status w/ message',
519
+ 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.',
495
520
  labelTooltip: 'tooltip text via info icon at label end',
496
521
  hasClear: 'Shows clear button when date is set. Clears value on click.',
497
522
  numberOfMonths: 'months shown simultaneously in calendar popover',
@@ -20,8 +20,11 @@ import {
20
20
  import userEvent from '@testing-library/user-event';
21
21
  import {getButton, queryButton} from '../__tests__/fastRoleQueries';
22
22
  import {DateInput} from './DateInput';
23
+ import {Icon} from '../Icon';
23
24
  import {InputGroup} from '../InputGroup';
24
25
  import {InputGroupText} from '../InputGroup/InputGroupText';
26
+ import {defineTheme} from '../theme/defineTheme';
27
+ import {generateThemeCSSFlat} from '../theme/generateThemeRules';
25
28
 
26
29
  describe('DateInput', () => {
27
30
  it('renders with label', () => {
@@ -943,3 +946,230 @@ describe('DateInput', () => {
943
946
  });
944
947
  });
945
948
  });
949
+
950
+ describe('DateInput statusVariant forwarding', () => {
951
+ it('defaults to attached (status renders with data-variant="attached")', () => {
952
+ const {container} = render(
953
+ <DateInput
954
+ label="Date"
955
+ onChange={() => {}}
956
+ status={{type: 'error', message: 'Bad date'}}
957
+ />,
958
+ );
959
+ expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
960
+ 'data-variant',
961
+ 'attached',
962
+ );
963
+ });
964
+
965
+ it('forwards statusVariant="detached" to the underlying Field status', () => {
966
+ const {container} = render(
967
+ <DateInput
968
+ label="Date"
969
+ onChange={() => {}}
970
+ status={{type: 'error', message: 'Bad date'}}
971
+ statusVariant="detached"
972
+ />,
973
+ );
974
+ expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
975
+ 'data-variant',
976
+ 'detached',
977
+ );
978
+ });
979
+ });
980
+
981
+ describe('DateInput clear icon theme target', () => {
982
+ // Resolve the clear glyph span (the astryx-icon element inside the clear
983
+ // button), independent of the theme target class.
984
+ const getClearIcon = (): HTMLElement => {
985
+ const button = getButton('Clear Date');
986
+ const icon = button.querySelector('.astryx-icon');
987
+ if (icon == null) {
988
+ throw new Error('clear icon not found');
989
+ }
990
+ return icon as HTMLElement;
991
+ };
992
+
993
+ it('renders the astryx-date-input-clear-icon target on the clear glyph', () => {
994
+ render(
995
+ <DateInput
996
+ label="Date"
997
+ value="2026-01-15"
998
+ onChange={() => {}}
999
+ hasClear
1000
+ />,
1001
+ );
1002
+ // The stable theme target lands on the icon element itself (not the
1003
+ // button), so a theme can restyle just this glyph (color, size, hover)
1004
+ // via `defineTheme` — a button-level target could not reach the icon's
1005
+ // own color/size.
1006
+ const icon = getClearIcon();
1007
+ expect(icon).toHaveClass('astryx-date-input-clear-icon');
1008
+ expect(icon).toHaveClass('astryx-icon');
1009
+ });
1010
+
1011
+ it('keeps the clear button functional alongside the target', () => {
1012
+ const onChange = vi.fn();
1013
+ render(
1014
+ <DateInput
1015
+ label="Date"
1016
+ value="2026-01-15"
1017
+ onChange={onChange}
1018
+ hasClear
1019
+ />,
1020
+ );
1021
+ const clear = getButton('Clear Date');
1022
+ expect(clear.tagName).toBe('BUTTON');
1023
+ fireEvent.click(clear);
1024
+ expect(onChange).toHaveBeenCalledWith(undefined);
1025
+ });
1026
+
1027
+ it('renders the default icon (secondary color, sm size) byte-identically', () => {
1028
+ // Pixel-identical default guard: the clear glyph must carry the exact same
1029
+ // StyleX color/size classes as a standalone secondary/sm icon. The added
1030
+ // target class is purely additive — it changes nothing until a theme
1031
+ // targets it.
1032
+ render(
1033
+ <DateInput
1034
+ label="Date"
1035
+ value="2026-01-15"
1036
+ onChange={() => {}}
1037
+ hasClear
1038
+ />,
1039
+ );
1040
+ const icon = getClearIcon();
1041
+
1042
+ const {container: refContainer} = render(
1043
+ <Icon icon="close" size="sm" color="secondary" />,
1044
+ );
1045
+ const refIcon = refContainer.querySelector('.astryx-icon') as HTMLElement;
1046
+
1047
+ const styleClasses = (el: HTMLElement) =>
1048
+ el.className
1049
+ .split(' ')
1050
+ .filter(c => c !== 'astryx-date-input-clear-icon')
1051
+ .sort();
1052
+
1053
+ expect(styleClasses(icon)).toEqual(styleClasses(refIcon));
1054
+ });
1055
+
1056
+ it('exposes date-input-clear-icon so a theme reaches the icon color, size, and hover', () => {
1057
+ // jsdom cannot resolve the @layer cascade, so the DOM-class assertion above
1058
+ // (target lands on the icon element) plus this generation assertion (the
1059
+ // theme emits same-element icon rules in @layer astryx-theme) together
1060
+ // prove the seam: a same-element theme rule wins over the icon's own
1061
+ // base-layer color/size.
1062
+ const theme = defineTheme({
1063
+ name: 'date-input-clear-icon-test',
1064
+ components: {
1065
+ 'date-input-clear-icon': {
1066
+ base: {
1067
+ width: '12px',
1068
+ height: '12px',
1069
+ fontSize: '12px',
1070
+ color: 'var(--color-icon-secondary)',
1071
+ ':hover': {color: 'var(--color-icon-primary)'},
1072
+ },
1073
+ },
1074
+ },
1075
+ });
1076
+ const css = generateThemeCSSFlat(theme);
1077
+ expect(css).toContain('.astryx-date-input-clear-icon {');
1078
+ expect(css).toContain('width: 12px');
1079
+ expect(css).toContain('height: 12px');
1080
+ expect(css).toContain('.astryx-date-input-clear-icon:hover {');
1081
+ expect(css).toContain('color: var(--color-icon-primary)');
1082
+ });
1083
+ });
1084
+
1085
+ describe('DateInput calendar-toggle icon theme target', () => {
1086
+ const iconIn = (button: HTMLElement): HTMLElement => {
1087
+ const icon = button.querySelector('.astryx-icon');
1088
+ if (icon == null) {
1089
+ throw new Error('toggle icon not found');
1090
+ }
1091
+ return icon as HTMLElement;
1092
+ };
1093
+
1094
+ it('renders the astryx-date-input-toggle-icon target on the toggle glyph', () => {
1095
+ render(<DateInput label="Date" onChange={() => {}} />);
1096
+ const icon = iconIn(getButton('Open calendar'));
1097
+ // The stable theme target lands on the icon element itself (not the
1098
+ // button), so a theme can restyle just this glyph (color, size, hover) —
1099
+ // and each open/closed state — via `defineTheme`. A button-level target
1100
+ // could not reach the icon's own color/size.
1101
+ expect(icon).toHaveClass('astryx-date-input-toggle-icon');
1102
+ expect(icon).toHaveClass('astryx-icon');
1103
+ // Open/closed state is reflected so a theme can target each state alone.
1104
+ expect(icon).toHaveAttribute('data-state', 'collapsed');
1105
+ });
1106
+
1107
+ it('reflects the expanded state on the toggle icon when the popover is open', async () => {
1108
+ render(<DateInput label="Date" onChange={() => {}} />);
1109
+ // Capture the toggle before opening — its aria-label changes to the close
1110
+ // label once open, but the element reference (and its icon) is stable.
1111
+ const toggle = getButton('Open calendar');
1112
+ fireEvent.click(toggle);
1113
+ await waitFor(() => {
1114
+ expect(iconIn(toggle)).toHaveAttribute('data-state', 'expanded');
1115
+ });
1116
+ });
1117
+
1118
+ it('keeps the calendar-toggle button functional alongside the target', () => {
1119
+ render(<DateInput label="Date" onChange={() => {}} />);
1120
+ const toggle = getButton('Open calendar');
1121
+ expect(toggle.tagName).toBe('BUTTON');
1122
+ });
1123
+
1124
+ it('renders the default icon (secondary color, sm size) byte-identically', () => {
1125
+ // Pixel-identical default guard: the toggle glyph must carry the exact same
1126
+ // StyleX color/size classes as a standalone secondary/sm icon. The added
1127
+ // target class + data-state are purely additive — they change nothing until
1128
+ // a theme targets them.
1129
+ render(<DateInput label="Date" onChange={() => {}} />);
1130
+ const icon = iconIn(getButton('Open calendar'));
1131
+
1132
+ const {container: refContainer} = render(
1133
+ <Icon icon="calendar" size="sm" color="secondary" />,
1134
+ );
1135
+ const refIcon = refContainer.querySelector('.astryx-icon') as HTMLElement;
1136
+
1137
+ // Exclude the additive theme-target classes (the stable target + its
1138
+ // reflected state class) so only the StyleX color/size classes remain.
1139
+ const themeTargetClasses = new Set([
1140
+ 'astryx-date-input-toggle-icon',
1141
+ 'collapsed',
1142
+ 'expanded',
1143
+ ]);
1144
+ const styleClasses = (el: HTMLElement) =>
1145
+ el.className
1146
+ .split(' ')
1147
+ .filter(c => !themeTargetClasses.has(c))
1148
+ .sort();
1149
+
1150
+ expect(styleClasses(icon)).toEqual(styleClasses(refIcon));
1151
+ });
1152
+
1153
+ it('exposes date-input-toggle-icon so a theme reaches the icon size and per-state color', () => {
1154
+ // jsdom cannot resolve the @layer cascade, so the DOM-class assertions
1155
+ // above (target lands on the icon element) plus this generation assertion
1156
+ // (the theme emits same-element icon rules in @layer astryx-theme) together
1157
+ // prove the seam: a same-element theme rule wins over the icon's own
1158
+ // base-layer color/size.
1159
+ const theme = defineTheme({
1160
+ name: 'date-input-toggle-icon-test',
1161
+ components: {
1162
+ 'date-input-toggle-icon': {
1163
+ base: {width: '14px', height: '14px', fontSize: '14px'},
1164
+ 'state:expanded': {color: 'var(--color-icon-primary)'},
1165
+ },
1166
+ },
1167
+ });
1168
+ const css = generateThemeCSSFlat(theme);
1169
+ expect(css).toContain('.astryx-date-input-toggle-icon {');
1170
+ expect(css).toContain('width: 14px');
1171
+ expect(css).toContain('height: 14px');
1172
+ expect(css).toContain('.astryx-date-input-toggle-icon.expanded');
1173
+ expect(css).toContain('color: var(--color-icon-primary)');
1174
+ });
1175
+ });
@@ -25,7 +25,6 @@ import {
25
25
  useTransition,
26
26
  } from 'react';
27
27
  import * as stylex from '@stylexjs/stylex';
28
- import type {IconName} from '../Icon';
29
28
  import {
30
29
  colorVars,
31
30
  sizeVars,
@@ -37,11 +36,11 @@ import {
37
36
  import {
38
37
  Field,
39
38
  type InputStatus,
40
- type InputStatusType,
41
39
  inputWrapperStyles,
42
40
  inputStatusBorderStyles,
43
41
  inputStatusHoverShadowStyles,
44
42
  inputStatusFocusWithinStyles,
43
+ type FieldStatusVariant,
45
44
  } from '../Field';
46
45
  import {Icon} from '../Icon';
47
46
  import {VisuallyHidden} from '../VisuallyHidden';
@@ -51,6 +50,7 @@ import {useSize} from '../SizeContext/SizeContext';
51
50
  import {Spinner} from '../Spinner';
52
51
  import {Calendar, type ISODateString, type CalendarHandle} from '../Calendar';
53
52
  import {useCalendarConstraints} from '../Calendar/hooks';
53
+ import {useInputStatusIcon} from '../hooks/useInputStatusIcon';
54
54
  import {usePopover} from '../Popover';
55
55
  import {useTooltip} from '../Tooltip';
56
56
  import {getInputARIA, parseDateInput} from '../utils';
@@ -278,6 +278,14 @@ export interface DateInputProps extends Omit<
278
278
  * If message is provided, displays below the input.
279
279
  */
280
280
  status?: InputStatus;
281
+ /**
282
+ * How the status message is placed relative to the input.
283
+ * - 'attached': message overlaps directly below the input (bordered treatment)
284
+ * - 'detached': message floats below as a separate element with spacing
285
+ * - 'tooltip': no message box; the status icon becomes a focusable info-tip button that reveals the message on hover, keyboard focus, or tap
286
+ * @default 'attached'
287
+ */
288
+ statusVariant?: FieldStatusVariant;
281
289
 
282
290
  /**
283
291
  * Width of the field. Numbers are treated as pixels, strings are used as-is
@@ -365,6 +373,7 @@ export function DateInput({
365
373
  placeholder: placeholderFromProps,
366
374
  size: sizeProp,
367
375
  status,
376
+ statusVariant = 'attached',
368
377
  labelTooltip,
369
378
  hasClear = false,
370
379
  numberOfMonths = 1,
@@ -410,30 +419,24 @@ export function DateInput({
410
419
  isEnabled: showsDisabledMessage,
411
420
  });
412
421
 
413
- // Status icon mapping
414
- const statusIconMap: Record<InputStatusType, IconName> = {
415
- warning: 'warning',
416
- error: 'error',
417
- success: 'success',
418
- };
419
-
420
- const statusIconColorMap: Record<
421
- InputStatusType,
422
- 'warning' | 'error' | 'success'
423
- > = {
424
- warning: 'warning',
425
- error: 'error',
426
- success: 'success',
427
- };
428
-
429
422
  // Constraint checking for text input validation (reuses calendar logic)
430
423
  const {isDateDisabled} = useCalendarConstraints({min, max, dateConstraints});
431
424
 
425
+ const {statusIcon, describedBy: statusTooltipDescribedBy} =
426
+ useInputStatusIcon({
427
+ status,
428
+ statusVariant,
429
+ isInGroup: !!inputGroup,
430
+ });
431
+
432
432
  const {ariaLabelledBy, ariaDescribedBy} = getInputARIA(
433
433
  inputLabelID,
434
434
  [
435
435
  description ? descriptionID : null,
436
- status?.message ? statusMessageID : null,
436
+ statusVariant !== 'tooltip' && status?.message ? statusMessageID : null,
437
+ // The tooltip variant renders no message box; describe the input by the
438
+ // tooltip's content instead so the status is still announced.
439
+ statusTooltipDescribedBy,
437
440
  showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
438
441
  ],
439
442
  inputGroup,
@@ -638,7 +641,9 @@ export function DateInput({
638
641
  sizeStyles[size],
639
642
  isEffectivelyDisabled && inputWrapperStyles.disabled,
640
643
  status && inputStatusBorderStyles[status.type],
641
- status && inputStatusHoverShadowStyles[status.type],
644
+ status &&
645
+ !isEffectivelyDisabled &&
646
+ inputStatusHoverShadowStyles[status.type],
642
647
  status && inputStatusFocusWithinStyles[status.type],
643
648
  inputGroup && groupStyles.inGroup,
644
649
  xstyle,
@@ -660,7 +665,20 @@ export function DateInput({
660
665
  styles.iconButton,
661
666
  isEffectivelyDisabled && styles.iconButtonDisabled,
662
667
  )}>
663
- <Icon icon="calendar" size="sm" color="secondary" />
668
+ <Icon
669
+ icon="calendar"
670
+ size="sm"
671
+ color="secondary"
672
+ // Stable theme target on the toggle glyph itself, so a theme can
673
+ // restyle just this icon (color, size, hover) — and each open/closed
674
+ // state — via `defineTheme`. Same-element rules in @layer astryx-theme
675
+ // win over the icon's own base color/size, which a button-level target
676
+ // could not reach. Reflects the popover's open/closed state as a
677
+ // `data-state` attribute.
678
+ {...themeProps('date-input-toggle-icon', {
679
+ state: popover.isOpen ? 'expanded' : 'collapsed',
680
+ })}
681
+ />
664
682
  </button>
665
683
  <input
666
684
  ref={mergeRefs(ref, inputRef)}
@@ -712,17 +730,20 @@ export function DateInput({
712
730
  onClick={handleClear}
713
731
  aria-label={t('@astryx.dateInput.clear', {label})}
714
732
  {...stylex.props(styles.iconButton)}>
715
- <Icon icon="close" size="sm" color="secondary" />
733
+ <Icon
734
+ icon="close"
735
+ size="sm"
736
+ color="secondary"
737
+ // Stable theme target on the clear glyph itself, so a theme can
738
+ // restyle just this icon (color, size, hover) via `defineTheme`.
739
+ // Same-element rules in @layer astryx-theme win over the icon's own
740
+ // base color/size, which a button-level target could not reach.
741
+ {...themeProps('date-input-clear-icon')}
742
+ />
716
743
  </button>
717
744
  )}
718
745
  {isBusy && <Spinner size="sm" />}
719
- {status && !inputGroup && (
720
- <Icon
721
- icon={statusIconMap[status.type]}
722
- size="md"
723
- color={statusIconColorMap[status.type]}
724
- />
725
- )}
746
+ {statusIcon}
726
747
  {popover.render(
727
748
  <Calendar
728
749
  handleRef={calendarRef}
@@ -764,6 +785,7 @@ export function DateInput({
764
785
  }
765
786
  : undefined
766
787
  }
788
+ statusVariant={statusVariant}
767
789
  labelTooltip={labelTooltip}
768
790
  width={width}>
769
791
  {inputWrapper}
@@ -127,6 +127,13 @@ export const docs = {
127
127
  type: "{type: 'warning' | 'error' | 'success', message?: string}",
128
128
  description: 'Status indicator for error, warning, or success states.',
129
129
  },
130
+ {
131
+ name: 'statusVariant',
132
+ type: "'attached' | 'detached' | 'tooltip'",
133
+ description:
134
+ '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.',
135
+ default: "'attached'",
136
+ },
130
137
  {
131
138
  name: 'labelTooltip',
132
139
  type: 'string',
@@ -138,6 +145,12 @@ export const docs = {
138
145
  description: 'Number of months in the calendar.',
139
146
  default: '2',
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: 'xstyle',
143
156
  type: 'StyleXStyles',
@@ -147,6 +160,8 @@ export const docs = {
147
160
  theming: {
148
161
  targets: [
149
162
  {className: 'astryx-date-range-input', visualProps: ['size', 'status']},
163
+ {className: 'astryx-date-range-input-toggle-icon', states: ['state']},
164
+ {className: 'astryx-date-range-input-clear-icon'},
150
165
  ],
151
166
  },
152
167
  usage: {
@@ -296,6 +311,7 @@ export const docsDense = {
296
311
  placeholder: 'placeholder when empty',
297
312
  size: 'trigger size',
298
313
  status: 'error/warning/success status',
314
+ 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.',
299
315
  labelTooltip: 'tooltip via info icon at label end',
300
316
  numberOfMonths: 'months in calendar (default 2)',
301
317
  changeAction: