@astryxdesign/core 0.4.7 → 0.5.0-canary.32a62fd

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 (554) 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 +13 -14
  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/Carousel/Carousel.d.ts.map +1 -1
  41. package/dist/Carousel/Carousel.js +3 -3
  42. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  43. package/dist/Chat/ChatComposerInput.js +2 -3
  44. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  45. package/dist/Chat/ChatLayout.js +3 -3
  46. package/dist/Chat/ChatMessageList.d.ts +22 -3
  47. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  48. package/dist/Chat/ChatMessageList.js +6 -3
  49. package/dist/Chat/ChatTokenizedText.d.ts +2 -0
  50. package/dist/Chat/ChatTokenizedText.d.ts.map +1 -1
  51. package/dist/Chat/ChatTokenizedText.js +11 -6
  52. package/dist/Chat/useChatComposerTokens.js +1 -1
  53. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  54. package/dist/CheckboxInput/CheckboxInput.js +10 -3
  55. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  56. package/dist/ClickableCard/ClickableCard.js +3 -3
  57. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  58. package/dist/CodeBlock/CodeBlock.js +15 -4
  59. package/dist/CodeBlock/highlightRanges.js +1 -1
  60. package/dist/CodeBlock/highlightStyles.js +13 -13
  61. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  62. package/dist/CommandPalette/CommandPaletteInput.js +3 -2
  63. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  64. package/dist/CommandPalette/CommandPaletteItem.js +3 -2
  65. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  66. package/dist/ContextMenu/ContextMenu.js +4 -3
  67. package/dist/DateInput/DateInput.d.ts +43 -0
  68. package/dist/DateInput/DateInput.d.ts.map +1 -1
  69. package/dist/DateInput/DateInput.js +22 -2
  70. package/dist/DateInput/NativeDateField.d.ts +10 -0
  71. package/dist/DateInput/NativeDateField.d.ts.map +1 -0
  72. package/dist/DateInput/NativeDateField.js +477 -0
  73. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  74. package/dist/DateInput/TouchDateField.js +38 -4
  75. package/dist/DateInput/index.d.ts +1 -1
  76. package/dist/DateInput/index.d.ts.map +1 -1
  77. package/dist/DateTimeInput/DateTimeInput.d.ts +21 -1
  78. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  79. package/dist/DateTimeInput/DateTimeInput.js +406 -12
  80. package/dist/DateTimeInput/index.d.ts +1 -1
  81. package/dist/DateTimeInput/index.d.ts.map +1 -1
  82. package/dist/Dialog/Dialog.d.ts +7 -1
  83. package/dist/Dialog/Dialog.d.ts.map +1 -1
  84. package/dist/Dialog/Dialog.js +89 -58
  85. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  86. package/dist/DropdownMenu/DropdownMenuSubMenu.js +6 -5
  87. package/dist/Field/FieldLabel.d.ts.map +1 -1
  88. package/dist/Field/FieldLabel.js +9 -1
  89. package/dist/FileInput/FileInput.d.ts.map +1 -1
  90. package/dist/FileInput/FileInput.js +3 -2
  91. package/dist/Heading/Heading.d.ts.map +1 -1
  92. package/dist/Heading/Heading.js +6 -2
  93. package/dist/HoverCard/HoverCard.d.ts +6 -0
  94. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  95. package/dist/HoverCard/HoverCard.js +52 -3
  96. package/dist/HoverCard/useHoverCard.d.ts +16 -2
  97. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  98. package/dist/HoverCard/useHoverCard.js +74 -26
  99. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
  100. package/dist/Indicator/CheckboxIndicator.js +6 -2
  101. package/dist/Item/Item.d.ts.map +1 -1
  102. package/dist/Item/Item.js +4 -2
  103. package/dist/Kbd/Kbd.d.ts.map +1 -1
  104. package/dist/Kbd/Kbd.js +2 -19
  105. package/dist/Layer/LayerDepthContext.d.ts +52 -0
  106. package/dist/Layer/LayerDepthContext.d.ts.map +1 -0
  107. package/dist/Layer/LayerDepthContext.js +66 -0
  108. package/dist/Layer/index.d.ts +3 -0
  109. package/dist/Layer/index.d.ts.map +1 -1
  110. package/dist/Layer/index.js +4 -0
  111. package/dist/Layer/layerStack.d.ts +98 -0
  112. package/dist/Layer/layerStack.d.ts.map +1 -0
  113. package/dist/Layer/layerStack.js +306 -0
  114. package/dist/Layer/useLayerDismissal.d.ts +93 -0
  115. package/dist/Layer/useLayerDismissal.d.ts.map +1 -0
  116. package/dist/Layer/useLayerDismissal.js +91 -0
  117. package/dist/Lightbox/Lightbox.d.ts +1 -0
  118. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  119. package/dist/Lightbox/Lightbox.js +103 -81
  120. package/dist/Markdown/index.d.ts +1 -1
  121. package/dist/Markdown/index.d.ts.map +1 -1
  122. package/dist/Markdown/parser.d.ts +38 -1
  123. package/dist/Markdown/parser.d.ts.map +1 -1
  124. package/dist/Markdown/parser.js +177 -29
  125. package/dist/MobileNav/MobileNav.d.ts +6 -1
  126. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  127. package/dist/MobileNav/MobileNav.js +61 -36
  128. package/dist/MultiSelector/MultiSelector.d.ts +15 -1
  129. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  130. package/dist/MultiSelector/MultiSelector.js +17 -6
  131. package/dist/MultiSelector/index.d.ts +1 -1
  132. package/dist/MultiSelector/index.d.ts.map +1 -1
  133. package/dist/NavIcon/NavIcon.d.ts.map +1 -1
  134. package/dist/NavIcon/NavIcon.js +5 -1
  135. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  136. package/dist/NavMenu/NavHeadingMenu.js +3 -2
  137. package/dist/NumberInput/NumberInput.d.ts +1 -0
  138. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  139. package/dist/NumberInput/NumberInput.js +27 -37
  140. package/dist/NumberInput/numberParser.d.ts +43 -0
  141. package/dist/NumberInput/numberParser.d.ts.map +1 -0
  142. package/dist/NumberInput/numberParser.js +293 -0
  143. package/dist/Outline/Outline.d.ts.map +1 -1
  144. package/dist/Outline/Outline.js +3 -2
  145. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  146. package/dist/OverflowList/OverflowList.js +3 -2
  147. package/dist/Overlay/Overlay.d.ts.map +1 -1
  148. package/dist/Overlay/Overlay.js +3 -2
  149. package/dist/Popover/usePopover.d.ts.map +1 -1
  150. package/dist/Popover/usePopover.js +20 -17
  151. package/dist/PowerSearch/PowerSearch.d.ts +8 -2
  152. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  153. package/dist/PowerSearch/PowerSearch.js +19 -6
  154. package/dist/PowerSearch/PowerSearchEditPopover.d.ts +3 -1
  155. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  156. package/dist/PowerSearch/PowerSearchEditPopover.js +28 -6
  157. package/dist/PowerSearch/PowerSearchValueEditor.d.ts +3 -1
  158. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  159. package/dist/PowerSearch/PowerSearchValueEditor.js +19 -9
  160. package/dist/PowerSearch/types.d.ts +2 -0
  161. package/dist/PowerSearch/types.d.ts.map +1 -1
  162. package/dist/PowerSearch/usePowerSearchSource.d.ts +6 -1
  163. package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
  164. package/dist/PowerSearch/usePowerSearchSource.js +14 -5
  165. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  166. package/dist/ProgressBar/ProgressBar.js +18 -5
  167. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  168. package/dist/Resizable/ResizeHandle.js +3 -2
  169. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  170. package/dist/SegmentedControl/SegmentedControl.js +3 -2
  171. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  172. package/dist/SelectableCard/SelectableCard.js +3 -3
  173. package/dist/Selector/Selector.d.ts.map +1 -1
  174. package/dist/Selector/Selector.js +5 -0
  175. package/dist/SideNav/SideNav.d.ts.map +1 -1
  176. package/dist/SideNav/SideNav.js +4 -3
  177. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  178. package/dist/SideNav/SideNavHeading.js +8 -7
  179. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  180. package/dist/SideNav/SideNavItem.js +4 -3
  181. package/dist/Slider/Slider.d.ts.map +1 -1
  182. package/dist/Slider/Slider.js +3 -3
  183. package/dist/Spinner/Spinner.d.ts +3 -3
  184. package/dist/Spinner/Spinner.d.ts.map +1 -1
  185. package/dist/Spinner/Spinner.js +130 -78
  186. package/dist/StatusDot/StatusDot.d.ts.map +1 -1
  187. package/dist/StatusDot/StatusDot.js +6 -1
  188. package/dist/Stepper/Step.d.ts +140 -0
  189. package/dist/Stepper/Step.d.ts.map +1 -0
  190. package/dist/Stepper/Step.js +1045 -0
  191. package/dist/Stepper/StepStatus.d.ts +24 -0
  192. package/dist/Stepper/StepStatus.d.ts.map +1 -0
  193. package/dist/Stepper/StepStatus.js +1 -0
  194. package/dist/Stepper/Stepper.d.ts +93 -0
  195. package/dist/Stepper/Stepper.d.ts.map +1 -0
  196. package/dist/Stepper/Stepper.js +184 -0
  197. package/dist/Stepper/StepperContext.d.ts +41 -0
  198. package/dist/Stepper/StepperContext.d.ts.map +1 -0
  199. package/dist/Stepper/StepperContext.js +34 -0
  200. package/dist/Stepper/index.d.ts +8 -0
  201. package/dist/Stepper/index.d.ts.map +1 -0
  202. package/dist/Stepper/index.js +7 -0
  203. package/dist/Stepper/stepper.stylex.d.ts +15 -0
  204. package/dist/Stepper/stepper.stylex.d.ts.map +1 -0
  205. package/dist/Stepper/stepper.stylex.js +20 -0
  206. package/dist/Switch/Switch.d.ts.map +1 -1
  207. package/dist/Switch/Switch.js +8 -3
  208. package/dist/TabList/Tab.d.ts +15 -2
  209. package/dist/TabList/Tab.d.ts.map +1 -1
  210. package/dist/TabList/Tab.js +32 -7
  211. package/dist/TabList/TabList.d.ts +46 -7
  212. package/dist/TabList/TabList.d.ts.map +1 -1
  213. package/dist/TabList/TabList.js +353 -23
  214. package/dist/TabList/TabListContext.d.ts +11 -1
  215. package/dist/TabList/TabListContext.d.ts.map +1 -1
  216. package/dist/TabList/TabListContext.js +11 -2
  217. package/dist/TabList/TabMenu.d.ts.map +1 -1
  218. package/dist/TabList/TabMenu.js +3 -2
  219. package/dist/TabList/index.d.ts +2 -2
  220. package/dist/TabList/index.d.ts.map +1 -1
  221. package/dist/Table/BaseTable.d.ts.map +1 -1
  222. package/dist/Table/BaseTable.js +10 -2
  223. package/dist/Table/Table.d.ts.map +1 -1
  224. package/dist/Table/Table.js +3 -6
  225. package/dist/Table/TableRow.d.ts +16 -5
  226. package/dist/Table/TableRow.d.ts.map +1 -1
  227. package/dist/Table/TableRow.js +16 -5
  228. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  229. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +20 -8
  230. package/dist/Table/plugins/pagination/useTablePagination.d.ts +3 -0
  231. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  232. package/dist/Table/plugins/pagination/useTablePagination.js +21 -4
  233. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  234. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +7 -2
  235. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -1
  236. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +10 -3
  237. package/dist/Table/plugins/selection/useTableSelection.d.ts +16 -0
  238. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  239. package/dist/Table/plugins/selection/useTableSelection.js +19 -5
  240. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  241. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +3 -4
  242. package/dist/Table/types.d.ts +22 -4
  243. package/dist/Table/types.d.ts.map +1 -1
  244. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  245. package/dist/Table/useBaseTablePlugins.js +5 -0
  246. package/dist/Text/Text.d.ts.map +1 -1
  247. package/dist/Text/Text.js +6 -2
  248. package/dist/TextArea/TextArea.d.ts.map +1 -1
  249. package/dist/TextArea/TextArea.js +9 -3
  250. package/dist/TextInput/TextInput.d.ts.map +1 -1
  251. package/dist/TextInput/TextInput.js +3 -2
  252. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  253. package/dist/TimeInput/TimeInput.js +3 -2
  254. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  255. package/dist/Timestamp/Timestamp.js +5 -3
  256. package/dist/Token/TokenLink.d.ts.map +1 -1
  257. package/dist/Token/TokenLink.js +2 -2
  258. package/dist/Tokenizer/Tokenizer.d.ts +3 -1
  259. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  260. package/dist/Tokenizer/Tokenizer.js +2 -0
  261. package/dist/Tooltip/Tooltip.d.ts +5 -0
  262. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  263. package/dist/Tooltip/useTooltip.d.ts +5 -0
  264. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  265. package/dist/Tooltip/useTooltip.js +39 -24
  266. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  267. package/dist/TopNav/TopNavHeading.js +5 -4
  268. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  269. package/dist/TopNav/TopNavMegaMenu.js +3 -3
  270. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  271. package/dist/TopNav/TopNavMenu.js +4 -3
  272. package/dist/Typeahead/BaseTypeahead.d.ts +2 -0
  273. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  274. package/dist/Typeahead/BaseTypeahead.js +68 -29
  275. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  276. package/dist/Typeahead/Typeahead.js +3 -2
  277. package/dist/astryx.css +94 -7
  278. package/dist/hooks/index.d.ts +1 -0
  279. package/dist/hooks/index.d.ts.map +1 -1
  280. package/dist/hooks/index.js +1 -0
  281. package/dist/hooks/useFocusTrap.d.ts +7 -4
  282. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  283. package/dist/hooks/useFocusTrap.js +60 -95
  284. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  285. package/dist/hooks/useHotkeys.js +3 -18
  286. package/dist/hooks/useMergedRefs.d.ts +18 -0
  287. package/dist/hooks/useMergedRefs.d.ts.map +1 -0
  288. package/dist/hooks/useMergedRefs.js +25 -0
  289. package/dist/index.d.ts +1 -0
  290. package/dist/index.d.ts.map +1 -1
  291. package/dist/index.js +1 -0
  292. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  293. package/dist/theme/derivedVarRegistry.js +24 -4
  294. package/dist/theme/syntax/tokens.d.ts +1 -1
  295. package/dist/theme/syntax/tokens.d.ts.map +1 -1
  296. package/dist/theme/syntax/tokens.js +5 -2
  297. package/dist/utils/groupItems.d.ts +5 -1
  298. package/dist/utils/groupItems.d.ts.map +1 -1
  299. package/dist/utils/groupItems.js +9 -9
  300. package/dist/utils/index.d.ts +1 -1
  301. package/dist/utils/index.d.ts.map +1 -1
  302. package/dist/utils/isApplePlatform.d.ts +18 -0
  303. package/dist/utils/isApplePlatform.d.ts.map +1 -0
  304. package/dist/utils/isApplePlatform.js +37 -0
  305. package/dist/utils/sharedResizeObserver.d.ts.map +1 -1
  306. package/dist/utils/sharedResizeObserver.js +10 -1
  307. package/dist/utils/timeParser.d.ts +1 -1
  308. package/dist/utils/timeParser.d.ts.map +1 -1
  309. package/dist/utils/timeParser.js +26 -12
  310. package/locales/en.json +31 -7
  311. package/locales/pseudo.json +18 -0
  312. package/package.json +8 -3
  313. package/src/AppShell/AppShell.tsx +3 -2
  314. package/src/AspectRatio/AspectRatio.doc.mjs +6 -3
  315. package/src/AspectRatio/AspectRatio.test.tsx +57 -10
  316. package/src/AspectRatio/AspectRatio.tsx +40 -1
  317. package/src/Avatar/Avatar.doc.mjs +14 -3
  318. package/src/Avatar/Avatar.test.tsx +97 -0
  319. package/src/Avatar/Avatar.tsx +125 -52
  320. package/src/Avatar/index.ts +2 -2
  321. package/src/AvatarGroup/AvatarGroup.doc.mjs +8 -2
  322. package/src/AvatarGroup/AvatarGroup.test.tsx +24 -0
  323. package/src/AvatarGroup/AvatarGroup.tsx +15 -6
  324. package/src/AvatarGroup/AvatarGroupContext.ts +5 -3
  325. package/src/AvatarGroup/AvatarGroupOverflow.tsx +10 -4
  326. package/src/Banner/Banner.doc.mjs +13 -11
  327. package/src/Banner/Banner.test.tsx +102 -15
  328. package/src/Banner/Banner.tsx +72 -25
  329. package/src/BottomSheet/BottomSheet.test.tsx +123 -0
  330. package/src/BottomSheet/BottomSheet.tsx +17 -4
  331. package/src/BottomSheet/BottomSheetSwitcher.tsx +3 -2
  332. package/src/Breadcrumbs/BreadcrumbItem.tsx +9 -5
  333. package/src/Button/Button.doc.mjs +22 -0
  334. package/src/Button/Button.test.tsx +13 -0
  335. package/src/Button/Button.tsx +5 -4
  336. package/src/ButtonGroup/ButtonGroup.doc.mjs +10 -1
  337. package/src/ButtonGroup/ButtonGroup.test.tsx +391 -1
  338. package/src/ButtonGroup/ButtonGroup.tsx +23 -5
  339. package/src/Calendar/Calendar.doc.mjs +4 -3
  340. package/src/Calendar/Calendar.test.tsx +52 -0
  341. package/src/Calendar/Calendar.tsx +18 -15
  342. package/src/Calendar/getInitialFocusDate.test.ts +94 -0
  343. package/src/Calendar/getInitialFocusDate.ts +93 -0
  344. package/src/Carousel/Carousel.tsx +3 -2
  345. package/src/Chat/ChatComposerInput.tsx +5 -1
  346. package/src/Chat/ChatLayout.tsx +3 -2
  347. package/src/Chat/ChatMessageList.doc.mjs +9 -1
  348. package/src/Chat/ChatMessageList.test.tsx +46 -0
  349. package/src/Chat/ChatMessageList.tsx +28 -4
  350. package/src/Chat/ChatTokenizedText.tsx +17 -10
  351. package/src/Chat/useChatComposerTokens.ts +1 -1
  352. package/src/CheckboxInput/CheckboxInput.doc.mjs +2 -0
  353. package/src/CheckboxInput/CheckboxInput.test.tsx +15 -0
  354. package/src/CheckboxInput/CheckboxInput.tsx +8 -2
  355. package/src/ClickableCard/ClickableCard.tsx +3 -2
  356. package/src/CodeBlock/CodeBlock.doc.mjs +16 -4
  357. package/src/CodeBlock/CodeBlock.test.tsx +37 -0
  358. package/src/CodeBlock/CodeBlock.tsx +20 -4
  359. package/src/CodeBlock/highlightRanges.ts +1 -1
  360. package/src/CodeBlock/highlightStyles.ts +13 -13
  361. package/src/CommandPalette/CommandPaletteInput.tsx +3 -2
  362. package/src/CommandPalette/CommandPaletteItem.tsx +3 -2
  363. package/src/ContextMenu/ContextMenu.doc.mjs +5 -0
  364. package/src/ContextMenu/ContextMenu.tsx +4 -3
  365. package/src/DateInput/DateInput.doc.mjs +16 -0
  366. package/src/DateInput/DateInput.tsx +55 -2
  367. package/src/DateInput/DateInputTouch.test.tsx +68 -9
  368. package/src/DateInput/NativeDateField.test.tsx +565 -0
  369. package/src/DateInput/NativeDateField.tsx +623 -0
  370. package/src/DateInput/TouchDateField.tsx +38 -4
  371. package/src/DateInput/index.ts +1 -0
  372. package/src/DateTimeInput/DateTimeInput.doc.mjs +18 -0
  373. package/src/DateTimeInput/DateTimeInput.test.tsx +1088 -0
  374. package/src/DateTimeInput/DateTimeInput.tsx +556 -24
  375. package/src/DateTimeInput/index.ts +1 -0
  376. package/src/Dialog/Dialog.doc.mjs +8 -3
  377. package/src/Dialog/Dialog.test.tsx +261 -1
  378. package/src/Dialog/Dialog.tsx +109 -58
  379. package/src/DropdownMenu/DropdownMenu.doc.mjs +6 -0
  380. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +8 -5
  381. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +8 -11
  382. package/src/Field/Field.doc.mjs +13 -2
  383. package/src/Field/FieldLabel.test.tsx +29 -0
  384. package/src/Field/FieldLabel.tsx +7 -0
  385. package/src/FileInput/FileInput.tsx +3 -2
  386. package/src/Heading/Heading.test.tsx +23 -1
  387. package/src/Heading/Heading.tsx +6 -2
  388. package/src/HoverCard/HoverCard.doc.mjs +13 -2
  389. package/src/HoverCard/HoverCard.test.tsx +101 -4
  390. package/src/HoverCard/HoverCard.tsx +71 -3
  391. package/src/HoverCard/useHoverCard.tsx +101 -33
  392. package/src/Indicator/CheckboxIndicator.tsx +14 -8
  393. package/src/Indicator/Indicator.doc.mjs +2 -0
  394. package/src/Item/Item.tsx +5 -4
  395. package/src/Kbd/Kbd.tsx +2 -19
  396. package/src/Layer/LayerDepthContext.tsx +62 -0
  397. package/src/Layer/index.ts +9 -0
  398. package/src/Layer/layerDismissalFamilies.test.tsx +359 -0
  399. package/src/Layer/layerDismissalInvariants.test.tsx +417 -0
  400. package/src/Layer/layerStack.ts +362 -0
  401. package/src/Layer/useLayerDismissal.test.tsx +469 -0
  402. package/src/Layer/useLayerDismissal.ts +173 -0
  403. package/src/Layout/Layout.doc.mjs +2 -2
  404. package/src/Lightbox/Lightbox.doc.mjs +12 -0
  405. package/src/Lightbox/Lightbox.tsx +124 -102
  406. package/src/Link/Link.doc.mjs +39 -0
  407. package/src/Markdown/Markdown.doc.mjs +5 -0
  408. package/src/Markdown/incremental.test.ts +16 -2
  409. package/src/Markdown/index.ts +1 -0
  410. package/src/Markdown/parser.perf.test.ts +96 -1
  411. package/src/Markdown/parser.test.ts +145 -2
  412. package/src/Markdown/parser.ts +244 -29
  413. package/src/MobileNav/MobileNav.tsx +62 -36
  414. package/src/MoreMenu/MoreMenu.doc.mjs +7 -1
  415. package/src/MultiSelector/MultiSelector.doc.mjs +27 -0
  416. package/src/MultiSelector/MultiSelector.test.tsx +106 -0
  417. package/src/MultiSelector/MultiSelector.tsx +50 -6
  418. package/src/MultiSelector/index.ts +1 -0
  419. package/src/NavIcon/NavIcon.doc.mjs +8 -2
  420. package/src/NavIcon/NavIcon.test.tsx +9 -0
  421. package/src/NavIcon/NavIcon.tsx +5 -1
  422. package/src/NavMenu/NavHeadingMenu.tsx +3 -2
  423. package/src/NumberInput/NumberInput.doc.mjs +20 -0
  424. package/src/NumberInput/NumberInput.tsx +28 -29
  425. package/src/NumberInput/numberParser.test.ts +137 -0
  426. package/src/NumberInput/numberParser.ts +346 -0
  427. package/src/Outline/Outline.tsx +3 -2
  428. package/src/OverflowList/OverflowList.tsx +3 -2
  429. package/src/Overlay/Overlay.tsx +3 -2
  430. package/src/Popover/usePopover.tsx +31 -28
  431. package/src/PowerSearch/PowerSearch.doc.mjs +160 -28
  432. package/src/PowerSearch/PowerSearch.test.tsx +240 -1
  433. package/src/PowerSearch/PowerSearch.tsx +34 -5
  434. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +201 -0
  435. package/src/PowerSearch/PowerSearchEditPopover.tsx +29 -2
  436. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +47 -1
  437. package/src/PowerSearch/PowerSearchValueEditor.tsx +15 -0
  438. package/src/PowerSearch/types.ts +2 -0
  439. package/src/PowerSearch/usePowerSearchSource.test.ts +82 -2
  440. package/src/PowerSearch/usePowerSearchSource.ts +13 -3
  441. package/src/ProgressBar/ProgressBar.doc.mjs +42 -6
  442. package/src/ProgressBar/ProgressBar.test.tsx +24 -0
  443. package/src/ProgressBar/ProgressBar.tsx +28 -8
  444. package/src/Resizable/ResizeHandle.tsx +3 -2
  445. package/src/SegmentedControl/SegmentedControl.tsx +3 -2
  446. package/src/SelectableCard/SelectableCard.tsx +3 -2
  447. package/src/Selector/Selector.doc.mjs +12 -1
  448. package/src/Selector/Selector.test.tsx +45 -1
  449. package/src/Selector/Selector.tsx +11 -1
  450. package/src/SideNav/SideNav.doc.mjs +6 -6
  451. package/src/SideNav/SideNav.tsx +4 -2
  452. package/src/SideNav/SideNavHeading.tsx +12 -12
  453. package/src/SideNav/SideNavItem.tsx +4 -2
  454. package/src/Slider/Slider.tsx +3 -2
  455. package/src/Spinner/Spinner.test.tsx +147 -1
  456. package/src/Spinner/Spinner.tsx +120 -101
  457. package/src/StatusDot/StatusDot.doc.mjs +8 -2
  458. package/src/StatusDot/StatusDot.test.tsx +9 -0
  459. package/src/StatusDot/StatusDot.tsx +7 -1
  460. package/src/Stepper/Step.doc.mjs +98 -0
  461. package/src/Stepper/Step.tsx +1638 -0
  462. package/src/Stepper/StepStatus.ts +26 -0
  463. package/src/Stepper/Stepper.doc.mjs +370 -0
  464. package/src/Stepper/Stepper.test.tsx +1024 -0
  465. package/src/Stepper/Stepper.tsx +258 -0
  466. package/src/Stepper/StepperContext.ts +71 -0
  467. package/src/Stepper/index.ts +18 -0
  468. package/src/Stepper/stepper.stylex.ts +19 -0
  469. package/src/Switch/Switch.doc.mjs +2 -0
  470. package/src/Switch/Switch.test.tsx +10 -0
  471. package/src/Switch/Switch.tsx +6 -2
  472. package/src/TabList/Tab.doc.mjs +13 -1
  473. package/src/TabList/Tab.tsx +67 -7
  474. package/src/TabList/TabList.doc.mjs +22 -6
  475. package/src/TabList/TabList.test.tsx +559 -0
  476. package/src/TabList/TabList.tsx +490 -22
  477. package/src/TabList/TabListContext.ts +13 -2
  478. package/src/TabList/TabMenu.tsx +3 -2
  479. package/src/TabList/index.ts +6 -2
  480. package/src/Table/BaseTable.tsx +12 -4
  481. package/src/Table/Table.doc.mjs +9 -2
  482. package/src/Table/Table.test.tsx +132 -14
  483. package/src/Table/Table.tsx +3 -8
  484. package/src/Table/TableBody.doc.mjs +40 -0
  485. package/src/Table/TableFooter.doc.mjs +40 -0
  486. package/src/Table/TableHeader.doc.mjs +40 -0
  487. package/src/Table/TableRow.tsx +16 -5
  488. package/src/Table/plugins/groupedRows/useTableGroupedRows-perf.test.tsx +112 -0
  489. package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +100 -0
  490. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +17 -8
  491. package/src/Table/plugins/pagination/useTablePagination.test.tsx +34 -4
  492. package/src/Table/plugins/pagination/useTablePagination.tsx +35 -3
  493. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +9 -2
  494. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +13 -7
  495. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +12 -3
  496. package/src/Table/plugins/selection/useTableSelection.test.tsx +76 -0
  497. package/src/Table/plugins/selection/useTableSelection.tsx +40 -7
  498. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +1 -2
  499. package/src/Table/types.ts +22 -4
  500. package/src/Table/useBaseTablePlugins.ts +5 -0
  501. package/src/Table/useTableGroupedRows.doc.mjs +5 -4
  502. package/src/Table/useTablePagination.doc.mjs +7 -0
  503. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  504. package/src/Table/useTableSelection.doc.mjs +31 -0
  505. package/src/Text/Text.test.tsx +23 -1
  506. package/src/Text/Text.tsx +6 -2
  507. package/src/TextArea/TextArea.doc.mjs +8 -2
  508. package/src/TextArea/TextArea.test.tsx +11 -0
  509. package/src/TextArea/TextArea.tsx +15 -8
  510. package/src/TextInput/TextInput.doc.mjs +10 -0
  511. package/src/TextInput/TextInput.tsx +3 -2
  512. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  513. package/src/TimeInput/TimeInput.tsx +2 -2
  514. package/src/Timestamp/Timestamp.tsx +4 -2
  515. package/src/Toast/Toast.doc.mjs +6 -0
  516. package/src/Token/TokenLink.tsx +2 -2
  517. package/src/Tokenizer/Tokenizer.doc.mjs +27 -0
  518. package/src/Tokenizer/Tokenizer.tsx +4 -0
  519. package/src/Toolbar/Toolbar.doc.mjs +5 -0
  520. package/src/Tooltip/Tooltip.doc.mjs +5 -0
  521. package/src/Tooltip/Tooltip.test.tsx +32 -0
  522. package/src/Tooltip/Tooltip.tsx +5 -0
  523. package/src/Tooltip/useTooltip.tsx +47 -24
  524. package/src/TopNav/TopNavHeading.tsx +5 -4
  525. package/src/TopNav/TopNavMegaMenu.tsx +3 -2
  526. package/src/TopNav/TopNavMenu.tsx +4 -3
  527. package/src/Typeahead/BaseTypeahead.doc.mjs +11 -0
  528. package/src/Typeahead/BaseTypeahead.tsx +85 -33
  529. package/src/Typeahead/Typeahead.doc.mjs +5 -0
  530. package/src/Typeahead/Typeahead.test.tsx +49 -0
  531. package/src/Typeahead/Typeahead.tsx +3 -2
  532. package/src/hooks/index.ts +2 -0
  533. package/src/hooks/useContainerReveal.doc.mjs +2 -2
  534. package/src/hooks/useFocusTrap.test.tsx +14 -0
  535. package/src/hooks/useFocusTrap.ts +62 -111
  536. package/src/hooks/useFocusTrapEscapeShim.test.tsx +165 -0
  537. package/src/hooks/useHotkeys.ts +3 -18
  538. package/src/hooks/useMergedRefs.doc.mjs +79 -0
  539. package/src/hooks/useMergedRefs.test.tsx +62 -0
  540. package/src/hooks/useMergedRefs.ts +36 -0
  541. package/src/index.ts +1 -0
  542. package/src/theme/MediaTheme.doc.mjs +5 -5
  543. package/src/theme/derivedVarRegistry.test.ts +19 -3
  544. package/src/theme/derivedVarRegistry.ts +23 -4
  545. package/src/theme/generateThemeRules.test.ts +37 -0
  546. package/src/theme/syntax/tokens.ts +5 -2
  547. package/src/utils/groupItems.ts +12 -6
  548. package/src/utils/index.ts +1 -1
  549. package/src/utils/isApplePlatform.test.ts +61 -0
  550. package/src/utils/isApplePlatform.ts +37 -0
  551. package/src/utils/sharedResizeObserver.ts +10 -2
  552. package/src/utils/timeParser.test.ts +6 -1
  553. package/src/utils/timeParser.ts +35 -13
  554. package/src/__tests__/inputWidthContract.test.tsx +0 -69
