@astryxdesign/core 0.1.9-canary.d2d84f8 → 0.1.9-canary.e00a241

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 (414) hide show
  1. package/dist/Avatar/Avatar.d.ts +6 -1
  2. package/dist/Avatar/Avatar.d.ts.map +1 -1
  3. package/dist/Avatar/Avatar.js +46 -12
  4. package/dist/Avatar/AvatarStatusDot.d.ts +6 -4
  5. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  6. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  7. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -4
  8. package/dist/Breadcrumbs/index.d.ts +1 -0
  9. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  10. package/dist/Breadcrumbs/index.js +2 -1
  11. package/dist/Calendar/Calendar.d.ts.map +1 -1
  12. package/dist/Calendar/Calendar.js +50 -9
  13. package/dist/Calendar/dayCellUtils.d.ts +4 -4
  14. package/dist/Calendar/dayCellUtils.js +2 -2
  15. package/dist/Calendar/styles.d.ts +30 -31
  16. package/dist/Calendar/styles.d.ts.map +1 -1
  17. package/dist/Calendar/styles.js +30 -31
  18. package/dist/Card/Card.js +1 -1
  19. package/dist/Carousel/Carousel.d.ts.map +1 -1
  20. package/dist/Carousel/Carousel.js +22 -12
  21. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  22. package/dist/Chat/ChatDictationButton.js +3 -1
  23. package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
  24. package/dist/Chat/ChatMessageBubble.js +8 -8
  25. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  26. package/dist/Chat/ChatToolCalls.js +10 -3
  27. package/dist/CheckboxInput/CheckboxInput.js +4 -4
  28. package/dist/Citation/Citation.d.ts +22 -1
  29. package/dist/Citation/Citation.d.ts.map +1 -1
  30. package/dist/Citation/Citation.js +20 -6
  31. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  32. package/dist/CodeBlock/CodeBlock.js +13 -7
  33. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  34. package/dist/Collapsible/Collapsible.js +3 -1
  35. package/dist/CommandPalette/CommandPalette.d.ts +1 -1
  36. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  37. package/dist/CommandPalette/CommandPalette.js +42 -3
  38. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  39. package/dist/ContextMenu/ContextMenu.js +14 -5
  40. package/dist/ContextMenu/index.d.ts +1 -0
  41. package/dist/ContextMenu/index.d.ts.map +1 -1
  42. package/dist/ContextMenu/index.js +3 -1
  43. package/dist/DateInput/DateInput.d.ts +1 -0
  44. package/dist/DateInput/DateInput.d.ts.map +1 -1
  45. package/dist/DateInput/DateInput.js +31 -19
  46. package/dist/DateRangeInput/DateRangeInput.d.ts +1 -0
  47. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  48. package/dist/DateRangeInput/DateRangeInput.js +20 -19
  49. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  50. package/dist/DateTimeInput/DateTimeInput.js +23 -18
  51. package/dist/Divider/Divider.d.ts +2 -2
  52. package/dist/Divider/Divider.d.ts.map +1 -1
  53. package/dist/Divider/Divider.js +13 -0
  54. package/dist/DropdownMenu/DropdownMenu.d.ts +6 -0
  55. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  56. package/dist/DropdownMenu/DropdownMenu.js +22 -5
  57. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  58. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +4 -1
  59. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  60. package/dist/DropdownMenu/DropdownMenuItem.js +4 -1
  61. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  62. package/dist/DropdownMenu/DropdownMenuRadioItem.js +4 -1
  63. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +104 -0
  64. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -0
  65. package/dist/DropdownMenu/DropdownMenuSubMenu.js +427 -0
  66. package/dist/DropdownMenu/index.d.ts +10 -0
  67. package/dist/DropdownMenu/index.d.ts.map +1 -1
  68. package/dist/DropdownMenu/index.js +13 -0
  69. package/dist/DropdownMenu/menuItemHover.d.ts +23 -0
  70. package/dist/DropdownMenu/menuItemHover.d.ts.map +1 -0
  71. package/dist/DropdownMenu/menuItemHover.js +33 -0
  72. package/dist/DropdownMenu/menuItemRoles.d.ts +8 -0
  73. package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -1
  74. package/dist/DropdownMenu/menuItemRoles.js +10 -1
  75. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  76. package/dist/DropdownMenu/renderDropdownItems.js +21 -6
  77. package/dist/Field/Field.d.ts +3 -1
  78. package/dist/Field/Field.d.ts.map +1 -1
  79. package/dist/Field/Field.js +4 -1
  80. package/dist/Field/inputStyles.stylex.d.ts +1 -0
  81. package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
  82. package/dist/Field/inputStyles.stylex.js +1 -0
  83. package/dist/FieldStatus/FieldStatus.d.ts +4 -1
  84. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  85. package/dist/FieldStatus/FieldStatus.js +4 -2
  86. package/dist/FileInput/FileInput.d.ts +1 -0
  87. package/dist/FileInput/FileInput.d.ts.map +1 -1
  88. package/dist/FileInput/FileInput.js +91 -65
  89. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  90. package/dist/HoverCard/useHoverCard.js +10 -4
  91. package/dist/Icon/Icon.d.ts +15 -1
  92. package/dist/Icon/Icon.d.ts.map +1 -1
  93. package/dist/Icon/Icon.js +19 -3
  94. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  95. package/dist/Lightbox/Lightbox.js +15 -9
  96. package/dist/List/List.d.ts.map +1 -1
  97. package/dist/List/List.js +6 -3
  98. package/dist/Markdown/Markdown.d.ts.map +1 -1
  99. package/dist/Markdown/Markdown.js +7 -0
  100. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  101. package/dist/MultiSelector/MultiSelector.js +65 -40
  102. package/dist/NumberInput/NumberInput.d.ts +1 -0
  103. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  104. package/dist/NumberInput/NumberInput.js +20 -17
  105. package/dist/Outline/Outline.js +2 -2
  106. package/dist/RadioList/RadioList.d.ts +2 -2
  107. package/dist/RadioList/RadioListItem.js +4 -4
  108. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  109. package/dist/Resizable/ResizeHandle.js +49 -19
  110. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
  111. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  112. package/dist/SegmentedControl/SegmentedControlItem.js +2 -1
  113. package/dist/Selector/Selector.d.ts.map +1 -1
  114. package/dist/Selector/Selector.js +58 -16
  115. package/dist/SideNav/SideNav.d.ts.map +1 -1
  116. package/dist/SideNav/SideNav.js +5 -5
  117. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  118. package/dist/SideNav/SideNavCollapseButton.js +13 -10
  119. package/dist/SideNav/SideNavItem.d.ts +1 -1
  120. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  121. package/dist/SideNav/SideNavItem.js +12 -10
  122. package/dist/Slider/Slider.d.ts.map +1 -1
  123. package/dist/Slider/Slider.js +87 -34
  124. package/dist/Spinner/Spinner.d.ts +3 -2
  125. package/dist/Spinner/Spinner.d.ts.map +1 -1
  126. package/dist/Spinner/Spinner.js +11 -2
  127. package/dist/Switch/Switch.d.ts +8 -1
  128. package/dist/Switch/Switch.d.ts.map +1 -1
  129. package/dist/Switch/Switch.js +160 -91
  130. package/dist/TabList/TabList.d.ts +2 -11
  131. package/dist/TabList/TabList.d.ts.map +1 -1
  132. package/dist/TabList/TabList.js +5 -9
  133. package/dist/TabList/TabListContext.d.ts +0 -5
  134. package/dist/TabList/TabListContext.d.ts.map +1 -1
  135. package/dist/TabList/TabListContext.js +0 -5
  136. package/dist/Table/BaseTable.d.ts.map +1 -1
  137. package/dist/Table/BaseTable.js +30 -4
  138. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  139. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +15 -11
  140. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  141. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +36 -26
  142. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  143. package/dist/Table/plugins/selection/useTableSelection.js +3 -0
  144. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  145. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +14 -6
  146. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  147. package/dist/Table/plugins/tree/useTableTreeData.js +29 -23
  148. package/dist/Table/types.d.ts +32 -0
  149. package/dist/Table/types.d.ts.map +1 -1
  150. package/dist/TextArea/TextArea.d.ts +1 -0
  151. package/dist/TextArea/TextArea.d.ts.map +1 -1
  152. package/dist/TextArea/TextArea.js +16 -19
  153. package/dist/TextInput/TextInput.d.ts +1 -0
  154. package/dist/TextInput/TextInput.d.ts.map +1 -1
  155. package/dist/TextInput/TextInput.js +18 -17
  156. package/dist/TimeInput/TimeInput.d.ts +1 -0
  157. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  158. package/dist/TimeInput/TimeInput.js +15 -17
  159. package/dist/Timestamp/Timestamp.d.ts +31 -3
  160. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  161. package/dist/Timestamp/Timestamp.js +47 -76
  162. package/dist/Timestamp/formatInstant.d.ts +41 -0
  163. package/dist/Timestamp/formatInstant.d.ts.map +1 -0
  164. package/dist/Timestamp/formatInstant.js +177 -0
  165. package/dist/Timestamp/index.d.ts +1 -0
  166. package/dist/Timestamp/index.d.ts.map +1 -1
  167. package/dist/Timestamp/tooltipEntries.d.ts +56 -0
  168. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -0
  169. package/dist/Timestamp/tooltipEntries.js +163 -0
  170. package/dist/Token/Token.d.ts +30 -3
  171. package/dist/Token/Token.d.ts.map +1 -1
  172. package/dist/Token/Token.js +17 -2
  173. package/dist/Token/index.d.ts +1 -1
  174. package/dist/Token/index.d.ts.map +1 -1
  175. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  176. package/dist/Tokenizer/Tokenizer.js +2 -2
  177. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  178. package/dist/Tooltip/useTooltip.js +17 -1
  179. package/dist/TopNav/TopNavItem.d.ts +3 -1
  180. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  181. package/dist/TopNav/TopNavItem.js +30 -3
  182. package/dist/TreeList/TreeList.d.ts.map +1 -1
  183. package/dist/TreeList/TreeList.js +1 -0
  184. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  185. package/dist/TreeList/TreeListBranches.js +8 -6
  186. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  187. package/dist/TreeList/TreeListItem.js +32 -14
  188. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  189. package/dist/Typeahead/BaseTypeahead.js +19 -10
  190. package/dist/Typeahead/Typeahead.js +1 -1
  191. package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
  192. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  193. package/dist/Typeahead/TypeaheadItem.js +20 -10
  194. package/dist/astryx.css +38 -14
  195. package/dist/astryx.umd.js +52 -52
  196. package/dist/astryx.umd.js.map +4 -4
  197. package/dist/hooks/index.d.ts +2 -0
  198. package/dist/hooks/index.d.ts.map +1 -1
  199. package/dist/hooks/index.js +1 -0
  200. package/dist/hooks/isRtlElement.d.ts +18 -0
  201. package/dist/hooks/isRtlElement.d.ts.map +1 -0
  202. package/dist/hooks/isRtlElement.js +27 -0
  203. package/dist/hooks/useAnnounce.d.ts +3 -0
  204. package/dist/hooks/useAnnounce.d.ts.map +1 -1
  205. package/dist/hooks/useAnnounce.js +39 -1
  206. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  207. package/dist/hooks/useFocusTrap.js +52 -11
  208. package/dist/hooks/useGridFocus.d.ts +6 -1
  209. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  210. package/dist/hooks/useGridFocus.js +12 -9
  211. package/dist/hooks/useHotkeys.d.ts +9 -0
  212. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  213. package/dist/hooks/useHotkeys.js +9 -0
  214. package/dist/hooks/useInputStatusIcon.d.ts +38 -0
  215. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -0
  216. package/dist/hooks/useInputStatusIcon.js +158 -0
  217. package/dist/hooks/useListFocus.d.ts +39 -1
  218. package/dist/hooks/useListFocus.d.ts.map +1 -1
  219. package/dist/hooks/useListFocus.js +54 -9
  220. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  221. package/dist/theme/derivedVarRegistry.js +13 -0
  222. package/dist/utils/index.d.ts +1 -0
  223. package/dist/utils/index.d.ts.map +1 -1
  224. package/dist/utils/index.js +2 -1
  225. package/dist/utils/plainDate.d.ts +17 -0
  226. package/dist/utils/plainDate.d.ts.map +1 -1
  227. package/dist/utils/plainDate.js +23 -4
  228. package/dist/utils/rtlStyles.d.ts +24 -0
  229. package/dist/utils/rtlStyles.d.ts.map +1 -0
  230. package/dist/utils/rtlStyles.js +27 -0
  231. package/locales/en.json +76 -0
  232. package/locales/pseudo.json +57 -0
  233. package/package.json +6 -1
  234. package/src/Avatar/Avatar.doc.mjs +18 -4
  235. package/src/Avatar/Avatar.test.tsx +106 -0
  236. package/src/Avatar/Avatar.tsx +62 -10
  237. package/src/Avatar/AvatarStatusDot.doc.mjs +4 -4
  238. package/src/Avatar/AvatarStatusDot.tsx +6 -4
  239. package/src/AvatarGroup/AvatarGroup.test.tsx +16 -1
  240. package/src/Breadcrumbs/BreadcrumbItem.tsx +10 -10
  241. package/src/Breadcrumbs/Breadcrumbs.test.tsx +51 -0
  242. package/src/Breadcrumbs/index.ts +4 -0
  243. package/src/Button/Button.doc.mjs +0 -4
  244. package/src/Calendar/Calendar.test.tsx +170 -20
  245. package/src/Calendar/Calendar.tsx +73 -16
  246. package/src/Calendar/dayCellUtils.test.ts +18 -18
  247. package/src/Calendar/dayCellUtils.ts +2 -2
  248. package/src/Calendar/styles.ts +33 -39
  249. package/src/Card/Card.tsx +1 -1
  250. package/src/Carousel/Carousel.tsx +25 -6
  251. package/src/Chat/ChatComposer.doc.mjs +2 -2
  252. package/src/Chat/ChatComposerInput.doc.mjs +1 -1
  253. package/src/Chat/ChatDictationButton.tsx +7 -1
  254. package/src/Chat/ChatMessageBubble.tsx +15 -10
  255. package/src/Chat/ChatSystemMessage.test.tsx +5 -0
  256. package/src/Chat/ChatToolCalls.tsx +9 -2
  257. package/src/CheckboxInput/CheckboxInput.doc.mjs +6 -0
  258. package/src/CheckboxInput/CheckboxInput.tsx +4 -4
  259. package/src/CheckboxList/CheckboxList.doc.mjs +6 -0
  260. package/src/Citation/Citation.doc.mjs +4 -3
  261. package/src/Citation/Citation.test.tsx +97 -0
  262. package/src/Citation/Citation.tsx +43 -11
  263. package/src/CodeBlock/CodeBlock.tsx +35 -8
  264. package/src/Collapsible/Collapsible.doc.mjs +1 -0
  265. package/src/Collapsible/Collapsible.test.tsx +7 -0
  266. package/src/Collapsible/Collapsible.tsx +16 -6
  267. package/src/CommandPalette/CommandPalette.test.tsx +95 -1
  268. package/src/CommandPalette/CommandPalette.tsx +41 -2
  269. package/src/ContextMenu/ContextMenu.test.tsx +42 -0
  270. package/src/ContextMenu/ContextMenu.tsx +13 -11
  271. package/src/ContextMenu/index.ts +5 -0
  272. package/src/DateInput/DateInput.doc.mjs +15 -5
  273. package/src/DateInput/DateInput.test.tsx +210 -3
  274. package/src/DateInput/DateInput.tsx +41 -29
  275. package/src/DateRangeInput/DateRangeInput.doc.mjs +11 -3
  276. package/src/DateRangeInput/DateRangeInput.test.tsx +120 -3
  277. package/src/DateRangeInput/DateRangeInput.tsx +29 -27
  278. package/src/DateTimeInput/DateTimeInput.doc.mjs +22 -1
  279. package/src/DateTimeInput/DateTimeInput.test.tsx +112 -0
  280. package/src/DateTimeInput/DateTimeInput.tsx +44 -39
  281. package/src/Divider/Divider.test.tsx +32 -8
  282. package/src/Divider/Divider.tsx +14 -1
  283. package/src/DropdownMenu/DropdownMenu.test.tsx +60 -0
  284. package/src/DropdownMenu/DropdownMenu.tsx +31 -12
  285. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +8 -2
  286. package/src/DropdownMenu/DropdownMenuItem.tsx +8 -2
  287. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +8 -2
  288. package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +88 -0
  289. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +635 -0
  290. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +553 -0
  291. package/src/DropdownMenu/index.ts +16 -0
  292. package/src/DropdownMenu/menuItemHover.ts +39 -0
  293. package/src/DropdownMenu/menuItemRoles.ts +9 -0
  294. package/src/DropdownMenu/renderDropdownItems.tsx +29 -15
  295. package/src/Field/Field.tsx +14 -9
  296. package/src/Field/inputStyles.stylex.ts +2 -0
  297. package/src/FieldStatus/FieldStatus.tsx +10 -4
  298. package/src/FileInput/FileInput.doc.mjs +13 -7
  299. package/src/FileInput/FileInput.test.tsx +84 -16
  300. package/src/FileInput/FileInput.tsx +96 -79
  301. package/src/HoverCard/useHoverCard.tsx +15 -3
  302. package/src/Icon/Icon.doc.mjs +13 -0
  303. package/src/Icon/Icon.test.tsx +89 -0
  304. package/src/Icon/Icon.tsx +51 -3
  305. package/src/Lightbox/Lightbox.tsx +11 -3
  306. package/src/List/List.test.tsx +17 -29
  307. package/src/List/List.tsx +6 -1
  308. package/src/Markdown/Markdown.test.tsx +21 -0
  309. package/src/Markdown/Markdown.tsx +4 -0
  310. package/src/MultiSelector/MultiSelector.doc.mjs +11 -0
  311. package/src/MultiSelector/MultiSelector.test.tsx +306 -3
  312. package/src/MultiSelector/MultiSelector.tsx +88 -57
  313. package/src/NumberInput/NumberInput.doc.mjs +11 -5
  314. package/src/NumberInput/NumberInput.test.tsx +48 -0
  315. package/src/NumberInput/NumberInput.tsx +25 -26
  316. package/src/Outline/Outline.test.tsx +12 -12
  317. package/src/Outline/Outline.tsx +2 -2
  318. package/src/RadioList/RadioList.doc.mjs +6 -0
  319. package/src/RadioList/RadioList.tsx +2 -2
  320. package/src/RadioList/RadioListItem.tsx +4 -4
  321. package/src/Resizable/ResizeHandle.test.tsx +94 -0
  322. package/src/Resizable/ResizeHandle.tsx +70 -20
  323. package/src/SegmentedControl/SegmentedControlItem.tsx +2 -0
  324. package/src/Selector/Selector.doc.mjs +8 -0
  325. package/src/Selector/Selector.test.tsx +328 -3
  326. package/src/Selector/Selector.tsx +70 -19
  327. package/src/SideNav/SideNav.tsx +1 -8
  328. package/src/SideNav/SideNavCollapseButton.tsx +9 -7
  329. package/src/SideNav/SideNavItem.tsx +5 -3
  330. package/src/Slider/Slider.doc.mjs +6 -0
  331. package/src/Slider/Slider.test.tsx +240 -12
  332. package/src/Slider/Slider.tsx +96 -23
  333. package/src/Spinner/Spinner.test.tsx +10 -8
  334. package/src/Spinner/Spinner.tsx +15 -5
  335. package/src/Switch/Switch.doc.mjs +142 -42
  336. package/src/Switch/Switch.test.tsx +22 -0
  337. package/src/Switch/Switch.tsx +90 -33
  338. package/src/TabList/TabList.doc.mjs +0 -6
  339. package/src/TabList/TabList.test.tsx +3 -15
  340. package/src/TabList/TabList.tsx +6 -17
  341. package/src/TabList/TabListContext.ts +0 -6
  342. package/src/Table/BaseTable.tsx +34 -2
  343. package/src/Table/Table.doc.mjs +11 -0
  344. package/src/Table/Table.test.tsx +85 -0
  345. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +9 -6
  346. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +24 -17
  347. package/src/Table/plugins/selection/useTableSelection.test.tsx +61 -3
  348. package/src/Table/plugins/selection/useTableSelection.tsx +3 -0
  349. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +37 -8
  350. package/src/Table/plugins/tree/useTableTreeData.tsx +17 -13
  351. package/src/Table/types.ts +33 -0
  352. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  353. package/src/TextArea/TextArea.doc.mjs +11 -5
  354. package/src/TextArea/TextArea.tsx +17 -26
  355. package/src/TextInput/TextInput.doc.mjs +11 -5
  356. package/src/TextInput/TextInput.test.tsx +130 -3
  357. package/src/TextInput/TextInput.tsx +18 -25
  358. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  359. package/src/TimeInput/TimeInput.doc.mjs +11 -5
  360. package/src/TimeInput/TimeInput.tsx +14 -25
  361. package/src/Timestamp/Timestamp.doc.mjs +32 -6
  362. package/src/Timestamp/Timestamp.test.tsx +371 -0
  363. package/src/Timestamp/Timestamp.tsx +96 -93
  364. package/src/Timestamp/formatInstant.ts +218 -0
  365. package/src/Timestamp/index.ts +4 -0
  366. package/src/Timestamp/tooltipEntries.test.ts +552 -0
  367. package/src/Timestamp/tooltipEntries.ts +210 -0
  368. package/src/Token/Token.test.tsx +21 -15
  369. package/src/Token/Token.tsx +31 -14
  370. package/src/Token/index.ts +1 -1
  371. package/src/Tokenizer/Tokenizer.doc.mjs +8 -1
  372. package/src/Tokenizer/Tokenizer.tsx +4 -2
  373. package/src/Tooltip/Tooltip.test.tsx +46 -0
  374. package/src/Tooltip/useTooltip.tsx +20 -0
  375. package/src/TopNav/TopNav.test.tsx +93 -2
  376. package/src/TopNav/TopNavItem.doc.mjs +3 -3
  377. package/src/TopNav/TopNavItem.tsx +35 -6
  378. package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +1 -1
  379. package/src/TreeList/TreeList.doc.mjs +6 -0
  380. package/src/TreeList/TreeList.test.tsx +50 -10
  381. package/src/TreeList/TreeList.tsx +5 -0
  382. package/src/TreeList/TreeListBranches.tsx +8 -6
  383. package/src/TreeList/TreeListItem.tsx +33 -11
  384. package/src/Typeahead/BaseTypeahead.tsx +29 -14
  385. package/src/Typeahead/Typeahead.doc.mjs +6 -0
  386. package/src/Typeahead/Typeahead.test.tsx +27 -0
  387. package/src/Typeahead/Typeahead.tsx +1 -1
  388. package/src/Typeahead/TypeaheadItem.tsx +2 -1
  389. package/src/__tests__/inputWidthContract.test.tsx +62 -0
  390. package/src/doc-mjs.d.ts +19 -0
  391. package/src/hooks/index.ts +6 -0
  392. package/src/hooks/isRtlElement.ts +27 -0
  393. package/src/hooks/useAnnounce.test.tsx +75 -2
  394. package/src/hooks/useAnnounce.ts +47 -1
  395. package/src/hooks/useFocusTrap.test.tsx +104 -0
  396. package/src/hooks/useFocusTrap.ts +61 -12
  397. package/src/hooks/useGridFocus.doc.mjs +3 -3
  398. package/src/hooks/useGridFocus.test.tsx +41 -0
  399. package/src/hooks/useGridFocus.ts +21 -10
  400. package/src/hooks/useHotkeys.ts +9 -0
  401. package/src/hooks/useInputStatusIcon.test.tsx +162 -0
  402. package/src/hooks/useInputStatusIcon.tsx +224 -0
  403. package/src/hooks/useListFocus.doc.mjs +25 -3
  404. package/src/hooks/useListFocus.test.tsx +153 -1
  405. package/src/hooks/useListFocus.ts +102 -9
  406. package/src/reset.css +0 -26
  407. package/src/reset.test.ts +60 -0
  408. package/src/theme/derivedVarRegistry.test.ts +1 -0
  409. package/src/theme/derivedVarRegistry.ts +6 -0
  410. package/src/theme/generateThemeRules.test.ts +42 -0
  411. package/src/utils/index.ts +2 -0
  412. package/src/utils/plainDate.test.ts +24 -6
  413. package/src/utils/plainDate.ts +18 -4
  414. package/src/utils/rtlStyles.ts +27 -0
