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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (601) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/dist/Avatar/Avatar.d.ts +42 -3
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +190 -59
  5. package/dist/Avatar/AvatarStatusDot.d.ts +6 -4
  6. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  7. package/dist/AvatarGroup/AvatarGroup.d.ts +7 -1
  8. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  9. package/dist/AvatarGroup/AvatarGroup.js +58 -6
  10. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  11. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  12. package/dist/AvatarGroup/AvatarGroupOverflow.js +7 -2
  13. package/dist/Banner/Banner.js +2 -2
  14. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  15. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -4
  16. package/dist/Breadcrumbs/index.d.ts +1 -0
  17. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  18. package/dist/Breadcrumbs/index.js +2 -1
  19. package/dist/Button/Button.d.ts +1 -1
  20. package/dist/Button/Button.d.ts.map +1 -1
  21. package/dist/Button/Button.js +6 -4
  22. package/dist/Calendar/Calendar.d.ts.map +1 -1
  23. package/dist/Calendar/Calendar.js +50 -9
  24. package/dist/Calendar/dayCellUtils.d.ts +4 -4
  25. package/dist/Calendar/dayCellUtils.js +2 -2
  26. package/dist/Calendar/styles.d.ts +30 -31
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +30 -31
  29. package/dist/Card/Card.js +1 -1
  30. package/dist/Carousel/Carousel.d.ts.map +1 -1
  31. package/dist/Carousel/Carousel.js +22 -12
  32. package/dist/Chat/ChatComposerInput.js +1 -1
  33. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  34. package/dist/Chat/ChatDictationButton.js +3 -1
  35. package/dist/Chat/ChatLayout.js +4 -4
  36. package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
  37. package/dist/Chat/ChatMessageBubble.js +8 -8
  38. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  39. package/dist/Chat/ChatToolCalls.js +10 -3
  40. package/dist/Chat/useTriggerMenu.js +2 -2
  41. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  42. package/dist/CheckboxInput/CheckboxInput.js +6 -6
  43. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  44. package/dist/CheckboxList/CheckboxListItem.js +19 -4
  45. package/dist/Citation/Citation.d.ts +22 -1
  46. package/dist/Citation/Citation.d.ts.map +1 -1
  47. package/dist/Citation/Citation.js +20 -6
  48. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  49. package/dist/CodeBlock/CodeBlock.js +16 -10
  50. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  51. package/dist/Collapsible/Collapsible.js +3 -1
  52. package/dist/CommandPalette/CommandPalette.d.ts +1 -1
  53. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  54. package/dist/CommandPalette/CommandPalette.js +42 -3
  55. package/dist/CommandPalette/CommandPaletteItem.js +1 -1
  56. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  57. package/dist/ContextMenu/ContextMenu.js +23 -5
  58. package/dist/ContextMenu/index.d.ts +1 -0
  59. package/dist/ContextMenu/index.d.ts.map +1 -1
  60. package/dist/ContextMenu/index.js +3 -1
  61. package/dist/DateInput/DateInput.d.ts +10 -2
  62. package/dist/DateInput/DateInput.d.ts.map +1 -1
  63. package/dist/DateInput/DateInput.js +33 -19
  64. package/dist/DateRangeInput/DateRangeInput.d.ts +10 -2
  65. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  66. package/dist/DateRangeInput/DateRangeInput.js +23 -20
  67. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  68. package/dist/DateTimeInput/DateTimeInput.js +23 -18
  69. package/dist/Divider/Divider.d.ts +2 -2
  70. package/dist/Divider/Divider.d.ts.map +1 -1
  71. package/dist/Divider/Divider.js +13 -0
  72. package/dist/DropdownMenu/DropdownMenu.d.ts +6 -0
  73. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  74. package/dist/DropdownMenu/DropdownMenu.js +22 -5
  75. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  76. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +4 -1
  77. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  78. package/dist/DropdownMenu/DropdownMenuItem.js +5 -2
  79. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +8 -11
  80. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
  81. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +3 -1
  82. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +1 -1
  83. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  84. package/dist/DropdownMenu/DropdownMenuRadioItem.js +5 -2
  85. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +104 -0
  86. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -0
  87. package/dist/DropdownMenu/DropdownMenuSubMenu.js +435 -0
  88. package/dist/DropdownMenu/index.d.ts +10 -0
  89. package/dist/DropdownMenu/index.d.ts.map +1 -1
  90. package/dist/DropdownMenu/index.js +13 -0
  91. package/dist/DropdownMenu/menuItemHover.d.ts +24 -0
  92. package/dist/DropdownMenu/menuItemHover.d.ts.map +1 -0
  93. package/dist/DropdownMenu/menuItemHover.js +34 -0
  94. package/dist/DropdownMenu/menuItemRoles.d.ts +8 -0
  95. package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -1
  96. package/dist/DropdownMenu/menuItemRoles.js +10 -1
  97. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  98. package/dist/DropdownMenu/renderDropdownItems.js +21 -6
  99. package/dist/Field/Field.d.ts +3 -1
  100. package/dist/Field/Field.d.ts.map +1 -1
  101. package/dist/Field/Field.js +4 -1
  102. package/dist/Field/FieldLabel.d.ts +2 -1
  103. package/dist/Field/FieldLabel.d.ts.map +1 -1
  104. package/dist/Field/FieldLabel.js +9 -5
  105. package/dist/Field/inputStyles.stylex.d.ts +1 -0
  106. package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
  107. package/dist/Field/inputStyles.stylex.js +1 -0
  108. package/dist/FieldStatus/FieldStatus.d.ts +10 -0
  109. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  110. package/dist/FieldStatus/FieldStatus.js +51 -5
  111. package/dist/FileInput/FileInput.d.ts +10 -2
  112. package/dist/FileInput/FileInput.d.ts.map +1 -1
  113. package/dist/FileInput/FileInput.js +93 -65
  114. package/dist/Heading/Heading.d.ts.map +1 -1
  115. package/dist/Heading/Heading.js +3 -2
  116. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  117. package/dist/HoverCard/useHoverCard.js +10 -4
  118. package/dist/Icon/Icon.d.ts +15 -1
  119. package/dist/Icon/Icon.d.ts.map +1 -1
  120. package/dist/Icon/Icon.js +19 -3
  121. package/dist/Item/Item.d.ts +16 -3
  122. package/dist/Item/Item.d.ts.map +1 -1
  123. package/dist/Item/Item.js +31 -6
  124. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  125. package/dist/Layer/layerAnimations.stylex.js +10 -2
  126. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  127. package/dist/Lightbox/Lightbox.js +34 -15
  128. package/dist/List/List.d.ts.map +1 -1
  129. package/dist/List/List.js +6 -3
  130. package/dist/List/ListItem.d.ts +11 -1
  131. package/dist/List/ListItem.d.ts.map +1 -1
  132. package/dist/List/ListItem.js +2 -0
  133. package/dist/Markdown/Markdown.d.ts.map +1 -1
  134. package/dist/Markdown/Markdown.js +7 -0
  135. package/dist/MetadataList/MetadataList.d.ts +1 -1
  136. package/dist/MetadataList/MetadataList.d.ts.map +1 -1
  137. package/dist/MetadataList/MetadataList.js +4 -2
  138. package/dist/MultiSelector/MultiSelector.d.ts +9 -1
  139. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  140. package/dist/MultiSelector/MultiSelector.js +81 -43
  141. package/dist/NavMenu/NavHeadingMenuItem.js +1 -1
  142. package/dist/NumberInput/NumberInput.d.ts +10 -2
  143. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  144. package/dist/NumberInput/NumberInput.js +22 -17
  145. package/dist/Outline/Outline.js +2 -2
  146. package/dist/Pagination/Pagination.d.ts.map +1 -1
  147. package/dist/Pagination/Pagination.js +11 -3
  148. package/dist/PowerSearch/PowerSearch.d.ts +9 -2
  149. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  150. package/dist/PowerSearch/PowerSearch.js +2 -0
  151. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  152. package/dist/ProgressBar/ProgressBar.js +6 -1
  153. package/dist/RadioList/RadioList.d.ts +2 -2
  154. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  155. package/dist/RadioList/RadioListItem.js +5 -5
  156. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  157. package/dist/Resizable/ResizeHandle.js +49 -19
  158. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
  159. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  160. package/dist/SegmentedControl/SegmentedControlItem.js +5 -3
  161. package/dist/Selector/Selector.d.ts +8 -0
  162. package/dist/Selector/Selector.d.ts.map +1 -1
  163. package/dist/Selector/Selector.js +66 -17
  164. package/dist/SideNav/SideNav.d.ts.map +1 -1
  165. package/dist/SideNav/SideNav.js +5 -5
  166. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  167. package/dist/SideNav/SideNavCollapseButton.js +13 -10
  168. package/dist/SideNav/SideNavItem.d.ts +1 -1
  169. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  170. package/dist/SideNav/SideNavItem.js +12 -10
  171. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  172. package/dist/Skeleton/Skeleton.js +2 -2
  173. package/dist/Slider/Slider.d.ts.map +1 -1
  174. package/dist/Slider/Slider.js +87 -34
  175. package/dist/Spinner/Spinner.d.ts +3 -2
  176. package/dist/Spinner/Spinner.d.ts.map +1 -1
  177. package/dist/Spinner/Spinner.js +11 -2
  178. package/dist/Switch/Switch.d.ts +8 -1
  179. package/dist/Switch/Switch.d.ts.map +1 -1
  180. package/dist/Switch/Switch.js +171 -91
  181. package/dist/TabList/Tab.js +2 -2
  182. package/dist/TabList/TabList.d.ts +2 -11
  183. package/dist/TabList/TabList.d.ts.map +1 -1
  184. package/dist/TabList/TabList.js +5 -9
  185. package/dist/TabList/TabListContext.d.ts +0 -5
  186. package/dist/TabList/TabListContext.d.ts.map +1 -1
  187. package/dist/TabList/TabListContext.js +0 -5
  188. package/dist/TabList/TabMenu.js +1 -1
  189. package/dist/Table/BaseTable.d.ts.map +1 -1
  190. package/dist/Table/BaseTable.js +30 -4
  191. package/dist/Table/TableCell.js +3 -3
  192. package/dist/Table/TableHeaderCell.js +3 -3
  193. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  194. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +15 -11
  195. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  196. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +37 -27
  197. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  198. package/dist/Table/plugins/selection/useTableSelection.js +3 -0
  199. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  200. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +15 -7
  201. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  202. package/dist/Table/plugins/tree/useTableTreeData.js +29 -23
  203. package/dist/Table/types.d.ts +32 -0
  204. package/dist/Table/types.d.ts.map +1 -1
  205. package/dist/Text/Text.d.ts +10 -1
  206. package/dist/Text/Text.d.ts.map +1 -1
  207. package/dist/Text/Text.js +25 -2
  208. package/dist/Text/index.d.ts +2 -2
  209. package/dist/Text/index.d.ts.map +1 -1
  210. package/dist/TextArea/TextArea.d.ts +10 -1
  211. package/dist/TextArea/TextArea.d.ts.map +1 -1
  212. package/dist/TextArea/TextArea.js +119 -52
  213. package/dist/TextInput/TextInput.d.ts +10 -2
  214. package/dist/TextInput/TextInput.d.ts.map +1 -1
  215. package/dist/TextInput/TextInput.js +20 -17
  216. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  217. package/dist/Thumbnail/Thumbnail.js +51 -45
  218. package/dist/TimeInput/TimeInput.d.ts +10 -2
  219. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  220. package/dist/TimeInput/TimeInput.js +17 -17
  221. package/dist/Timestamp/Timestamp.d.ts +31 -3
  222. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  223. package/dist/Timestamp/Timestamp.js +48 -77
  224. package/dist/Timestamp/formatInstant.d.ts +41 -0
  225. package/dist/Timestamp/formatInstant.d.ts.map +1 -0
  226. package/dist/Timestamp/formatInstant.js +177 -0
  227. package/dist/Timestamp/index.d.ts +1 -0
  228. package/dist/Timestamp/index.d.ts.map +1 -1
  229. package/dist/Timestamp/tooltipEntries.d.ts +56 -0
  230. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -0
  231. package/dist/Timestamp/tooltipEntries.js +163 -0
  232. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  233. package/dist/Toast/ToastViewport.js +61 -9
  234. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  235. package/dist/ToggleButton/ToggleButton.js +3 -1
  236. package/dist/Token/Token.d.ts +34 -4
  237. package/dist/Token/Token.d.ts.map +1 -1
  238. package/dist/Token/Token.js +85 -45
  239. package/dist/Token/TokenLink.d.ts +62 -0
  240. package/dist/Token/TokenLink.d.ts.map +1 -0
  241. package/dist/Token/TokenLink.js +76 -0
  242. package/dist/Token/index.d.ts +1 -1
  243. package/dist/Token/index.d.ts.map +1 -1
  244. package/dist/Tokenizer/Tokenizer.d.ts +9 -2
  245. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  246. package/dist/Tokenizer/Tokenizer.js +4 -2
  247. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  248. package/dist/Tooltip/useTooltip.js +17 -1
  249. package/dist/TopNav/TopNavItem.d.ts +3 -1
  250. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  251. package/dist/TopNav/TopNavItem.js +30 -3
  252. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  253. package/dist/TopNav/TopNavMegaMenu.js +9 -3
  254. package/dist/TreeList/TreeList.d.ts.map +1 -1
  255. package/dist/TreeList/TreeList.js +1 -0
  256. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  257. package/dist/TreeList/TreeListBranches.js +8 -6
  258. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  259. package/dist/TreeList/TreeListItem.js +32 -14
  260. package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
  261. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  262. package/dist/Typeahead/BaseTypeahead.js +22 -11
  263. package/dist/Typeahead/Typeahead.d.ts +9 -2
  264. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  265. package/dist/Typeahead/Typeahead.js +11 -2
  266. package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
  267. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  268. package/dist/Typeahead/TypeaheadItem.js +20 -10
  269. package/dist/astryx.css +114 -35
  270. package/dist/astryx.umd.js +50 -50
  271. package/dist/astryx.umd.js.map +4 -4
  272. package/dist/hooks/containerReveal.pool.stylex.d.ts +281 -0
  273. package/dist/hooks/containerReveal.pool.stylex.d.ts.map +1 -0
  274. package/dist/hooks/containerReveal.pool.stylex.js +281 -0
  275. package/dist/hooks/index.d.ts +4 -0
  276. package/dist/hooks/index.d.ts.map +1 -1
  277. package/dist/hooks/index.js +3 -1
  278. package/dist/hooks/isRtlElement.d.ts +18 -0
  279. package/dist/hooks/isRtlElement.d.ts.map +1 -0
  280. package/dist/hooks/isRtlElement.js +27 -0
  281. package/dist/hooks/useAnnounce.d.ts +3 -0
  282. package/dist/hooks/useAnnounce.d.ts.map +1 -1
  283. package/dist/hooks/useAnnounce.js +39 -1
  284. package/dist/hooks/useContainerReveal.d.ts +83 -0
  285. package/dist/hooks/useContainerReveal.d.ts.map +1 -0
  286. package/dist/hooks/useContainerReveal.js +123 -0
  287. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  288. package/dist/hooks/useFocusTrap.js +52 -11
  289. package/dist/hooks/useGridFocus.d.ts +6 -1
  290. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  291. package/dist/hooks/useGridFocus.js +12 -9
  292. package/dist/hooks/useHotkeys.d.ts +9 -0
  293. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  294. package/dist/hooks/useHotkeys.js +9 -0
  295. package/dist/hooks/useInputStatusIcon.d.ts +38 -0
  296. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -0
  297. package/dist/hooks/useInputStatusIcon.js +169 -0
  298. package/dist/hooks/useKeyboardHint.d.ts +1 -1
  299. package/dist/hooks/useKeyboardHint.d.ts.map +1 -1
  300. package/dist/hooks/useKeyboardHint.js +4 -2
  301. package/dist/hooks/useListFocus.d.ts +39 -1
  302. package/dist/hooks/useListFocus.d.ts.map +1 -1
  303. package/dist/hooks/useListFocus.js +54 -9
  304. package/dist/i18n/InternationalizationContext.d.ts +1 -0
  305. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  306. package/dist/i18n/InternationalizationContext.js +3 -0
  307. package/dist/i18n/InternationalizationProvider.d.ts +8 -1
  308. package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
  309. package/dist/i18n/InternationalizationProvider.js +5 -1
  310. package/dist/i18n/getLocaleDirection.d.ts +18 -0
  311. package/dist/i18n/getLocaleDirection.d.ts.map +1 -0
  312. package/dist/i18n/getLocaleDirection.js +29 -0
  313. package/dist/i18n/index.d.ts +6 -0
  314. package/dist/i18n/index.d.ts.map +1 -1
  315. package/dist/i18n/index.js +7 -1
  316. package/dist/i18n/useDirection.d.ts +2 -0
  317. package/dist/i18n/useDirection.d.ts.map +1 -0
  318. package/dist/i18n/useDirection.js +27 -0
  319. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  320. package/dist/theme/derivedVarRegistry.js +13 -0
  321. package/dist/theme/index.d.ts +3 -3
  322. package/dist/theme/index.d.ts.map +1 -1
  323. package/dist/theme/types.d.ts +39 -2
  324. package/dist/theme/types.d.ts.map +1 -1
  325. package/dist/utils/index.d.ts +1 -0
  326. package/dist/utils/index.d.ts.map +1 -1
  327. package/dist/utils/index.js +2 -1
  328. package/dist/utils/plainDate.d.ts +17 -0
  329. package/dist/utils/plainDate.d.ts.map +1 -1
  330. package/dist/utils/plainDate.js +23 -4
  331. package/dist/utils/rtlStyles.d.ts +24 -0
  332. package/dist/utils/rtlStyles.d.ts.map +1 -0
  333. package/dist/utils/rtlStyles.js +27 -0
  334. package/locales/en.json +120 -0
  335. package/locales/pseudo.json +90 -0
  336. package/package.json +7 -2
  337. package/src/Avatar/Avatar.doc.mjs +50 -3
  338. package/src/Avatar/Avatar.test.tsx +218 -0
  339. package/src/Avatar/Avatar.tsx +285 -79
  340. package/src/Avatar/AvatarStatusDot.doc.mjs +4 -4
  341. package/src/Avatar/AvatarStatusDot.tsx +6 -4
  342. package/src/AvatarGroup/AvatarGroup.doc.mjs +2 -0
  343. package/src/AvatarGroup/AvatarGroup.test.tsx +162 -2
  344. package/src/AvatarGroup/AvatarGroup.tsx +58 -3
  345. package/src/AvatarGroup/AvatarGroupOverflow.tsx +5 -2
  346. package/src/Banner/Banner.tsx +6 -6
  347. package/src/Breadcrumbs/BreadcrumbItem.tsx +10 -10
  348. package/src/Breadcrumbs/Breadcrumbs.test.tsx +52 -1
  349. package/src/Breadcrumbs/index.ts +4 -0
  350. package/src/Button/Button.doc.mjs +0 -4
  351. package/src/Button/Button.test.tsx +16 -0
  352. package/src/Button/Button.tsx +6 -4
  353. package/src/Calendar/Calendar.test.tsx +170 -20
  354. package/src/Calendar/Calendar.tsx +73 -16
  355. package/src/Calendar/dayCellUtils.test.ts +18 -18
  356. package/src/Calendar/dayCellUtils.ts +2 -2
  357. package/src/Calendar/styles.ts +33 -39
  358. package/src/Card/Card.tsx +1 -1
  359. package/src/Carousel/Carousel.tsx +25 -6
  360. package/src/Chat/ChatComposer.doc.mjs +2 -2
  361. package/src/Chat/ChatComposerInput.doc.mjs +1 -1
  362. package/src/Chat/ChatComposerInput.tsx +2 -2
  363. package/src/Chat/ChatDictationButton.tsx +7 -1
  364. package/src/Chat/ChatLayout.tsx +4 -4
  365. package/src/Chat/ChatMessageBubble.tsx +15 -10
  366. package/src/Chat/ChatSystemMessage.test.tsx +5 -0
  367. package/src/Chat/ChatToolCalls.tsx +9 -2
  368. package/src/Chat/useTriggerMenu.tsx +1 -1
  369. package/src/CheckboxInput/CheckboxInput.doc.mjs +6 -0
  370. package/src/CheckboxInput/CheckboxInput.test.tsx +23 -0
  371. package/src/CheckboxInput/CheckboxInput.tsx +21 -6
  372. package/src/CheckboxList/CheckboxList.doc.mjs +6 -0
  373. package/src/CheckboxList/CheckboxList.test.tsx +94 -3
  374. package/src/CheckboxList/CheckboxListItem.tsx +17 -10
  375. package/src/Citation/Citation.doc.mjs +4 -3
  376. package/src/Citation/Citation.test.tsx +97 -0
  377. package/src/Citation/Citation.tsx +43 -11
  378. package/src/CodeBlock/CodeBlock.test.tsx +17 -0
  379. package/src/CodeBlock/CodeBlock.tsx +38 -11
  380. package/src/Collapsible/Collapsible.doc.mjs +1 -0
  381. package/src/Collapsible/Collapsible.test.tsx +7 -0
  382. package/src/Collapsible/Collapsible.tsx +16 -6
  383. package/src/CommandPalette/CommandPalette.test.tsx +95 -1
  384. package/src/CommandPalette/CommandPalette.tsx +41 -2
  385. package/src/CommandPalette/CommandPaletteItem.tsx +1 -1
  386. package/src/ContextMenu/ContextMenu.test.tsx +59 -2
  387. package/src/ContextMenu/ContextMenu.tsx +22 -11
  388. package/src/ContextMenu/index.ts +5 -0
  389. package/src/DateInput/DateInput.doc.mjs +25 -0
  390. package/src/DateInput/DateInput.test.tsx +230 -0
  391. package/src/DateInput/DateInput.tsx +51 -29
  392. package/src/DateRangeInput/DateRangeInput.doc.mjs +16 -0
  393. package/src/DateRangeInput/DateRangeInput.test.tsx +140 -0
  394. package/src/DateRangeInput/DateRangeInput.tsx +42 -30
  395. package/src/DateTimeInput/DateTimeInput.doc.mjs +22 -1
  396. package/src/DateTimeInput/DateTimeInput.test.tsx +112 -0
  397. package/src/DateTimeInput/DateTimeInput.tsx +44 -39
  398. package/src/Divider/Divider.test.tsx +32 -8
  399. package/src/Divider/Divider.tsx +14 -1
  400. package/src/DropdownMenu/DropdownMenu.test.tsx +60 -0
  401. package/src/DropdownMenu/DropdownMenu.tsx +31 -12
  402. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +8 -2
  403. package/src/DropdownMenu/DropdownMenuItem.tsx +9 -3
  404. package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +4 -12
  405. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +9 -10
  406. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +9 -3
  407. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +20 -2
  408. package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +88 -0
  409. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +663 -0
  410. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +561 -0
  411. package/src/DropdownMenu/index.ts +16 -0
  412. package/src/DropdownMenu/menuItemHover.ts +40 -0
  413. package/src/DropdownMenu/menuItemRoles.ts +9 -0
  414. package/src/DropdownMenu/renderDropdownItems.tsx +29 -15
  415. package/src/Field/Field.doc.mjs +4 -0
  416. package/src/Field/Field.tsx +14 -9
  417. package/src/Field/FieldLabel.test.tsx +25 -0
  418. package/src/Field/FieldLabel.tsx +12 -3
  419. package/src/Field/inputStyles.stylex.ts +2 -0
  420. package/src/FieldStatus/FieldStatus.doc.mjs +1 -0
  421. package/src/FieldStatus/FieldStatus.test.tsx +133 -0
  422. package/src/FieldStatus/FieldStatus.tsx +61 -4
  423. package/src/FileInput/FileInput.doc.mjs +23 -2
  424. package/src/FileInput/FileInput.test.tsx +104 -13
  425. package/src/FileInput/FileInput.tsx +106 -79
  426. package/src/Heading/Heading.tsx +2 -1
  427. package/src/HoverCard/useHoverCard.tsx +15 -3
  428. package/src/Icon/Icon.doc.mjs +13 -0
  429. package/src/Icon/Icon.test.tsx +89 -0
  430. package/src/Icon/Icon.tsx +51 -3
  431. package/src/Item/Item.doc.mjs +5 -0
  432. package/src/Item/Item.test.tsx +88 -0
  433. package/src/Item/Item.tsx +57 -11
  434. package/src/Layer/layerAnimations.stylex.ts +23 -0
  435. package/src/Lightbox/Lightbox.test.tsx +33 -0
  436. package/src/Lightbox/Lightbox.tsx +30 -10
  437. package/src/List/List.test.tsx +17 -29
  438. package/src/List/List.tsx +6 -1
  439. package/src/List/ListItem.doc.mjs +12 -0
  440. package/src/List/ListItem.tsx +13 -0
  441. package/src/Markdown/Markdown.test.tsx +21 -0
  442. package/src/Markdown/Markdown.tsx +4 -0
  443. package/src/MetadataList/MetadataList.test.tsx +24 -0
  444. package/src/MetadataList/MetadataList.tsx +6 -2
  445. package/src/MultiSelector/MultiSelector.doc.mjs +20 -0
  446. package/src/MultiSelector/MultiSelector.test.tsx +402 -0
  447. package/src/MultiSelector/MultiSelector.tsx +126 -64
  448. package/src/NavMenu/NavHeadingMenuItem.tsx +1 -1
  449. package/src/NumberInput/NumberInput.doc.mjs +21 -0
  450. package/src/NumberInput/NumberInput.test.tsx +71 -0
  451. package/src/NumberInput/NumberInput.tsx +35 -26
  452. package/src/Outline/Outline.test.tsx +12 -12
  453. package/src/Outline/Outline.tsx +2 -2
  454. package/src/Pagination/Pagination.tsx +11 -3
  455. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  456. package/src/PowerSearch/PowerSearch.test.tsx +23 -0
  457. package/src/PowerSearch/PowerSearch.tsx +10 -1
  458. package/src/ProgressBar/ProgressBar.test.tsx +34 -0
  459. package/src/ProgressBar/ProgressBar.tsx +16 -1
  460. package/src/RadioList/RadioList.doc.mjs +6 -0
  461. package/src/RadioList/RadioList.test.tsx +26 -4
  462. package/src/RadioList/RadioList.tsx +2 -2
  463. package/src/RadioList/RadioListItem.tsx +12 -5
  464. package/src/Resizable/ResizeHandle.test.tsx +94 -0
  465. package/src/Resizable/ResizeHandle.tsx +70 -20
  466. package/src/SegmentedControl/SegmentedControl.test.tsx +28 -0
  467. package/src/SegmentedControl/SegmentedControlItem.tsx +22 -2
  468. package/src/Selector/Selector.doc.mjs +15 -0
  469. package/src/Selector/Selector.test.tsx +370 -0
  470. package/src/Selector/Selector.tsx +88 -20
  471. package/src/SideNav/SideNav.tsx +1 -8
  472. package/src/SideNav/SideNavCollapseButton.tsx +9 -7
  473. package/src/SideNav/SideNavItem.tsx +5 -3
  474. package/src/Skeleton/Skeleton.test.tsx +16 -0
  475. package/src/Skeleton/Skeleton.tsx +13 -1
  476. package/src/Slider/Slider.doc.mjs +6 -0
  477. package/src/Slider/Slider.test.tsx +240 -12
  478. package/src/Slider/Slider.tsx +96 -23
  479. package/src/Spinner/Spinner.test.tsx +10 -8
  480. package/src/Spinner/Spinner.tsx +15 -5
  481. package/src/Switch/Switch.doc.mjs +142 -42
  482. package/src/Switch/Switch.test.tsx +111 -0
  483. package/src/Switch/Switch.tsx +149 -32
  484. package/src/TabList/Tab.tsx +2 -2
  485. package/src/TabList/TabList.doc.mjs +0 -6
  486. package/src/TabList/TabList.test.tsx +3 -15
  487. package/src/TabList/TabList.tsx +6 -17
  488. package/src/TabList/TabListContext.ts +0 -6
  489. package/src/TabList/TabMenu.tsx +2 -2
  490. package/src/Table/BaseTable.tsx +34 -2
  491. package/src/Table/Table.doc.mjs +11 -0
  492. package/src/Table/Table.test.tsx +85 -0
  493. package/src/Table/TableCell.tsx +3 -3
  494. package/src/Table/TableHeaderCell.tsx +3 -3
  495. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +9 -6
  496. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +19 -0
  497. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +27 -18
  498. package/src/Table/plugins/selection/useTableSelection.test.tsx +61 -3
  499. package/src/Table/plugins/selection/useTableSelection.tsx +3 -0
  500. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +38 -9
  501. package/src/Table/plugins/tree/useTableTreeData.tsx +17 -13
  502. package/src/Table/types.ts +33 -0
  503. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  504. package/src/Text/Heading.doc.mjs +3 -3
  505. package/src/Text/Text.doc.mjs +1 -1
  506. package/src/Text/Text.test.tsx +42 -0
  507. package/src/Text/Text.tsx +25 -1
  508. package/src/Text/index.ts +2 -6
  509. package/src/TextArea/TextArea.doc.mjs +23 -2
  510. package/src/TextArea/TextArea.test.tsx +132 -4
  511. package/src/TextArea/TextArea.tsx +161 -67
  512. package/src/TextInput/TextInput.doc.mjs +21 -0
  513. package/src/TextInput/TextInput.test.tsx +150 -0
  514. package/src/TextInput/TextInput.tsx +28 -25
  515. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  516. package/src/Thumbnail/Thumbnail.test.tsx +24 -1
  517. package/src/Thumbnail/Thumbnail.tsx +28 -35
  518. package/src/TimeInput/TimeInput.doc.mjs +21 -0
  519. package/src/TimeInput/TimeInput.test.tsx +23 -0
  520. package/src/TimeInput/TimeInput.tsx +24 -25
  521. package/src/Timestamp/Timestamp.doc.mjs +32 -6
  522. package/src/Timestamp/Timestamp.test.tsx +371 -0
  523. package/src/Timestamp/Timestamp.tsx +96 -93
  524. package/src/Timestamp/formatInstant.ts +218 -0
  525. package/src/Timestamp/index.ts +4 -0
  526. package/src/Timestamp/tooltipEntries.test.ts +552 -0
  527. package/src/Timestamp/tooltipEntries.ts +210 -0
  528. package/src/Toast/ToastViewport.test.tsx +218 -8
  529. package/src/Toast/ToastViewport.tsx +71 -12
  530. package/src/ToggleButton/ToggleButton.test.tsx +24 -0
  531. package/src/ToggleButton/ToggleButton.tsx +19 -1
  532. package/src/Token/Token.test.tsx +166 -15
  533. package/src/Token/Token.tsx +104 -47
  534. package/src/Token/TokenLink.tsx +107 -0
  535. package/src/Token/index.ts +1 -1
  536. package/src/Tokenizer/Tokenizer.doc.mjs +23 -1
  537. package/src/Tokenizer/Tokenizer.test.tsx +23 -0
  538. package/src/Tokenizer/Tokenizer.tsx +14 -2
  539. package/src/Tooltip/Tooltip.test.tsx +46 -0
  540. package/src/Tooltip/useTooltip.tsx +20 -0
  541. package/src/TopNav/TopNav.test.tsx +93 -2
  542. package/src/TopNav/TopNavItem.doc.mjs +3 -3
  543. package/src/TopNav/TopNavItem.tsx +35 -6
  544. package/src/TopNav/TopNavMegaMenu.tsx +26 -2
  545. package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +1 -1
  546. package/src/TreeList/TreeList.doc.mjs +6 -0
  547. package/src/TreeList/TreeList.test.tsx +50 -10
  548. package/src/TreeList/TreeList.tsx +5 -0
  549. package/src/TreeList/TreeListBranches.tsx +8 -6
  550. package/src/TreeList/TreeListItem.tsx +33 -11
  551. package/src/Typeahead/BaseTypeahead.tsx +42 -15
  552. package/src/Typeahead/Typeahead.doc.mjs +13 -0
  553. package/src/Typeahead/Typeahead.test.tsx +112 -0
  554. package/src/Typeahead/Typeahead.tsx +17 -1
  555. package/src/Typeahead/TypeaheadItem.tsx +2 -1
  556. package/src/__tests__/babelPluginAddExtensions.test.ts +190 -0
  557. package/src/__tests__/forcedColors.ts +60 -0
  558. package/src/__tests__/inputWidthContract.test.tsx +62 -0
  559. package/src/doc-mjs.d.ts +19 -0
  560. package/src/hooks/containerReveal.pool.stylex.ts +464 -0
  561. package/src/hooks/index.ts +13 -0
  562. package/src/hooks/isRtlElement.ts +27 -0
  563. package/src/hooks/useAnnounce.test.tsx +75 -2
  564. package/src/hooks/useAnnounce.ts +47 -1
  565. package/src/hooks/useContainerReveal.test.tsx +116 -0
  566. package/src/hooks/useContainerReveal.ts +180 -0
  567. package/src/hooks/useFocusTrap.test.tsx +104 -0
  568. package/src/hooks/useFocusTrap.ts +61 -12
  569. package/src/hooks/useGridFocus.doc.mjs +3 -3
  570. package/src/hooks/useGridFocus.test.tsx +41 -0
  571. package/src/hooks/useGridFocus.ts +21 -10
  572. package/src/hooks/useHotkeys.ts +9 -0
  573. package/src/hooks/useInputStatusIcon.test.tsx +221 -0
  574. package/src/hooks/useInputStatusIcon.tsx +235 -0
  575. package/src/hooks/useKeyboardHint.test.tsx +22 -0
  576. package/src/hooks/useKeyboardHint.tsx +6 -2
  577. package/src/hooks/useListFocus.doc.mjs +25 -3
  578. package/src/hooks/useListFocus.test.tsx +153 -1
  579. package/src/hooks/useListFocus.ts +102 -9
  580. package/src/i18n/InternationalizationContext.ts +4 -0
  581. package/src/i18n/InternationalizationProvider.tsx +12 -2
  582. package/src/i18n/__tests__/getLocaleDirection.test.ts +40 -0
  583. package/src/i18n/__tests__/useDirection.test.tsx +66 -0
  584. package/src/i18n/getLocaleDirection.ts +29 -0
  585. package/src/i18n/index.ts +6 -0
  586. package/src/i18n/useDirection.ts +29 -0
  587. package/src/reset.css +0 -26
  588. package/src/reset.test.ts +60 -0
  589. package/src/theme/derivedVarRegistry.test.ts +1 -0
  590. package/src/theme/derivedVarRegistry.ts +6 -0
  591. package/src/theme/generateThemeRules.test.ts +42 -0
  592. package/src/theme/index.ts +4 -8
  593. package/src/theme/types.ts +43 -13
  594. package/src/utils/index.ts +2 -0
  595. package/src/utils/plainDate.test.ts +24 -6
  596. package/src/utils/plainDate.ts +18 -4
  597. package/src/utils/rtlStyles.ts +27 -0
  598. package/dist/Thumbnail/thumbnail.markers.stylex.d.ts +0 -9
  599. package/dist/Thumbnail/thumbnail.markers.stylex.d.ts.map +0 -1
  600. package/dist/Thumbnail/thumbnail.markers.stylex.js +0 -14
  601. package/src/Thumbnail/thumbnail.markers.stylex.ts +0 -12