@@ -476,20 +476,17 @@ export function DropdownMenuSubMenu(
476
476
  );
477
477
 
478
478
  const endAffordance = hasSpinner ? (
479
- <span
480
- {...mergeProps(
481
- themeProps('dropdown-menu-indicator-icon'),
482
- stylex.props(triggerStyles.caret),
483
- )}>
479
+ <span {...stylex.props(triggerStyles.caret)}>
484
480
  <Spinner size="sm" />
485
481
  </span>
486
482
  ) : (
487
- <span
488
- {...mergeProps(
489
- themeProps('dropdown-menu-indicator-icon'),
490
- stylex.props(triggerStyles.caret),
491
- )}>
492
- <Icon icon="chevronRight" size="sm" color="secondary" />
483
+ <span {...stylex.props(triggerStyles.caret)}>
484
+ <Icon
485
+ icon="chevronRight"
486
+ size="sm"
487
+ color="secondary"
488
+ {...themeProps('dropdown-menu-indicator-icon')}
489
+ />
493
490
  </span>
494
491
  );
495
492
 
@@ -39,8 +39,8 @@ export const docs = {
39
39
  ],
40
40
  vars: [
41
41
  {name: '--_field-radius', description: 'Border radius of input fields', default: 'var(--radius-element)', private: true},
42
- {name: '--_input-clear-hit-inset', description: 'Outset of the clear (\u2715) button\u2019s invisible hit area, applied to a ::after overlay. 0 on a fine pointer; negative on a coarse one, which grows the 20px button to the 24px touch target without changing what is drawn.', default: '0px', private: true},
43
- {name: '--_input-clear-hit-content', description: 'Whether the clear (\u2715) button\u2019s invisible hit overlay exists. `none` on a fine pointer, so no ::after is generated and hover still reaches the glyph; `""` on a coarse one, where the overlay provides the 24px touch target.', default: 'none', private: true},
42
+ {name: '--_input-clear-hit-inset', description: 'Outset of the clear (\u2715) button\'s invisible hit area, applied to a ::after overlay. 0 on a fine pointer; negative on a coarse one, which grows the 20px button to the 24px touch target without changing what is drawn.', default: '0px', private: true},
43
+ {name: '--_input-clear-hit-content', description: 'Whether the clear (\u2715) button\'s invisible hit overlay exists. `none` on a fine pointer, so no ::after is generated and hover still reaches the glyph; `""` on a coarse one, where the overlay provides the 24px touch target.', default: 'none', private: true},
44
44
  ],
