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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (560) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/dist/Avatar/Avatar.d.ts +42 -3
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +190 -59
  5. package/dist/Avatar/AvatarStatusDot.d.ts +6 -4
  6. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  7. package/dist/AvatarGroup/AvatarGroup.d.ts +7 -1
  8. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  9. package/dist/AvatarGroup/AvatarGroup.js +58 -6
  10. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  11. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  12. package/dist/AvatarGroup/AvatarGroupOverflow.js +7 -2
  13. package/dist/Banner/Banner.js +2 -2
  14. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  15. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -4
  16. package/dist/Breadcrumbs/index.d.ts +1 -0
  17. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  18. package/dist/Breadcrumbs/index.js +2 -1
  19. package/dist/Button/Button.d.ts +1 -1
  20. package/dist/Button/Button.d.ts.map +1 -1
  21. package/dist/Button/Button.js +6 -4
  22. package/dist/Calendar/Calendar.d.ts.map +1 -1
  23. package/dist/Calendar/Calendar.js +50 -9
  24. package/dist/Calendar/dayCellUtils.d.ts +4 -4
  25. package/dist/Calendar/dayCellUtils.js +2 -2
  26. package/dist/Calendar/styles.d.ts +30 -31
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +30 -31
  29. package/dist/Card/Card.js +1 -1
  30. package/dist/Carousel/Carousel.d.ts.map +1 -1
  31. package/dist/Carousel/Carousel.js +22 -12
  32. package/dist/Chat/ChatComposerInput.js +1 -1
  33. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  34. package/dist/Chat/ChatDictationButton.js +3 -1
  35. package/dist/Chat/ChatLayout.js +4 -4
  36. package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
  37. package/dist/Chat/ChatMessageBubble.js +8 -8
  38. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  39. package/dist/Chat/ChatToolCalls.js +10 -3
  40. package/dist/Chat/useTriggerMenu.js +2 -2
  41. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  42. package/dist/CheckboxInput/CheckboxInput.js +6 -6
  43. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  44. package/dist/CheckboxList/CheckboxListItem.js +19 -4
  45. package/dist/Citation/Citation.d.ts +22 -1
  46. package/dist/Citation/Citation.d.ts.map +1 -1
  47. package/dist/Citation/Citation.js +20 -6
  48. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  49. package/dist/CodeBlock/CodeBlock.js +16 -10
  50. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  51. package/dist/Collapsible/Collapsible.js +3 -1
  52. package/dist/CommandPalette/CommandPalette.d.ts +1 -1
  53. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  54. package/dist/CommandPalette/CommandPalette.js +42 -3
  55. package/dist/CommandPalette/CommandPaletteItem.js +1 -1
  56. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  57. package/dist/ContextMenu/ContextMenu.js +23 -5
  58. package/dist/ContextMenu/index.d.ts +1 -0
  59. package/dist/ContextMenu/index.d.ts.map +1 -1
  60. package/dist/ContextMenu/index.js +3 -1
  61. package/dist/DateInput/DateInput.d.ts +10 -2
  62. package/dist/DateInput/DateInput.d.ts.map +1 -1
  63. package/dist/DateInput/DateInput.js +33 -19
  64. package/dist/DateRangeInput/DateRangeInput.d.ts +10 -2
  65. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  66. package/dist/DateRangeInput/DateRangeInput.js +23 -20
  67. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  68. package/dist/DateTimeInput/DateTimeInput.js +23 -18
  69. package/dist/Divider/Divider.d.ts +2 -2
  70. package/dist/Divider/Divider.d.ts.map +1 -1
  71. package/dist/Divider/Divider.js +13 -0
  72. package/dist/DropdownMenu/DropdownMenu.d.ts +6 -0
  73. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  74. package/dist/DropdownMenu/DropdownMenu.js +22 -5
  75. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  76. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +4 -1
  77. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  78. package/dist/DropdownMenu/DropdownMenuItem.js +5 -2
  79. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +8 -11
  80. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
  81. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +3 -1
  82. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +1 -1
  83. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  84. package/dist/DropdownMenu/DropdownMenuRadioItem.js +5 -2
  85. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +104 -0
  86. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -0
  87. package/dist/DropdownMenu/DropdownMenuSubMenu.js +427 -0
  88. package/dist/DropdownMenu/index.d.ts +10 -0
  89. package/dist/DropdownMenu/index.d.ts.map +1 -1
  90. package/dist/DropdownMenu/index.js +13 -0
  91. package/dist/DropdownMenu/menuItemHover.d.ts +23 -0
  92. package/dist/DropdownMenu/menuItemHover.d.ts.map +1 -0
  93. package/dist/DropdownMenu/menuItemHover.js +33 -0
  94. package/dist/DropdownMenu/menuItemRoles.d.ts +8 -0
  95. package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -1
  96. package/dist/DropdownMenu/menuItemRoles.js +10 -1
  97. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  98. package/dist/DropdownMenu/renderDropdownItems.js +21 -6
  99. package/dist/Field/Field.d.ts +3 -1
  100. package/dist/Field/Field.d.ts.map +1 -1
  101. package/dist/Field/Field.js +4 -1
  102. package/dist/Field/FieldLabel.d.ts +2 -1
  103. package/dist/Field/FieldLabel.d.ts.map +1 -1
  104. package/dist/Field/FieldLabel.js +9 -5
  105. package/dist/Field/inputStyles.stylex.d.ts +1 -0
  106. package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
  107. package/dist/Field/inputStyles.stylex.js +1 -0
  108. package/dist/FieldStatus/FieldStatus.d.ts +10 -0
  109. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  110. package/dist/FieldStatus/FieldStatus.js +39 -3
  111. package/dist/FileInput/FileInput.d.ts +10 -2
  112. package/dist/FileInput/FileInput.d.ts.map +1 -1
  113. package/dist/FileInput/FileInput.js +93 -65
  114. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  115. package/dist/HoverCard/useHoverCard.js +10 -4
  116. package/dist/Icon/Icon.d.ts +15 -1
  117. package/dist/Icon/Icon.d.ts.map +1 -1
  118. package/dist/Icon/Icon.js +19 -3
  119. package/dist/Item/Item.d.ts +16 -3
  120. package/dist/Item/Item.d.ts.map +1 -1
  121. package/dist/Item/Item.js +31 -6
  122. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  123. package/dist/Layer/layerAnimations.stylex.js +10 -2
  124. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  125. package/dist/Lightbox/Lightbox.js +19 -13
  126. package/dist/List/List.d.ts.map +1 -1
  127. package/dist/List/List.js +6 -3
  128. package/dist/List/ListItem.d.ts +11 -1
  129. package/dist/List/ListItem.d.ts.map +1 -1
  130. package/dist/List/ListItem.js +2 -0
  131. package/dist/Markdown/Markdown.d.ts.map +1 -1
  132. package/dist/Markdown/Markdown.js +7 -0
  133. package/dist/MetadataList/MetadataList.d.ts +1 -1
  134. package/dist/MetadataList/MetadataList.d.ts.map +1 -1
  135. package/dist/MetadataList/MetadataList.js +4 -2
  136. package/dist/MultiSelector/MultiSelector.d.ts +9 -1
  137. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  138. package/dist/MultiSelector/MultiSelector.js +80 -42
  139. package/dist/NavMenu/NavHeadingMenuItem.js +1 -1
  140. package/dist/NumberInput/NumberInput.d.ts +10 -2
  141. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  142. package/dist/NumberInput/NumberInput.js +22 -17
  143. package/dist/Outline/Outline.js +2 -2
  144. package/dist/Pagination/Pagination.d.ts.map +1 -1
  145. package/dist/Pagination/Pagination.js +11 -3
  146. package/dist/PowerSearch/PowerSearch.d.ts +9 -2
  147. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  148. package/dist/PowerSearch/PowerSearch.js +2 -0
  149. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  150. package/dist/ProgressBar/ProgressBar.js +6 -1
  151. package/dist/RadioList/RadioList.d.ts +2 -2
  152. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  153. package/dist/RadioList/RadioListItem.js +5 -5
  154. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  155. package/dist/Resizable/ResizeHandle.js +49 -19
  156. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
  157. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  158. package/dist/SegmentedControl/SegmentedControlItem.js +5 -3
  159. package/dist/Selector/Selector.d.ts +8 -0
  160. package/dist/Selector/Selector.d.ts.map +1 -1
  161. package/dist/Selector/Selector.js +66 -17
  162. package/dist/SideNav/SideNav.d.ts.map +1 -1
  163. package/dist/SideNav/SideNav.js +5 -5
  164. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  165. package/dist/SideNav/SideNavCollapseButton.js +13 -10
  166. package/dist/SideNav/SideNavItem.d.ts +1 -1
  167. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  168. package/dist/SideNav/SideNavItem.js +12 -10
  169. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  170. package/dist/Skeleton/Skeleton.js +2 -2
  171. package/dist/Slider/Slider.d.ts.map +1 -1
  172. package/dist/Slider/Slider.js +87 -34
  173. package/dist/Spinner/Spinner.d.ts +3 -2
  174. package/dist/Spinner/Spinner.d.ts.map +1 -1
  175. package/dist/Spinner/Spinner.js +11 -2
  176. package/dist/Switch/Switch.d.ts +8 -1
  177. package/dist/Switch/Switch.d.ts.map +1 -1
  178. package/dist/Switch/Switch.js +171 -91
  179. package/dist/TabList/Tab.js +2 -2
  180. package/dist/TabList/TabList.d.ts +2 -11
  181. package/dist/TabList/TabList.d.ts.map +1 -1
  182. package/dist/TabList/TabList.js +5 -9
  183. package/dist/TabList/TabListContext.d.ts +0 -5
  184. package/dist/TabList/TabListContext.d.ts.map +1 -1
  185. package/dist/TabList/TabListContext.js +0 -5
  186. package/dist/TabList/TabMenu.js +1 -1
  187. package/dist/Table/BaseTable.d.ts.map +1 -1
  188. package/dist/Table/BaseTable.js +30 -4
  189. package/dist/Table/TableCell.js +3 -3
  190. package/dist/Table/TableHeaderCell.js +3 -3
  191. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  192. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +15 -11
  193. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  194. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +37 -27
  195. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  196. package/dist/Table/plugins/selection/useTableSelection.js +3 -0
  197. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  198. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +15 -7
  199. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  200. package/dist/Table/plugins/tree/useTableTreeData.js +29 -23
  201. package/dist/Table/types.d.ts +32 -0
  202. package/dist/Table/types.d.ts.map +1 -1
  203. package/dist/TextArea/TextArea.d.ts +10 -1
  204. package/dist/TextArea/TextArea.d.ts.map +1 -1
  205. package/dist/TextArea/TextArea.js +18 -19
  206. package/dist/TextInput/TextInput.d.ts +10 -2
  207. package/dist/TextInput/TextInput.d.ts.map +1 -1
  208. package/dist/TextInput/TextInput.js +20 -17
  209. package/dist/Thumbnail/Thumbnail.js +2 -2
  210. package/dist/TimeInput/TimeInput.d.ts +10 -2
  211. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  212. package/dist/TimeInput/TimeInput.js +17 -17
  213. package/dist/Timestamp/Timestamp.d.ts +31 -3
  214. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  215. package/dist/Timestamp/Timestamp.js +47 -76
  216. package/dist/Timestamp/formatInstant.d.ts +41 -0
  217. package/dist/Timestamp/formatInstant.d.ts.map +1 -0
  218. package/dist/Timestamp/formatInstant.js +177 -0
  219. package/dist/Timestamp/index.d.ts +1 -0
  220. package/dist/Timestamp/index.d.ts.map +1 -1
  221. package/dist/Timestamp/tooltipEntries.d.ts +56 -0
  222. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -0
  223. package/dist/Timestamp/tooltipEntries.js +163 -0
  224. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  225. package/dist/Toast/ToastViewport.js +61 -9
  226. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  227. package/dist/ToggleButton/ToggleButton.js +3 -1
  228. package/dist/Token/Token.d.ts +34 -4
  229. package/dist/Token/Token.d.ts.map +1 -1
  230. package/dist/Token/Token.js +85 -45
  231. package/dist/Token/TokenLink.d.ts +62 -0
  232. package/dist/Token/TokenLink.d.ts.map +1 -0
  233. package/dist/Token/TokenLink.js +76 -0
  234. package/dist/Token/index.d.ts +1 -1
  235. package/dist/Token/index.d.ts.map +1 -1
  236. package/dist/Tokenizer/Tokenizer.d.ts +9 -2
  237. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  238. package/dist/Tokenizer/Tokenizer.js +4 -2
  239. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  240. package/dist/Tooltip/useTooltip.js +17 -1
  241. package/dist/TopNav/TopNavItem.d.ts +3 -1
  242. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  243. package/dist/TopNav/TopNavItem.js +30 -3
  244. package/dist/TreeList/TreeList.d.ts.map +1 -1
  245. package/dist/TreeList/TreeList.js +1 -0
  246. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  247. package/dist/TreeList/TreeListBranches.js +8 -6
  248. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  249. package/dist/TreeList/TreeListItem.js +32 -14
  250. package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
  251. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  252. package/dist/Typeahead/BaseTypeahead.js +22 -11
  253. package/dist/Typeahead/Typeahead.d.ts +9 -2
  254. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  255. package/dist/Typeahead/Typeahead.js +11 -2
  256. package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
  257. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  258. package/dist/Typeahead/TypeaheadItem.js +20 -10
  259. package/dist/astryx.css +70 -30
  260. package/dist/astryx.umd.js +51 -51
  261. package/dist/astryx.umd.js.map +4 -4
  262. package/dist/hooks/index.d.ts +2 -0
  263. package/dist/hooks/index.d.ts.map +1 -1
  264. package/dist/hooks/index.js +1 -0
  265. package/dist/hooks/isRtlElement.d.ts +18 -0
  266. package/dist/hooks/isRtlElement.d.ts.map +1 -0
  267. package/dist/hooks/isRtlElement.js +27 -0
  268. package/dist/hooks/useAnnounce.d.ts +3 -0
  269. package/dist/hooks/useAnnounce.d.ts.map +1 -1
  270. package/dist/hooks/useAnnounce.js +39 -1
  271. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  272. package/dist/hooks/useFocusTrap.js +52 -11
  273. package/dist/hooks/useGridFocus.d.ts +6 -1
  274. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  275. package/dist/hooks/useGridFocus.js +12 -9
  276. package/dist/hooks/useHotkeys.d.ts +9 -0
  277. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  278. package/dist/hooks/useHotkeys.js +9 -0
  279. package/dist/hooks/useInputStatusIcon.d.ts +38 -0
  280. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -0
  281. package/dist/hooks/useInputStatusIcon.js +158 -0
  282. package/dist/hooks/useKeyboardHint.d.ts +1 -1
  283. package/dist/hooks/useKeyboardHint.d.ts.map +1 -1
  284. package/dist/hooks/useKeyboardHint.js +4 -2
  285. package/dist/hooks/useListFocus.d.ts +39 -1
  286. package/dist/hooks/useListFocus.d.ts.map +1 -1
  287. package/dist/hooks/useListFocus.js +54 -9
  288. package/dist/i18n/InternationalizationContext.d.ts +1 -0
  289. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  290. package/dist/i18n/InternationalizationContext.js +3 -0
  291. package/dist/i18n/InternationalizationProvider.d.ts +8 -1
  292. package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
  293. package/dist/i18n/InternationalizationProvider.js +5 -1
  294. package/dist/i18n/getLocaleDirection.d.ts +18 -0
  295. package/dist/i18n/getLocaleDirection.d.ts.map +1 -0
  296. package/dist/i18n/getLocaleDirection.js +29 -0
  297. package/dist/i18n/index.d.ts +6 -0
  298. package/dist/i18n/index.d.ts.map +1 -1
  299. package/dist/i18n/index.js +7 -1
  300. package/dist/i18n/useDirection.d.ts +2 -0
  301. package/dist/i18n/useDirection.d.ts.map +1 -0
  302. package/dist/i18n/useDirection.js +27 -0
  303. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  304. package/dist/theme/derivedVarRegistry.js +13 -0
  305. package/dist/utils/index.d.ts +1 -0
  306. package/dist/utils/index.d.ts.map +1 -1
  307. package/dist/utils/index.js +2 -1
  308. package/dist/utils/plainDate.d.ts +17 -0
  309. package/dist/utils/plainDate.d.ts.map +1 -1
  310. package/dist/utils/plainDate.js +23 -4
  311. package/dist/utils/rtlStyles.d.ts +24 -0
  312. package/dist/utils/rtlStyles.d.ts.map +1 -0
  313. package/dist/utils/rtlStyles.js +27 -0
  314. package/locales/en.json +112 -0
  315. package/locales/pseudo.json +84 -0
  316. package/package.json +6 -1
  317. package/src/Avatar/Avatar.doc.mjs +50 -3
  318. package/src/Avatar/Avatar.test.tsx +218 -0
  319. package/src/Avatar/Avatar.tsx +285 -79
  320. package/src/Avatar/AvatarStatusDot.doc.mjs +4 -4
  321. package/src/Avatar/AvatarStatusDot.tsx +6 -4
  322. package/src/AvatarGroup/AvatarGroup.doc.mjs +2 -0
  323. package/src/AvatarGroup/AvatarGroup.test.tsx +162 -2
  324. package/src/AvatarGroup/AvatarGroup.tsx +58 -3
  325. package/src/AvatarGroup/AvatarGroupOverflow.tsx +5 -2
  326. package/src/Banner/Banner.tsx +6 -6
  327. package/src/Breadcrumbs/BreadcrumbItem.tsx +10 -10
  328. package/src/Breadcrumbs/Breadcrumbs.test.tsx +52 -1
  329. package/src/Breadcrumbs/index.ts +4 -0
  330. package/src/Button/Button.doc.mjs +0 -4
  331. package/src/Button/Button.test.tsx +16 -0
  332. package/src/Button/Button.tsx +6 -4
  333. package/src/Calendar/Calendar.test.tsx +170 -20
  334. package/src/Calendar/Calendar.tsx +73 -16
  335. package/src/Calendar/dayCellUtils.test.ts +18 -18
  336. package/src/Calendar/dayCellUtils.ts +2 -2
  337. package/src/Calendar/styles.ts +33 -39
  338. package/src/Card/Card.tsx +1 -1
  339. package/src/Carousel/Carousel.tsx +25 -6
  340. package/src/Chat/ChatComposer.doc.mjs +2 -2
  341. package/src/Chat/ChatComposerInput.doc.mjs +1 -1
  342. package/src/Chat/ChatComposerInput.tsx +2 -2
  343. package/src/Chat/ChatDictationButton.tsx +7 -1
  344. package/src/Chat/ChatLayout.tsx +4 -4
  345. package/src/Chat/ChatMessageBubble.tsx +15 -10
  346. package/src/Chat/ChatSystemMessage.test.tsx +5 -0
  347. package/src/Chat/ChatToolCalls.tsx +9 -2
  348. package/src/Chat/useTriggerMenu.tsx +1 -1
  349. package/src/CheckboxInput/CheckboxInput.doc.mjs +6 -0
  350. package/src/CheckboxInput/CheckboxInput.test.tsx +23 -0
  351. package/src/CheckboxInput/CheckboxInput.tsx +21 -6
  352. package/src/CheckboxList/CheckboxList.doc.mjs +6 -0
  353. package/src/CheckboxList/CheckboxList.test.tsx +94 -3
  354. package/src/CheckboxList/CheckboxListItem.tsx +17 -10
  355. package/src/Citation/Citation.doc.mjs +4 -3
  356. package/src/Citation/Citation.test.tsx +97 -0
  357. package/src/Citation/Citation.tsx +43 -11
  358. package/src/CodeBlock/CodeBlock.test.tsx +17 -0
  359. package/src/CodeBlock/CodeBlock.tsx +38 -11
  360. package/src/Collapsible/Collapsible.doc.mjs +1 -0
  361. package/src/Collapsible/Collapsible.test.tsx +7 -0
  362. package/src/Collapsible/Collapsible.tsx +16 -6
  363. package/src/CommandPalette/CommandPalette.test.tsx +95 -1
  364. package/src/CommandPalette/CommandPalette.tsx +41 -2
  365. package/src/CommandPalette/CommandPaletteItem.tsx +1 -1
  366. package/src/ContextMenu/ContextMenu.test.tsx +59 -2
  367. package/src/ContextMenu/ContextMenu.tsx +22 -11
  368. package/src/ContextMenu/index.ts +5 -0
  369. package/src/DateInput/DateInput.doc.mjs +25 -0
  370. package/src/DateInput/DateInput.test.tsx +230 -0
  371. package/src/DateInput/DateInput.tsx +51 -29
  372. package/src/DateRangeInput/DateRangeInput.doc.mjs +16 -0
  373. package/src/DateRangeInput/DateRangeInput.test.tsx +140 -0
  374. package/src/DateRangeInput/DateRangeInput.tsx +42 -30
  375. package/src/DateTimeInput/DateTimeInput.doc.mjs +22 -1
  376. package/src/DateTimeInput/DateTimeInput.test.tsx +112 -0
  377. package/src/DateTimeInput/DateTimeInput.tsx +44 -39
  378. package/src/Divider/Divider.test.tsx +32 -8
  379. package/src/Divider/Divider.tsx +14 -1
  380. package/src/DropdownMenu/DropdownMenu.test.tsx +60 -0
  381. package/src/DropdownMenu/DropdownMenu.tsx +31 -12
  382. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +8 -2
  383. package/src/DropdownMenu/DropdownMenuItem.tsx +9 -3
  384. package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +4 -12
  385. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +9 -10
  386. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +9 -3
  387. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +20 -2
  388. package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +88 -0
  389. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +635 -0
  390. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +553 -0
  391. package/src/DropdownMenu/index.ts +16 -0
  392. package/src/DropdownMenu/menuItemHover.ts +39 -0
  393. package/src/DropdownMenu/menuItemRoles.ts +9 -0
  394. package/src/DropdownMenu/renderDropdownItems.tsx +29 -15
  395. package/src/Field/Field.tsx +14 -9
  396. package/src/Field/FieldLabel.test.tsx +25 -0
  397. package/src/Field/FieldLabel.tsx +12 -3
  398. package/src/Field/inputStyles.stylex.ts +2 -0
  399. package/src/FieldStatus/FieldStatus.test.tsx +72 -0
  400. package/src/FieldStatus/FieldStatus.tsx +48 -2
  401. package/src/FileInput/FileInput.doc.mjs +23 -2
  402. package/src/FileInput/FileInput.test.tsx +104 -13
  403. package/src/FileInput/FileInput.tsx +106 -79
  404. package/src/HoverCard/useHoverCard.tsx +15 -3
  405. package/src/Icon/Icon.doc.mjs +13 -0
  406. package/src/Icon/Icon.test.tsx +89 -0
  407. package/src/Icon/Icon.tsx +51 -3
  408. package/src/Item/Item.doc.mjs +5 -0
  409. package/src/Item/Item.test.tsx +88 -0
  410. package/src/Item/Item.tsx +57 -11
  411. package/src/Layer/layerAnimations.stylex.ts +23 -0
  412. package/src/Lightbox/Lightbox.tsx +15 -7
  413. package/src/List/List.test.tsx +17 -29
  414. package/src/List/List.tsx +6 -1
  415. package/src/List/ListItem.doc.mjs +12 -0
  416. package/src/List/ListItem.tsx +13 -0
  417. package/src/Markdown/Markdown.test.tsx +21 -0
  418. package/src/Markdown/Markdown.tsx +4 -0
  419. package/src/MetadataList/MetadataList.test.tsx +24 -0
  420. package/src/MetadataList/MetadataList.tsx +6 -2
  421. package/src/MultiSelector/MultiSelector.doc.mjs +20 -0
  422. package/src/MultiSelector/MultiSelector.test.tsx +402 -0
  423. package/src/MultiSelector/MultiSelector.tsx +120 -59
  424. package/src/NavMenu/NavHeadingMenuItem.tsx +1 -1
  425. package/src/NumberInput/NumberInput.doc.mjs +21 -0
  426. package/src/NumberInput/NumberInput.test.tsx +71 -0
  427. package/src/NumberInput/NumberInput.tsx +35 -26
  428. package/src/Outline/Outline.test.tsx +12 -12
  429. package/src/Outline/Outline.tsx +2 -2
  430. package/src/Pagination/Pagination.tsx +11 -3
  431. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  432. package/src/PowerSearch/PowerSearch.test.tsx +23 -0
  433. package/src/PowerSearch/PowerSearch.tsx +10 -1
  434. package/src/ProgressBar/ProgressBar.test.tsx +34 -0
  435. package/src/ProgressBar/ProgressBar.tsx +16 -1
  436. package/src/RadioList/RadioList.doc.mjs +6 -0
  437. package/src/RadioList/RadioList.test.tsx +26 -4
  438. package/src/RadioList/RadioList.tsx +2 -2
  439. package/src/RadioList/RadioListItem.tsx +12 -5
  440. package/src/Resizable/ResizeHandle.test.tsx +94 -0
  441. package/src/Resizable/ResizeHandle.tsx +70 -20
  442. package/src/SegmentedControl/SegmentedControl.test.tsx +28 -0
  443. package/src/SegmentedControl/SegmentedControlItem.tsx +22 -2
  444. package/src/Selector/Selector.doc.mjs +15 -0
  445. package/src/Selector/Selector.test.tsx +370 -0
  446. package/src/Selector/Selector.tsx +88 -20
  447. package/src/SideNav/SideNav.tsx +1 -8
  448. package/src/SideNav/SideNavCollapseButton.tsx +9 -7
  449. package/src/SideNav/SideNavItem.tsx +5 -3
  450. package/src/Skeleton/Skeleton.test.tsx +16 -0
  451. package/src/Skeleton/Skeleton.tsx +13 -1
  452. package/src/Slider/Slider.doc.mjs +6 -0
  453. package/src/Slider/Slider.test.tsx +240 -12
  454. package/src/Slider/Slider.tsx +96 -23
  455. package/src/Spinner/Spinner.test.tsx +10 -8
  456. package/src/Spinner/Spinner.tsx +15 -5
  457. package/src/Switch/Switch.doc.mjs +142 -42
  458. package/src/Switch/Switch.test.tsx +111 -0
  459. package/src/Switch/Switch.tsx +149 -32
  460. package/src/TabList/Tab.tsx +2 -2
  461. package/src/TabList/TabList.doc.mjs +0 -6
  462. package/src/TabList/TabList.test.tsx +3 -15
  463. package/src/TabList/TabList.tsx +6 -17
  464. package/src/TabList/TabListContext.ts +0 -6
  465. package/src/TabList/TabMenu.tsx +2 -2
  466. package/src/Table/BaseTable.tsx +34 -2
  467. package/src/Table/Table.doc.mjs +11 -0
  468. package/src/Table/Table.test.tsx +85 -0
  469. package/src/Table/TableCell.tsx +3 -3
  470. package/src/Table/TableHeaderCell.tsx +3 -3
  471. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +9 -6
  472. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +19 -0
  473. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +27 -18
  474. package/src/Table/plugins/selection/useTableSelection.test.tsx +61 -3
  475. package/src/Table/plugins/selection/useTableSelection.tsx +3 -0
  476. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +38 -9
  477. package/src/Table/plugins/tree/useTableTreeData.tsx +17 -13
  478. package/src/Table/types.ts +33 -0
  479. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  480. package/src/TextArea/TextArea.doc.mjs +21 -0
  481. package/src/TextArea/TextArea.test.tsx +23 -0
  482. package/src/TextArea/TextArea.tsx +27 -26
  483. package/src/TextInput/TextInput.doc.mjs +21 -0
  484. package/src/TextInput/TextInput.test.tsx +150 -0
  485. package/src/TextInput/TextInput.tsx +28 -25
  486. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  487. package/src/Thumbnail/Thumbnail.tsx +1 -1
  488. package/src/TimeInput/TimeInput.doc.mjs +21 -0
  489. package/src/TimeInput/TimeInput.test.tsx +23 -0
  490. package/src/TimeInput/TimeInput.tsx +24 -25
  491. package/src/Timestamp/Timestamp.doc.mjs +32 -6
  492. package/src/Timestamp/Timestamp.test.tsx +371 -0
  493. package/src/Timestamp/Timestamp.tsx +96 -93
  494. package/src/Timestamp/formatInstant.ts +218 -0
  495. package/src/Timestamp/index.ts +4 -0
  496. package/src/Timestamp/tooltipEntries.test.ts +552 -0
  497. package/src/Timestamp/tooltipEntries.ts +210 -0
  498. package/src/Toast/ToastViewport.test.tsx +218 -8
  499. package/src/Toast/ToastViewport.tsx +71 -12
  500. package/src/ToggleButton/ToggleButton.test.tsx +24 -0
  501. package/src/ToggleButton/ToggleButton.tsx +19 -1
  502. package/src/Token/Token.test.tsx +166 -15
  503. package/src/Token/Token.tsx +104 -47
  504. package/src/Token/TokenLink.tsx +107 -0
  505. package/src/Token/index.ts +1 -1
  506. package/src/Tokenizer/Tokenizer.doc.mjs +23 -1
  507. package/src/Tokenizer/Tokenizer.test.tsx +23 -0
  508. package/src/Tokenizer/Tokenizer.tsx +14 -2
  509. package/src/Tooltip/Tooltip.test.tsx +46 -0
  510. package/src/Tooltip/useTooltip.tsx +20 -0
  511. package/src/TopNav/TopNav.test.tsx +93 -2
  512. package/src/TopNav/TopNavItem.doc.mjs +3 -3
  513. package/src/TopNav/TopNavItem.tsx +35 -6
  514. package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +1 -1
  515. package/src/TreeList/TreeList.doc.mjs +6 -0
  516. package/src/TreeList/TreeList.test.tsx +50 -10
  517. package/src/TreeList/TreeList.tsx +5 -0
  518. package/src/TreeList/TreeListBranches.tsx +8 -6
  519. package/src/TreeList/TreeListItem.tsx +33 -11
  520. package/src/Typeahead/BaseTypeahead.tsx +42 -15
  521. package/src/Typeahead/Typeahead.doc.mjs +13 -0
  522. package/src/Typeahead/Typeahead.test.tsx +112 -0
  523. package/src/Typeahead/Typeahead.tsx +17 -1
  524. package/src/Typeahead/TypeaheadItem.tsx +2 -1
  525. package/src/__tests__/forcedColors.ts +60 -0
  526. package/src/__tests__/inputWidthContract.test.tsx +62 -0
  527. package/src/doc-mjs.d.ts +19 -0
  528. package/src/hooks/index.ts +6 -0
  529. package/src/hooks/isRtlElement.ts +27 -0
  530. package/src/hooks/useAnnounce.test.tsx +75 -2
  531. package/src/hooks/useAnnounce.ts +47 -1
  532. package/src/hooks/useFocusTrap.test.tsx +104 -0
  533. package/src/hooks/useFocusTrap.ts +61 -12
  534. package/src/hooks/useGridFocus.doc.mjs +3 -3
  535. package/src/hooks/useGridFocus.test.tsx +41 -0
  536. package/src/hooks/useGridFocus.ts +21 -10
  537. package/src/hooks/useHotkeys.ts +9 -0
  538. package/src/hooks/useInputStatusIcon.test.tsx +162 -0
  539. package/src/hooks/useInputStatusIcon.tsx +224 -0
  540. package/src/hooks/useKeyboardHint.test.tsx +22 -0
  541. package/src/hooks/useKeyboardHint.tsx +6 -2
  542. package/src/hooks/useListFocus.doc.mjs +25 -3
  543. package/src/hooks/useListFocus.test.tsx +153 -1
  544. package/src/hooks/useListFocus.ts +102 -9
  545. package/src/i18n/InternationalizationContext.ts +4 -0
  546. package/src/i18n/InternationalizationProvider.tsx +12 -2
  547. package/src/i18n/__tests__/getLocaleDirection.test.ts +40 -0
  548. package/src/i18n/__tests__/useDirection.test.tsx +66 -0
  549. package/src/i18n/getLocaleDirection.ts +29 -0
  550. package/src/i18n/index.ts +6 -0
  551. package/src/i18n/useDirection.ts +29 -0
  552. package/src/reset.css +0 -26
  553. package/src/reset.test.ts +60 -0
  554. package/src/theme/derivedVarRegistry.test.ts +1 -0
  555. package/src/theme/derivedVarRegistry.ts +6 -0
  556. package/src/theme/generateThemeRules.test.ts +42 -0
  557. package/src/utils/index.ts +2 -0
  558. package/src/utils/plainDate.test.ts +24 -6
  559. package/src/utils/plainDate.ts +18 -4
  560. package/src/utils/rtlStyles.ts +27 -0