@@ -13,6 +13,7 @@ import {describe, it, expect, vi} from 'vitest';
13
13
  import {render, screen, fireEvent} from '@testing-library/react';
14
14
  import userEvent from '@testing-library/user-event';
15
15
  import {Token} from './Token';
16
+ import type {TokenColor} from './Token';
16
17
 
17
18
  describe('Token', () => {
18
19
  it('renders with label', () => {
@@ -51,6 +52,23 @@ describe('Token', () => {
51
52
  }
52
53
  });
53
54
 
55
+ it('reflects a custom (theme-augmented) color as a class and data attribute', () => {
56
+ // Themes extend TokenColorMap via module augmentation and supply the
57
+ // styling through generated theme CSS; the runtime just forwards the
58
+ // value as the stable class + data-color reflection so the theme
59
+ // selector can match. Cast simulates a consumer-augmented color.
60
+ render(
61
+ <Token
62
+ label="Brand"
63
+ color={'brand' as TokenColor}
64
+ data-testid="token-custom"
65
+ />,
66
+ );
67
+ const token = screen.getByTestId('token-custom');
68
+ expect(token).toHaveClass('astryx-token', 'brand');
69
+ expect(token).toHaveAttribute('data-color', 'brand');
70
+ });
71
+
54
72
  it('renders as a span with invisible button when onClick is provided', () => {
55
73
  const handleClick = vi.fn();
56
74
  render(
@@ -135,10 +153,7 @@ describe('Token', () => {
135
153
 
136
154
  it('renders endContent', () => {
137
155
  render(
138
- <Token
139
- label="Token"
140
- endContent={<span data-testid="end">End</span>}
141
- />,
156
+ <Token label="Token" endContent={<span data-testid="end">End</span>} />,
142
157
  );
143
158
  expect(screen.getByTestId('end')).toBeInTheDocument();
144
159
  expect(screen.getByText('End')).toBeInTheDocument();
@@ -241,12 +256,7 @@ describe('Token accessibility', () => {
241
256
 
242
257
  it('disables both buttons when isDisabled is true', () => {
243
258
  render(
244
- <Token
245
- label="Token"
246
- onClick={() => {}}
247
- onRemove={() => {}}
248
- isDisabled
249
- />,
259
+ <Token label="Token" onClick={() => {}} onRemove={() => {}} isDisabled />,
250
260
  );
251
261
  const buttons = screen.getAllByRole('button');
252
262
  for (const button of buttons) {
@@ -320,6 +330,151 @@ describe('Token accessibility', () => {
320
330
  });
321
331
  });
322
332
 
333
+ describe('Token link with remove button', () => {
334
+ it('does not nest the remove button inside the link', () => {
335
+ render(<Token label="Tag" href="/test" onRemove={() => {}} />);
336
+ const link = screen.getByRole('link', {name: 'Tag'});
337
+ const removeButton = screen.getByRole('button', {name: 'Remove Tag'});
338
+ expect(link.contains(removeButton)).toBe(false);
339
+ });
340
+
341
+ it('keeps the link keyboard-focusable and fires onRemove from the sibling button', async () => {
342
+ const user = userEvent.setup();
343
+ const handleRemove = vi.fn();
344
+ render(<Token label="Tag" href="/test" onRemove={handleRemove} />);
345
+
346
+ const link = screen.getByRole('link', {name: 'Tag'});
347
+ expect(link.tagName).toBe('A');
348
+ expect(link).toHaveAttribute('href', '/test');
349
+
350
+ // Tab reaches the link first, then the remove button
351
+ await user.tab();
352
+ expect(link).toHaveFocus();
353
+ await user.tab();
354
+ const removeButton = screen.getByRole('button', {name: 'Remove Tag'});
355
+ expect(removeButton).toHaveFocus();
356
+
357
+ // Activating the remove button fires onRemove without involving the link
358
+ await user.keyboard('{Enter}');
359
+ expect(handleRemove).toHaveBeenCalledTimes(1);
360
+ });
361
+
362
+ it('wrapper structure matches the onClick branch pattern', () => {
363
+ render(
364
+ <Token
365
+ label="Tag"
366
+ href="/test"
367
+ onRemove={() => {}}
368
+ data-testid="link-token"
369
+ />,
370
+ );
371
+ render(
372
+ <Token
373
+ label="Tag"
374
+ onClick={() => {}}
375
+ onRemove={() => {}}
376
+ data-testid="click-token"
377
+ />,
378
+ );
379
+
380
+ const linkRoot = screen.getByTestId('link-token');
381
+ const clickRoot = screen.getByTestId('click-token');
382
+
383
+ // Both render a <span> container with the same visual classes
384
+ expect(linkRoot.tagName).toBe('SPAN');
385
+ expect(linkRoot.className).toBe(clickRoot.className);
386
+ });
387
+
388
+ it('clicking the container activates the link', () => {
389
+ render(
390
+ <Token
391
+ label="Tag"
392
+ href="/test"
393
+ onRemove={() => {}}
394
+ icon={<span data-testid="icon">★</span>}
395
+ data-testid="link-token"
396
+ />,
397
+ );
398
+ const link = screen.getByRole('link', {name: 'Tag'});
399
+ const handleLinkClick = vi.fn((e: MouseEvent) => e.preventDefault());
400
+ link.addEventListener('click', handleLinkClick);
401
+
402
+ // Clicking outside the anchor (e.g. the icon) still activates the link
403
+ fireEvent.click(screen.getByTestId('icon'));
404
+ expect(handleLinkClick).toHaveBeenCalledTimes(1);
405
+ });
406
+
407
+ it('clicking the remove button does not activate the link', () => {
408
+ const handleRemove = vi.fn();
409
+ render(<Token label="Tag" href="/test" onRemove={handleRemove} />);
410
+ const link = screen.getByRole('link', {name: 'Tag'});
411
+ const handleLinkClick = vi.fn((e: MouseEvent) => e.preventDefault());
412
+ link.addEventListener('click', handleLinkClick);
413
+
414
+ fireEvent.click(screen.getByRole('button', {name: 'Remove Tag'}));
415
+ expect(handleRemove).toHaveBeenCalledTimes(1);
416
+ expect(handleLinkClick).not.toHaveBeenCalled();
417
+ });
418
+
419
+ it('cmd/ctrl+click on the container opens the link in a new tab', () => {
420
+ const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null);
421
+ render(
422
+ <Token
423
+ label="Tag"
424
+ href="/test"
425
+ onRemove={() => {}}
426
+ icon={<span data-testid="icon">★</span>}
427
+ />,
428
+ );
429
+
430
+ // Clicking the surface (not the anchor) with a modifier opens a new tab
431
+ // rather than navigating in place — provided by useClickableContainer.
432
+ fireEvent.click(screen.getByTestId('icon'), {metaKey: true});
433
+ expect(openSpy).toHaveBeenCalledWith('/test', '_blank', 'noopener');
434
+ openSpy.mockRestore();
435
+ });
436
+
437
+ it('middle-click on the container opens the link in a new tab', () => {
438
+ const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null);
439
+ render(
440
+ <Token
441
+ label="Tag"
442
+ href="/test"
443
+ onRemove={() => {}}
444
+ icon={<span data-testid="icon">★</span>}
445
+ />,
446
+ );
447
+
448
+ fireEvent.mouseUp(screen.getByTestId('icon'), {button: 1});
449
+ expect(openSpy).toHaveBeenCalledWith('/test', '_blank', 'noopener');
450
+ openSpy.mockRestore();
451
+ });
452
+
453
+ it('a modified click that opens a new tab does not fire onRemove', () => {
454
+ const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null);
455
+ const handleRemove = vi.fn();
456
+ render(
457
+ <Token
458
+ label="Tag"
459
+ href="/test"
460
+ onRemove={handleRemove}
461
+ icon={<span data-testid="icon">★</span>}
462
+ />,
463
+ );
464
+
465
+ fireEvent.click(screen.getByTestId('icon'), {metaKey: true});
466
+ fireEvent.mouseUp(screen.getByTestId('icon'), {button: 1});
467
+ expect(handleRemove).not.toHaveBeenCalled();
468
+ expect(openSpy).toHaveBeenCalledTimes(2);
469
+ openSpy.mockRestore();
470
+ });
471
+
472
+ it('link without onRemove still renders the anchor as the root', () => {
473
+ const {container} = render(<Token label="Link" href="/test" />);
474
+ expect(container.firstElementChild?.tagName).toBe('A');
475
+ });
476
+ });
477
+
323
478
  describe('Token text overflow', () => {
324
479
  it('label element has overflow hidden and text-overflow ellipsis styles', () => {
325
480
  const {container} = render(<Token label="A very long label text" />);
@@ -355,11 +510,7 @@ describe('Token focus outline', () => {
355
510
  it('invisible button does not show its own focus outline', async () => {
356
511
  const user = userEvent.setup();
357
512
  render(
358
- <Token
359
- label="Focusable"
360
- onClick={() => {}}
361
- data-testid="focus-token"
362
- />,
513
+ <Token label="Focusable" onClick={() => {}} data-testid="focus-token" />,
363
514
  );
364
515
  const button = screen.getByRole('button', {name: 'Focusable'});
365
516
  await user.tab();
@@ -5,10 +5,11 @@
5
5
  /**
6
6
  * @file Token.tsx
7
7
  * @input Uses React, ReactNode, StyleXStyles
8
- * @output Exports Token component, TokenProps, TokenColor types
8
+ * @output Exports Token component, TokenProps, TokenColor, TokenColorMap types
9
9
  * @position Core implementation; consumed by index.ts, tested by Token.test.tsx
10
10
  *
11
11
  * SYNC: When modified, update these files to stay in sync:
12
+ * - /packages/core/src/Token/TokenLink.tsx (interactive wrapper for href+onRemove)
12
13
  * - /packages/core/src/Token/Token.doc.mjs (props table, features, implementation notes)
13
14
  * - /packages/core/src/Token/Token.test.tsx (tests for new/changed behavior)
14
15
  * - /packages/core/src/Token/index.ts (exports if types change)
@@ -32,6 +33,7 @@ import {Icon} from '../Icon';
32
33
  import {mergeProps} from '../utils';
33
34
  import {useLinkComponent} from '../Link/useLinkComponent';
34
35
  import {useInteractiveRole} from '../hooks/useInteractiveRole';
36
+ import {TokenLink} from './TokenLink';
35
37
  import type {BaseProps} from '../BaseProps';
36
38
  import {themeProps} from '../utils/themeProps';
37
39
  import {useTranslator} from '../i18n';
@@ -41,20 +43,37 @@ import {useTranslator} from '../i18n';
41
43
  // =============================================================================
42
44
 
43
45
  /**
44
- * Available color variants for the token.
46
+ * Extensible color map for Token.
47
+ *
48
+ * Theme packages can add custom colors via TypeScript module augmentation:
49
+ * @example
50
+ * ```
51
+ * declare module '@astryxdesign/core/Token' {
52
+ * interface TokenColorMap {
53
+ * brand: true;
54
+ * }
55
+ * }
56
+ * ```
57
+ */
58
+ export interface TokenColorMap {
59
+ default: true;
60
+ red: true;
61
+ orange: true;
62
+ yellow: true;
63
+ green: true;
64
+ teal: true;
65
+ cyan: true;
66
+ blue: true;
67
+ purple: true;
68
+ pink: true;
69
+ gray: true;
70
+ }
71
+
72
+ /**
73
+ * Token color type derived from TokenColorMap.
74
+ * Extensible via module augmentation of TokenColorMap.
45
75
  */
46
- export type TokenColor =
47
- | 'default'
48
- | 'red'
49
- | 'orange'
50
- | 'yellow'
51
- | 'green'
52
- | 'teal'
53
- | 'cyan'
54
- | 'blue'
55
- | 'purple'
56
- | 'pink'
57
- | 'gray';
76
+ export type TokenColor = keyof TokenColorMap;
58
77
 
59
78
  export type TokenSize = 'sm' | 'md' | 'lg';
60
79
 
@@ -296,7 +315,9 @@ const colorStyles = stylex.create({
296
315
  * `<span>` container with an invisible `<button>` when `onClick` is provided.
297
316
  * The invisible button pattern provides real button semantics for accessibility
298
317
  * while the container uses `:focus-within` to show focus outlines around the
299
- * entire token.
318
+ * entire token. When both `href` and `onRemove` are provided, the same
319
+ * container pattern is used with an invisible `<a>` so the remove `<button>`
320
+ * renders as a sibling of the link rather than nested inside it.
300
321
  *
301
322
  * @example
302
323
  * ```
@@ -333,6 +354,20 @@ export function Token({
333
354
  // if onClick was provided — the popover attaches its own handler.
334
355
  const effectiveOnClick = onClick ?? (role === 'button' ? () => {} : null);
335
356
 
357
+ const removeButton = onRemove != null && (
358
+ <button
359
+ type="button"
360
+ aria-label={t('@astryx.token.remove', {label})}
361
+ onClick={e => {
362
+ e.stopPropagation();
363
+ onRemove(e);
364
+ }}
365
+ disabled={isDisabled}
366
+ {...stylex.props(styles.removeButton)}>
367
+ <Icon icon="close" size="xsm" color="inherit" />
368
+ </button>
369
+ );
370
+
336
371
  const content = (
337
372
  <>
338
373
  {icon}
@@ -341,19 +376,7 @@ export function Token({
341
376
  {label}
342
377
  </span>
343
378
  {endContent}
344
- {onRemove != null && (
345
- <button
346
- type="button"
347
- aria-label={t('@astryx.token.remove', {label})}
348
- onClick={e => {
349
- e.stopPropagation();
350
- onRemove(e);
351
- }}
352
- disabled={isDisabled}
353
- {...stylex.props(styles.removeButton)}>
354
- <Icon icon="close" size="xsm" color="inherit" />
355
- </button>
356
- )}
379
+ {removeButton}
357
380
  </>
358
381
  );
359
382
 
@@ -364,11 +387,57 @@ export function Token({
364
387
  };
365
388
 
366
389
  if (role === 'link') {
390
+ if (onRemove == null) {
391
+ return (
392
+ <LinkComponent
393
+ ref={ref as React.Ref<HTMLAnchorElement>}
394
+ href={href as string}
395
+ aria-disabled={isDisabled || undefined}
396
+ {...sharedProps}
397
+ {...mergeProps(
398
+ themeProps('token', {color, size}),
399
+ stylex.props(
400
+ styles.base,
401
+ sizeStyles[size],
402
+ colorStyles[color],
403
+ styles.interactive,
404
+ isDisabled && styles.disabled,
405
+ xstyle,
406
+ ),
407
+ className,
408
+ style,
409
+ )}>
410
+ {content}
411
+ </LinkComponent>
412
+ );
413
+ }
414
+
415
+ // With a remove button, the link cannot be the root — nesting a <button>
416
+ // inside an <a> is invalid HTML (WCAG 4.1.2). Delegate to TokenLink, a
417
+ // non-presentational wrapper that renders a <span> container with the link
418
+ // and the remove button as siblings and wires up useClickableContainer so
419
+ // the whole token surface activates the link (including middle-click and
420
+ // cmd/ctrl-click to open in a new tab). Token itself stays presentational
421
+ // (no refs/effects) per the @astryx/presentational-component rule.
367
422
  return (
368
- <LinkComponent
369
- ref={ref as React.Ref<HTMLAnchorElement>}
423
+ <TokenLink
424
+ ref={ref}
370
425
  href={href as string}
371
- aria-disabled={isDisabled || undefined}
426
+ isDisabled={isDisabled}
427
+ LinkComponent={LinkComponent}
428
+ icon={icon}
429
+ endContent={endContent}
430
+ removeButton={removeButton}
431
+ linkStyleProps={stylex.props(styles.invisibleButton)}
432
+ labelContent={
433
+ <span
434
+ {...stylex.props(
435
+ styles.label,
436
+ isLabelHidden && styles.labelHidden,
437
+ )}>
438
+ {label}
439
+ </span>
440
+ }
372
441
  {...sharedProps}
373
442
  {...mergeProps(
374
443
  themeProps('token', {color, size}),
@@ -377,14 +446,14 @@ export function Token({
377
446
  sizeStyles[size],
378
447
  colorStyles[color],
379
448
  styles.interactive,
449
+ styles.focusVisibleOutline,
380
450
  isDisabled && styles.disabled,
381
451
  xstyle,
382
452
  ),
383
453
  className,
384
454
  style,
385
- )}>
386
- {content}
387
- </LinkComponent>
455
+ )}
456
+ />
388
457
  );
389
458
  }
390
459
 
@@ -431,19 +500,7 @@ export function Token({
431
500
  </span>
432
501
  </button>
433
502
  {endContent}
434
- {onRemove != null && (
435
- <button
436
- type="button"
437
- aria-label={t('@astryx.token.remove', {label})}
438
- onClick={e => {
439
- e.stopPropagation();
440
- onRemove(e);
441
- }}
442
- disabled={isDisabled}
443
- {...stylex.props(styles.removeButton)}>
444
- <Icon icon="close" size="xsm" color="inherit" />
445
- </button>
446
- )}
503
+ {removeButton}
447
504
  </span>
448
505
  );
449
506
  }
@@ -0,0 +1,107 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file TokenLink.tsx
7
+ * @input Rendered token pieces (icon, label, endContent, remove button) plus
8
+ * container/link styling props and the resolved LinkComponent
9
+ * @output Exports TokenLink, the interactive container for the href+onRemove Token variant
10
+ * @position Internal to Token; keeps Token.tsx presentational
11
+ *
12
+ * Token is a presentational component (see
13
+ * /internal/eslint-plugin-astryx/presentational-component.js) and must not use
14
+ * refs or effects. When a Token has both `href` and `onRemove`, the remove
15
+ * <button> must be a sibling of the link (nesting a button inside an anchor is
16
+ * invalid HTML — WCAG 4.1.2), so the surface becomes a clickable container that
17
+ * delegates to an inner link. That delegation needs refs, so it lives here.
18
+ *
19
+ * useClickableContainer gives the container middle-click and cmd/ctrl-click
20
+ * open-in-new-tab behavior for the link, ignores clicks on nested interactive
21
+ * elements (the remove button), and skips activation during text selection.
22
+ *
23
+ * SYNC: When modified, update these files to stay in sync:
24
+ * - /packages/core/src/Token/Token.tsx (renders TokenLink for the href+onRemove case)
25
+ * - /packages/core/src/Token/Token.test.tsx (tests for delegated behavior)
26
+ */
27
+
28
+ import type {ReactNode, CSSProperties, Ref, HTMLAttributes} from 'react';
29
+ import {useRef} from 'react';
30
+ import {mergeRefs} from '../utils';
31
+ import {useClickableContainer} from '../hooks/useClickableContainer';
32
+ import type {LinkComponentType} from '../Link/types';
33
+
34
+ export interface TokenLinkProps extends HTMLAttributes<HTMLElement> {
35
+ /** Ref forwarded to the container element. */
36
+ ref?: Ref<HTMLElement>;
37
+ /** Link URL. */
38
+ href: string;
39
+ /** Whether the token is disabled. */
40
+ isDisabled: boolean;
41
+ /** Resolved link component (native `<a>` or a router link). */
42
+ LinkComponent: LinkComponentType;
43
+ /** `stylex.props()` output applied to the invisible inner link. */
44
+ linkStyleProps: {className?: string; style?: CSSProperties};
45
+ /** Visible (or visually hidden) label node rendered inside the link. */
46
+ labelContent: ReactNode;
47
+ /** Optional leading icon. */
48
+ icon?: ReactNode;
49
+ /** Content rendered after the label, before the remove button. */
50
+ endContent?: ReactNode;
51
+ /** The remove button node, rendered as a sibling of the link. */
52
+ removeButton: ReactNode;
53
+ /** Allow data-* attributes (e.g. data-testid, theme targets) on the container. */
54
+ [dataAttr: `data-${string}`]: unknown;
55
+ }
56
+
57
+ /**
58
+ * Interactive container for a Token that is both a link and removable.
59
+ *
60
+ * Renders a `<span>` wrapping an invisible inner link and the remove button as
61
+ * siblings. useClickableContainer delegates clicks on the surface to the link
62
+ * (including middle-click / cmd+click to open in a new tab) while the remove
63
+ * button keeps handling its own clicks.
64
+ */
65
+ export function TokenLink({
66
+ ref,
67
+ href,
68
+ isDisabled,
69
+ LinkComponent,
70
+ linkStyleProps,
71
+ labelContent,
72
+ icon,
73
+ endContent,
74
+ removeButton,
75
+ ...containerProps
76
+ }: TokenLinkProps) {
77
+ const containerRef = useRef<HTMLElement | null>(null);
78
+ const linkRef = useRef<HTMLElement | null>(null);
79
+
80
+ const {onClick, onMouseUp} = useClickableContainer({
81
+ containerRef,
82
+ interactiveRef: linkRef,
83
+ href,
84
+ disabled: isDisabled,
85
+ });
86
+
87
+ return (
88
+ <span
89
+ ref={mergeRefs(ref, containerRef)}
90
+ onClick={isDisabled ? undefined : onClick}
91
+ onMouseUp={isDisabled ? undefined : onMouseUp}
92
+ {...containerProps}>
93
+ {icon}
94
+ <LinkComponent
95
+ ref={linkRef as Ref<HTMLAnchorElement>}
96
+ href={href}
97
+ aria-disabled={isDisabled || undefined}
98
+ {...linkStyleProps}>
99
+ {labelContent}
100
+ </LinkComponent>
101
+ {endContent}
102
+ {removeButton}
103
+ </span>
104
+ );
105
+ }
106
+
107
+ TokenLink.displayName = 'TokenLink';
@@ -12,4 +12,4 @@
12
12
  */
13
13
 
14
14
  export {Token} from './Token';
15
- export type {TokenProps, TokenColor, TokenSize} from './Token';
15
+ export type {TokenProps, TokenColor, TokenColorMap, TokenSize} from './Token';
@@ -88,6 +88,13 @@ export const docs = {
88
88
  description:
89
89
  'Validation status object with type and message for error/warning/success states.',
90
90
  },
91
+ {
92
+ name: 'statusVariant',
93
+ type: "'attached' | 'detached'",
94
+ description:
95
+ '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.',
96
+ default: "'attached'",
97
+ },
91
98
  {
92
99
  name: 'isLabelHidden',
93
100
  type: 'boolean',
@@ -185,7 +192,14 @@ export const docs = {
185
192
  {
186
193
  name: 'handleRef',
187
194
  type: 'React.Ref<TokenizerHandle>',
188
- description: 'Imperative handle for focus() and blur() control.',
195
+ description:
196
+ 'Imperative handle exposing focusInput(), focusFirstToken(), focusLastToken(), clearInput(), and selectAll().',
197
+ },
198
+ {
199
+ name: 'width',
200
+ type: 'SizeValue',
201
+ description:
202
+ '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.',
189
203
  },
190
204
  {
191
205
  name: 'xstyle',
@@ -307,6 +321,13 @@ export const docsZh = {
307
321
  description:
308
322
  '\u9a8c\u8bc1\u72b6\u6001\u5bf9\u8c61\uff0c\u5305\u542b\u7c7b\u578b\u548c\u6d88\u606f\uff0c\u7528\u4e8e\u9519\u8bef/\u8b66\u544a/\u6210\u529f\u72b6\u6001\u3002',
309
323
  },
324
+ {
325
+ name: 'statusVariant',
326
+ type: "'attached' | 'detached'",
327
+ description:
328
+ '状态消息相对于输入框的放置方式。attached 直接叠加在输入框下方(带边框处理);detached 作为独立元素浮于下方并留有间距。',
329
+ default: "'attached'",
330
+ },
310
331
  {
311
332
  name: 'isLabelHidden',
312
333
  type: 'boolean',
@@ -461,6 +482,7 @@ export const docsDense = {
461
482
  isDisabled: 'Disables input+all token interactions.',
462
483
  htmlName: 'HTML name attr; one hidden input per selected item id.',
463
484
  status: 'Validation status w/ type+message for error/warning/success.',
485
+ statusVariant: 'How status message is placed: attached overlaps below input; detached floats below w/ spacing.',
464
486
  isLabelHidden: 'Visually hides label; keeps a11y.',
465
487
  description: 'Helper text below label.',
466
488
  isRequired: 'Marks field required.',
@@ -1136,3 +1136,26 @@ describe('Tokenizer', () => {
1136
1136
  });
1137
1137
  });
1138
1138
  });
1139
+
1140
+
1141
+ describe('Tokenizer statusVariant forwarding', () => {
1142
+ it('defaults to attached (status renders with data-variant="attached")', () => {
1143
+ const {container} = render(
1144
+ <Tokenizer label="Members" searchSource={userSource} value={[]} onChange={() => {}} status={{type: 'error', message: 'Required'}} />,
1145
+ );
1146
+ expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
1147
+ 'data-variant',
1148
+ 'attached',
1149
+ );
1150
+ });
1151
+
1152
+ it('forwards statusVariant="detached" to the underlying Field status', () => {
1153
+ const {container} = render(
1154
+ <Tokenizer label="Members" searchSource={userSource} value={[]} onChange={() => {}} status={{type: 'error', message: 'Required'}} statusVariant="detached" />,
1155
+ );
1156
+ expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
1157
+ 'data-variant',
1158
+ 'detached',
1159
+ );
1160
+ });
1161
+ });
@@ -37,6 +37,7 @@ import {
37
37
  inputStatusBorderStyles,
38
38
  inputStatusHoverShadowStyles,
39
39
  inputStatusFocusWithinStyles,
40
+ type FieldStatusVariant,
40
41
  } from '../Field';
41
42
  import {Token} from '../Token';
42
43
  import {renderIconSlot, type IconType} from '../Icon';
@@ -111,6 +112,13 @@ export interface TokenizerProps<T extends SearchableItem> extends Omit<
111
112
  isOptional?: boolean;
112
113
  /** Validation status. */
113
114
  status?: InputStatus;
115
+ /**
116
+ * How the status message is placed relative to the input.
117
+ * - 'attached': message overlaps directly below the input (bordered treatment)
118
+ * - 'detached': message floats below as a separate element with spacing
119
+ * @default 'attached'
120
+ */
121
+ statusVariant?: FieldStatusVariant;
114
122
  /**
115
123
  * Icon to display at the start of the input.
116
124
  * Accepts a ReactNode (e.g. `<Icon icon={SearchIcon} />`) or an SVG icon component directly.
@@ -370,6 +378,7 @@ export function Tokenizer<T extends SearchableItem>({
370
378
  isRequired = false,
371
379
  isOptional = false,
372
380
  status,
381
+ statusVariant = 'attached',
373
382
  startIcon,
374
383
  labelTooltip,
375
384
  searchSource,
@@ -730,7 +739,7 @@ export function Tokenizer<T extends SearchableItem>({
730
739
  isTruncated && styles.truncatedWrapper,
731
740
  isDisabled && inputWrapperStyles.disabled,
732
741
  status && inputStatusBorderStyles[status.type],
733
- status && inputStatusHoverShadowStyles[status.type],
742
+ status && !isDisabled && inputStatusHoverShadowStyles[status.type],
734
743
  status && inputStatusFocusWithinStyles[status.type],
735
744
  ),
736
745
  )}>
@@ -828,7 +837,9 @@ export function Tokenizer<T extends SearchableItem>({
828
837
  isTruncated && styles.truncatedWrapper,
829
838
  isDisabled && inputWrapperStyles.disabled,
830
839
  status && inputStatusBorderStyles[status.type],
831
- status && inputStatusHoverShadowStyles[status.type],
840
+ status &&
841
+ !isDisabled &&
842
+ inputStatusHoverShadowStyles[status.type],
832
843
  status && inputStatusFocusWithinStyles[status.type],
833
844
  ),
834
845
  )}>
@@ -891,6 +902,7 @@ export function Tokenizer<T extends SearchableItem>({
891
902
  }
892
903
  : undefined
893
904
  }
905
+ statusVariant={statusVariant}
894
906
  labelTooltip={labelTooltip}
895
907
  width={width}
896
908
  xstyle={xstyle}