@astryxdesign/core 0.4.7 → 0.5.0-canary.1f8525f

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 (566) hide show
  1. package/CHANGELOG.md +161 -1
  2. package/dist/AppShell/AppShell.d.ts.map +1 -1
  3. package/dist/AppShell/AppShell.js +3 -2
  4. package/dist/AspectRatio/AspectRatio.d.ts +24 -0
  5. package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
  6. package/dist/AspectRatio/AspectRatio.js +23 -4
  7. package/dist/Avatar/Avatar.d.ts +25 -1
  8. package/dist/Avatar/Avatar.d.ts.map +1 -1
  9. package/dist/Avatar/Avatar.js +58 -19
  10. package/dist/Avatar/index.d.ts +2 -2
  11. package/dist/Avatar/index.d.ts.map +1 -1
  12. package/dist/Avatar/index.js +1 -1
  13. package/dist/AvatarGroup/AvatarGroup.d.ts +9 -2
  14. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  15. package/dist/AvatarGroup/AvatarGroup.js +8 -4
  16. package/dist/AvatarGroup/AvatarGroupContext.d.ts +2 -1
  17. package/dist/AvatarGroup/AvatarGroupContext.d.ts.map +1 -1
  18. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  19. package/dist/AvatarGroup/AvatarGroupOverflow.js +10 -6
  20. package/dist/Banner/Banner.d.ts +40 -15
  21. package/dist/Banner/Banner.d.ts.map +1 -1
  22. package/dist/Banner/Banner.js +51 -18
  23. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  24. package/dist/BottomSheet/BottomSheet.js +17 -4
  25. package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
  26. package/dist/BottomSheet/BottomSheetSwitcher.js +3 -2
  27. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  28. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -10
  29. package/dist/Button/Button.d.ts.map +1 -1
  30. package/dist/Button/Button.js +5 -4
  31. package/dist/ButtonGroup/ButtonGroup.d.ts +5 -1
  32. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  33. package/dist/ButtonGroup/ButtonGroup.js +26 -7
  34. package/dist/Calendar/Calendar.d.ts +3 -1
  35. package/dist/Calendar/Calendar.d.ts.map +1 -1
  36. package/dist/Calendar/Calendar.js +14 -15
  37. package/dist/Calendar/getInitialFocusDate.d.ts +46 -0
  38. package/dist/Calendar/getInitialFocusDate.d.ts.map +1 -0
  39. package/dist/Calendar/getInitialFocusDate.js +62 -0
  40. package/dist/Calendar/styles.d.ts +5 -14
  41. package/dist/Calendar/styles.d.ts.map +1 -1
  42. package/dist/Calendar/styles.js +15 -15
  43. package/dist/Carousel/Carousel.d.ts.map +1 -1
  44. package/dist/Carousel/Carousel.js +3 -3
  45. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  46. package/dist/Chat/ChatComposerInput.js +2 -3
  47. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  48. package/dist/Chat/ChatLayout.js +3 -3
  49. package/dist/Chat/ChatMessageList.d.ts +22 -3
  50. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  51. package/dist/Chat/ChatMessageList.js +6 -3
  52. package/dist/Chat/ChatTokenizedText.d.ts +2 -0
  53. package/dist/Chat/ChatTokenizedText.d.ts.map +1 -1
  54. package/dist/Chat/ChatTokenizedText.js +11 -6
  55. package/dist/Chat/useChatComposerTokens.js +1 -1
  56. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  57. package/dist/CheckboxInput/CheckboxInput.js +10 -3
  58. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  59. package/dist/ClickableCard/ClickableCard.js +3 -3
  60. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  61. package/dist/CodeBlock/CodeBlock.js +15 -4
  62. package/dist/CodeBlock/highlightRanges.js +1 -1
  63. package/dist/CodeBlock/highlightStyles.js +13 -13
  64. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  65. package/dist/CommandPalette/CommandPaletteInput.js +3 -2
  66. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  67. package/dist/CommandPalette/CommandPaletteItem.js +3 -2
  68. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  69. package/dist/ContextMenu/ContextMenu.js +4 -3
  70. package/dist/DateInput/DateInput.d.ts +43 -0
  71. package/dist/DateInput/DateInput.d.ts.map +1 -1
  72. package/dist/DateInput/DateInput.js +22 -2
  73. package/dist/DateInput/NativeDateField.d.ts +10 -0
  74. package/dist/DateInput/NativeDateField.d.ts.map +1 -0
  75. package/dist/DateInput/NativeDateField.js +494 -0
  76. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  77. package/dist/DateInput/TouchDateField.js +38 -4
  78. package/dist/DateInput/index.d.ts +1 -1
  79. package/dist/DateInput/index.d.ts.map +1 -1
  80. package/dist/DateInput/nativeDateSegments.d.ts +22 -0
  81. package/dist/DateInput/nativeDateSegments.d.ts.map +1 -0
  82. package/dist/DateInput/nativeDateSegments.js +111 -0
  83. package/dist/DateTimeInput/DateTimeInput.d.ts +21 -1
  84. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  85. package/dist/DateTimeInput/DateTimeInput.js +406 -12
  86. package/dist/DateTimeInput/index.d.ts +1 -1
  87. package/dist/DateTimeInput/index.d.ts.map +1 -1
  88. package/dist/Dialog/Dialog.d.ts +7 -1
  89. package/dist/Dialog/Dialog.d.ts.map +1 -1
  90. package/dist/Dialog/Dialog.js +89 -58
  91. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  92. package/dist/DropdownMenu/DropdownMenuSubMenu.js +6 -5
  93. package/dist/Field/FieldLabel.d.ts.map +1 -1
  94. package/dist/Field/FieldLabel.js +9 -1
  95. package/dist/FileInput/FileInput.d.ts.map +1 -1
  96. package/dist/FileInput/FileInput.js +3 -2
  97. package/dist/Heading/Heading.d.ts.map +1 -1
  98. package/dist/Heading/Heading.js +6 -2
  99. package/dist/HoverCard/HoverCard.d.ts +6 -0
  100. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  101. package/dist/HoverCard/HoverCard.js +52 -3
  102. package/dist/HoverCard/useHoverCard.d.ts +16 -2
  103. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  104. package/dist/HoverCard/useHoverCard.js +74 -26
  105. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
  106. package/dist/Indicator/CheckboxIndicator.js +6 -2
  107. package/dist/Item/Item.d.ts.map +1 -1
  108. package/dist/Item/Item.js +4 -2
  109. package/dist/Kbd/Kbd.d.ts.map +1 -1
  110. package/dist/Kbd/Kbd.js +2 -19
  111. package/dist/Layer/LayerDepthContext.d.ts +52 -0
  112. package/dist/Layer/LayerDepthContext.d.ts.map +1 -0
  113. package/dist/Layer/LayerDepthContext.js +66 -0
  114. package/dist/Layer/index.d.ts +3 -0
  115. package/dist/Layer/index.d.ts.map +1 -1
  116. package/dist/Layer/index.js +4 -0
  117. package/dist/Layer/layerStack.d.ts +98 -0
  118. package/dist/Layer/layerStack.d.ts.map +1 -0
  119. package/dist/Layer/layerStack.js +306 -0
  120. package/dist/Layer/useLayerDismissal.d.ts +93 -0
  121. package/dist/Layer/useLayerDismissal.d.ts.map +1 -0
  122. package/dist/Layer/useLayerDismissal.js +91 -0
  123. package/dist/Lightbox/Lightbox.d.ts +1 -0
  124. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  125. package/dist/Lightbox/Lightbox.js +103 -81
  126. package/dist/Markdown/index.d.ts +1 -1
  127. package/dist/Markdown/index.d.ts.map +1 -1
  128. package/dist/Markdown/parser.d.ts +38 -1
  129. package/dist/Markdown/parser.d.ts.map +1 -1
  130. package/dist/Markdown/parser.js +177 -29
  131. package/dist/MobileNav/MobileNav.d.ts +6 -1
  132. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  133. package/dist/MobileNav/MobileNav.js +61 -36
  134. package/dist/MultiSelector/MultiSelector.d.ts +15 -1
  135. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  136. package/dist/MultiSelector/MultiSelector.js +17 -6
  137. package/dist/MultiSelector/index.d.ts +1 -1
  138. package/dist/MultiSelector/index.d.ts.map +1 -1
  139. package/dist/NavIcon/NavIcon.d.ts.map +1 -1
  140. package/dist/NavIcon/NavIcon.js +5 -1
  141. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  142. package/dist/NavMenu/NavHeadingMenu.js +3 -2
  143. package/dist/NumberInput/NumberInput.d.ts +1 -0
  144. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  145. package/dist/NumberInput/NumberInput.js +27 -37
  146. package/dist/NumberInput/numberParser.d.ts +51 -0
  147. package/dist/NumberInput/numberParser.d.ts.map +1 -0
  148. package/dist/NumberInput/numberParser.js +333 -0
  149. package/dist/Outline/Outline.d.ts.map +1 -1
  150. package/dist/Outline/Outline.js +3 -2
  151. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  152. package/dist/OverflowList/OverflowList.js +3 -2
  153. package/dist/Overlay/Overlay.d.ts.map +1 -1
  154. package/dist/Overlay/Overlay.js +3 -2
  155. package/dist/Popover/usePopover.d.ts.map +1 -1
  156. package/dist/Popover/usePopover.js +20 -17
  157. package/dist/PowerSearch/PowerSearch.d.ts +8 -2
  158. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  159. package/dist/PowerSearch/PowerSearch.js +19 -6
  160. package/dist/PowerSearch/PowerSearchEditPopover.d.ts +3 -1
  161. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  162. package/dist/PowerSearch/PowerSearchEditPopover.js +28 -6
  163. package/dist/PowerSearch/PowerSearchValueEditor.d.ts +3 -1
  164. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  165. package/dist/PowerSearch/PowerSearchValueEditor.js +19 -9
  166. package/dist/PowerSearch/types.d.ts +2 -0
  167. package/dist/PowerSearch/types.d.ts.map +1 -1
  168. package/dist/PowerSearch/usePowerSearchSource.d.ts +6 -1
  169. package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
  170. package/dist/PowerSearch/usePowerSearchSource.js +14 -5
  171. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  172. package/dist/ProgressBar/ProgressBar.js +18 -5
  173. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  174. package/dist/Resizable/ResizeHandle.js +3 -2
  175. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  176. package/dist/SegmentedControl/SegmentedControl.js +3 -2
  177. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  178. package/dist/SelectableCard/SelectableCard.js +3 -3
  179. package/dist/Selector/Selector.d.ts.map +1 -1
  180. package/dist/Selector/Selector.js +5 -0
  181. package/dist/SideNav/SideNav.d.ts.map +1 -1
  182. package/dist/SideNav/SideNav.js +4 -3
  183. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  184. package/dist/SideNav/SideNavHeading.js +8 -7
  185. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  186. package/dist/SideNav/SideNavItem.js +4 -3
  187. package/dist/Slider/Slider.d.ts.map +1 -1
  188. package/dist/Slider/Slider.js +37 -5
  189. package/dist/Spinner/Spinner.d.ts +3 -3
  190. package/dist/Spinner/Spinner.d.ts.map +1 -1
  191. package/dist/Spinner/Spinner.js +130 -78
  192. package/dist/StatusDot/StatusDot.d.ts.map +1 -1
  193. package/dist/StatusDot/StatusDot.js +6 -1
  194. package/dist/Stepper/Step.d.ts +140 -0
  195. package/dist/Stepper/Step.d.ts.map +1 -0
  196. package/dist/Stepper/Step.js +1045 -0
  197. package/dist/Stepper/StepStatus.d.ts +24 -0
  198. package/dist/Stepper/StepStatus.d.ts.map +1 -0
  199. package/dist/Stepper/StepStatus.js +1 -0
  200. package/dist/Stepper/Stepper.d.ts +93 -0
  201. package/dist/Stepper/Stepper.d.ts.map +1 -0
  202. package/dist/Stepper/Stepper.js +184 -0
  203. package/dist/Stepper/StepperContext.d.ts +41 -0
  204. package/dist/Stepper/StepperContext.d.ts.map +1 -0
  205. package/dist/Stepper/StepperContext.js +34 -0
  206. package/dist/Stepper/index.d.ts +8 -0
  207. package/dist/Stepper/index.d.ts.map +1 -0
  208. package/dist/Stepper/index.js +7 -0
  209. package/dist/Stepper/stepper.stylex.d.ts +15 -0
  210. package/dist/Stepper/stepper.stylex.d.ts.map +1 -0
  211. package/dist/Stepper/stepper.stylex.js +20 -0
  212. package/dist/Switch/Switch.d.ts.map +1 -1
  213. package/dist/Switch/Switch.js +8 -3
  214. package/dist/TabList/Tab.d.ts +15 -2
  215. package/dist/TabList/Tab.d.ts.map +1 -1
  216. package/dist/TabList/Tab.js +32 -7
  217. package/dist/TabList/TabList.d.ts +46 -7
  218. package/dist/TabList/TabList.d.ts.map +1 -1
  219. package/dist/TabList/TabList.js +353 -23
  220. package/dist/TabList/TabListContext.d.ts +11 -1
  221. package/dist/TabList/TabListContext.d.ts.map +1 -1
  222. package/dist/TabList/TabListContext.js +11 -2
  223. package/dist/TabList/TabMenu.d.ts.map +1 -1
  224. package/dist/TabList/TabMenu.js +3 -2
  225. package/dist/TabList/index.d.ts +2 -2
  226. package/dist/TabList/index.d.ts.map +1 -1
  227. package/dist/Table/BaseTable.d.ts.map +1 -1
  228. package/dist/Table/BaseTable.js +10 -2
  229. package/dist/Table/Table.d.ts.map +1 -1
  230. package/dist/Table/Table.js +3 -6
  231. package/dist/Table/TableRow.d.ts +16 -5
  232. package/dist/Table/TableRow.d.ts.map +1 -1
  233. package/dist/Table/TableRow.js +16 -5
  234. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  235. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +20 -8
  236. package/dist/Table/plugins/pagination/useTablePagination.d.ts +3 -0
  237. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  238. package/dist/Table/plugins/pagination/useTablePagination.js +21 -4
  239. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  240. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +7 -2
  241. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -1
  242. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +10 -3
  243. package/dist/Table/plugins/selection/useTableSelection.d.ts +16 -0
  244. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  245. package/dist/Table/plugins/selection/useTableSelection.js +19 -5
  246. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  247. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +3 -4
  248. package/dist/Table/types.d.ts +22 -4
  249. package/dist/Table/types.d.ts.map +1 -1
  250. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  251. package/dist/Table/useBaseTablePlugins.js +5 -0
  252. package/dist/Text/Text.d.ts.map +1 -1
  253. package/dist/Text/Text.js +6 -2
  254. package/dist/TextArea/TextArea.d.ts.map +1 -1
  255. package/dist/TextArea/TextArea.js +9 -3
  256. package/dist/TextInput/TextInput.d.ts.map +1 -1
  257. package/dist/TextInput/TextInput.js +3 -2
  258. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  259. package/dist/TimeInput/TimeInput.js +3 -2
  260. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  261. package/dist/Timestamp/Timestamp.js +5 -3
  262. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  263. package/dist/Toast/ToastViewport.js +27 -8
  264. package/dist/Token/TokenLink.d.ts.map +1 -1
  265. package/dist/Token/TokenLink.js +2 -2
  266. package/dist/Tokenizer/Tokenizer.d.ts +3 -1
  267. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  268. package/dist/Tokenizer/Tokenizer.js +2 -0
  269. package/dist/Tooltip/Tooltip.d.ts +5 -0
  270. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  271. package/dist/Tooltip/useTooltip.d.ts +5 -0
  272. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  273. package/dist/Tooltip/useTooltip.js +39 -24
  274. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  275. package/dist/TopNav/TopNavHeading.js +5 -4
  276. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  277. package/dist/TopNav/TopNavMegaMenu.js +3 -3
  278. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  279. package/dist/TopNav/TopNavMenu.js +4 -3
  280. package/dist/Typeahead/BaseTypeahead.d.ts +2 -0
  281. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  282. package/dist/Typeahead/BaseTypeahead.js +68 -29
  283. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  284. package/dist/Typeahead/Typeahead.js +3 -2
  285. package/dist/astryx.css +96 -7
  286. package/dist/hooks/index.d.ts +1 -0
  287. package/dist/hooks/index.d.ts.map +1 -1
  288. package/dist/hooks/index.js +1 -0
  289. package/dist/hooks/useFocusTrap.d.ts +7 -4
  290. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  291. package/dist/hooks/useFocusTrap.js +60 -95
  292. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  293. package/dist/hooks/useHotkeys.js +3 -18
  294. package/dist/hooks/useMergedRefs.d.ts +18 -0
  295. package/dist/hooks/useMergedRefs.d.ts.map +1 -0
  296. package/dist/hooks/useMergedRefs.js +25 -0
  297. package/dist/index.d.ts +1 -0
  298. package/dist/index.d.ts.map +1 -1
  299. package/dist/index.js +1 -0
  300. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  301. package/dist/theme/derivedVarRegistry.js +24 -4
  302. package/dist/theme/syntax/tokens.d.ts +1 -1
  303. package/dist/theme/syntax/tokens.d.ts.map +1 -1
  304. package/dist/theme/syntax/tokens.js +5 -2
  305. package/dist/utils/groupItems.d.ts +5 -1
  306. package/dist/utils/groupItems.d.ts.map +1 -1
  307. package/dist/utils/groupItems.js +9 -9
  308. package/dist/utils/index.d.ts +1 -1
  309. package/dist/utils/index.d.ts.map +1 -1
  310. package/dist/utils/isApplePlatform.d.ts +18 -0
  311. package/dist/utils/isApplePlatform.d.ts.map +1 -0
  312. package/dist/utils/isApplePlatform.js +37 -0
  313. package/dist/utils/sharedResizeObserver.d.ts.map +1 -1
  314. package/dist/utils/sharedResizeObserver.js +10 -1
  315. package/dist/utils/timeParser.d.ts +1 -1
  316. package/dist/utils/timeParser.d.ts.map +1 -1
  317. package/dist/utils/timeParser.js +26 -12
  318. package/locales/en.json +31 -7
  319. package/locales/pseudo.json +18 -0
  320. package/package.json +8 -3
  321. package/src/AppShell/AppShell.tsx +3 -2
  322. package/src/AspectRatio/AspectRatio.doc.mjs +6 -3
  323. package/src/AspectRatio/AspectRatio.test.tsx +57 -10
  324. package/src/AspectRatio/AspectRatio.tsx +40 -1
  325. package/src/Avatar/Avatar.doc.mjs +14 -3
  326. package/src/Avatar/Avatar.test.tsx +97 -0
  327. package/src/Avatar/Avatar.tsx +125 -52
  328. package/src/Avatar/index.ts +2 -2
  329. package/src/AvatarGroup/AvatarGroup.doc.mjs +8 -2
  330. package/src/AvatarGroup/AvatarGroup.test.tsx +24 -0
  331. package/src/AvatarGroup/AvatarGroup.tsx +15 -6
  332. package/src/AvatarGroup/AvatarGroupContext.ts +5 -3
  333. package/src/AvatarGroup/AvatarGroupOverflow.tsx +10 -4
  334. package/src/Banner/Banner.doc.mjs +13 -11
  335. package/src/Banner/Banner.test.tsx +102 -15
  336. package/src/Banner/Banner.tsx +72 -25
  337. package/src/BottomSheet/BottomSheet.test.tsx +123 -0
  338. package/src/BottomSheet/BottomSheet.tsx +17 -4
  339. package/src/BottomSheet/BottomSheetSwitcher.tsx +3 -2
  340. package/src/Breadcrumbs/BreadcrumbItem.tsx +9 -5
  341. package/src/Button/Button.doc.mjs +22 -0
  342. package/src/Button/Button.test.tsx +13 -0
  343. package/src/Button/Button.tsx +5 -4
  344. package/src/ButtonGroup/ButtonGroup.doc.mjs +10 -1
  345. package/src/ButtonGroup/ButtonGroup.test.tsx +391 -1
  346. package/src/ButtonGroup/ButtonGroup.tsx +23 -5
  347. package/src/Calendar/Calendar.doc.mjs +4 -3
  348. package/src/Calendar/Calendar.test.tsx +103 -0
  349. package/src/Calendar/Calendar.tsx +19 -16
  350. package/src/Calendar/getInitialFocusDate.test.ts +94 -0
  351. package/src/Calendar/getInitialFocusDate.ts +93 -0
  352. package/src/Calendar/styles.ts +49 -31
  353. package/src/Carousel/Carousel.tsx +3 -2
  354. package/src/Chat/ChatComposerInput.tsx +5 -1
  355. package/src/Chat/ChatLayout.tsx +3 -2
  356. package/src/Chat/ChatMessageList.doc.mjs +9 -1
  357. package/src/Chat/ChatMessageList.test.tsx +46 -0
  358. package/src/Chat/ChatMessageList.tsx +28 -4
  359. package/src/Chat/ChatTokenizedText.tsx +17 -10
  360. package/src/Chat/useChatComposerTokens.ts +1 -1
  361. package/src/CheckboxInput/CheckboxInput.doc.mjs +2 -0
  362. package/src/CheckboxInput/CheckboxInput.test.tsx +15 -0
  363. package/src/CheckboxInput/CheckboxInput.tsx +8 -2
  364. package/src/ClickableCard/ClickableCard.tsx +3 -2
  365. package/src/CodeBlock/CodeBlock.doc.mjs +16 -4
  366. package/src/CodeBlock/CodeBlock.test.tsx +37 -0
  367. package/src/CodeBlock/CodeBlock.tsx +20 -4
  368. package/src/CodeBlock/highlightRanges.ts +1 -1
  369. package/src/CodeBlock/highlightStyles.ts +13 -13
  370. package/src/CommandPalette/CommandPaletteInput.tsx +3 -2
  371. package/src/CommandPalette/CommandPaletteItem.tsx +3 -2
  372. package/src/ContextMenu/ContextMenu.doc.mjs +5 -0
  373. package/src/ContextMenu/ContextMenu.tsx +4 -3
  374. package/src/DateInput/DateInput.doc.mjs +16 -0
  375. package/src/DateInput/DateInput.tsx +55 -2
  376. package/src/DateInput/DateInputTouch.test.tsx +68 -9
  377. package/src/DateInput/NativeDateField.test.tsx +635 -0
  378. package/src/DateInput/NativeDateField.tsx +641 -0
  379. package/src/DateInput/TouchDateField.tsx +38 -4
  380. package/src/DateInput/index.ts +1 -0
  381. package/src/DateInput/nativeDateSegments.ts +127 -0
  382. package/src/DateTimeInput/DateTimeInput.doc.mjs +18 -0
  383. package/src/DateTimeInput/DateTimeInput.test.tsx +1088 -0
  384. package/src/DateTimeInput/DateTimeInput.tsx +556 -24
  385. package/src/DateTimeInput/index.ts +1 -0
  386. package/src/Dialog/Dialog.doc.mjs +8 -3
  387. package/src/Dialog/Dialog.test.tsx +261 -1
  388. package/src/Dialog/Dialog.tsx +109 -58
  389. package/src/DropdownMenu/DropdownMenu.doc.mjs +6 -0
  390. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +8 -5
  391. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +8 -11
  392. package/src/Field/Field.doc.mjs +13 -2
  393. package/src/Field/FieldLabel.test.tsx +29 -0
  394. package/src/Field/FieldLabel.tsx +7 -0
  395. package/src/FileInput/FileInput.tsx +3 -2
  396. package/src/Heading/Heading.test.tsx +23 -1
  397. package/src/Heading/Heading.tsx +6 -2
  398. package/src/HoverCard/HoverCard.doc.mjs +13 -2
  399. package/src/HoverCard/HoverCard.test.tsx +101 -4
  400. package/src/HoverCard/HoverCard.tsx +71 -3
  401. package/src/HoverCard/useHoverCard.tsx +101 -33
  402. package/src/Indicator/CheckboxIndicator.tsx +14 -8
  403. package/src/Indicator/Indicator.doc.mjs +2 -0
  404. package/src/Item/Item.tsx +5 -4
  405. package/src/Kbd/Kbd.tsx +2 -19
  406. package/src/Layer/LayerDepthContext.tsx +62 -0
  407. package/src/Layer/index.ts +9 -0
  408. package/src/Layer/layerDismissalFamilies.test.tsx +359 -0
  409. package/src/Layer/layerDismissalInvariants.test.tsx +417 -0
  410. package/src/Layer/layerStack.ts +362 -0
  411. package/src/Layer/useLayerDismissal.test.tsx +469 -0
  412. package/src/Layer/useLayerDismissal.ts +173 -0
  413. package/src/Layout/Layout.doc.mjs +2 -2
  414. package/src/Lightbox/Lightbox.doc.mjs +12 -0
  415. package/src/Lightbox/Lightbox.tsx +124 -102
  416. package/src/Link/Link.doc.mjs +39 -0
  417. package/src/Markdown/Markdown.doc.mjs +5 -0
  418. package/src/Markdown/incremental.test.ts +16 -2
  419. package/src/Markdown/index.ts +1 -0
  420. package/src/Markdown/parser.perf.test.ts +96 -1
  421. package/src/Markdown/parser.test.ts +145 -2
  422. package/src/Markdown/parser.ts +244 -29
  423. package/src/MobileNav/MobileNav.tsx +62 -36
  424. package/src/MoreMenu/MoreMenu.doc.mjs +7 -1
  425. package/src/MultiSelector/MultiSelector.doc.mjs +27 -0
  426. package/src/MultiSelector/MultiSelector.test.tsx +106 -0
  427. package/src/MultiSelector/MultiSelector.tsx +50 -6
  428. package/src/MultiSelector/index.ts +1 -0
  429. package/src/NavIcon/NavIcon.doc.mjs +8 -2
  430. package/src/NavIcon/NavIcon.test.tsx +9 -0
  431. package/src/NavIcon/NavIcon.tsx +5 -1
  432. package/src/NavMenu/NavHeadingMenu.tsx +3 -2
  433. package/src/NumberInput/NumberInput.doc.mjs +20 -0
  434. package/src/NumberInput/NumberInput.tsx +28 -29
  435. package/src/NumberInput/numberParser.test.ts +210 -0
  436. package/src/NumberInput/numberParser.ts +386 -0
  437. package/src/Outline/Outline.tsx +3 -2
  438. package/src/OverflowList/OverflowList.tsx +3 -2
  439. package/src/Overlay/Overlay.tsx +3 -2
  440. package/src/Popover/usePopover.tsx +31 -28
  441. package/src/PowerSearch/PowerSearch.doc.mjs +160 -28
  442. package/src/PowerSearch/PowerSearch.test.tsx +240 -1
  443. package/src/PowerSearch/PowerSearch.tsx +34 -5
  444. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +201 -0
  445. package/src/PowerSearch/PowerSearchEditPopover.tsx +29 -2
  446. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +47 -1
  447. package/src/PowerSearch/PowerSearchValueEditor.tsx +15 -0
  448. package/src/PowerSearch/types.ts +2 -0
  449. package/src/PowerSearch/usePowerSearchSource.test.ts +82 -2
  450. package/src/PowerSearch/usePowerSearchSource.ts +13 -3
  451. package/src/ProgressBar/ProgressBar.doc.mjs +42 -6
  452. package/src/ProgressBar/ProgressBar.test.tsx +24 -0
  453. package/src/ProgressBar/ProgressBar.tsx +28 -8
  454. package/src/Resizable/ResizeHandle.tsx +3 -2
  455. package/src/SegmentedControl/SegmentedControl.tsx +3 -2
  456. package/src/SelectableCard/SelectableCard.tsx +3 -2
  457. package/src/Selector/Selector.doc.mjs +12 -1
  458. package/src/Selector/Selector.test.tsx +45 -1
  459. package/src/Selector/Selector.tsx +11 -1
  460. package/src/SideNav/SideNav.doc.mjs +6 -6
  461. package/src/SideNav/SideNav.tsx +4 -2
  462. package/src/SideNav/SideNavHeading.tsx +12 -12
  463. package/src/SideNav/SideNavItem.tsx +4 -2
  464. package/src/Slider/Slider.test.tsx +106 -1
  465. package/src/Slider/Slider.tsx +55 -3
  466. package/src/Spinner/Spinner.test.tsx +147 -1
  467. package/src/Spinner/Spinner.tsx +120 -101
  468. package/src/StatusDot/StatusDot.doc.mjs +8 -2
  469. package/src/StatusDot/StatusDot.test.tsx +9 -0
  470. package/src/StatusDot/StatusDot.tsx +7 -1
  471. package/src/Stepper/Step.doc.mjs +98 -0
  472. package/src/Stepper/Step.tsx +1638 -0
  473. package/src/Stepper/StepStatus.ts +26 -0
  474. package/src/Stepper/Stepper.doc.mjs +370 -0
  475. package/src/Stepper/Stepper.test.tsx +1024 -0
  476. package/src/Stepper/Stepper.tsx +258 -0
  477. package/src/Stepper/StepperContext.ts +71 -0
  478. package/src/Stepper/index.ts +18 -0
  479. package/src/Stepper/stepper.stylex.ts +19 -0
  480. package/src/Switch/Switch.doc.mjs +2 -0
  481. package/src/Switch/Switch.test.tsx +10 -0
  482. package/src/Switch/Switch.tsx +6 -2
  483. package/src/TabList/Tab.doc.mjs +13 -1
  484. package/src/TabList/Tab.tsx +67 -7
  485. package/src/TabList/TabList.doc.mjs +22 -6
  486. package/src/TabList/TabList.test.tsx +559 -0
  487. package/src/TabList/TabList.tsx +490 -22
  488. package/src/TabList/TabListContext.ts +13 -2
  489. package/src/TabList/TabMenu.tsx +3 -2
  490. package/src/TabList/index.ts +6 -2
  491. package/src/Table/BaseTable.tsx +12 -4
  492. package/src/Table/Table.doc.mjs +9 -2
  493. package/src/Table/Table.test.tsx +132 -14
  494. package/src/Table/Table.tsx +3 -8
  495. package/src/Table/TableBody.doc.mjs +40 -0
  496. package/src/Table/TableFooter.doc.mjs +40 -0
  497. package/src/Table/TableHeader.doc.mjs +40 -0
  498. package/src/Table/TableRow.tsx +16 -5
  499. package/src/Table/plugins/groupedRows/useTableGroupedRows-perf.test.tsx +112 -0
  500. package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +100 -0
  501. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +17 -8
  502. package/src/Table/plugins/pagination/useTablePagination.test.tsx +34 -4
  503. package/src/Table/plugins/pagination/useTablePagination.tsx +35 -3
  504. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +9 -2
  505. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +13 -7
  506. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +12 -3
  507. package/src/Table/plugins/selection/useTableSelection.test.tsx +76 -0
  508. package/src/Table/plugins/selection/useTableSelection.tsx +40 -7
  509. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +1 -2
  510. package/src/Table/types.ts +22 -4
  511. package/src/Table/useBaseTablePlugins.ts +5 -0
  512. package/src/Table/useTableGroupedRows.doc.mjs +5 -4
  513. package/src/Table/useTablePagination.doc.mjs +7 -0
  514. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  515. package/src/Table/useTableSelection.doc.mjs +31 -0
  516. package/src/Text/Text.test.tsx +23 -1
  517. package/src/Text/Text.tsx +6 -2
  518. package/src/TextArea/TextArea.doc.mjs +8 -2
  519. package/src/TextArea/TextArea.test.tsx +11 -0
  520. package/src/TextArea/TextArea.tsx +15 -8
  521. package/src/TextInput/TextInput.doc.mjs +10 -0
  522. package/src/TextInput/TextInput.tsx +3 -2
  523. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  524. package/src/TimeInput/TimeInput.tsx +2 -2
  525. package/src/Timestamp/Timestamp.tsx +4 -2
  526. package/src/Toast/Toast.doc.mjs +6 -0
  527. package/src/Toast/ToastViewport.tsx +17 -1
  528. package/src/Token/TokenLink.tsx +2 -2
  529. package/src/Tokenizer/Tokenizer.doc.mjs +27 -0
  530. package/src/Tokenizer/Tokenizer.tsx +4 -0
  531. package/src/Toolbar/Toolbar.doc.mjs +5 -0
  532. package/src/Tooltip/Tooltip.doc.mjs +5 -0
  533. package/src/Tooltip/Tooltip.test.tsx +32 -0
  534. package/src/Tooltip/Tooltip.tsx +5 -0
  535. package/src/Tooltip/useTooltip.tsx +47 -24
  536. package/src/TopNav/TopNavHeading.tsx +5 -4
  537. package/src/TopNav/TopNavMegaMenu.tsx +3 -2
  538. package/src/TopNav/TopNavMenu.tsx +4 -3
  539. package/src/Typeahead/BaseTypeahead.doc.mjs +11 -0
  540. package/src/Typeahead/BaseTypeahead.tsx +85 -33
  541. package/src/Typeahead/Typeahead.doc.mjs +5 -0
  542. package/src/Typeahead/Typeahead.test.tsx +49 -0
  543. package/src/Typeahead/Typeahead.tsx +3 -2
  544. package/src/hooks/index.ts +2 -0
  545. package/src/hooks/useContainerReveal.doc.mjs +2 -2
  546. package/src/hooks/useFocusTrap.test.tsx +14 -0
  547. package/src/hooks/useFocusTrap.ts +62 -111
  548. package/src/hooks/useFocusTrapEscapeShim.test.tsx +165 -0
  549. package/src/hooks/useHotkeys.ts +3 -18
  550. package/src/hooks/useMergedRefs.doc.mjs +79 -0
  551. package/src/hooks/useMergedRefs.test.tsx +62 -0
  552. package/src/hooks/useMergedRefs.ts +36 -0
  553. package/src/index.ts +1 -0
  554. package/src/theme/MediaTheme.doc.mjs +5 -5
  555. package/src/theme/derivedVarRegistry.test.ts +19 -3
  556. package/src/theme/derivedVarRegistry.ts +23 -4
  557. package/src/theme/generateThemeRules.test.ts +37 -0
  558. package/src/theme/syntax/tokens.ts +5 -2
  559. package/src/utils/groupItems.ts +12 -6
  560. package/src/utils/index.ts +1 -1
  561. package/src/utils/isApplePlatform.test.ts +61 -0
  562. package/src/utils/isApplePlatform.ts +37 -0
  563. package/src/utils/sharedResizeObserver.ts +10 -2
  564. package/src/utils/timeParser.test.ts +6 -1
  565. package/src/utils/timeParser.ts +35 -13
  566. package/src/__tests__/inputWidthContract.test.tsx +0 -69