@@ -5,7 +5,7 @@
5
5
  /**
6
6
  * @file Avatar.tsx
7
7
  * @input Uses React, HTMLAttributes, ReactNode, useState; useTooltip
8
- * (Tooltip hook) for the optional name-on-hover tooltip
8
+ * (Tooltip hook) for the optional name-on-hover tooltip; useTranslator (i18n)
9
9
  * @output Exports Avatar component, AvatarProps, AvatarSize types
10
10
  * @position Core implementation; consumed by index.ts
11
11
  *
@@ -14,9 +14,11 @@
14
14
  * - /packages/core/src/Avatar/index.ts (exports if types change)
15
15
  * - /apps/storybook/stories/Avatar.stories.tsx (storybook stories)
16
16
  * - /packages/cli/templates/blocks/components/Avatar/ (showcase blocks)
17
+ *
18
+ * Last synced props: alt, fallbackSrc, name, size, src, status, href, as, target, rel, onClick
17
19
  */
18
20
 
19
- import {useMemo, useState, type ReactNode} from 'react';
21
+ import {isValidElement, useMemo, useState, type ReactNode} from 'react';
20
22
  import type {BaseProps} from '../BaseProps';
21
23
  import * as stylex from '@stylexjs/stylex';
22
24
  import {
@@ -30,6 +32,9 @@ import {useAvatarGroup} from '../AvatarGroup/AvatarGroupContext';
30
32
  import {mergeProps, mergeRefs} from '../utils';
31
33
  import {themeProps} from '../utils/themeProps';
32
34
  import {useTooltip} from '../Tooltip/useTooltip';
35
+ import {useLinkComponent} from '../Link/useLinkComponent';
36
+ import type {LinkComponentType} from '../Link/types';
37
+ import {useTranslator} from '../i18n';
33
38
 
34
39
  /**
35
40
  * The offset ratio for positioning elements on a circle's edge at 45°.
@@ -61,7 +66,7 @@ const INITIALS_FONT_SIZE_RATIO = 0.4;
61
66
  *
62
67
  * Avatar uses the same abbreviated scale as Icon (`xsm`/`sm`/`md`/`lg`/`xl`),
63
68
  * but the values are larger because avatars align with media rather than
64
- * glyphs. The tiers match EPS's avatar sizes.
69
+ * glyphs. The tiers follow the standard avatar size scale.
65
70
  */
66
71
  type AvatarNamedSize = 'xsm' | 'sm' | 'md' | 'lg' | 'xl';
67
72
 
@@ -106,6 +111,11 @@ const styles = stylex.create({
106
111
  position: 'relative',
107
112
  display: 'inline-flex',
108
113
  flexShrink: 0,
114
+ // The wrapper is not clipped (so the status dot can overflow), so it must be
115
+ // rounded itself: a themed fallback background lands on `.astryx-avatar` (the
116
+ // class-bearing wrapper) as well as the internal var, and an unrounded
117
+ // wrapper would show that fill as square corners behind the circular content.
118
+ borderRadius: radiusVars['--radius-full'],
109
119
  },
110
120
  content: {
111
121
  display: 'flex',
@@ -126,10 +136,14 @@ const styles = stylex.create({
126
136
  justifyContent: 'center',
127
137
  width: '100%',
128
138
  height: '100%',
129
- backgroundColor: colorVars['--color-neutral'],
130
- color: colorVars['--color-text-secondary'],
139
+ // Fallback surface (initials + default icon). Each property reads an
140
+ // Avatar-scoped internal var so a theme can re-scope the fallback wash and
141
+ // initials weight/color without forking; the defaults reproduce today's
142
+ // exact output. See derivedVarRegistry (avatar) + Avatar.doc.mjs theming.
143
+ backgroundColor: `var(--_avatar-fallback-background, ${colorVars['--color-neutral']})`,
144
+ color: `var(--_avatar-fallback-color, ${colorVars['--color-text-secondary']})`,
131
145
  fontFamily: typographyVars['--font-family-body'],
132
- fontWeight: fontWeightVars['--font-weight-medium'],
146
+ fontWeight: `var(--_avatar-fallback-font-weight, ${fontWeightVars['--font-weight-medium']})`,
133
147
  textTransform: 'uppercase',
134
148
  },
135
149
  status: {
@@ -148,6 +162,39 @@ const styles = stylex.create({
148
162
  ':focus-visible': '2px',
149
163
  },
150
164
  },
165
+ // Reset the intrinsic styling of the interactive element (<a>/<button>) so it
166
+ // is a transparent, correctly-sized wrapper around the avatar visuals. The
167
+ // element carries the focus-visible accent ring for keyboard users.
168
+ interactive: {
169
+ appearance: 'none',
170
+ padding: 0,
171
+ margin: 0,
172
+ borderWidth: 0,
173
+ borderStyle: 'none',
174
+ backgroundColor: 'transparent',
175
+ color: 'inherit',
176
+ font: 'inherit',
177
+ textDecoration: 'none',
178
+ cursor: 'pointer',
179
+ // Match the avatar's circular shape so the focus ring hugs it.
180
+ borderRadius: radiusVars['--radius-full'],
181
+ outlineWidth: {
182
+ default: 0,
183
+ ':focus-visible': 2,
184
+ },
185
+ outlineStyle: {
186
+ default: 'none',
187
+ ':focus-visible': 'solid',
188
+ },
189
+ outlineColor: {
190
+ default: null,
191
+ ':focus-visible': colorVars['--color-accent'],
192
+ },
193
+ outlineOffset: {
194
+ default: 0,
195
+ ':focus-visible': 2,
196
+ },
197
+ },
151
198
  });
152
199
 
153
200
  /**
@@ -158,8 +205,13 @@ const dynamicStyles = stylex.create({
158
205
  width: size,
159
206
  height: size,
160
207
  }),
208
+ // Initials font size defaults to the proportional `size × ratio` scale but is
209
+ // reachable via the `--_avatar-fallback-font-size` derived var, so a theme can
210
+ // set a per-size type scale (e.g. `components.avatar['size:sm']`).
161
211
  fontSize: (size: number) => ({
162
- fontSize: size * INITIALS_FONT_SIZE_RATIO,
212
+ fontSize: `var(--_avatar-fallback-font-size, ${
213
+ size * INITIALS_FONT_SIZE_RATIO
214
+ }px)`,
163
215
  }),
164
216
  statusPosition: (size: number) => ({
165
217
  bottom: size * CIRCLE_EDGE_OFFSET_RATIO,
@@ -229,6 +281,11 @@ export interface AvatarProps extends BaseProps<HTMLDivElement> {
229
281
  /**
230
282
  * Content displayed in the corner of the avatar.
231
283
  * Typically used for status indicators or badges.
284
+ *
285
+ * When the element carries a string `label` prop (as `AvatarStatusDot`
286
+ * does), the label is composed into the avatar's accessible name
287
+ * (e.g. "Jane Doe, Online") so assistive tech can reach the status —
288
+ * the `role="img"` root prunes descendant semantics (WCAG 4.1.2).
232
289
  */
233
290
  status?: ReactNode;
234
291
  /**
@@ -244,6 +301,35 @@ export interface AvatarProps extends BaseProps<HTMLDivElement> {
244
301
  * @default true
245
302
  */
246
303
  tooltip?: string | boolean;
304
+ /**
305
+ * When provided, the avatar becomes an interactive link (`<a>` or custom
306
+ * link component) pointing at `href`. Follows the same element-swap rules as
307
+ * Button: `href` renders a link, otherwise `onClick` renders a
308
+ * `<button type="button">`, otherwise the avatar stays a static (non-focusable)
309
+ * element. An interactive avatar requires a meaningful accessible name via
310
+ * `alt` or `name`.
311
+ */
312
+ href?: string;
313
+ /**
314
+ * Custom link component to use when `href` is provided. Overrides the
315
+ * provider-level default set by LinkProvider. Useful for Next.js `<Link>` or
316
+ * other router-aware components. Only applies when `href` is provided.
317
+ */
318
+ as?: LinkComponentType;
319
+ /**
320
+ * HTML target attribute for the link. Only applies when `href` is provided.
321
+ */
322
+ target?: string;
323
+ /**
324
+ * HTML rel attribute for the link. Only applies when `href` is provided.
325
+ */
326
+ rel?: string;
327
+ /**
328
+ * Click handler. When provided without `href`, renders the avatar as a
329
+ * focusable `<button type="button">`. An interactive avatar requires a
330
+ * meaningful accessible name via `alt` or `name`.
331
+ */
332
+ onClick?: React.MouseEventHandler<HTMLElement>;
247
333
  }
248
334
 
249
335
  /**
@@ -266,6 +352,24 @@ function getInitials(name: string): string {
266
352
  return (words[0].charAt(0) + words[words.length - 1].charAt(0)).toUpperCase();
267
353
  }
268
354
 
355
+ /**
356
+ * Reads the accessible status label off the `status` element, when it
357
+ * exposes one. `AvatarStatusDot`'s `label` prop is the canonical source,
358
+ * but any custom status element with a string `label` prop participates.
359
+ *
360
+ * The avatar root is `role="img"`, which prunes ALL descendant semantics
361
+ * from the accessibility tree — a label inside the status subtree is never
362
+ * announced on its own. Composing it into the avatar's own accessible name
363
+ * is the only way the status reaches assistive tech (WCAG 4.1.2).
364
+ */
365
+ function getStatusLabel(status: ReactNode): string | undefined {
366
+ if (!isValidElement(status)) {
367
+ return undefined;
368
+ }
369
+ const {label} = status.props as {label?: unknown};
370
+ return typeof label === 'string' && label !== '' ? label : undefined;
371
+ }
372
+
269
373
  /**
270
374
  * Default person icon SVG for when no image or name is provided
271
375
  */
@@ -295,6 +399,8 @@ function DefaultIcon({size}: {size: number}) {
295
399
  * <Avatar src="/user.jpg" status={<OnlineIndicator />} />
296
400
  * <Avatar name="jsmith" tooltip="Jane Smith, Staff Engineer" />
297
401
  * <Avatar name="Jane" tooltip={false} />
402
+ * <Avatar src="/user.jpg" name="John Doe" href="/users/john" />
403
+ * <Avatar src="/user.jpg" name="John Doe" onClick={() => openProfile()} />
298
404
  * ```
299
405
  */
300
406
  export function Avatar({
@@ -306,6 +412,11 @@ export function Avatar({
306
412
  src,
307
413
  status,
308
414
  tooltip = true,
415
+ href,
416
+ as,
417
+ target,
418
+ rel,
419
+ onClick,
309
420
  xstyle,
310
421
  className,
311
422
  style,
@@ -325,10 +436,24 @@ export function Avatar({
325
436
  const showInitials = !showImage && !showFallbackImage && name;
326
437
  const showIcon = !showImage && !showFallbackImage && !name;
327
438
 
328
- // A meaningful accessible name comes from `alt` or `name`. With neither, the
329
- // avatar is decorative — expose it as `presentation`/`aria-hidden` rather than
330
- // announcing a meaningless generic "Avatar" (obs-9).
331
- const accessibleName = alt || name;
439
+ // A meaningful accessible name comes from `alt`/`name`, composed with the
440
+ // status element's `label` when one is present ("Jane Doe, Online") — the
441
+ // `role="img"` root prunes descendant semantics, so surfacing the label in
442
+ // the avatar's own name is the only way assistive tech can reach the
443
+ // status (WCAG 4.1.2). A labelled status alone is also meaningful. With
444
+ // neither a name nor a labelled status, the avatar is decorative — expose
445
+ // it as `presentation`/`aria-hidden` rather than announcing a meaningless
446
+ // generic "Avatar" (obs-9).
447
+ const t = useTranslator();
448
+ const nameLabel = alt || name;
449
+ const statusLabel = getStatusLabel(status);
450
+ const accessibleName =
451
+ nameLabel && statusLabel
452
+ ? t('@astryx.avatar.nameWithStatus', {
453
+ name: nameLabel,
454
+ status: statusLabel,
455
+ })
456
+ : nameLabel || statusLabel;
332
457
  const isDecorative = !accessibleName;
333
458
  const avatarGroup = useAvatarGroup();
334
459
  const resolvedSize = avatarGroup?.size ?? size;
@@ -359,11 +484,14 @@ export function Avatar({
359
484
  // returns `describedBy` as a value we choose whether to apply — the only way
360
485
  // to satisfy the per-case aria-describedby rule (default name: none; custom
361
486
  // string: describe) without editing Tooltip. `focusTrigger: 'auto'` shows the
362
- // tooltip on keyboard focus once the root is made focusable (below).
487
+ // tooltip on keyboard focus once the root is focusable (natively for the
488
+ // interactive <a>/<button>, or via an explicit tab stop on the static div).
363
489
  const tooltipHook = useTooltip({
364
490
  placement: 'above',
365
491
  isEnabled: showTooltip,
366
492
  });
493
+ // The tooltip ref attaches to whichever root element renders (static or
494
+ // interactive), so the tooltip works for link/button avatars too.
367
495
  const rootRef = mergeRefs(ref, showTooltip ? tooltipHook.ref : undefined);
368
496
  const describedByProp =
369
497
  showTooltip && isCustomTooltip
@@ -375,8 +503,136 @@ export function Avatar({
375
503
  }
376
504
  : null;
377
505
 
378
- const avatarElement = (
379
- <AvatarSizeContext value={numericSize}>
506
+ // Element-swap trichotomy, copied from Button: `href` renders a link,
507
+ // otherwise `onClick` renders a `<button>`, otherwise today's static element
508
+ // is unchanged (the non-breaking default).
509
+ const renderAsLink = href != null;
510
+ const renderAsButton = !renderAsLink && onClick != null;
511
+ const isInteractive = renderAsLink || renderAsButton;
512
+ const LinkComponent = useLinkComponent(as);
513
+
514
+ // An interactive control with no accessible name is an unacceptable control
515
+ // name. Warn in the same client-safe way sibling components do (Field,
516
+ // Timestamp, Popover) — a plain `console.warn`, never gated on `process.env`
517
+ // (which is not available on the client in this codebase).
518
+ if (isInteractive && !accessibleName) {
519
+ console.warn(
520
+ 'Avatar: an interactive avatar (with `href` or `onClick`) needs a ' +
521
+ 'meaningful accessible name. Pass `alt` or `name`.',
522
+ );
523
+ }
524
+
525
+ // The inner visuals are identical across the static and interactive variants.
526
+ const visualContent = (
527
+ <>
528
+ <div {...stylex.props(styles.content, dynamicStyles.size(numericSize))}>
529
+ {showImage && (
530
+ <img
531
+ src={src}
532
+ alt=""
533
+ onError={() => setErroredSrc(src)}
534
+ {...stylex.props(styles.image)}
535
+ />
536
+ )}
537
+ {showFallbackImage && (
538
+ <img
539
+ src={fallbackSrc}
540
+ alt=""
541
+ onError={() => setErroredFallbackSrc(fallbackSrc)}
542
+ {...stylex.props(styles.image)}
543
+ />
544
+ )}
545
+ {showInitials && (
546
+ <div
547
+ {...stylex.props(
548
+ styles.fallback,
549
+ dynamicStyles.fontSize(numericSize),
550
+ )}>
551
+ {getInitials(name)}
552
+ </div>
553
+ )}
554
+ {showIcon && (
555
+ <div {...stylex.props(styles.fallback)}>
556
+ <DefaultIcon size={numericSize} />
557
+ </div>
558
+ )}
559
+ </div>
560
+ {status && (
561
+ <div
562
+ {...stylex.props(
563
+ styles.status,
564
+ dynamicStyles.statusPosition(numericSize),
565
+ )}>
566
+ {status}
567
+ </div>
568
+ )}
569
+ </>
570
+ );
571
+
572
+ // Shared StyleX + theme props for the root element in every variant. The
573
+ // group ring/overlap, the interactive focus-visible ring, and the
574
+ // tooltip tab-stop focus ring all live here so the interactive
575
+ // `<a>`/`<button>` and the static `<div>` carry the exact same box.
576
+ const rootStylexProps = mergeProps(
577
+ themeProps('avatar', {size: resolvedSize}),
578
+ stylex.props(
579
+ styles.wrapper,
580
+ isInteractive && styles.interactive,
581
+ !isInteractive && showTooltip && !avatarGroup && styles.focusable,
582
+ avatarGroup && groupStyles.ring,
583
+ avatarGroup && groupStyles.overlap,
584
+ avatarGroup && groupDynamicStyles.overlap(-avatarGroup.overlap),
585
+ xstyle,
586
+ ),
587
+ className,
588
+ style,
589
+ );
590
+
591
+ let rootElement: ReactNode;
592
+
593
+ // `props` is typed for the default `<div>` root (its event handlers are
594
+ // HTMLDivElement-typed). The interactive branches render an `<a>`/`<button>`,
595
+ // so the passthrough props are re-typed to the generic element here — the
596
+ // avatar's own handlers (onClick) are declared on HTMLElement and stay typed.
597
+ const interactivePassthrough = props as React.HTMLAttributes<HTMLElement>;
598
+
599
+ if (renderAsLink) {
600
+ // The rendered link carries the `data-avatar-item` marker so AvatarGroup's
601
+ // roving focus (which selects on `[data-avatar-item]`, not a tag/role) picks
602
+ // it up while ignoring nested buttons in a custom status/badge slot.
603
+ rootElement = (
604
+ <LinkComponent
605
+ {...interactivePassthrough}
606
+ {...describedByProp}
607
+ ref={rootRef as React.Ref<HTMLAnchorElement>}
608
+ href={href}
609
+ target={target}
610
+ rel={rel}
611
+ aria-label={accessibleName}
612
+ data-avatar-item=""
613
+ data-testid={testId}
614
+ onClick={onClick}
615
+ {...rootStylexProps}>
616
+ {visualContent}
617
+ </LinkComponent>
618
+ );
619
+ } else if (renderAsButton) {
620
+ rootElement = (
621
+ <button
622
+ {...interactivePassthrough}
623
+ {...describedByProp}
624
+ ref={rootRef}
625
+ type="button"
626
+ aria-label={accessibleName}
627
+ data-avatar-item=""
628
+ data-testid={testId}
629
+ onClick={onClick}
630
+ {...rootStylexProps}>
631
+ {visualContent}
632
+ </button>
633
+ );
634
+ } else {
635
+ rootElement = (
380
636
  <div
381
637
  {...props}
382
638
  ref={rootRef}
@@ -385,79 +641,29 @@ export function Avatar({
385
641
  aria-hidden={isDecorative || undefined}
386
642
  // The root is a div[role="img"], not natively focusable. When a name
387
643
  // tooltip is active, add a tab stop so keyboard users can reveal it
388
- // (WCAG 1.4.13 / 2.1.1) — matching Timestamp/Button. Only while active.
389
- tabIndex={showTooltip ? 0 : undefined}
644
+ // (WCAG 1.4.13 / 2.1.1) — matching Timestamp/Button. Suppressed inside
645
+ // an AvatarGroup, which owns a single roving tab stop for its members.
646
+ tabIndex={showTooltip && !avatarGroup ? 0 : undefined}
390
647
  data-testid={testId}
391
648
  {...describedByProp}
392
- {...mergeProps(
393
- themeProps('avatar', {size: resolvedSize}),
394
- stylex.props(
395
- styles.wrapper,
396
- showTooltip && styles.focusable,
397
- avatarGroup && groupStyles.ring,
398
- avatarGroup && groupStyles.overlap,
399
- avatarGroup && groupDynamicStyles.overlap(-avatarGroup.overlap),
400
- xstyle,
401
- ),
402
- className,
403
- style,
404
- )}>
405
- <div {...stylex.props(styles.content, dynamicStyles.size(numericSize))}>
406
- {showImage && (
407
- <img
408
- src={src}
409
- alt=""
410
- onError={() => setErroredSrc(src)}
411
- {...stylex.props(styles.image)}
412
- />
413
- )}
414
- {showFallbackImage && (
415
- <img
416
- src={fallbackSrc}
417
- alt=""
418
- onError={() => setErroredFallbackSrc(fallbackSrc)}
419
- {...stylex.props(styles.image)}
420
- />
421
- )}
422
- {showInitials && (
423
- <div
424
- {...stylex.props(
425
- styles.fallback,
426
- dynamicStyles.fontSize(numericSize),
427
- )}>
428
- {getInitials(name)}
429
- </div>
430
- )}
431
- {showIcon && (
432
- <div {...stylex.props(styles.fallback)}>
433
- <DefaultIcon size={numericSize} />
434
- </div>
435
- )}
436
- </div>
437
- {status && (
438
- <div
439
- {...stylex.props(
440
- styles.status,
441
- dynamicStyles.statusPosition(numericSize),
442
- )}>
443
- {status}
444
- </div>
445
- )}
649
+ {...rootStylexProps}>
650
+ {visualContent}
446
651
  </div>
447
- </AvatarSizeContext>
448
- );
449
-
450
- if (!showTooltip) {
451
- return avatarElement;
652
+ );
452
653
  }
453
654
 
454
- // Render the tooltip as a sibling (no wrapper DOM) — the hook's ref is already
455
- // attached to the root via `rootRef` above. `trimmedTooltip` is guaranteed
456
- // non-empty here.
655
+ const avatarElement = (
656
+ <AvatarSizeContext value={numericSize}>{rootElement}</AvatarSizeContext>
657
+ );
658
+
659
+ // Always return the same structure so the avatar keeps its position in the
660
+ // React tree regardless of the tooltip flag — toggling it must not remount
661
+ // the avatar subtree (and lose image-load state). The tooltip is a sibling
662
+ // (no wrapper DOM); the hook's ref is already on the root via `rootRef`.
457
663
  return (
458
664
  <>
459
665
  {avatarElement}
460
- {tooltipHook.renderTooltip(trimmedTooltip)}
666
+ {showTooltip ? tooltipHook.renderTooltip(trimmedTooltip) : null}
461
667
  </>
462
668
  );
463
669
  }
@@ -21,7 +21,7 @@ export const docs = {
21
21
  name: 'label',
22
22
  type: 'string',
23
23
  description:
24
- 'Accessible label describing the status. Not announced inside an Avatar today (the Avatar root is role="img", which prunes child semantics) — pass it anyway; an Avatar-level name-composition fix is planned.',
24
+ 'Accessible label describing the status. Inside an Avatar it is composed into the avatar\'s accessible name (e.g. "Jane Doe, Online"); the Avatar root is role="img", which prunes child semantics, so the composed name is how the status reaches assistive tech. Standalone dots expose role="img" with this label directly.',
25
25
  },
26
26
  {
27
27
  name: 'icon',
@@ -59,7 +59,7 @@ export const docsZh = {
59
59
  name: 'label',
60
60
  type: 'string',
61
61
  description:
62
- '描述状态的无障碍标签。目前在 Avatar 内不会被朗读(Avatar 根元素为 role="img",会裁剪子元素语义)——仍请传入;Avatar 层面的名称合成修复已在计划中。',
62
+ '描述状态的无障碍标签。在 Avatar 内会被合成进头像自身的无障碍名称(如 "Jane Doe, Online")——Avatar 根元素为 role="img",会裁剪子元素语义,因此合成名称是辅助技术获知状态的途径。独立使用时状态点直接以 role="img" 暴露该标签。',
63
63
  },
64
64
  {
65
65
  name: 'icon',
@@ -80,7 +80,7 @@ export const docsDense = {
80
80
  variant:
81
81
  'colour + shape variant: success filled, neutral ring, error minus',
82
82
  label:
83
- 'accessible status label (not yet announced inside Avatar — role="img" prunes children)',
84
- icon: 'icon centered in dot (hidden at tiny sizes); replaces the built-in shape glyph — differ it per status',
83
+ 'accessible status label; composed into the Avatar accessible name ("Jane Doe, Online"); standalone dots expose role="img" with it directly',
84
+ icon: 'icon centered in dot (hidden at tiny sizes); replaces the built-in shape glyph, so differ it per status',
85
85
  },
86
86
  };
@@ -115,10 +115,12 @@ export interface AvatarStatusDotProps extends BaseProps<HTMLDivElement> {
115
115
  * Describes the meaning of the indicator for screen readers
116
116
  * (e.g. "Online", "Accepted", "John Doe is busy").
117
117
  *
118
- * Note: inside an Avatar the label is currently not announced — the
119
- * Avatar root is `role="img"`, which prunes descendant semantics.
120
- * Pass it anyway; composing status into the avatar's accessible name
121
- * is a planned Avatar-level fix.
118
+ * Note: inside an Avatar the dot sits in the avatar's `role="img"`
119
+ * subtree, where descendant semantics are pruned — the dot is never its
120
+ * own stop there. Instead, Avatar reads this `label` and composes it into
121
+ * its own accessible name (e.g. "Jane Doe, Online"), which is how the
122
+ * status reaches assistive tech (WCAG 4.1.2). Standalone dots (outside an
123
+ * Avatar) expose `role="img"` with this label directly.
122
124
  */
123
125
  label?: string;
124
126
  /**
@@ -14,6 +14,7 @@ export const docs = {
14
14
  {guidance: true, description: 'Set max to limit visible avatars when the list is long; 3-5 is typical.'},
15
15
  {guidance: true, description: 'Use AvatarGroupOverflow for custom overflow content like a popover trigger or "add member" button.'},
16
16
  {guidance: true, description: 'Pass status dots, click handlers, or tooltips directly on each Avatar child.'},
17
+ {guidance: true, description: 'Make avatars interactive with href or onClick to link to profiles. Interactive avatars share a single Tab stop; arrow keys move between them, and the group announces a keyboard hint to assistive tech.'},
17
18
  {guidance: false, description: "Don't nest AvatarGroups; use a single group with all avatars."},
18
19
  ],
19
20
  anatomy: [
@@ -94,6 +95,7 @@ export const docsDense = {
94
95
  {guidance: true, description: 'Set max to limit visible avatars (3-5 typical).'},
95
96
  {guidance: true, description: 'Use AvatarGroupOverflow for custom overflow (popover, add button).'},
96
97
  {guidance: true, description: 'Pass status dots / click handlers directly on each Avatar.'},
98
+ {guidance: true, description: 'Interactive avatars (href/onClick) share one Tab stop; arrows rove between them + the overflow button.'},
97
99
  {guidance: false, description: "Don't nest AvatarGroups."},
98
100
  ],
99
101
  },