45
45
  derived: [
46
46
  {property: 'borderRadius', vars: ['--_field-radius']},
@@ -60,6 +60,17 @@ export const docs = {
60
60
  description: 'ID for the input element (used for the label htmlFor attribute).',
61
61
  required: true,
62
62
  },
63
+ {
64
+ name: 'labelID',
65
+ type: 'string',
66
+ description: 'ID applied to the label element itself for group accessibility.',
67
+ },
68
+ {
69
+ name: 'isGroupLabel',
70
+ type: 'boolean',
71
+ description: 'Renders the label as a span for control groups (radiogroup, checkbox list).',
72
+ default: 'false',
73
+ },
63
74
  {
64
75
  name: 'children',
65
76
  type: 'ReactNode',
@@ -15,6 +15,7 @@ import userEvent from '@testing-library/user-event';
15
15
  import {TestIcon} from '../__tests__/TestIcon';
16
16
  import {InternationalizationProvider} from '../i18n';
17
17
  import {FieldLabel} from './FieldLabel';
18
+ import {themeProps} from '../utils/themeProps';
18
19
 
19
20
  describe('FieldLabel', () => {
20
21
  it('renders label text', () => {
@@ -197,4 +198,32 @@ describe('FieldLabel', () => {
197
198
  expect(description.closest('label')).toBeNull();
198
199
  });
199
200
  });
201
+
202
+ describe('a control can name its own label target', () => {
203
+ it('composes a passed target onto the field-label one', () => {
204
+ // How CheckboxInput and Switch reach their label: the control spreads
205
+ // its own themeProps in, and both classes end up on the same element,
206
+ // so a theme can style every label or just this kind.
207
+ render(
208
+ <FieldLabel
209
+ {...themeProps('checkbox-label')}
210
+ label="Notify me"
211
+ inputID="notify-input"
212
+ />,
213
+ );
214
+ const label = screen.getByText('Notify me').closest('label');
215
+ expect(label).toHaveClass('astryx-field-label');
216
+ expect(label).toHaveClass('astryx-checkbox-label');
217
+ });
218
+
219
+ it('describes no placement of its own', () => {
220
+ // The label cannot know how its caller arranged it, so it says nothing:
221
+ // a `data-layout` here would be a claim that is wrong for at least one
222
+ // caller (Field's horizontal-labels puts a label beside its control),
223
+ // and a consumer could set it untruthfully.
224
+ render(<FieldLabel label="Email" inputID="email-input" />);
225
+ const label = screen.getByText('Email').closest('label');
226
+ expect(label).not.toHaveAttribute('data-layout');
227
+ });
228
+ });
200
229
  });
@@ -285,6 +285,13 @@ export function FieldLabel({
285
285
  htmlFor={isGroupLabel ? undefined : inputID}
286
286
  {...rest}
287
287
  {...mergeProps(
288
+ // A control that knows what kind of label this is passes its own
289
+ // target down (CheckboxInput's `checkbox-label`, Switch's
290
+ // `switch-label`); it arrives as `className` and composes onto this
291
+ // one. The label itself does not describe its own placement — it
292
+ // cannot know it, and any encoding it guessed would be wrong for a
293
+ // caller that arranges labels differently (Field's
294
+ // `horizontal-labels`).
288
295
  themeProps('field-label'),
289
296
  stylex.props(
290
297
  styles.label,
@@ -54,13 +54,14 @@ export type {
54
54
  InputStatus as FileInputStatus,
55
55
  InputStatusType as FileInputStatusType,
56
56
  } from '../Field';
57
- import {mergeProps, mergeRefs} from '../utils';
57
+ import {mergeProps} from '../utils';
58
58
  import type {BaseProps} from '../BaseProps';
59
59
  import type {SizeValue} from '../utils/types';
60
60
  import {themeProps} from '../utils/themeProps';
61
61
  import {useTranslator} from '../i18n';
62
62
  import type {TranslatorFn} from '../i18n';
63
63
 
64
+ import {useMergedRefs} from '../hooks/useMergedRefs';
64
65
  function formatFileSize(bytes: number): string {
65
66
  if (bytes < 1024) {
66
67
  return `${bytes} B`;
@@ -827,7 +828,7 @@ export function FileInput({
827
828
  </VisuallyHidden>
828
829
  <input
829
830
  {...rest}
830
- ref={mergeRefs(ref, inputRef)}
831
+ ref={useMergedRefs(ref, inputRef)}
831
832
  id={id}
832
833
  type="file"
833
834
  accept={accept}
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import {render, screen} from '@testing-library/react';
9
- import {describe, it, expect} from 'vitest';
9
+ import {describe, it, expect, vi} from 'vitest';
10
10
  import {Heading} from './Heading';
11
11
 
12
12
  describe('Heading', () => {
@@ -16,6 +16,28 @@ describe('Heading', () => {
16
16
  expect(screen.getByText('Page Title')).toBeInTheDocument();
17
17
  });
18
18
 
19
+ it('keeps a forwarded ref attached across rerenders', () => {
20
+ const ref = vi.fn();
21
+ const {rerender} = render(
22
+ <Heading ref={ref} level={1}>
23
+ Before
24
+ </Heading>,
25
+ );
26
+
27
+ const element = screen.getByText('Before');
28
+ expect(ref).toHaveBeenLastCalledWith(element);
29
+ ref.mockClear();
30
+
31
+ rerender(
32
+ <Heading ref={ref} level={1}>
33
+ After
34
+ </Heading>,
35
+ );
36
+
37
+ expect(ref).not.toHaveBeenCalled();
38
+ expect(screen.getByText('After')).toBe(element);
39
+ });
40
+
19
41
  it('renders h1 for level 1', () => {
20
42
  render(<Heading level={1}>H1</Heading>);
21
43
  const element = screen.getByText('H1');
@@ -43,7 +43,8 @@ import {
43
43
  import {useTruncation} from '../Text/useTruncation';
44
44
  import {resolveStyleColor} from '../Text/Text';
45
45
  import type {LayerPlacement} from '../Layer';
46
- import {mergeProps, mergeRefs} from '../utils';
46
+ import {mergeProps} from '../utils';
47
+ import {useMergedRefs} from '../hooks/useMergedRefs';
47
48
  import type {BaseProps} from '../BaseProps';
48
49
  import {themeProps} from '../utils/themeProps';
49
50
 
@@ -242,13 +243,16 @@ export function Heading({
242
243
  // Ref for the heading element (used as tooltip anchor)
243
244
  const headingRef = useRef<HTMLHeadingElement>(null);
244
245
 
246
+ // Keep the merged ref stable across rerenders.
247
+ const mergedRef = useMergedRefs(ref, truncation.ref, headingRef);
248
+
245
249
  // Build inline style for -webkit-line-clamp (dynamic value)
246
250
  const inlineStyle = maxLines > 1 ? {WebkitLineClamp: maxLines} : undefined;
247
251
 
248
252
  return (
249
253
  <>
250
254
  <Component
251
- ref={mergeRefs(ref, truncation.ref, headingRef)}
255
+ ref={mergedRef}
252
256
  {...mergeProps(
253
257
  themeProps('heading', {level, color, ...(type && {type})}),
254
258
  stylex.props(
@@ -16,7 +16,10 @@ export const docs = {
16
16
  },
17
17
  theming: {
18
18
  targets: [
19
- {className: 'astryx-hovercard'},
19
+ {className: 'astryx-hover-card'},
20
+ // Still emitted beside the names above, so themes written against
21
+ // them keep working. Drop in the next major.
22
+ {className: 'astryx-hovercard', deprecatedFor: 'hover-card'},
20
23
  ],
21
24
  vars: [
22
25
  {name: '--_hovercard-radius', description: 'Border radius of the hover card', default: 'var(--radius-container)', private: true},
@@ -109,6 +112,11 @@ export const docs = {
109
112
  type: 'boolean',
110
113
  description: 'Whether the hover card should be shown on mount. Still dismissible.',
111
114
  },
115
+ {
116
+ name: 'isOpen',
117
+ type: 'boolean',
118
+ description: 'Controlled open state for the hover card.',
119
+ },
112
120
  ],
113
121
  },
114
122
  ],
@@ -139,7 +147,10 @@ export const docsZh = {
139
147
  displayName: 'Hover Card',
140
148
  theming: {
141
149
  targets: [
142
- {className: 'astryx-hovercard'},
150
+ {className: 'astryx-hover-card'},
151
+ // Still emitted beside the names above, so themes written against
152
+ // them keep working. Drop in the next major.
153
+ {className: 'astryx-hovercard', deprecatedFor: 'hover-card'},
143
154
  ],
144
155
  vars: [
145
156
  {name: '--_hovercard-radius', description: 'Border radius of the hover card', default: 'var(--radius-container)', private: true},
@@ -365,7 +365,42 @@ describe('HoverCard', () => {
365
365
  );
366
366
  });
367
367
 
368
- it('injects aria-describedby on trigger', () => {
368
+ it('advertises a dialog popup on the trigger when labelled', () => {
369
+ render(
370
+ <HoverCard content={<span>Card content</span>} label="Profile actions">
371
+ <button type="button">Trigger</button>
372
+ </HoverCard>,
373
+ );
374
+ const trigger = screen.getByRole('button', {name: 'Trigger'});
375
+ expect(trigger).toHaveAttribute('aria-haspopup', 'dialog');
376
+ // While closed, the layer is not in the DOM, so aria-controls must not
377
+ // point at a missing id (see DateInput). It is set once the card opens.
378
+ expect(trigger).not.toHaveAttribute('aria-controls');
379
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
380
+ expect(trigger).not.toHaveAttribute('aria-describedby');
381
+ });
382
+
383
+ it('merges existing popup attributes on the trigger when labelled', () => {
384
+ render(
385
+ <HoverCard content={<span>Card content</span>} label="Profile actions">
386
+ <button
387
+ type="button"
388
+ aria-haspopup="menu"
389
+ aria-controls="menu-id"
390
+ aria-expanded="true">
391
+ Trigger
392
+ </button>
393
+ </HoverCard>,
394
+ );
395
+ const trigger = screen.getByRole('button', {name: 'Trigger'});
396
+ // The hover card's dialog popup is advertised, but the trigger's own
397
+ // popup semantics are preserved rather than overwritten.
398
+ expect(trigger).toHaveAttribute('aria-haspopup', 'dialog');
399
+ expect(trigger.getAttribute('aria-controls')).toContain('menu-id');
400
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
401
+ });
402
+
403
+ it('keeps aria-describedby on the trigger when no label is provided', () => {
369
404
  render(
370
405
  <HoverCard content={<span>Card content</span>}>
371
406
  <button type="button">Trigger</button>
@@ -373,9 +408,10 @@ describe('HoverCard', () => {
373
408
  );
374
409
  const trigger = screen.getByRole('button', {name: 'Trigger'});
375
410
  expect(trigger).toHaveAttribute('aria-describedby');
411
+ expect(trigger).not.toHaveAttribute('aria-haspopup');
376
412
  });
377
413
 
378
- it('merges existing aria-describedby', () => {
414
+ it('preserves existing aria-describedby when no label is provided', () => {
379
415
  render(
380
416
  <HoverCard content={<span>Card content</span>}>
381
417
  <button type="button" aria-describedby="existing-id">
@@ -388,6 +424,34 @@ describe('HoverCard', () => {
388
424
  expect(describedBy).toContain('existing-id');
389
425
  });
390
426
 
427
+ it('updates aria-expanded when the labelled hover card opens and closes', async () => {
428
+ render(
429
+ <HoverCard
430
+ content={<span>Card content</span>}
431
+ label="Profile actions"
432
+ delay={0}>
433
+ <button type="button">Trigger</button>
434
+ </HoverCard>,
435
+ );
436
+ const trigger = screen.getByRole('button', {name: 'Trigger'});
437
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
438
+ expect(trigger).not.toHaveAttribute('aria-controls');
439
+
440
+ fireEvent.mouseEnter(trigger);
441
+ await waitFor(() => {
442
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
443
+ // The layer is in the DOM now, so aria-controls points at it.
444
+ expect(trigger).toHaveAttribute('aria-controls');
445
+ });
446
+
447
+ fireEvent.mouseLeave(trigger);
448
+ await waitFor(() => {
449
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
450
+ // The layer is gone from the DOM, so aria-controls is cleared.
451
+ expect(trigger).not.toHaveAttribute('aria-controls');
452
+ });
453
+ });
454
+
391
455
  it('calls onOpenChange(true) when shown', async () => {
392
456
  const onOpenChange = vi.fn();
393
457
  render(
@@ -429,16 +493,32 @@ describe('HoverCard', () => {
429
493
 
430
494
  it('supports text-only children with inline wrapper', () => {
431
495
  render(
432
- <HoverCard content={<span>Card content</span>}>
496
+ <HoverCard content={<span>Card content</span>} label="Profile actions">
433
497
  Just text, no element
434
498
  </HoverCard>,
435
499
  );
436
500
  // Text should be rendered
437
501
  expect(screen.getByText('Just text, no element')).toBeInTheDocument();
438
- // Should have aria-describedby on the wrapper span
502
+ // When labelled, the wrapper advertises a dialog popup.
503
+ const wrapper = screen.getByText('Just text, no element');
504
+ expect(wrapper.tagName).toBe('SPAN');
505
+ expect(wrapper).toHaveAttribute('aria-haspopup', 'dialog');
506
+ expect(wrapper).toHaveAttribute('aria-expanded', 'false');
507
+ // While closed, the layer is not in the DOM, so aria-controls is unset.
508
+ expect(wrapper).not.toHaveAttribute('aria-controls');
509
+ expect(wrapper).not.toHaveAttribute('aria-describedby');
510
+ });
511
+
512
+ it('keeps aria-describedby on text-only children when no label is provided', () => {
513
+ render(
514
+ <HoverCard content={<span>Card content</span>}>
515
+ Just text, no element
516
+ </HoverCard>,
517
+ );
439
518
  const wrapper = screen.getByText('Just text, no element');
440
519
  expect(wrapper.tagName).toBe('SPAN');
441
520
  expect(wrapper).toHaveAttribute('aria-describedby');
521
+ expect(wrapper).not.toHaveAttribute('aria-haspopup');
442
522
  });
443
523
 
444
524
  describe('isDefaultOpen', () => {
@@ -994,3 +1074,20 @@ describe('HoverCard', () => {
994
1074
  });
995
1075
  });
996
1076
  });
1077
+
1078
+ describe('HoverCard theme target names', () => {
1079
+ it('renders the deprecated class beside the current one on the card surface', async () => {
1080
+ render(
1081
+ <HoverCard content={<span>Card content</span>} delay={0}>
1082
+ <button type="button">Trigger</button>
1083
+ </HoverCard>,
1084
+ );
1085
+ fireEvent.mouseEnter(screen.getByRole('button', {name: 'Trigger'}));
1086
+
1087
+ await waitFor(() => {
1088
+ const layer = screen.getByText('Card content').closest('[popover]');
1089
+ expect(layer).toHaveClass('astryx-hover-card');
1090
+ expect(layer).toHaveClass('astryx-hovercard');
1091
+ });
1092
+ });
1093
+ });
@@ -144,6 +144,12 @@ export interface HoverCardProps extends Pick<
144
144
  * - `true`: force-show the hover card (hover/focus hide is suppressed)
145
145
  * - `false`: force-hide the hover card
146
146
  * - `undefined`: uncontrolled — hover/focus triggers manage visibility
147
+ *
148
+ * A controlled hover card still takes Escape when it is the top-most layer,
149
+ * and answers by calling `onOpenChange(false)` without hiding itself —
150
+ * closing is your update's decision, exactly as for a controlled Dialog.
151
+ * Ignore the call and the card stays, and so does the press: nothing
152
+ * underneath dismisses.
147
153
  */
148
154
  isOpen?: boolean;
149
155
 
@@ -253,7 +259,59 @@ export function HoverCard({
253
259
  // Use combined ref for position + interaction
254
260
  hoverCard.ref(firstChild);
255
261
 
256
- // Set aria-describedby, merging with existing
262
+ if (label) {
263
+ // When named, the hover card is a dialog. The trigger should advertise the
264
+ // popup relationship with aria-haspopup/aria-expanded, not describe the
265
+ // trigger with the dialog's content (aria-describedby is for plain-text
266
+ // descriptions, not navigable regions). See #5049.
267
+ //
268
+ // Merge rather than overwrite: the trigger may already carry its own
269
+ // popup semantics (e.g. a menu button wrapped in a labelled HoverCard),
270
+ // so preserve the existing values and restore them on cleanup.
271
+ const existingHaspopup = firstChild.getAttribute('aria-haspopup');
272
+ const existingControls = firstChild.getAttribute('aria-controls');
273
+ const existingExpanded = firstChild.getAttribute('aria-expanded');
274
+
275
+ firstChild.setAttribute('aria-haspopup', 'dialog');
276
+ // Only point aria-controls at the layer while it is open and in the DOM.
277
+ // While closed, useLayer leaves only an inert <template> marker, so the
278
+ // id would reference nothing. DateInput gates it the same way. The
279
+ // trigger's own aria-controls (if any) is preserved either way.
280
+ if (hoverCard.isOpen) {
281
+ firstChild.setAttribute(
282
+ 'aria-controls',
283
+ mergeIds(existingControls, hoverCard.id) ?? '',
284
+ );
285
+ } else if (existingControls) {
286
+ firstChild.setAttribute('aria-controls', existingControls);
287
+ } else {
288
+ firstChild.removeAttribute('aria-controls');
289
+ }
290
+ firstChild.setAttribute('aria-expanded', String(hoverCard.isOpen));
291
+
292
+ return () => {
293
+ hoverCard.ref(null);
294
+ if (existingHaspopup) {
295
+ firstChild.setAttribute('aria-haspopup', existingHaspopup);
296
+ } else {
297
+ firstChild.removeAttribute('aria-haspopup');
298
+ }
299
+ if (existingControls) {
300
+ firstChild.setAttribute('aria-controls', existingControls);
301
+ } else {
302
+ firstChild.removeAttribute('aria-controls');
303
+ }
304
+ if (existingExpanded) {
305
+ firstChild.setAttribute('aria-expanded', existingExpanded);
306
+ } else {
307
+ firstChild.removeAttribute('aria-expanded');
308
+ }
309
+ };
310
+ }
311
+
312
+ // Unnamed fallback: the popup remains role="group", which is not a dialog,
313
+ // so keep the previous description relationship until a naming decision is
314
+ // made for the no-label case (tracked in #5049).
257
315
  const existingDescribedBy = firstChild.getAttribute('aria-describedby');
258
316
  firstChild.setAttribute(
259
317
  'aria-describedby',
@@ -268,7 +326,14 @@ export function HoverCard({
268
326
  firstChild.removeAttribute('aria-describedby');
269
327
  }
270
328
  };
271
- }, [textOnly, hoverCard.ref, hoverCard.describedBy]);
329
+ }, [
330
+ textOnly,
331
+ label,
332
+ hoverCard.ref,
333
+ hoverCard.id,
334
+ hoverCard.isOpen,
335
+ hoverCard.describedBy,
336
+ ]);
272
337
 
273
338
  // While closed, useLayer leaves only an inert <template> marker at this JSX
274
339
  // position. When the card needs to open, it uses that marker to keep the
@@ -287,7 +352,10 @@ export function HoverCard({
287
352
  <span
288
353
  ref={hoverCard.ref}
289
354
  tabIndex={0}
290
- aria-describedby={hoverCard.describedBy}
355
+ aria-haspopup={label ? 'dialog' : undefined}
356
+ aria-controls={label && hoverCard.isOpen ? hoverCard.id : undefined}
357
+ aria-expanded={label ? hoverCard.isOpen : undefined}
358
+ aria-describedby={label ? undefined : hoverCard.describedBy}
291
359
  {...stylex.props(
292
360
  styles.wrapperInline,
293
361
  showHoverIndication && styles.hoverIndication,