@@ -6,6 +6,7 @@ import {act, render, screen, fireEvent} from '@testing-library/react';
6
6
  import userEvent from '@testing-library/user-event';
7
7
  import {Avatar} from './Avatar';
8
8
  import {AvatarStatusDot} from './AvatarStatusDot';
9
+ import {AvatarGroup} from '../AvatarGroup';
9
10
 
10
11
  describe('Avatar', () => {
11
12
  it('exposes role="img" with the name as accessible name', () => {
@@ -416,6 +417,85 @@ describe('Avatar', () => {
416
417
  expect(img).toHaveAttribute('src', 'https://example.com/ada.jpg');
417
418
  });
418
419
 
420
+ it('renders without error for all shape variants', () => {
421
+ const shapes = ['circle', 'rounded', 'square'] as const;
422
+ for (const shape of shapes) {
423
+ const {unmount} = render(
424
+ <Avatar
425
+ name="Ana Thomas"
426
+ shape={shape}
427
+ data-testid={`avatar-${shape}`}
428
+ />,
429
+ );
430
+ expect(screen.getByTestId(`avatar-${shape}`)).toBeInTheDocument();
431
+ unmount();
432
+ }
433
+ });
434
+
435
+ it('defaults to circle shape (no shape prop)', () => {
436
+ render(<Avatar name="Lee" data-testid="avatar-default" />);
437
+ // Should still render with role="img" using the name as the accessible label.
438
+ expect(screen.getByRole('img', {name: 'Lee'})).toBeInTheDocument();
439
+ });
440
+
441
+ it('renders status dot for all shape variants', () => {
442
+ const shapes = ['circle', 'rounded', 'square'] as const;
443
+ const StatusDot = () => <span data-testid="status-dot" />;
444
+ for (const shape of shapes) {
445
+ const {unmount} = render(
446
+ <Avatar
447
+ name="Ana"
448
+ shape={shape}
449
+ status={<StatusDot />}
450
+ data-testid={`avatar-${shape}-status`}
451
+ />,
452
+ );
453
+ expect(screen.getByTestId('status-dot')).toBeInTheDocument();
454
+ unmount();
455
+ }
456
+ });
457
+
458
+ it('applies shapeStyles to wrapper when rendered inside AvatarGroup', () => {
459
+ render(
460
+ <AvatarGroup>
461
+ <Avatar name="Alice" shape="rounded" data-testid="group-avatar" />
462
+ </AvatarGroup>,
463
+ );
464
+ expect(screen.getByTestId('group-avatar')).toBeInTheDocument();
465
+ });
466
+
467
+ it("an AvatarGroup's shape overrides an individual Avatar's own shape prop, matching how size already works", () => {
468
+ render(
469
+ <AvatarGroup shape="square">
470
+ <Avatar name="Alice" shape="rounded" data-testid="group-avatar" />
471
+ </AvatarGroup>,
472
+ );
473
+ expect(screen.getByTestId('group-avatar')).toHaveAttribute(
474
+ 'data-shape',
475
+ 'square',
476
+ );
477
+ });
478
+
479
+ it("an Avatar's own shape prop applies outside any AvatarGroup", () => {
480
+ render(<Avatar name="Ana" shape="rounded" data-testid="solo-avatar" />);
481
+ expect(screen.getByTestId('solo-avatar')).toHaveAttribute(
482
+ 'data-shape',
483
+ 'rounded',
484
+ );
485
+ });
486
+
487
+ it('defaults to circle shape inside an AvatarGroup with no explicit shape', () => {
488
+ render(
489
+ <AvatarGroup>
490
+ <Avatar name="Alice" shape="square" data-testid="group-avatar" />
491
+ </AvatarGroup>,
492
+ );
493
+ expect(screen.getByTestId('group-avatar')).toHaveAttribute(
494
+ 'data-shape',
495
+ 'circle',
496
+ );
497
+ });
498
+
419
499
  // --- Name tooltip (tooltip?: string | boolean) ---
420
500
  describe('name tooltip', () => {
421
501
  const originalShowPopover = HTMLElement.prototype.showPopover;
@@ -569,6 +649,23 @@ describe('Avatar', () => {
569
649
  render(<Avatar name="Ada Lovelace" ref={ref} data-testid="a" />);
570
650
  expect(ref.current).toBe(screen.getByTestId('a'));
571
651
  });
652
+
653
+ it('keeps its merged ref attached across unrelated rerenders', () => {
654
+ const ref = vi.fn();
655
+ const {rerender} = render(
656
+ <Avatar name="Ada Lovelace" size="sm" tooltip={false} ref={ref} />,
657
+ );
658
+ const avatar = screen.getByRole('img', {name: 'Ada Lovelace'});
659
+ expect(ref).toHaveBeenLastCalledWith(avatar);
660
+ ref.mockClear();
661
+
662
+ rerender(
663
+ <Avatar name="Ada Lovelace" size="lg" tooltip={false} ref={ref} />,
664
+ );
665
+
666
+ expect(ref).not.toHaveBeenCalled();
667
+ expect(screen.getByRole('img', {name: 'Ada Lovelace'})).toBe(avatar);
668
+ });
572
669
  });
573
670
  });
574
671
 
@@ -19,7 +19,14 @@
19
19
  * Last synced props: alt, fallbackSrc, name, size, src, status, href, as, target, rel, onClick
20
20
  */
21
21
 
22
- import {isValidElement, useMemo, useRef, useState, type ReactNode} from 'react';
22
+ import {
23
+ isValidElement,
24
+ useCallback,
25
+ useMemo,
26
+ useRef,
27
+ useState,
28
+ type ReactNode,
29
+ } from 'react';
23
30
  import type {BaseProps} from '../BaseProps';
24
31
  import * as stylex from '@stylexjs/stylex';
25
32
  import {
@@ -34,7 +41,8 @@ import {
34
41
  type AvatarStatusLabelTarget,
35
42
  } from './AvatarStatusLabelContext';
36
43
  import {useAvatarGroup} from '../AvatarGroup/AvatarGroupContext';
37
- import {mergeProps, mergeRefs} from '../utils';
44
+ import {mergeProps} from '../utils';
45
+ import {useMergedRefs} from '../hooks/useMergedRefs';
38
46
  import {themeProps} from '../utils/themeProps';
39
47
  import {firstCharacter} from '../utils/characters';
40
48
  import {focusOutlineProps} from '../utils/focusOutline.stylex';
@@ -89,6 +97,11 @@ type AvatarNumericSize =
89
97
  */
90
98
  export type AvatarSize = AvatarNamedSize | AvatarNumericSize;
91
99
 
100
+ /**
101
+ * Avatar shape options
102
+ */
103
+ export type AvatarShape = 'circle' | 'rounded' | 'square';
104
+
92
105
  /**
93
106
  * Resolves named sizes to their numeric pixel values
94
107
  */
@@ -110,6 +123,25 @@ export function resolveSize(size: AvatarSize): number {
110
123
  }
111
124
  }
112
125
 
126
+ // Sets one local custom property rather than `borderRadius` directly, so
127
+ // every clipping/ring surface that needs the shape's radius (the wrapper,
128
+ // the interactive focus ring, the content div that actually clips via
129
+ // `overflow: hidden`, and AvatarGroupOverflow's "+N" indicator) reads the
130
+ // same value instead of each hardcoding its own borderRadius independently.
131
+ // A theme override only has to reach this one property, not fight the
132
+ // per-surface StyleX class order to change the shape everywhere at once.
133
+ export const shapeStyles = stylex.create({
134
+ circle: {
135
+ '--_avatar-radius': radiusVars['--radius-full'],
136
+ },
137
+ rounded: {
138
+ '--_avatar-radius': radiusVars['--radius-element'],
139
+ },
140
+ square: {
141
+ '--_avatar-radius': radiusVars['--radius-none'],
142
+ },
143
+ });
144
+
113
145
  /**
114
146
  * Base styles for the avatar
115
147
  * Uses a wrapper/content structure so status isn't clipped by overflow:hidden
@@ -119,10 +151,10 @@ const styles = stylex.create({
119
151
  position: 'relative',
120
152
  display: 'inline-flex',
121
153
  flexShrink: 0,
122
- // The wrapper carries the avatar's box as well as its radius, so a theme
123
- // rule on the `.astryx-avatar` target reaches both: the size the `size`
124
- // visual prop selects on is set here, and the content below fills it.
125
- borderRadius: radiusVars['--radius-full'],
154
+ // Reads the shape variant's `--_avatar-radius` (set by `shapeStyles`
155
+ // below) rather than a hardcoded value, so `shape` actually changes the
156
+ // wrapper's own radius instead of only the content div's clip.
157
+ borderRadius: 'var(--_avatar-radius)',
126
158
  },
127
159
  content: {
128
160
  display: 'flex',
@@ -130,9 +162,14 @@ const styles = stylex.create({
130
162
  justifyContent: 'center',
131
163
  width: '100%',
132
164
  height: '100%',
133
- borderRadius: radiusVars['--radius-full'],
134
165
  overflow: 'hidden',
135
166
  userSelect: 'none',
167
+ // The content div is the one that actually clips (via overflow: hidden
168
+ // above), so it needs its own border-radius matching the wrapper's.
169
+ // It inherits `--_avatar-radius` from the wrapper, since custom
170
+ // properties cascade to descendants even though border-radius itself
171
+ // does not.
172
+ borderRadius: 'var(--_avatar-radius)',
136
173
  },
137
174
  image: {
138
175
  width: '100%',
@@ -159,9 +196,6 @@ const styles = stylex.create({
159
196
  status: {
160
197
  position: 'absolute',
161
198
  },
162
- // Visible focus ring for the name-tooltip tab stop, matching the repo-wide
163
- // focus-visible outline treatment (see Timestamp, Token, Thumbnail). Only
164
- // applied when a tooltip is active so keyboard users can reveal it.
165
199
  // Reset the intrinsic styling of the interactive element (<a>/<button>) so it
166
200
  // is a transparent, correctly-sized wrapper around the avatar visuals. The
167
201
  // element carries the focus-visible accent ring for keyboard users.
@@ -179,8 +213,9 @@ const styles = stylex.create({
179
213
  default: 'pointer',
180
214
  ':is(:disabled,[aria-disabled="true"])': 'default',
181
215
  },
182
- // Match the avatar's circular shape so the focus ring hugs it.
183
- borderRadius: radiusVars['--radius-full'],
216
+ // Match the avatar's shape (via `--_avatar-radius`) so the focus ring
217
+ // hugs it, whichever shape variant is in effect.
218
+ borderRadius: 'var(--_avatar-radius)',
184
219
  },
185
220
  });
186
221
 
@@ -199,7 +234,7 @@ const dynamicStyles = stylex.create({
199
234
  fontSize: (size: number) => ({
200
235
  fontSize: `${size * INITIALS_FONT_SIZE_RATIO}px`,
201
236
  }),
202
- statusPosition: (size: number) => ({
237
+ statusPositionCircle: (size: number) => ({
203
238
  bottom: size * CIRCLE_EDGE_OFFSET_RATIO,
204
239
  insetInlineEnd: size * CIRCLE_EDGE_OFFSET_RATIO,
205
240
  // `insetInlineEnd` anchors to the right edge in LTR / left in RTL, so the
@@ -209,13 +244,23 @@ const dynamicStyles = stylex.create({
209
244
  ':is([dir="rtl"] *)': 'translate(-50%, 50%)',
210
245
  },
211
246
  }),
247
+ statusPositionCorner: {
248
+ bottom: 0,
249
+ insetInlineEnd: 0,
250
+ // Same RTL mirroring as statusPositionCircle above: insetInlineEnd
251
+ // anchors to the right edge in LTR / left in RTL, so the outward push
252
+ // must flip sign too.
253
+ transform: {
254
+ default: 'translate(25%, 25%)',
255
+ ':is([dir="rtl"] *)': 'translate(-25%, 25%)',
256
+ },
257
+ },
212
258
  });
213
259
 
214
260
  const BORDER_WIDTH = 2;
215
261
 
216
262
  const groupStyles = stylex.create({
217
263
  ring: {
218
- borderRadius: radiusVars['--radius-full'],
219
264
  borderWidth: BORDER_WIDTH,
220
265
  borderStyle: 'solid',
221
266
  borderColor: colorVars['--color-background-surface'],
@@ -268,6 +313,14 @@ export interface AvatarProps extends BaseProps<HTMLDivElement> {
268
313
  * @default 'md'
269
314
  */
270
315
  size?: AvatarSize;
316
+ /**
317
+ * Shape variant of the avatar.
318
+ * - 'circle': Full circle (default)
319
+ * - 'rounded': Rounded square
320
+ * - 'square': Sharp square
321
+ * @default 'circle'
322
+ */
323
+ shape?: AvatarShape;
271
324
  /**
272
325
  * The primary image source for the avatar.
273
326
  */
@@ -471,6 +524,7 @@ export function Avatar({
471
524
  fallbackSrc,
472
525
  name,
473
526
  size = 'md',
527
+ shape = 'circle',
474
528
  src,
475
529
  status,
476
530
  tooltip = true,
@@ -530,6 +584,7 @@ export function Avatar({
530
584
  : undefined;
531
585
  const avatarGroup = useAvatarGroup();
532
586
  const resolvedSize = avatarGroup?.size ?? size;
587
+ const resolvedShape = avatarGroup?.shape ?? shape;
533
588
  const numericSize = useMemo(() => resolveSize(resolvedSize), [resolvedSize]);
534
589
 
535
590
  // Resolve the tooltip content:
@@ -608,47 +663,62 @@ export function Avatar({
608
663
  label: undefined,
609
664
  update: null,
610
665
  });
611
- // A fresh callback ref every render, so React reattaches it on every commit
612
- // and the name is recomposed after React has written this render's props.
666
+ const {
667
+ 'aria-hidden': consumerAriaHidden,
668
+ 'aria-label': consumerAriaLabel,
669
+ role: consumerRole,
670
+ } = props;
671
+ // Recompose the name after React has written any relevant prop changes.
613
672
  // `update` covers the other direction: a label that changes while the avatar
614
673
  // itself does not re-render.
615
- const nameRef = (element: HTMLElement | null) => {
616
- if (element == null) {
617
- return;
618
- }
619
- const target = statusLabelRef.current;
620
- target.update = () => {
621
- const composed = composeAccessibleName(
622
- t,
623
- nameLabel,
624
- meaningful(target.label) ?? getStatusLabel(status),
625
- );
626
- // A consumer's own ARIA wins here exactly as it does in render, where
627
- // the derived props spread before `{...props}`.
628
- if (props['aria-label'] == null) {
629
- setAttributeOrRemove(element, 'aria-label', composed);
674
+ const nameRef = useCallback(
675
+ (element: HTMLElement | null) => {
676
+ if (element == null) {
677
+ return;
630
678
  }
631
- // An `<a>`/`<button>` root carries its own role and must not be hidden,
632
- // so only the name transfers there.
633
- if (!isInteractive) {
634
- if (props.role == null) {
635
- element.setAttribute('role', composed ? 'img' : 'presentation');
679
+ const target = statusLabelRef.current;
680
+ target.update = () => {
681
+ const composed = composeAccessibleName(
682
+ t,
683
+ nameLabel,
684
+ meaningful(target.label) ?? getStatusLabel(status),
685
+ );
686
+ // A consumer's own ARIA wins here exactly as it does in render, where
687
+ // the derived props spread before `{...props}`.
688
+ if (consumerAriaLabel == null) {
689
+ setAttributeOrRemove(element, 'aria-label', composed);
636
690
  }
637
- if (props['aria-hidden'] == null) {
638
- setAttributeOrRemove(
639
- element,
640
- 'aria-hidden',
641
- composed ? undefined : 'true',
642
- );
691
+ // An `<a>`/`<button>` root carries its own role and must not be hidden,
692
+ // so only the name transfers there.
693
+ if (!isInteractive) {
694
+ if (consumerRole == null) {
695
+ element.setAttribute('role', composed ? 'img' : 'presentation');
696
+ }
697
+ if (consumerAriaHidden == null) {
698
+ setAttributeOrRemove(
699
+ element,
700
+ 'aria-hidden',
701
+ composed ? undefined : 'true',
702
+ );
703
+ }
643
704
  }
644
- }
645
- };
646
- target.update();
647
- return () => {
648
- target.update = null;
649
- };
650
- };
651
- const rootRef = mergeRefs(
705
+ };
706
+ target.update();
707
+ return () => {
708
+ target.update = null;
709
+ };
710
+ },
711
+ [
712
+ consumerAriaHidden,
713
+ consumerAriaLabel,
714
+ consumerRole,
715
+ isInteractive,
716
+ nameLabel,
717
+ status,
718
+ t,
719
+ ],
720
+ );
721
+ const rootRef = useMergedRefs(
652
722
  ref,
653
723
  showTooltip ? tooltipHook.ref : undefined,
654
724
  nameRef,
@@ -700,7 +770,9 @@ export function Avatar({
700
770
  <div
701
771
  {...stylex.props(
702
772
  styles.status,
703
- dynamicStyles.statusPosition(numericSize),
773
+ resolvedShape === 'circle'
774
+ ? dynamicStyles.statusPositionCircle(numericSize)
775
+ : dynamicStyles.statusPositionCorner,
704
776
  )}>
705
777
  {status}
706
778
  </div>
@@ -713,7 +785,7 @@ export function Avatar({
713
785
  // tooltip tab-stop focus ring all live here so the interactive
714
786
  // `<a>`/`<button>` and the static `<div>` carry the exact same box.
715
787
  const rootStylexProps = mergeProps(
716
- themeProps('avatar', {size: resolvedSize}),
788
+ themeProps('avatar', {size: resolvedSize, shape: resolvedShape}),
717
789
  focusOutlineProps.focusVisible(
718
790
  styles.wrapper,
719
791
  dynamicStyles.size(numericSize),
@@ -721,6 +793,7 @@ export function Avatar({
721
793
  avatarGroup && groupStyles.ring,
722
794
  avatarGroup && groupStyles.overlap,
723
795
  avatarGroup && groupDynamicStyles.overlap(-avatarGroup.overlap),
796
+ shapeStyles[resolvedShape],
724
797
  xstyle,
725
798
  ),
726
799
  className,
@@ -5,7 +5,7 @@
5
5
  /**
6
6
  * @file index.ts
7
7
  * @input Imports Avatar component and types from Avatar.tsx, AvatarStatusDot from AvatarStatusDot.tsx
8
- * @output Exports Avatar, AvatarProps, AvatarSize, AvatarStatusDot, AvatarStatusDotProps, AvatarStatusDotVariant
8
+ * @output Exports Avatar, AvatarProps, AvatarSize, AvatarShape, resolveSize, AvatarStatusDot, AvatarStatusDotProps, AvatarStatusDotVariant
9
9
  * @position Component entry point; re-exported by /packages/core/src/index.ts
10
10
  *
11
11
  * SYNC: When modified, update this header and /packages/core/src/Avatar/Avatar.doc.mjs
@@ -38,7 +38,7 @@ export interface AvatarStatusDotVariantMap {
38
38
  }
39
39
 
40
40
  export {Avatar, resolveSize} from './Avatar';
41
- export type {AvatarProps, AvatarSize} from './Avatar';
41
+ export type {AvatarProps, AvatarSize, AvatarShape} from './Avatar';
42
42
  export {AvatarStatusDot} from './AvatarStatusDot';
43
43
  export type {
44
44
  AvatarStatusDotProps,
@@ -25,8 +25,8 @@ export const docs = {
25
25
  },
26
26
  theming: {
27
27
  targets: [
28
- {className: 'astryx-avatar-group', visualProps: ['size']},
29
- {className: 'astryx-avatar-group-overflow', visualProps: ['size']},
28
+ {className: 'astryx-avatar-group', visualProps: ['size', 'shape']},
29
+ {className: 'astryx-avatar-group-overflow', visualProps: ['size', 'shape']},
30
30
  ],
31
31
  },
32
32
  description: 'Stacked avatar display with overlapping layout and optional overflow indicator. Children are Avatar elements.',
@@ -52,6 +52,12 @@ export const docs = {
52
52
  "Size applied to all avatars via context. This wins over each child Avatar's own size prop, including when it is left at the default, so set the size here rather than on the children.",
53
53
  default: "'md'",
54
54
  },
55
+ {
56
+ name: 'shape',
57
+ type: "'circle' | 'rounded' | 'square'",
58
+ description: "Shape applied to all avatars via context, overriding each avatar's own shape prop. Also applied to AvatarGroupOverflow's \"+N\" indicator.",
59
+ default: "'circle'",
60
+ },
55
61
  {
56
62
  name: 'ref',
57
63
  type: 'React.Ref<HTMLDivElement>',
@@ -118,6 +118,30 @@ describe('AvatarGroupOverflow', () => {
118
118
  expect(overflow).toHaveAttribute('data-size', 'lg');
119
119
  });
120
120
 
121
+ it('applies the group shape to the overflow chip, matching its avatars', () => {
122
+ render(
123
+ <AvatarGroup shape="square">
124
+ <Avatar name="Alice" />
125
+ <AvatarGroupOverflow count={5} />
126
+ </AvatarGroup>,
127
+ );
128
+
129
+ const overflow = screen.getByLabelText('5 more');
130
+ expect(overflow).toHaveAttribute('data-shape', 'square');
131
+ });
132
+
133
+ it('defaults the overflow chip to circle shape with no explicit AvatarGroup shape', () => {
134
+ render(
135
+ <AvatarGroup>
136
+ <Avatar name="Alice" />
137
+ <AvatarGroupOverflow count={5} />
138
+ </AvatarGroup>,
139
+ );
140
+
141
+ const overflow = screen.getByLabelText('5 more');
142
+ expect(overflow).toHaveAttribute('data-shape', 'circle');
143
+ });
144
+
121
145
  it('renders as button when onClick is provided', () => {
122
146
  render(
123
147
  <AvatarGroup>
@@ -26,9 +26,9 @@
26
26
 
27
27
  import {useId, useMemo, useState, type ReactNode} from 'react';
28
28
  import type {BaseProps} from '../BaseProps';
29
- import {resolveSize, type AvatarSize} from '../Avatar';
29
+ import {resolveSize, type AvatarShape, type AvatarSize} from '../Avatar';
30
30
  import * as stylex from '@stylexjs/stylex';
31
- import {mergeProps, mergeRefs} from '../utils';
31
+ import {mergeProps} from '../utils';
32
32
  import {composeEventHandlers} from '../utils/composeEventHandlers';
33
33
  import {AvatarGroupContext} from './AvatarGroupContext';
34
34
  import {themeProps} from '../utils/themeProps';
@@ -37,6 +37,7 @@ import {useListFocus} from '../hooks/useListFocus';
37
37
  import {useIsomorphicLayoutEffect} from '../hooks/useIsomorphicLayoutEffect';
38
38
  import {VisuallyHidden} from '../VisuallyHidden';
39
39
 
40
+ import {useMergedRefs} from '../hooks/useMergedRefs';
40
41
  const OVERLAP_RATIO = 0.25;
41
42
 
42
43
  export interface AvatarGroupProps extends BaseProps<HTMLDivElement> {
@@ -54,6 +55,13 @@ export interface AvatarGroupProps extends BaseProps<HTMLDivElement> {
54
55
  * @default 'md'
55
56
  */
56
57
  size?: AvatarSize;
58
+ /**
59
+ * Shape applied to all avatars via context, overriding each avatar's own
60
+ * `shape` prop so a group stays visually uniform. Also applied to the
61
+ * `AvatarGroupOverflow` "+N" indicator, so it matches the group.
62
+ * @default 'circle'
63
+ */
64
+ shape?: AvatarShape;
57
65
  /**
58
66
  * Test ID for integration testing.
59
67
  */
@@ -88,6 +96,7 @@ const styles = stylex.create({
88
96
  export function AvatarGroup({
89
97
  children,
90
98
  size = 'md',
99
+ shape = 'circle',
91
100
  'data-testid': testId,
92
101
  'aria-label': ariaLabelFromProps,
93
102
  'aria-describedby': ariaDescribedByFromProps,
@@ -105,8 +114,8 @@ export function AvatarGroup({
105
114
  const overlap = Math.round(numericSize * OVERLAP_RATIO);
106
115
 
107
116
  const contextValue = useMemo(
108
- () => ({size, overlap, numericSize}),
109
- [size, overlap, numericSize],
117
+ () => ({size, shape, overlap, numericSize}),
118
+ [size, shape, overlap, numericSize],
110
119
  );
111
120
 
112
121
  // The keyboard hint and roving tab stop only make sense once the group has
@@ -143,7 +152,7 @@ export function AvatarGroup({
143
152
  <AvatarGroupContext value={contextValue}>
144
153
  <div
145
154
  {...props}
146
- ref={mergeRefs(ref, listRef)}
155
+ ref={useMergedRefs(ref, listRef)}
147
156
  role="group"
148
157
  aria-label={ariaLabel}
149
158
  aria-describedby={describedBy}
@@ -151,7 +160,7 @@ export function AvatarGroup({
151
160
  onKeyDown={composeEventHandlers(onKeyDown, handleKeyDown)}
152
161
  onFocus={composeEventHandlers(onFocus, handleFocus)}
153
162
  {...mergeProps(
154
- themeProps('avatar-group', {size}),
163
+ themeProps('avatar-group', {size, shape}),
155
164
  stylex.props(styles.root, xstyle),
156
165
  className,
157
166
  style,
@@ -9,16 +9,18 @@
9
9
  */
10
10
 
11
11
  import {createContext, use} from 'react';
12
- import type {AvatarSize} from '../Avatar';
12
+ import type {AvatarShape, AvatarSize} from '../Avatar';
13
13
 
14
14
  export interface AvatarGroupContextValue {
15
15
  size: AvatarSize;
16
+ shape: AvatarShape;
16
17
  overlap: number;
17
18
  numericSize: number;
18
19
  }
19
20
 
20
- export const AvatarGroupContext =
21
- createContext<AvatarGroupContextValue | null>(null);
21
+ export const AvatarGroupContext = createContext<AvatarGroupContextValue | null>(
22
+ null,
23
+ );
22
24
  AvatarGroupContext.displayName = 'AvatarGroupContext';
23
25
 
24
26
  export function useAvatarGroup(): AvatarGroupContextValue | null {
@@ -20,9 +20,9 @@ import {
20
20
  typographyVars,
21
21
  typeScaleVars,
22
22
  fontWeightVars,
23
- radiusVars,
24
23
  spacingVars,
25
24
  } from '../theme/tokens.stylex';
25
+ import {shapeStyles} from '../Avatar/Avatar';
26
26
  import {mergeProps} from '../utils';
27
27
  import {resolveSize} from '../Avatar';
28
28
  import {useAvatarGroup} from './AvatarGroupContext';
@@ -65,7 +65,10 @@ const styles = stylex.create({
65
65
  display: 'inline-flex',
66
66
  alignItems: 'center',
67
67
  justifyContent: 'center',
68
- borderRadius: radiusVars['--radius-full'],
68
+ // Reads the shape variant's `--_avatar-radius` (set via `shapeStyles`,
69
+ // shared with Avatar) so the overflow indicator matches the group's
70
+ // shape instead of always staying a circle.
71
+ borderRadius: 'var(--_avatar-radius)',
69
72
  // Use opaque background to prevent avatar bleed-through
70
73
  backgroundColor: colorVars['--color-background-surface'],
71
74
  color: colorVars['--color-text-secondary'],
@@ -166,6 +169,7 @@ export function AvatarGroupOverflow({
166
169
  const t = useTranslator();
167
170
  const group = useAvatarGroup();
168
171
  const size = group?.size ?? 'md';
172
+ const shape = group?.shape ?? 'circle';
169
173
  const numericSize = group?.numericSize ?? resolveSize('md');
170
174
  const overlap = group?.overlap ?? 0;
171
175
 
@@ -186,7 +190,7 @@ export function AvatarGroupOverflow({
186
190
  aria-label={label}
187
191
  data-avatar-item=""
188
192
  {...mergeProps(
189
- themeProps('avatar-group-overflow', {size}),
193
+ themeProps('avatar-group-overflow', {size, shape}),
190
194
  focusOutlineProps.focusVisible(
191
195
  styles.base,
192
196
  styles.button,
@@ -194,6 +198,7 @@ export function AvatarGroupOverflow({
194
198
  dynamicStyles.size(numericSize),
195
199
  dynamicStyles.fontSize(numericSize),
196
200
  dynamicStyles.overlap(-overlap),
201
+ shapeStyles[shape],
197
202
  xstyle,
198
203
  ),
199
204
  className,
@@ -210,13 +215,14 @@ export function AvatarGroupOverflow({
210
215
  {...rest}
211
216
  aria-label={label}
212
217
  {...mergeProps(
213
- themeProps('avatar-group-overflow', {size}),
218
+ themeProps('avatar-group-overflow', {size, shape}),
214
219
  stylex.props(
215
220
  styles.base,
216
221
  styles.overlap,
217
222
  dynamicStyles.size(numericSize),
218
223
  dynamicStyles.fontSize(numericSize),
219
224
  dynamicStyles.overlap(-overlap),
225
+ shapeStyles[shape],
220
226
  xstyle,
221
227
  ),
222
228
  className,