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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (414) hide show
  1. package/dist/Avatar/Avatar.d.ts +6 -1
  2. package/dist/Avatar/Avatar.d.ts.map +1 -1
  3. package/dist/Avatar/Avatar.js +46 -12
  4. package/dist/Avatar/AvatarStatusDot.d.ts +6 -4
  5. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  6. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  7. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -4
  8. package/dist/Breadcrumbs/index.d.ts +1 -0
  9. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  10. package/dist/Breadcrumbs/index.js +2 -1
  11. package/dist/Calendar/Calendar.d.ts.map +1 -1
  12. package/dist/Calendar/Calendar.js +50 -9
  13. package/dist/Calendar/dayCellUtils.d.ts +4 -4
  14. package/dist/Calendar/dayCellUtils.js +2 -2
  15. package/dist/Calendar/styles.d.ts +30 -31
  16. package/dist/Calendar/styles.d.ts.map +1 -1
  17. package/dist/Calendar/styles.js +30 -31
  18. package/dist/Card/Card.js +1 -1
  19. package/dist/Carousel/Carousel.d.ts.map +1 -1
  20. package/dist/Carousel/Carousel.js +22 -12
  21. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  22. package/dist/Chat/ChatDictationButton.js +3 -1
  23. package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
  24. package/dist/Chat/ChatMessageBubble.js +8 -8
  25. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  26. package/dist/Chat/ChatToolCalls.js +10 -3
  27. package/dist/CheckboxInput/CheckboxInput.js +4 -4
  28. package/dist/Citation/Citation.d.ts +22 -1
  29. package/dist/Citation/Citation.d.ts.map +1 -1
  30. package/dist/Citation/Citation.js +20 -6
  31. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  32. package/dist/CodeBlock/CodeBlock.js +13 -7
  33. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  34. package/dist/Collapsible/Collapsible.js +3 -1
  35. package/dist/CommandPalette/CommandPalette.d.ts +1 -1
  36. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  37. package/dist/CommandPalette/CommandPalette.js +42 -3
  38. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  39. package/dist/ContextMenu/ContextMenu.js +14 -5
  40. package/dist/ContextMenu/index.d.ts +1 -0
  41. package/dist/ContextMenu/index.d.ts.map +1 -1
  42. package/dist/ContextMenu/index.js +3 -1
  43. package/dist/DateInput/DateInput.d.ts +1 -0
  44. package/dist/DateInput/DateInput.d.ts.map +1 -1
  45. package/dist/DateInput/DateInput.js +31 -19
  46. package/dist/DateRangeInput/DateRangeInput.d.ts +1 -0
  47. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  48. package/dist/DateRangeInput/DateRangeInput.js +20 -19
  49. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  50. package/dist/DateTimeInput/DateTimeInput.js +23 -18
  51. package/dist/Divider/Divider.d.ts +2 -2
  52. package/dist/Divider/Divider.d.ts.map +1 -1
  53. package/dist/Divider/Divider.js +13 -0
  54. package/dist/DropdownMenu/DropdownMenu.d.ts +6 -0
  55. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  56. package/dist/DropdownMenu/DropdownMenu.js +22 -5
  57. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  58. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +4 -1
  59. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  60. package/dist/DropdownMenu/DropdownMenuItem.js +4 -1
  61. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  62. package/dist/DropdownMenu/DropdownMenuRadioItem.js +4 -1
  63. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +104 -0
  64. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -0
  65. package/dist/DropdownMenu/DropdownMenuSubMenu.js +427 -0
  66. package/dist/DropdownMenu/index.d.ts +10 -0
  67. package/dist/DropdownMenu/index.d.ts.map +1 -1
  68. package/dist/DropdownMenu/index.js +13 -0
  69. package/dist/DropdownMenu/menuItemHover.d.ts +23 -0
  70. package/dist/DropdownMenu/menuItemHover.d.ts.map +1 -0
  71. package/dist/DropdownMenu/menuItemHover.js +33 -0
  72. package/dist/DropdownMenu/menuItemRoles.d.ts +8 -0
  73. package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -1
  74. package/dist/DropdownMenu/menuItemRoles.js +10 -1
  75. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  76. package/dist/DropdownMenu/renderDropdownItems.js +21 -6
  77. package/dist/Field/Field.d.ts +3 -1
  78. package/dist/Field/Field.d.ts.map +1 -1
  79. package/dist/Field/Field.js +4 -1
  80. package/dist/Field/inputStyles.stylex.d.ts +1 -0
  81. package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
  82. package/dist/Field/inputStyles.stylex.js +1 -0
  83. package/dist/FieldStatus/FieldStatus.d.ts +4 -1
  84. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  85. package/dist/FieldStatus/FieldStatus.js +4 -2
  86. package/dist/FileInput/FileInput.d.ts +1 -0
  87. package/dist/FileInput/FileInput.d.ts.map +1 -1
  88. package/dist/FileInput/FileInput.js +91 -65
  89. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  90. package/dist/HoverCard/useHoverCard.js +10 -4
  91. package/dist/Icon/Icon.d.ts +15 -1
  92. package/dist/Icon/Icon.d.ts.map +1 -1
  93. package/dist/Icon/Icon.js +19 -3
  94. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  95. package/dist/Lightbox/Lightbox.js +15 -9
  96. package/dist/List/List.d.ts.map +1 -1
  97. package/dist/List/List.js +6 -3
  98. package/dist/Markdown/Markdown.d.ts.map +1 -1
  99. package/dist/Markdown/Markdown.js +7 -0
  100. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  101. package/dist/MultiSelector/MultiSelector.js +65 -40
  102. package/dist/NumberInput/NumberInput.d.ts +1 -0
  103. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  104. package/dist/NumberInput/NumberInput.js +20 -17
  105. package/dist/Outline/Outline.js +2 -2
  106. package/dist/RadioList/RadioList.d.ts +2 -2
  107. package/dist/RadioList/RadioListItem.js +4 -4
  108. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  109. package/dist/Resizable/ResizeHandle.js +49 -19
  110. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
  111. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  112. package/dist/SegmentedControl/SegmentedControlItem.js +2 -1
  113. package/dist/Selector/Selector.d.ts.map +1 -1
  114. package/dist/Selector/Selector.js +58 -16
  115. package/dist/SideNav/SideNav.d.ts.map +1 -1
  116. package/dist/SideNav/SideNav.js +5 -5
  117. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  118. package/dist/SideNav/SideNavCollapseButton.js +13 -10
  119. package/dist/SideNav/SideNavItem.d.ts +1 -1
  120. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  121. package/dist/SideNav/SideNavItem.js +12 -10
  122. package/dist/Slider/Slider.d.ts.map +1 -1
  123. package/dist/Slider/Slider.js +87 -34
  124. package/dist/Spinner/Spinner.d.ts +3 -2
  125. package/dist/Spinner/Spinner.d.ts.map +1 -1
  126. package/dist/Spinner/Spinner.js +11 -2
  127. package/dist/Switch/Switch.d.ts +8 -1
  128. package/dist/Switch/Switch.d.ts.map +1 -1
  129. package/dist/Switch/Switch.js +160 -91
  130. package/dist/TabList/TabList.d.ts +2 -11
  131. package/dist/TabList/TabList.d.ts.map +1 -1
  132. package/dist/TabList/TabList.js +5 -9
  133. package/dist/TabList/TabListContext.d.ts +0 -5
  134. package/dist/TabList/TabListContext.d.ts.map +1 -1
  135. package/dist/TabList/TabListContext.js +0 -5
  136. package/dist/Table/BaseTable.d.ts.map +1 -1
  137. package/dist/Table/BaseTable.js +30 -4
  138. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  139. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +15 -11
  140. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  141. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +36 -26
  142. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  143. package/dist/Table/plugins/selection/useTableSelection.js +3 -0
  144. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  145. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +14 -6
  146. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  147. package/dist/Table/plugins/tree/useTableTreeData.js +29 -23
  148. package/dist/Table/types.d.ts +32 -0
  149. package/dist/Table/types.d.ts.map +1 -1
  150. package/dist/TextArea/TextArea.d.ts +1 -0
  151. package/dist/TextArea/TextArea.d.ts.map +1 -1
  152. package/dist/TextArea/TextArea.js +16 -19
  153. package/dist/TextInput/TextInput.d.ts +1 -0
  154. package/dist/TextInput/TextInput.d.ts.map +1 -1
  155. package/dist/TextInput/TextInput.js +18 -17
  156. package/dist/TimeInput/TimeInput.d.ts +1 -0
  157. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  158. package/dist/TimeInput/TimeInput.js +15 -17
  159. package/dist/Timestamp/Timestamp.d.ts +31 -3
  160. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  161. package/dist/Timestamp/Timestamp.js +47 -76
  162. package/dist/Timestamp/formatInstant.d.ts +41 -0
  163. package/dist/Timestamp/formatInstant.d.ts.map +1 -0
  164. package/dist/Timestamp/formatInstant.js +177 -0
  165. package/dist/Timestamp/index.d.ts +1 -0
  166. package/dist/Timestamp/index.d.ts.map +1 -1
  167. package/dist/Timestamp/tooltipEntries.d.ts +56 -0
  168. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -0
  169. package/dist/Timestamp/tooltipEntries.js +163 -0
  170. package/dist/Token/Token.d.ts +30 -3
  171. package/dist/Token/Token.d.ts.map +1 -1
  172. package/dist/Token/Token.js +17 -2
  173. package/dist/Token/index.d.ts +1 -1
  174. package/dist/Token/index.d.ts.map +1 -1
  175. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  176. package/dist/Tokenizer/Tokenizer.js +2 -2
  177. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  178. package/dist/Tooltip/useTooltip.js +17 -1
  179. package/dist/TopNav/TopNavItem.d.ts +3 -1
  180. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  181. package/dist/TopNav/TopNavItem.js +30 -3
  182. package/dist/TreeList/TreeList.d.ts.map +1 -1
  183. package/dist/TreeList/TreeList.js +1 -0
  184. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  185. package/dist/TreeList/TreeListBranches.js +8 -6
  186. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  187. package/dist/TreeList/TreeListItem.js +32 -14
  188. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  189. package/dist/Typeahead/BaseTypeahead.js +19 -10
  190. package/dist/Typeahead/Typeahead.js +1 -1
  191. package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
  192. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  193. package/dist/Typeahead/TypeaheadItem.js +20 -10
  194. package/dist/astryx.css +38 -14
  195. package/dist/astryx.umd.js +52 -52
  196. package/dist/astryx.umd.js.map +4 -4
  197. package/dist/hooks/index.d.ts +2 -0
  198. package/dist/hooks/index.d.ts.map +1 -1
  199. package/dist/hooks/index.js +1 -0
  200. package/dist/hooks/isRtlElement.d.ts +18 -0
  201. package/dist/hooks/isRtlElement.d.ts.map +1 -0
  202. package/dist/hooks/isRtlElement.js +27 -0
  203. package/dist/hooks/useAnnounce.d.ts +3 -0
  204. package/dist/hooks/useAnnounce.d.ts.map +1 -1
  205. package/dist/hooks/useAnnounce.js +39 -1
  206. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  207. package/dist/hooks/useFocusTrap.js +52 -11
  208. package/dist/hooks/useGridFocus.d.ts +6 -1
  209. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  210. package/dist/hooks/useGridFocus.js +12 -9
  211. package/dist/hooks/useHotkeys.d.ts +9 -0
  212. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  213. package/dist/hooks/useHotkeys.js +9 -0
  214. package/dist/hooks/useInputStatusIcon.d.ts +38 -0
  215. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -0
  216. package/dist/hooks/useInputStatusIcon.js +158 -0
  217. package/dist/hooks/useListFocus.d.ts +39 -1
  218. package/dist/hooks/useListFocus.d.ts.map +1 -1
  219. package/dist/hooks/useListFocus.js +54 -9
  220. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  221. package/dist/theme/derivedVarRegistry.js +13 -0
  222. package/dist/utils/index.d.ts +1 -0
  223. package/dist/utils/index.d.ts.map +1 -1
  224. package/dist/utils/index.js +2 -1
  225. package/dist/utils/plainDate.d.ts +17 -0
  226. package/dist/utils/plainDate.d.ts.map +1 -1
  227. package/dist/utils/plainDate.js +23 -4
  228. package/dist/utils/rtlStyles.d.ts +24 -0
  229. package/dist/utils/rtlStyles.d.ts.map +1 -0
  230. package/dist/utils/rtlStyles.js +27 -0
  231. package/locales/en.json +76 -0
  232. package/locales/pseudo.json +57 -0
  233. package/package.json +6 -1
  234. package/src/Avatar/Avatar.doc.mjs +18 -4
  235. package/src/Avatar/Avatar.test.tsx +106 -0
  236. package/src/Avatar/Avatar.tsx +62 -10
  237. package/src/Avatar/AvatarStatusDot.doc.mjs +4 -4
  238. package/src/Avatar/AvatarStatusDot.tsx +6 -4
  239. package/src/AvatarGroup/AvatarGroup.test.tsx +16 -1
  240. package/src/Breadcrumbs/BreadcrumbItem.tsx +10 -10
  241. package/src/Breadcrumbs/Breadcrumbs.test.tsx +51 -0
  242. package/src/Breadcrumbs/index.ts +4 -0
  243. package/src/Button/Button.doc.mjs +0 -4
  244. package/src/Calendar/Calendar.test.tsx +170 -20
  245. package/src/Calendar/Calendar.tsx +73 -16
  246. package/src/Calendar/dayCellUtils.test.ts +18 -18
  247. package/src/Calendar/dayCellUtils.ts +2 -2
  248. package/src/Calendar/styles.ts +33 -39
  249. package/src/Card/Card.tsx +1 -1
  250. package/src/Carousel/Carousel.tsx +25 -6
  251. package/src/Chat/ChatComposer.doc.mjs +2 -2
  252. package/src/Chat/ChatComposerInput.doc.mjs +1 -1
  253. package/src/Chat/ChatDictationButton.tsx +7 -1
  254. package/src/Chat/ChatMessageBubble.tsx +15 -10
  255. package/src/Chat/ChatSystemMessage.test.tsx +5 -0
  256. package/src/Chat/ChatToolCalls.tsx +9 -2
  257. package/src/CheckboxInput/CheckboxInput.doc.mjs +6 -0
  258. package/src/CheckboxInput/CheckboxInput.tsx +4 -4
  259. package/src/CheckboxList/CheckboxList.doc.mjs +6 -0
  260. package/src/Citation/Citation.doc.mjs +4 -3
  261. package/src/Citation/Citation.test.tsx +97 -0
  262. package/src/Citation/Citation.tsx +43 -11
  263. package/src/CodeBlock/CodeBlock.tsx +35 -8
  264. package/src/Collapsible/Collapsible.doc.mjs +1 -0
  265. package/src/Collapsible/Collapsible.test.tsx +7 -0
  266. package/src/Collapsible/Collapsible.tsx +16 -6
  267. package/src/CommandPalette/CommandPalette.test.tsx +95 -1
  268. package/src/CommandPalette/CommandPalette.tsx +41 -2
  269. package/src/ContextMenu/ContextMenu.test.tsx +42 -0
  270. package/src/ContextMenu/ContextMenu.tsx +13 -11
  271. package/src/ContextMenu/index.ts +5 -0
  272. package/src/DateInput/DateInput.doc.mjs +15 -5
  273. package/src/DateInput/DateInput.test.tsx +210 -3
  274. package/src/DateInput/DateInput.tsx +41 -29
  275. package/src/DateRangeInput/DateRangeInput.doc.mjs +11 -3
  276. package/src/DateRangeInput/DateRangeInput.test.tsx +120 -3
  277. package/src/DateRangeInput/DateRangeInput.tsx +29 -27
  278. package/src/DateTimeInput/DateTimeInput.doc.mjs +22 -1
  279. package/src/DateTimeInput/DateTimeInput.test.tsx +112 -0
  280. package/src/DateTimeInput/DateTimeInput.tsx +44 -39
  281. package/src/Divider/Divider.test.tsx +32 -8
  282. package/src/Divider/Divider.tsx +14 -1
  283. package/src/DropdownMenu/DropdownMenu.test.tsx +60 -0
  284. package/src/DropdownMenu/DropdownMenu.tsx +31 -12
  285. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +8 -2
  286. package/src/DropdownMenu/DropdownMenuItem.tsx +8 -2
  287. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +8 -2
  288. package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +88 -0
  289. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +635 -0
  290. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +553 -0
  291. package/src/DropdownMenu/index.ts +16 -0
  292. package/src/DropdownMenu/menuItemHover.ts +39 -0
  293. package/src/DropdownMenu/menuItemRoles.ts +9 -0
  294. package/src/DropdownMenu/renderDropdownItems.tsx +29 -15
  295. package/src/Field/Field.tsx +14 -9
  296. package/src/Field/inputStyles.stylex.ts +2 -0
  297. package/src/FieldStatus/FieldStatus.tsx +10 -4
  298. package/src/FileInput/FileInput.doc.mjs +13 -7
  299. package/src/FileInput/FileInput.test.tsx +84 -16
  300. package/src/FileInput/FileInput.tsx +96 -79
  301. package/src/HoverCard/useHoverCard.tsx +15 -3
  302. package/src/Icon/Icon.doc.mjs +13 -0
  303. package/src/Icon/Icon.test.tsx +89 -0
  304. package/src/Icon/Icon.tsx +51 -3
  305. package/src/Lightbox/Lightbox.tsx +11 -3
  306. package/src/List/List.test.tsx +17 -29
  307. package/src/List/List.tsx +6 -1
  308. package/src/Markdown/Markdown.test.tsx +21 -0
  309. package/src/Markdown/Markdown.tsx +4 -0
  310. package/src/MultiSelector/MultiSelector.doc.mjs +11 -0
  311. package/src/MultiSelector/MultiSelector.test.tsx +306 -3
  312. package/src/MultiSelector/MultiSelector.tsx +88 -57
  313. package/src/NumberInput/NumberInput.doc.mjs +11 -5
  314. package/src/NumberInput/NumberInput.test.tsx +48 -0
  315. package/src/NumberInput/NumberInput.tsx +25 -26
  316. package/src/Outline/Outline.test.tsx +12 -12
  317. package/src/Outline/Outline.tsx +2 -2
  318. package/src/RadioList/RadioList.doc.mjs +6 -0
  319. package/src/RadioList/RadioList.tsx +2 -2
  320. package/src/RadioList/RadioListItem.tsx +4 -4
  321. package/src/Resizable/ResizeHandle.test.tsx +94 -0
  322. package/src/Resizable/ResizeHandle.tsx +70 -20
  323. package/src/SegmentedControl/SegmentedControlItem.tsx +2 -0
  324. package/src/Selector/Selector.doc.mjs +8 -0
  325. package/src/Selector/Selector.test.tsx +328 -3
  326. package/src/Selector/Selector.tsx +70 -19
  327. package/src/SideNav/SideNav.tsx +1 -8
  328. package/src/SideNav/SideNavCollapseButton.tsx +9 -7
  329. package/src/SideNav/SideNavItem.tsx +5 -3
  330. package/src/Slider/Slider.doc.mjs +6 -0
  331. package/src/Slider/Slider.test.tsx +240 -12
  332. package/src/Slider/Slider.tsx +96 -23
  333. package/src/Spinner/Spinner.test.tsx +10 -8
  334. package/src/Spinner/Spinner.tsx +15 -5
  335. package/src/Switch/Switch.doc.mjs +142 -42
  336. package/src/Switch/Switch.test.tsx +22 -0
  337. package/src/Switch/Switch.tsx +90 -33
  338. package/src/TabList/TabList.doc.mjs +0 -6
  339. package/src/TabList/TabList.test.tsx +3 -15
  340. package/src/TabList/TabList.tsx +6 -17
  341. package/src/TabList/TabListContext.ts +0 -6
  342. package/src/Table/BaseTable.tsx +34 -2
  343. package/src/Table/Table.doc.mjs +11 -0
  344. package/src/Table/Table.test.tsx +85 -0
  345. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +9 -6
  346. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +24 -17
  347. package/src/Table/plugins/selection/useTableSelection.test.tsx +61 -3
  348. package/src/Table/plugins/selection/useTableSelection.tsx +3 -0
  349. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +37 -8
  350. package/src/Table/plugins/tree/useTableTreeData.tsx +17 -13
  351. package/src/Table/types.ts +33 -0
  352. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  353. package/src/TextArea/TextArea.doc.mjs +11 -5
  354. package/src/TextArea/TextArea.tsx +17 -26
  355. package/src/TextInput/TextInput.doc.mjs +11 -5
  356. package/src/TextInput/TextInput.test.tsx +130 -3
  357. package/src/TextInput/TextInput.tsx +18 -25
  358. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  359. package/src/TimeInput/TimeInput.doc.mjs +11 -5
  360. package/src/TimeInput/TimeInput.tsx +14 -25
  361. package/src/Timestamp/Timestamp.doc.mjs +32 -6
  362. package/src/Timestamp/Timestamp.test.tsx +371 -0
  363. package/src/Timestamp/Timestamp.tsx +96 -93
  364. package/src/Timestamp/formatInstant.ts +218 -0
  365. package/src/Timestamp/index.ts +4 -0
  366. package/src/Timestamp/tooltipEntries.test.ts +552 -0
  367. package/src/Timestamp/tooltipEntries.ts +210 -0
  368. package/src/Token/Token.test.tsx +21 -15
  369. package/src/Token/Token.tsx +31 -14
  370. package/src/Token/index.ts +1 -1
  371. package/src/Tokenizer/Tokenizer.doc.mjs +8 -1
  372. package/src/Tokenizer/Tokenizer.tsx +4 -2
  373. package/src/Tooltip/Tooltip.test.tsx +46 -0
  374. package/src/Tooltip/useTooltip.tsx +20 -0
  375. package/src/TopNav/TopNav.test.tsx +93 -2
  376. package/src/TopNav/TopNavItem.doc.mjs +3 -3
  377. package/src/TopNav/TopNavItem.tsx +35 -6
  378. package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +1 -1
  379. package/src/TreeList/TreeList.doc.mjs +6 -0
  380. package/src/TreeList/TreeList.test.tsx +50 -10
  381. package/src/TreeList/TreeList.tsx +5 -0
  382. package/src/TreeList/TreeListBranches.tsx +8 -6
  383. package/src/TreeList/TreeListItem.tsx +33 -11
  384. package/src/Typeahead/BaseTypeahead.tsx +29 -14
  385. package/src/Typeahead/Typeahead.doc.mjs +6 -0
  386. package/src/Typeahead/Typeahead.test.tsx +27 -0
  387. package/src/Typeahead/Typeahead.tsx +1 -1
  388. package/src/Typeahead/TypeaheadItem.tsx +2 -1
  389. package/src/__tests__/inputWidthContract.test.tsx +62 -0
  390. package/src/doc-mjs.d.ts +19 -0
  391. package/src/hooks/index.ts +6 -0
  392. package/src/hooks/isRtlElement.ts +27 -0
  393. package/src/hooks/useAnnounce.test.tsx +75 -2
  394. package/src/hooks/useAnnounce.ts +47 -1
  395. package/src/hooks/useFocusTrap.test.tsx +104 -0
  396. package/src/hooks/useFocusTrap.ts +61 -12
  397. package/src/hooks/useGridFocus.doc.mjs +3 -3
  398. package/src/hooks/useGridFocus.test.tsx +41 -0
  399. package/src/hooks/useGridFocus.ts +21 -10
  400. package/src/hooks/useHotkeys.ts +9 -0
  401. package/src/hooks/useInputStatusIcon.test.tsx +162 -0
  402. package/src/hooks/useInputStatusIcon.tsx +224 -0
  403. package/src/hooks/useListFocus.doc.mjs +25 -3
  404. package/src/hooks/useListFocus.test.tsx +153 -1
  405. package/src/hooks/useListFocus.ts +102 -9
  406. package/src/reset.css +0 -26
  407. package/src/reset.test.ts +60 -0
  408. package/src/theme/derivedVarRegistry.test.ts +1 -0
  409. package/src/theme/derivedVarRegistry.ts +6 -0
  410. package/src/theme/generateThemeRules.test.ts +42 -0
  411. package/src/utils/index.ts +2 -0
  412. package/src/utils/plainDate.test.ts +24 -6
  413. package/src/utils/plainDate.ts +18 -4
  414. package/src/utils/rtlStyles.ts +27 -0