@@ -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,7 +36,6 @@ import {
37
36
  import {
38
37
  Field,
39
38
  type InputStatus,
40
- type InputStatusType,
41
39
  inputWrapperStyles,
42
40
  inputStatusBorderStyles,
43
41
  inputStatusHoverShadowStyles,
@@ -52,6 +50,7 @@ import {useSize} from '../SizeContext/SizeContext';
52
50
  import {Spinner} from '../Spinner';
53
51
  import {Calendar, type ISODateString, type CalendarHandle} from '../Calendar';
54
52
  import {useCalendarConstraints} from '../Calendar/hooks';
53
+ import {useInputStatusIcon} from '../hooks/useInputStatusIcon';
55
54
  import {usePopover} from '../Popover';
56
55
  import {useTooltip} from '../Tooltip';
57
56
  import {getInputARIA, parseDateInput} from '../utils';
@@ -283,6 +282,7 @@ export interface DateInputProps extends Omit<
283
282
  * How the status message is placed relative to the input.
284
283
  * - 'attached': message overlaps directly below the input (bordered treatment)
285
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
286
  * @default 'attached'
287
287
  */
288
288
  statusVariant?: FieldStatusVariant;
@@ -419,30 +419,24 @@ export function DateInput({
419
419
  isEnabled: showsDisabledMessage,
420
420
  });
421
421
 
422
- // Status icon mapping
423
- const statusIconMap: Record<InputStatusType, IconName> = {
424
- warning: 'warning',
425
- error: 'error',
426
- success: 'success',
427
- };
428
-
429
- const statusIconColorMap: Record<
430
- InputStatusType,
431
- 'warning' | 'error' | 'success'
432
- > = {
433
- warning: 'warning',
434
- error: 'error',
435
- success: 'success',
436
- };
437
-
438
422
  // Constraint checking for text input validation (reuses calendar logic)
439
423
  const {isDateDisabled} = useCalendarConstraints({min, max, dateConstraints});
440
424
 
425
+ const {statusIcon, describedBy: statusTooltipDescribedBy} =
426
+ useInputStatusIcon({
427
+ status,
428
+ statusVariant,
429
+ isInGroup: !!inputGroup,
430
+ });
431
+
441
432
  const {ariaLabelledBy, ariaDescribedBy} = getInputARIA(
442
433
  inputLabelID,
443
434
  [
444
435
  description ? descriptionID : null,
445
- 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,
446
440
  showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
447
441
  ],
448
442
  inputGroup,
@@ -647,7 +641,9 @@ export function DateInput({
647
641
  sizeStyles[size],
648
642
  isEffectivelyDisabled && inputWrapperStyles.disabled,
649
643
  status && inputStatusBorderStyles[status.type],
650
- status && inputStatusHoverShadowStyles[status.type],
644
+ status &&
645
+ !isEffectivelyDisabled &&
646
+ inputStatusHoverShadowStyles[status.type],
651
647
  status && inputStatusFocusWithinStyles[status.type],
652
648
  inputGroup && groupStyles.inGroup,
653
649
  xstyle,
@@ -669,7 +665,20 @@ export function DateInput({
669
665
  styles.iconButton,
670
666
  isEffectivelyDisabled && styles.iconButtonDisabled,
671
667
  )}>
672
- <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
+ />
673
682
  </button>
674
683
  <input
675
684
  ref={mergeRefs(ref, inputRef)}
@@ -721,17 +730,20 @@ export function DateInput({
721
730
  onClick={handleClear}
722
731
  aria-label={t('@astryx.dateInput.clear', {label})}
723
732
  {...stylex.props(styles.iconButton)}>
724
- <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
+ />
725
743
  </button>
726
744
  )}
727
745
  {isBusy && <Spinner size="sm" />}
728
- {status && !inputGroup && (
729
- <Icon
730
- icon={statusIconMap[status.type]}
731
- size="md"
732
- color={statusIconColorMap[status.type]}
733
- />
734
- )}
746
+ {statusIcon}
735
747
  {popover.render(
736
748
  <Calendar
737
749
  handleRef={calendarRef}
@@ -129,9 +129,9 @@ export const docs = {
129
129
  },
130
130
  {
131
131
  name: 'statusVariant',
132
- type: "'attached' | 'detached'",
132
+ type: "'attached' | 'detached' | 'tooltip'",
133
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.',
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
135
  default: "'attached'",
136
136
  },
137
137
  {
@@ -145,6 +145,12 @@ export const docs = {
145
145
  description: 'Number of months in the calendar.',
146
146
  default: '2',
147
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
+ },
148
154
  {
149
155
  name: 'xstyle',
150
156
  type: 'StyleXStyles',
@@ -154,6 +160,8 @@ export const docs = {
154
160
  theming: {
155
161
  targets: [
156
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'},
157
165
  ],
158
166
  },
159
167
  usage: {
@@ -303,7 +311,7 @@ export const docsDense = {
303
311
  placeholder: 'placeholder when empty',
304
312
  size: 'trigger size',
305
313
  status: 'error/warning/success status',
306
- statusVariant: 'How status message is placed: attached overlaps below input; detached floats below w/ spacing.',
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.',
307
315
  labelTooltip: 'tooltip via info icon at label end',
308
316
  numberOfMonths: 'months in calendar (default 2)',
309
317
  changeAction:
@@ -19,6 +19,9 @@ import userEvent from '@testing-library/user-event';
19
19
  import {getButton, queryButton} from '../__tests__/fastRoleQueries';
20
20
  import {DateRangeInput} from './DateRangeInput';
21
21
  import type {DateRange} from './DateRangeInput';
22
+ import {Icon} from '../Icon';
23
+ import {defineTheme} from '../theme/defineTheme';
24
+ import {generateThemeCSSFlat} from '../theme/generateThemeRules';
22
25
 
23
26
  describe('DateRangeInput', () => {
24
27
  it('renders with label', () => {
@@ -472,11 +475,15 @@ describe('DateRangeInput', () => {
472
475
  });
473
476
  });
474
477
 
475
-
476
478
  describe('DateRangeInput statusVariant forwarding', () => {
477
479
  it('defaults to attached (status renders with data-variant="attached")', () => {
478
480
  const {container} = render(
479
- <DateRangeInput label="Range" value={null} onChange={() => {}} status={{type: 'error', message: 'Required'}} />,
481
+ <DateRangeInput
482
+ label="Range"
483
+ value={null}
484
+ onChange={() => {}}
485
+ status={{type: 'error', message: 'Required'}}
486
+ />,
480
487
  );
481
488
  expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
482
489
  'data-variant',
@@ -486,7 +493,13 @@ describe('DateRangeInput statusVariant forwarding', () => {
486
493
 
487
494
  it('forwards statusVariant="detached" to the underlying Field status', () => {
488
495
  const {container} = render(
489
- <DateRangeInput label="Range" value={null} onChange={() => {}} status={{type: 'error', message: 'Required'}} statusVariant="detached" />,
496
+ <DateRangeInput
497
+ label="Range"
498
+ value={null}
499
+ onChange={() => {}}
500
+ status={{type: 'error', message: 'Required'}}
501
+ statusVariant="detached"
502
+ />,
490
503
  );
491
504
  expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
492
505
  'data-variant',
@@ -494,3 +507,107 @@ describe('DateRangeInput statusVariant forwarding', () => {
494
507
  );
495
508
  });
496
509
  });
510
+
511
+ describe('DateRangeInput icon theme targets', () => {
512
+ const RANGE: DateRange = {start: '2026-03-15', end: '2026-03-22'};
513
+
514
+ // Resolve a glyph span (the astryx-icon element) inside a given button,
515
+ // independent of the theme target class.
516
+ const iconIn = (button: HTMLElement): HTMLElement => {
517
+ const icon = button.querySelector('.astryx-icon');
518
+ if (icon == null) {
519
+ throw new Error('icon not found');
520
+ }
521
+ return icon as HTMLElement;
522
+ };
523
+
524
+ it('renders astryx-date-range-input-clear-icon on the clear glyph', () => {
525
+ render(
526
+ <DateRangeInput
527
+ label="Range"
528
+ value={RANGE}
529
+ onChange={() => {}}
530
+ hasClear
531
+ />,
532
+ );
533
+ // The stable target lands on the icon element itself (not the button), so a
534
+ // theme can restyle just this glyph (color, size, hover) via defineTheme —
535
+ // a button-level target could not reach the icon's own color/size.
536
+ const icon = iconIn(getButton('Clear Range'));
537
+ expect(icon).toHaveClass('astryx-date-range-input-clear-icon');
538
+ expect(icon).toHaveClass('astryx-icon');
539
+ });
540
+
541
+ it('renders astryx-date-range-input-toggle-icon on the calendar-toggle glyph, reflecting state', () => {
542
+ render(<DateRangeInput label="Range" value={null} onChange={() => {}} />);
543
+ const icon = iconIn(getButton('Open calendar'));
544
+ expect(icon).toHaveClass('astryx-date-range-input-toggle-icon');
545
+ expect(icon).toHaveClass('astryx-icon');
546
+ // Closed by default → data-state="collapsed".
547
+ expect(icon).toHaveAttribute('data-state', 'collapsed');
548
+ });
549
+
550
+ it('renders the default icons (secondary color, sm size) byte-identically', () => {
551
+ // Pixel-identical default guard: the glyphs must carry the exact same
552
+ // StyleX color/size classes as a standalone secondary/sm icon. The added
553
+ // target class is purely additive — it changes nothing until a theme
554
+ // targets it.
555
+ render(
556
+ <DateRangeInput
557
+ label="Range"
558
+ value={RANGE}
559
+ onChange={() => {}}
560
+ hasClear
561
+ />,
562
+ );
563
+ const clearIcon = iconIn(getButton('Clear Range'));
564
+
565
+ const {container: refContainer} = render(
566
+ <Icon icon="close" size="sm" color="secondary" />,
567
+ );
568
+ const refIcon = refContainer.querySelector('.astryx-icon') as HTMLElement;
569
+
570
+ const styleClasses = (el: HTMLElement) =>
571
+ el.className
572
+ .split(' ')
573
+ .filter(
574
+ c =>
575
+ c !== 'astryx-date-range-input-clear-icon' &&
576
+ c !== 'astryx-date-range-input-toggle-icon',
577
+ )
578
+ .sort();
579
+
580
+ expect(styleClasses(clearIcon)).toEqual(styleClasses(refIcon));
581
+ });
582
+
583
+ it('exposes the icon targets so a theme reaches icon color, size, and hover', () => {
584
+ // jsdom cannot resolve the @layer cascade, so the DOM-class assertions
585
+ // above (targets land on the icon elements) plus this generation assertion
586
+ // (the theme emits same-element icon rules in @layer astryx-theme) together
587
+ // prove the seam: a same-element theme rule wins over the icon's own
588
+ // base-layer color/size.
589
+ const theme = defineTheme({
590
+ name: 'date-range-input-icon-test',
591
+ components: {
592
+ 'date-range-input-clear-icon': {
593
+ base: {
594
+ width: '12px',
595
+ height: '12px',
596
+ fontSize: '12px',
597
+ color: 'var(--color-icon-secondary)',
598
+ ':hover': {color: 'var(--color-icon-primary)'},
599
+ },
600
+ },
601
+ 'date-range-input-toggle-icon': {
602
+ base: {width: '14px', height: '14px', fontSize: '14px'},
603
+ },
604
+ },
605
+ });
606
+ const css = generateThemeCSSFlat(theme);
607
+ expect(css).toContain('.astryx-date-range-input-clear-icon');
608
+ expect(css).toContain('.astryx-date-range-input-toggle-icon');
609
+ expect(css).toContain(':hover');
610
+ expect(css).toContain('12px');
611
+ expect(css).toContain('14px');
612
+ });
613
+ });
@@ -25,7 +25,6 @@ import {
25
25
  DATE_FORMAT_SHORT,
26
26
  DATE_FORMAT_SHORT_WITH_YEAR,
27
27
  } from '../utils/plainDate';
28
- import type {IconName} from '../Icon';
29
28
  import {
30
29
  colorVars,
31
30
  sizeVars,
@@ -38,7 +37,6 @@ import {
38
37
  import {
39
38
  Field,
40
39
  type InputStatus,
41
- type InputStatusType,
42
40
  inputWrapperStyles,
43
41
  inputStatusBorderStyles,
44
42
  inputStatusHoverShadowStyles,
@@ -54,6 +52,7 @@ import {mergeProps} from '../utils';
54
52
  import type {BaseProps} from '../BaseProps';
55
53
  import type {SizeValue} from '../utils/types';
56
54
  import {useSize} from '../SizeContext/SizeContext';
55
+ import {useInputStatusIcon} from '../hooks/useInputStatusIcon';
57
56
  import {themeProps} from '../utils/themeProps';
58
57
  import {useTranslator} from '../i18n';
59
58
 
@@ -335,6 +334,7 @@ export interface DateRangeInputProps extends Omit<
335
334
  * How the status message is placed relative to the input.
336
335
  * - 'attached': message overlaps directly below the input (bordered treatment)
337
336
  * - 'detached': message floats below as a separate element with spacing
337
+ * - 'tooltip': no message box; the status icon becomes a focusable info-tip button that reveals the message on hover, keyboard focus, or tap
338
338
  * @default 'attached'
339
339
  */
340
340
  statusVariant?: FieldStatusVariant;
@@ -431,25 +431,19 @@ export function DateRangeInput({
431
431
  isEnabled: showsDisabledMessage,
432
432
  });
433
433
 
434
- const statusIconMap: Record<InputStatusType, IconName> = {
435
- warning: 'warning',
436
- error: 'error',
437
- success: 'success',
438
- };
439
-
440
- const statusIconColorMap: Record<
441
- InputStatusType,
442
- 'warning' | 'error' | 'success'
443
- > = {
444
- warning: 'warning',
445
- error: 'error',
446
- success: 'success',
447
- };
434
+ const {statusIcon, describedBy: statusTooltipDescribedBy} =
435
+ useInputStatusIcon({
436
+ status,
437
+ statusVariant,
438
+ });
448
439
 
449
440
  const ariaDescribedBy =
450
441
  [
451
442
  description ? descriptionID : null,
452
- status?.message ? statusMessageID : null,
443
+ statusVariant !== 'tooltip' && status?.message ? statusMessageID : null,
444
+ // The tooltip variant renders no message box; describe the input by the
445
+ // tooltip's content instead so the status is still announced.
446
+ statusTooltipDescribedBy,
453
447
  showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
454
448
  ]
455
449
  .filter(Boolean)
@@ -560,7 +554,9 @@ export function DateRangeInput({
560
554
  sizeStyles[size],
561
555
  isEffectivelyDisabled && inputWrapperStyles.disabled,
562
556
  status && inputStatusBorderStyles[status.type],
563
- status && inputStatusHoverShadowStyles[status.type],
557
+ status &&
558
+ !isEffectivelyDisabled &&
559
+ inputStatusHoverShadowStyles[status.type],
564
560
  status && inputStatusFocusWithinStyles[status.type],
565
561
  xstyle,
566
562
  ),
@@ -581,7 +577,14 @@ export function DateRangeInput({
581
577
  styles.iconButton,
582
578
  isEffectivelyDisabled && styles.iconButtonDisabled,
583
579
  )}>
584
- <Icon icon="calendar" size="sm" color="secondary" />
580
+ <Icon
581
+ icon="calendar"
582
+ size="sm"
583
+ color="secondary"
584
+ {...themeProps('date-range-input-toggle-icon', {
585
+ state: popover.isOpen ? 'expanded' : 'collapsed',
586
+ })}
587
+ />
585
588
  </button>
586
589
  <button
587
590
  ref={ref}
@@ -614,17 +617,16 @@ export function DateRangeInput({
614
617
  onClick={handleClear}
615
618
  aria-label={t('@astryx.dateInput.clear', {label})}
616
619
  {...stylex.props(styles.iconButton)}>
617
- <Icon icon="close" size="sm" color="secondary" />
620
+ <Icon
621
+ icon="close"
622
+ size="sm"
623
+ color="secondary"
624
+ {...themeProps('date-range-input-clear-icon')}
625
+ />
618
626
  </button>
619
627
  )}
620
628
  {isBusy && <Spinner size="sm" />}
621
- {status && (
622
- <Icon
623
- icon={statusIconMap[status.type]}
624
- size="md"
625
- color={statusIconColorMap[status.type]}
626
- />
627
- )}
629
+ {statusIcon}
628
630
  </div>
629
631
  {popover.render(
630
632
  <div {...stylex.props(styles.popoverLayout)}>
@@ -173,6 +173,12 @@ export const docs = {
173
173
  description: 'Number of months displayed simultaneously in the calendar.',
174
174
  default: '1',
175
175
  },
176
+ {
177
+ name: 'width',
178
+ type: 'SizeValue',
179
+ description:
180
+ '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.',
181
+ },
176
182
  {
177
183
  name: 'xstyle',
178
184
  type: 'StyleXStyles',
@@ -183,6 +189,14 @@ export const docs = {
183
189
  theming: {
184
190
  targets: [
185
191
  {className: 'astryx-date-time-input', visualProps: ['size', 'status']},
192
+ {
193
+ className: 'astryx-date-time-input-date-segment',
194
+ visualProps: ['size', 'status'],
195
+ },
196
+ {
197
+ className: 'astryx-date-time-input-time-segment',
198
+ visualProps: ['size', 'status'],
199
+ },
186
200
  ],
187
201
  },
188
202
  usage: {
@@ -469,11 +483,18 @@ export const docsZh = {
469
483
  description:
470
484
  '用于布局自定义的 StyleX 样式。必须是 stylex.create() 的值。',
471
485
  },
472
-
473
486
  ],
474
487
  theming: {
475
488
  targets: [
476
489
  {className: 'astryx-date-time-input', visualProps: ['size', 'status']},
490
+ {
491
+ className: 'astryx-date-time-input-date-segment',
492
+ visualProps: ['size', 'status'],
493
+ },
494
+ {
495
+ className: 'astryx-date-time-input-time-segment',
496
+ visualProps: ['size', 'status'],
497
+ },
477
498
  ],
478
499
  },
479
500
  };
@@ -15,6 +15,8 @@ import userEvent from '@testing-library/user-event';
15
15
  import {getButton, queryButton} from '../__tests__/fastRoleQueries';
16
16
  import {DateTimeInput} from './DateTimeInput';
17
17
  import type {ISODateTimeString} from './DateTimeInput';
18
+ import {defineTheme} from '../theme/defineTheme';
19
+ import {generateThemeCSSFlat} from '../theme/generateThemeRules';
18
20
 
19
21
  describe('DateTimeInput', () => {
20
22
  it('renders with label', () => {
@@ -814,4 +816,114 @@ describe('DateTimeInput', () => {
814
816
  expect(onChange.mock.calls[0][0]).toContain('14:31');
815
817
  });
816
818
  });
819
+ // ===========================================================================
820
+ // Segment theme targets (#4075)
821
+ // ===========================================================================
822
+
823
+ describe('segment theme targets', () => {
824
+ // The root only publishes `astryx-date-time-input`; the date and time
825
+ // wrappers were anonymous nodes carrying hashed atomic classes only, so a
826
+ // theme that restyles input geometry through the text-input/date-input/
827
+ // time-input targets could not reach them and DateTimeInput rendered
828
+ // shorter than every other input under that theme.
829
+
830
+ it('renders the date segment target on the date wrapper', () => {
831
+ render(<DateTimeInput label="Meeting" onChange={() => {}} />);
832
+ const wrapper = screen
833
+ .getByLabelText('Meeting')
834
+ .closest('.astryx-date-time-input-date-segment');
835
+
836
+ expect(wrapper).not.toBeNull();
837
+ // The wrapper is the input's own container, not an ancestor further up.
838
+ expect(wrapper).toBe(screen.getByLabelText('Meeting').parentElement);
839
+ });
840
+
841
+ it('renders the time segment target on the time wrapper', () => {
842
+ render(<DateTimeInput label="Meeting" onChange={() => {}} />);
843
+ const wrapper = screen
844
+ .getByLabelText('Meeting time')
845
+ .closest('.astryx-date-time-input-time-segment');
846
+
847
+ expect(wrapper).not.toBeNull();
848
+ expect(wrapper).toBe(screen.getByLabelText('Meeting time').parentElement);
849
+ });
850
+
851
+ it('reflects size on both segments so themes can restyle geometry', () => {
852
+ render(<DateTimeInput label="Meeting" size="lg" onChange={() => {}} />);
853
+
854
+ const date = screen
855
+ .getByLabelText('Meeting')
856
+ .closest('.astryx-date-time-input-date-segment');
857
+ const time = screen
858
+ .getByLabelText('Meeting time')
859
+ .closest('.astryx-date-time-input-time-segment');
860
+
861
+ expect(date).toHaveAttribute('data-size', 'lg');
862
+ expect(date).toHaveClass('lg');
863
+ expect(time).toHaveAttribute('data-size', 'lg');
864
+ expect(time).toHaveClass('lg');
865
+ });
866
+
867
+ it('reflects status on both segments, mirroring the root', () => {
868
+ render(
869
+ <DateTimeInput
870
+ label="Meeting"
871
+ status={{type: 'error', message: 'Required'}}
872
+ onChange={() => {}}
873
+ />,
874
+ );
875
+
876
+ const date = screen
877
+ .getByLabelText('Meeting')
878
+ .closest('.astryx-date-time-input-date-segment');
879
+ const time = screen
880
+ .getByLabelText('Meeting time')
881
+ .closest('.astryx-date-time-input-time-segment');
882
+
883
+ expect(date).toHaveAttribute('data-status', 'error');
884
+ expect(time).toHaveAttribute('data-status', 'error');
885
+ });
886
+
887
+ it('omits data-status when there is no status, like the root does', () => {
888
+ render(<DateTimeInput label="Meeting" onChange={() => {}} />);
889
+
890
+ const date = screen
891
+ .getByLabelText('Meeting')
892
+ .closest('.astryx-date-time-input-date-segment');
893
+
894
+ expect(date).not.toHaveAttribute('data-status');
895
+ });
896
+
897
+ it('keeps the root target intact', () => {
898
+ const {container} = render(
899
+ <DateTimeInput label="Meeting" onChange={() => {}} />,
900
+ );
901
+ // Additive change — the existing root target still renders.
902
+ expect(container.querySelector('.astryx-date-time-input')).not.toBeNull();
903
+ });
904
+
905
+ it('exposes both segments as themeable defineTheme targets', () => {
906
+ // jsdom cannot resolve the @layer cascade, so the generated CSS is what
907
+ // proves a theme can actually reach these nodes.
908
+ const theme = defineTheme({
909
+ name: 'date-time-input-segments-test',
910
+ components: {
911
+ 'date-time-input-date-segment': {
912
+ base: {blockSize: 'var(--size-element-lg)'},
913
+ lg: {paddingInline: 'var(--spacing-4)'},
914
+ },
915
+ 'date-time-input-time-segment': {
916
+ base: {blockSize: 'var(--size-element-lg)'},
917
+ },
918
+ },
919
+ });
920
+ const css = generateThemeCSSFlat(theme);
921
+
922
+ expect(css).toContain('.astryx-date-time-input-date-segment {');
923
+ expect(css).toContain('.astryx-date-time-input-date-segment.lg');
924
+ expect(css).toContain('.astryx-date-time-input-time-segment {');
925
+ expect(css).toContain('block-size: var(--size-element-lg)');
926
+ expect(css).toContain('padding-inline: var(--spacing-4)');
927
+ });
928
+ });
817
929
  });