@@ -27,7 +27,6 @@ import {
27
27
  type KeyboardEvent,
28
28
  } from 'react';
29
29
  import * as stylex from '@stylexjs/stylex';
30
- import type {IconName} from '../Icon';
31
30
  import {
32
31
  colorVars,
33
32
  sizeVars,
@@ -40,7 +39,6 @@ import {
40
39
  import {
41
40
  Field,
42
41
  type InputStatus,
43
- type InputStatusType,
44
42
  inputWrapperStyles,
45
43
  inputStatusBorderStyles,
46
44
  inputStatusHoverShadowStyles,
@@ -54,6 +52,7 @@ import {useCalendarConstraints} from '../Calendar/hooks';
54
52
  import {usePopover} from '../Popover';
55
53
  import {useTooltip} from '../Tooltip';
56
54
  import {useInputContainer} from '../hooks/useInputContainer';
55
+ import {useInputStatusIcon} from '../hooks/useInputStatusIcon';
57
56
  import {
58
57
  type ISOTimeString,
59
58
  parseDateInput,
@@ -470,25 +469,21 @@ export function DateTimeInput({
470
469
  isEnabled: showsDisabledMessage,
471
470
  });
472
471
 
473
- const statusIconMap: Record<InputStatusType, IconName> = {
474
- warning: 'warning',
475
- error: 'error',
476
- success: 'success',
477
- };
478
-
479
- const statusIconColorMap: Record<
480
- InputStatusType,
481
- 'warning' | 'error' | 'success'
482
- > = {
483
- warning: 'warning',
484
- error: 'error',
485
- success: 'success',
486
- };
472
+ // DateTimeInput fixes its status presentation to the detached message box,
473
+ // so the shared helper suppresses the on-field icon (the message box carries
474
+ // its own leading glyph). Routed through the helper for consistency with the
475
+ // rest of the bordered input family.
476
+ const {statusIcon, describedBy: statusTooltipDescribedBy} =
477
+ useInputStatusIcon({
478
+ status,
479
+ statusVariant: 'detached',
480
+ });
487
481
 
488
482
  const ariaDescribedBy =
489
483
  [
490
484
  description ? descriptionID : null,
491
485
  status?.message ? statusMessageID : null,
486
+ statusTooltipDescribedBy,
492
487
  showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
493
488
  ]
494
489
  .filter(Boolean)
@@ -890,14 +885,22 @@ export function DateTimeInput({
890
885
  {/* Date input */}
891
886
  <div
892
887
  ref={popover.triggerRef}
893
- {...stylex.props(
894
- inputWrapperStyles.base,
895
- sizeStyles[size],
896
- styles.dateWrapper,
897
- isEffectivelyDisabled && inputWrapperStyles.disabled,
898
- status && inputStatusBorderStyles[status.type],
899
- status && inputStatusHoverShadowStyles[status.type],
900
- status && inputStatusFocusWithinStyles[status.type],
888
+ {...mergeProps(
889
+ themeProps('date-time-input-date-segment', {
890
+ size,
891
+ status: status?.type ?? null,
892
+ }),
893
+ stylex.props(
894
+ inputWrapperStyles.base,
895
+ sizeStyles[size],
896
+ styles.dateWrapper,
897
+ isEffectivelyDisabled && inputWrapperStyles.disabled,
898
+ status && inputStatusBorderStyles[status.type],
899
+ status &&
900
+ !isEffectivelyDisabled &&
901
+ inputStatusHoverShadowStyles[status.type],
902
+ status && inputStatusFocusWithinStyles[status.type],
903
+ ),
901
904
  )}>
902
905
  <button
903
906
  type="button"
@@ -968,13 +971,7 @@ export function DateTimeInput({
968
971
  </button>
969
972
  )}
970
973
  {isBusy && <Spinner size="sm" />}
971
- {status && (
972
- <Icon
973
- icon={statusIconMap[status.type]}
974
- size="md"
975
- color={statusIconColorMap[status.type]}
976
- />
977
- )}
974
+ {statusIcon}
978
975
  </div>
979
976
 
980
977
  {/* Time input */}
@@ -982,14 +979,22 @@ export function DateTimeInput({
982
979
  ref={timeContainerRef}
983
980
  onClick={handleTimeWrapperClick}
984
981
  onMouseUp={handleTimeWrapperMouseUp}
985
- {...stylex.props(
986
- inputWrapperStyles.base,
987
- sizeStyles[size],
988
- styles.timeWrapper,
989
- isEffectivelyDisabled && inputWrapperStyles.disabled,
990
- status && inputStatusBorderStyles[status.type],
991
- status && inputStatusHoverShadowStyles[status.type],
992
- status && inputStatusFocusWithinStyles[status.type],
982
+ {...mergeProps(
983
+ themeProps('date-time-input-time-segment', {
984
+ size,
985
+ status: status?.type ?? null,
986
+ }),
987
+ stylex.props(
988
+ inputWrapperStyles.base,
989
+ sizeStyles[size],
990
+ styles.timeWrapper,
991
+ isEffectivelyDisabled && inputWrapperStyles.disabled,
992
+ status && inputStatusBorderStyles[status.type],
993
+ status &&
994
+ !isEffectivelyDisabled &&
995
+ inputStatusHoverShadowStyles[status.type],
996
+ status && inputStatusFocusWithinStyles[status.type],
997
+ ),
993
998
  )}>
994
999
  <div {...stylex.props(styles.icon)}>
995
1000
  <Icon icon="clock" size="sm" color="secondary" />
@@ -89,25 +89,49 @@ describe('Divider', () => {
89
89
 
90
90
  it('renders vertical divider with label', () => {
91
91
  render(
92
- <Divider
93
- orientation="vertical"
94
- label="Vertical"
95
- data-testid="divider"
96
- />,
92
+ <Divider orientation="vertical" label="Vertical" data-testid="divider" />,
97
93
  );
98
94
  const divider = screen.getByTestId('divider');
99
95
  expect(divider).toHaveAttribute('aria-orientation', 'vertical');
100
96
  expect(screen.getByText('Vertical')).toBeInTheDocument();
101
97
  });
102
98
 
103
- it('renders astryx-* class names for theme targeting', () => {
99
+ it('exposes the label as the accessible name of the separator', () => {
100
+ render(<Divider label="Section" data-testid="divider" />);
101
+ const divider = screen.getByTestId('divider');
102
+ expect(divider).toHaveAttribute('aria-labelledby');
103
+ expect(divider).toHaveAccessibleName('Section');
104
+ });
105
+
106
+ it('names the separator from a ReactNode label via aria-labelledby', () => {
107
+ render(<Divider label={<span>Custom</span>} data-testid="divider" />);
108
+ expect(screen.getByTestId('divider')).toHaveAccessibleName('Custom');
109
+ });
110
+
111
+ it('does not set aria-labelledby without a label', () => {
112
+ render(<Divider data-testid="divider" />);
113
+ expect(screen.getByTestId('divider')).not.toHaveAttribute(
114
+ 'aria-labelledby',
115
+ );
116
+ });
117
+
118
+ it('prefers an explicit aria-label over the rendered label', () => {
104
119
  render(
105
120
  <Divider
106
- variant="strong"
107
- orientation="vertical"
121
+ label="Section"
122
+ aria-label="Custom name"
108
123
  data-testid="divider"
109
124
  />,
110
125
  );
126
+ const divider = screen.getByTestId('divider');
127
+ expect(divider).toHaveAccessibleName('Custom name');
128
+ expect(divider).not.toHaveAttribute('aria-labelledby');
129
+ });
130
+
131
+ it('renders astryx-* class names for theme targeting', () => {
132
+ render(
133
+ <Divider variant="strong" orientation="vertical" data-testid="divider" />,
134
+ );
111
135
  const root = screen.getByTestId('divider');
112
136
  expect(root.className).toContain('astryx-divider');
113
137
  expect(root.className).toContain('strong');
@@ -15,7 +15,7 @@
15
15
  * - /packages/cli/templates/blocks/components/Divider/ (showcase blocks)
16
16
  */
17
17
 
18
- import type {ReactNode} from 'react';
18
+ import {useId, type ReactNode} from 'react';
19
19
  import type {BaseProps} from '../BaseProps';
20
20
  import * as stylex from '@stylexjs/stylex';
21
21
 
@@ -165,9 +165,19 @@ export function Divider({
165
165
  className,
166
166
  style,
167
167
  ref,
168
+ 'aria-label': ariaLabel,
169
+ 'aria-labelledby': ariaLabelledBy,
168
170
  ...props
169
171
  }: DividerProps) {
170
172
  const isHorizontal = orientation === 'horizontal';
173
+ const labelId = useId();
174
+
175
+ // A separator does not derive its accessible name from its content, so the
176
+ // rendered label must be referenced explicitly via aria-labelledby to be
177
+ // exposed as the separator's name (WCAG 1.3.1). An explicit aria-label or
178
+ // aria-labelledby from the consumer takes precedence.
179
+ const resolvedLabelledBy =
180
+ ariaLabelledBy ?? (label && ariaLabel == null ? labelId : undefined);
171
181
 
172
182
  return (
173
183
  <div
@@ -175,6 +185,8 @@ export function Divider({
175
185
  {...props}
176
186
  role="separator"
177
187
  aria-orientation={orientation}
188
+ aria-label={ariaLabel}
189
+ aria-labelledby={resolvedLabelledBy}
178
190
  {...mergeProps(
179
191
  themeProps('divider', {variant, orientation}),
180
192
  stylex.props(
@@ -196,6 +208,7 @@ export function Divider({
196
208
  />
197
209
  {label && (
198
210
  <div
211
+ id={labelId}
199
212
  {...stylex.props(
200
213
  labelStyles.label,
201
214
  !isHorizontal && labelStyles.verticalLabel,
@@ -781,4 +781,64 @@ describe('DropdownMenu keyboard access for menuitemradio/menuitemcheckbox (#3829
781
781
  screen.getByRole('menuitemradio', {name: 'Oldest', hidden: true}),
782
782
  ).toHaveFocus();
783
783
  });
784
+
785
+ it('moves focus to the item the mouse hovers, keeping a single highlight', async () => {
786
+ const user = userEvent.setup();
787
+ render(
788
+ <DropdownMenu button={{label: 'Actions'}}>
789
+ <DropdownMenuItem label="Edit" onClick={() => {}} />
790
+ <DropdownMenuItem label="Duplicate" onClick={() => {}} />
791
+ <DropdownMenuItem label="Delete" onClick={() => {}} />
792
+ </DropdownMenu>,
793
+ );
794
+
795
+ await user.click(screen.getByRole('button', {name: /Actions/}));
796
+ // Keyboard focus starts on the first item.
797
+ const edit = screen.getByRole('menuitem', {name: 'Edit', hidden: true});
798
+ const del = screen.getByRole('menuitem', {name: 'Delete', hidden: true});
799
+ edit.focus();
800
+ expect(edit).toHaveFocus();
801
+
802
+ // A mouse hover over another item moves focus to it, so the single
803
+ // focus-driven highlight follows the pointer instead of leaving two.
804
+ fireEvent.pointerMove(del, {pointerType: 'mouse'});
805
+ expect(del).toHaveFocus();
806
+ expect(edit).not.toHaveFocus();
807
+ });
808
+
809
+ it('does not move focus on hover for a disabled item', async () => {
810
+ const user = userEvent.setup();
811
+ render(
812
+ <DropdownMenu button={{label: 'Actions'}}>
813
+ <DropdownMenuItem label="Edit" onClick={() => {}} />
814
+ <DropdownMenuItem label="Delete" isDisabled onClick={() => {}} />
815
+ </DropdownMenu>,
816
+ );
817
+
818
+ await user.click(screen.getByRole('button', {name: /Actions/}));
819
+ const edit = screen.getByRole('menuitem', {name: 'Edit', hidden: true});
820
+ const del = screen.getByRole('menuitem', {name: 'Delete', hidden: true});
821
+ edit.focus();
822
+
823
+ fireEvent.pointerMove(del, {pointerType: 'mouse'});
824
+ expect(edit).toHaveFocus();
825
+ });
826
+
827
+ it('does not move focus for a non-mouse (touch) pointer', async () => {
828
+ const user = userEvent.setup();
829
+ render(
830
+ <DropdownMenu button={{label: 'Actions'}}>
831
+ <DropdownMenuItem label="Edit" onClick={() => {}} />
832
+ <DropdownMenuItem label="Delete" onClick={() => {}} />
833
+ </DropdownMenu>,
834
+ );
835
+
836
+ await user.click(screen.getByRole('button', {name: /Actions/}));
837
+ const edit = screen.getByRole('menuitem', {name: 'Edit', hidden: true});
838
+ const del = screen.getByRole('menuitem', {name: 'Delete', hidden: true});
839
+ edit.focus();
840
+
841
+ fireEvent.pointerMove(del, {pointerType: 'touch'});
842
+ expect(edit).toHaveFocus();
843
+ });
784
844
  });
@@ -38,7 +38,11 @@ import {Icon} from '../Icon';
38
38
  import type {IconType} from '../Icon';
39
39
 
40
40
  import {renderDropdownItems} from './renderDropdownItems';
41
- import {MENU_ITEM_ROLES, MENU_ITEM_SELECTOR} from './menuItemRoles';
41
+ import {
42
+ MENU_ITEM_ROLES,
43
+ MENU_ITEM_SELECTOR,
44
+ MENU_BOUNDARY_SELECTOR,
45
+ } from './menuItemRoles';
42
46
  import {
43
47
  DropdownMenuContext,
44
48
  type DropdownMenuContextValue,
@@ -100,6 +104,12 @@ export interface DropdownMenuItemData {
100
104
  onClick?: () => void;
101
105
  isDisabled?: boolean;
102
106
  icon?: ReactNode | IconType;
107
+ /**
108
+ * Nested submenu entries. When present, this row becomes a submenu (a
109
+ * flyout revealing `items`) instead of a leaf action — no separate item
110
+ * "type" is needed. Data-mode parity for the compound DropdownMenuSubMenu API.
111
+ */
112
+ items?: DropdownMenuOption[];
103
113
  }
104
114
 
105
115
  export interface DropdownMenuDivider {
@@ -200,6 +210,14 @@ export function DropdownMenu({
200
210
  const items = ('items' in props ? props.items : undefined) ?? [];
201
211
  const children = props.children;
202
212
 
213
+ // Extract BaseProps pass-throughs (aria-*, id, event handlers) from the
214
+ // discriminated-union rest bag so they can be forwarded to the menu element.
215
+ const {
216
+ items: _items,
217
+ children: _children,
218
+ ...rest
219
+ } = props as Record<string, unknown>;
220
+
203
221
  const menuId = useId();
204
222
  const menuSize = button.size ?? 'md';
205
223
  const buttonRef = useRef<HTMLButtonElement>(null);
@@ -265,23 +283,18 @@ export function DropdownMenu({
265
283
  handleKeyDown: listNavKeyDown,
266
284
  focusFirst,
267
285
  focusItem,
286
+ ownsEvent,
287
+ getItems: getMenuItems,
268
288
  } = useListFocus<HTMLDivElement>({
269
289
  itemSelector: MENU_ITEM_SELECTOR,
290
+ boundarySelector: MENU_BOUNDARY_SELECTOR,
270
291
  wrap: false,
271
292
  onEscape: closeMenu,
272
293
  });
273
294
 
274
295
  // First-character typeahead over the (enabled) menu items — jump to the next
275
- // item whose label starts with the typed text (menus-11).
276
- const getMenuItems = useCallback(
277
- (): HTMLElement[] =>
278
- listRef.current
279
- ? Array.from(
280
- listRef.current.querySelectorAll<HTMLElement>(MENU_ITEM_SELECTOR),
281
- )
282
- : [],
283
- [listRef],
284
- );
296
+ // item whose label starts with the typed text (menus-11). Reuses the hook's
297
+ // scoped item collection so an inline submenu flyout's items aren't swept in.
285
298
  const typeahead = useTypeahead({
286
299
  getItemLabels: () => getMenuItems().map(el => el.textContent),
287
300
  onMatch: focusItem,
@@ -307,6 +320,11 @@ export function DropdownMenu({
307
320
  // Extend useListFocus with Enter/Space activation + typeahead
308
321
  const listKeyDown = useCallback(
309
322
  (e: React.KeyboardEvent) => {
323
+ // A submenu flyout renders inline inside this menu; its key events bubble
324
+ // up here. Let that level own them — only handle events from this level.
325
+ if (!ownsEvent(e)) {
326
+ return;
327
+ }
310
328
  if (e.key === 'Enter' || e.key === ' ') {
311
329
  e.preventDefault();
312
330
  const focused = document.activeElement as HTMLElement | null;
@@ -334,7 +352,7 @@ export function DropdownMenu({
334
352
  }
335
353
  listNavKeyDown(e);
336
354
  },
337
- [listNavKeyDown, closeMenu, typeahead],
355
+ [listNavKeyDown, closeMenu, typeahead, ownsEvent],
338
356
  );
339
357
 
340
358
  const openAndFocus = useCallback(() => {
@@ -435,6 +453,7 @@ export function DropdownMenu({
435
453
 
436
454
  {popover.render(
437
455
  <div
456
+ {...rest}
438
457
  ref={listRef}
439
458
  id={menuId}
440
459
  role="menu"
@@ -23,11 +23,12 @@
23
23
  * checkmark uses the `check` icon from the active theme's icon registry.
24
24
  */
25
25
 
26
- import {useCallback, type ReactNode} from 'react';
26
+ import {useCallback, type PointerEvent, type ReactNode} from 'react';
27
27
  import * as stylex from '@stylexjs/stylex';
28
28
  import {Icon, renderIconSlot, type IconType} from '../Icon';
29
29
  import {Item} from '../Item';
30
30
  import {useDropdownMenuContext} from './DropdownMenuContext';
31
+ import {focusMenuItemOnHover} from './menuItemHover';
31
32
  import {
32
33
  colorVars,
33
34
  spacingVars,
@@ -47,7 +48,6 @@ const styles = stylex.create({
47
48
  backgroundColor: {
48
49
  default: 'transparent',
49
50
  ':focus': colorVars['--color-overlay-hover'],
50
- ':hover': colorVars['--color-overlay-hover'],
51
51
  },
52
52
  cursor: 'pointer',
53
53
  outline: 'none',
@@ -190,12 +190,18 @@ export function DropdownMenuCheckboxItem({
190
190
  }
191
191
  }, [isDisabled, onChange, value, hasCloseOnSelect, ctx]);
192
192
 
193
+ const handlePointerMove = useCallback(
194
+ (e: PointerEvent<HTMLElement>) => focusMenuItemOnHover(e, isDisabled),
195
+ [isDisabled],
196
+ );
197
+
193
198
  return (
194
199
  <Item
195
200
  {...rest}
196
201
  role="menuitemcheckbox"
197
202
  aria-checked={value}
198
203
  tabIndex={isDisabled ? undefined : -1}
204
+ onPointerMove={handlePointerMove}
199
205
  marker={
200
206
  <span
201
207
  aria-hidden="true"
@@ -23,7 +23,7 @@
23
23
  * - /packages/cli/templates/blocks/components/DropdownMenu/ (showcase blocks)
24
24
  */
25
25
 
26
- import {useCallback, type ReactNode} from 'react';
26
+ import {useCallback, type PointerEvent, type ReactNode} from 'react';
27
27
  import * as stylex from '@stylexjs/stylex';
28
28
  import {renderIconSlot, type IconType} from '../Icon';
29
29
  import {Item} from '../Item';
@@ -36,6 +36,7 @@ import {
36
36
  import {mergeProps} from '../utils';
37
37
  import type {BaseProps} from '../BaseProps';
38
38
  import {useDropdownMenuContext} from './DropdownMenuContext';
39
+ import {focusMenuItemOnHover} from './menuItemHover';
39
40
  import {themeProps} from '../utils/themeProps';
40
41
 
41
42
  const menuItemStyles = stylex.create({
@@ -51,7 +52,6 @@ const menuItemStyles = stylex.create({
51
52
  backgroundColor: {
52
53
  default: 'transparent',
53
54
  ':focus': colorVars['--color-overlay-hover'],
54
- ':hover': colorVars['--color-overlay-hover'],
55
55
  },
56
56
  border: 'none',
57
57
  cursor: 'pointer',
@@ -129,10 +129,16 @@ export function DropdownMenuItem({
129
129
  ctx?.closeMenu();
130
130
  }, [isDisabled, onClick, ctx]);
131
131
 
132
+ const handlePointerMove = useCallback(
133
+ (e: PointerEvent<HTMLElement>) => focusMenuItemOnHover(e, isDisabled),
134
+ [isDisabled],
135
+ );
136
+
132
137
  return (
133
138
  <Item
134
139
  role="menuitem"
135
140
  tabIndex={isDisabled ? undefined : -1}
141
+ onPointerMove={handlePointerMove}
136
142
  startContent={
137
143
  icon
138
144
  ? renderIconSlot(icon, {size: 'sm', color: 'secondary'})
@@ -21,11 +21,12 @@
21
21
  * has no registry glyph).
22
22
  */
23
23
 
24
- import {useCallback, type ReactNode} from 'react';
24
+ import {useCallback, type PointerEvent, type ReactNode} from 'react';
25
25
  import * as stylex from '@stylexjs/stylex';
26
26
  import {renderIconSlot, type IconType} from '../Icon';
27
27
  import {Item} from '../Item';
28
28
  import {useDropdownMenuContext} from './DropdownMenuContext';
29
+ import {focusMenuItemOnHover} from './menuItemHover';
29
30
  import {
30
31
  colorVars,
31
32
  spacingVars,
@@ -45,7 +46,6 @@ const styles = stylex.create({
45
46
  backgroundColor: {
46
47
  default: 'transparent',
47
48
  ':focus': colorVars['--color-overlay-hover'],
48
- ':hover': colorVars['--color-overlay-hover'],
49
49
  },
50
50
  cursor: 'pointer',
51
51
  outline: 'none',
@@ -184,12 +184,18 @@ export function DropdownMenuRadioItem({
184
184
  }
185
185
  }, [isDisabled, groupCtx, value, menuCtx]);
186
186
 
187
+ const handlePointerMove = useCallback(
188
+ (e: PointerEvent<HTMLElement>) => focusMenuItemOnHover(e, isDisabled),
189
+ [isDisabled],
190
+ );
191
+
187
192
  return (
188
193
  <Item
189
194
  {...rest}
190
195
  role="menuitemradio"
191
196
  aria-checked={isChecked}
192
197
  tabIndex={isDisabled ? undefined : -1}
198
+ onPointerMove={handlePointerMove}
193
199
  marker={
194
200
  <span
195
201
  aria-hidden="true"
@@ -0,0 +1,88 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../docs-types').ComponentDoc} */
4
+
5
+ export const docs = {
6
+ name: 'DropdownMenuSubMenu',
7
+ subComponentOf: 'DropdownMenu',
8
+ displayName: 'Dropdown Menu Submenu',
9
+ isHiddenFromOverview: true,
10
+ description:
11
+ 'A single menu row that reveals a nested flyout of its own items. The row adopts DropdownMenuItem semantics (label / icon / description / isDisabled); its children become the flyout content. Opens inline-end with viewport auto-flip; Right/Enter/Space opens and focuses the first item, Left/Escape closes and returns focus to the trigger (Right/Left swap in RTL). For data-driven menus, give a menu item a nested `items` array instead of using this component directly.',
12
+ playground: {
13
+ defaults: {label: 'Move to'},
14
+ },
15
+ props: [
16
+ {
17
+ name: 'label',
18
+ type: 'ReactNode',
19
+ description: 'Primary label text for the trigger row.',
20
+ },
21
+ {
22
+ name: 'icon',
23
+ type: 'IconType',
24
+ description:
25
+ 'Icon to display before the label. See `astryx docs icons` for valid semantic names.',
26
+ },
27
+ {
28
+ name: 'description',
29
+ type: 'ReactNode',
30
+ description: 'Secondary description text displayed below the label.',
31
+ },
32
+ {
33
+ name: 'children',
34
+ type: 'ReactNode',
35
+ description:
36
+ 'The flyout menu items: the same components used at the top level (DropdownMenuItem, nested DropdownMenuSubMenu, selectable items).',
37
+ },
38
+ {
39
+ name: 'isDisabled',
40
+ type: 'boolean',
41
+ default: 'false',
42
+ description:
43
+ 'A disabled submenu renders its trigger row but never opens the flyout.',
44
+ },
45
+ {
46
+ name: 'hasSpinner',
47
+ type: 'boolean',
48
+ default: 'false',
49
+ description:
50
+ 'Show a spinner in place of the caret, e.g. while a lazy submenu\'s children are loading.',
51
+ },
52
+ {
53
+ name: 'menuWidth',
54
+ type: 'number | string',
55
+ description: 'Fixed flyout width. Defaults to sizing to its content (min 160px).',
56
+ },
57
+ {
58
+ name: 'onOpenChange',
59
+ type: '(isOpen: boolean) => void',
60
+ description: 'Called when the flyout opens or closes.',
61
+ },
62
+ {
63
+ name: 'xstyle',
64
+ type: 'StyleXStyles',
65
+ description:
66
+ 'StyleX styles for the trigger row. Must be a stylex.create() value: not an inline style object like style={{}}.',
67
+ },
68
+ ],
69
+ };
70
+
71
+ export const docsDense = {
72
+ name: 'DropdownMenuSubMenu',
73
+ isHiddenFromOverview: true,
74
+ displayName: 'Dropdown Menu Submenu',
75
+ description:
76
+ 'menu row that reveals a nested flyout of its own children/items (single component, not Sub/SubTrigger/SubContent)',
77
+ propDescriptions: {
78
+ label: 'primary label text for the trigger row',
79
+ icon: 'icon before label',
80
+ description: 'secondary text below label',
81
+ children: 'flyout menu items (same components as top level)',
82
+ isDisabled: 'renders trigger but never opens',
83
+ hasSpinner: 'spinner instead of caret for async children',
84
+ menuWidth: 'fixed flyout width (default: content, min 160px)',
85
+ onOpenChange: 'called when flyout opens/closes',
86
+ xstyle: 'StyleX styles for the trigger row',
87
+ },
88
+ };