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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (560) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/dist/Avatar/Avatar.d.ts +42 -3
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +190 -59
  5. package/dist/Avatar/AvatarStatusDot.d.ts +6 -4
  6. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  7. package/dist/AvatarGroup/AvatarGroup.d.ts +7 -1
  8. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  9. package/dist/AvatarGroup/AvatarGroup.js +58 -6
  10. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  11. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  12. package/dist/AvatarGroup/AvatarGroupOverflow.js +7 -2
  13. package/dist/Banner/Banner.js +2 -2
  14. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  15. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -4
  16. package/dist/Breadcrumbs/index.d.ts +1 -0
  17. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  18. package/dist/Breadcrumbs/index.js +2 -1
  19. package/dist/Button/Button.d.ts +1 -1
  20. package/dist/Button/Button.d.ts.map +1 -1
  21. package/dist/Button/Button.js +6 -4
  22. package/dist/Calendar/Calendar.d.ts.map +1 -1
  23. package/dist/Calendar/Calendar.js +50 -9
  24. package/dist/Calendar/dayCellUtils.d.ts +4 -4
  25. package/dist/Calendar/dayCellUtils.js +2 -2
  26. package/dist/Calendar/styles.d.ts +30 -31
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +30 -31
  29. package/dist/Card/Card.js +1 -1
  30. package/dist/Carousel/Carousel.d.ts.map +1 -1
  31. package/dist/Carousel/Carousel.js +22 -12
  32. package/dist/Chat/ChatComposerInput.js +1 -1
  33. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  34. package/dist/Chat/ChatDictationButton.js +3 -1
  35. package/dist/Chat/ChatLayout.js +4 -4
  36. package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
  37. package/dist/Chat/ChatMessageBubble.js +8 -8
  38. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  39. package/dist/Chat/ChatToolCalls.js +10 -3
  40. package/dist/Chat/useTriggerMenu.js +2 -2
  41. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  42. package/dist/CheckboxInput/CheckboxInput.js +6 -6
  43. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  44. package/dist/CheckboxList/CheckboxListItem.js +19 -4
  45. package/dist/Citation/Citation.d.ts +22 -1
  46. package/dist/Citation/Citation.d.ts.map +1 -1
  47. package/dist/Citation/Citation.js +20 -6
  48. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  49. package/dist/CodeBlock/CodeBlock.js +16 -10
  50. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  51. package/dist/Collapsible/Collapsible.js +3 -1
  52. package/dist/CommandPalette/CommandPalette.d.ts +1 -1
  53. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  54. package/dist/CommandPalette/CommandPalette.js +42 -3
  55. package/dist/CommandPalette/CommandPaletteItem.js +1 -1
  56. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  57. package/dist/ContextMenu/ContextMenu.js +23 -5
  58. package/dist/ContextMenu/index.d.ts +1 -0
  59. package/dist/ContextMenu/index.d.ts.map +1 -1
  60. package/dist/ContextMenu/index.js +3 -1
  61. package/dist/DateInput/DateInput.d.ts +10 -2
  62. package/dist/DateInput/DateInput.d.ts.map +1 -1
  63. package/dist/DateInput/DateInput.js +33 -19
  64. package/dist/DateRangeInput/DateRangeInput.d.ts +10 -2
  65. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  66. package/dist/DateRangeInput/DateRangeInput.js +23 -20
  67. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  68. package/dist/DateTimeInput/DateTimeInput.js +23 -18
  69. package/dist/Divider/Divider.d.ts +2 -2
  70. package/dist/Divider/Divider.d.ts.map +1 -1
  71. package/dist/Divider/Divider.js +13 -0
  72. package/dist/DropdownMenu/DropdownMenu.d.ts +6 -0
  73. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  74. package/dist/DropdownMenu/DropdownMenu.js +22 -5
  75. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  76. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +4 -1
  77. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  78. package/dist/DropdownMenu/DropdownMenuItem.js +5 -2
  79. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +8 -11
  80. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
  81. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +3 -1
  82. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +1 -1
  83. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  84. package/dist/DropdownMenu/DropdownMenuRadioItem.js +5 -2
  85. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +104 -0
  86. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -0
  87. package/dist/DropdownMenu/DropdownMenuSubMenu.js +427 -0
  88. package/dist/DropdownMenu/index.d.ts +10 -0
  89. package/dist/DropdownMenu/index.d.ts.map +1 -1
  90. package/dist/DropdownMenu/index.js +13 -0
  91. package/dist/DropdownMenu/menuItemHover.d.ts +23 -0
  92. package/dist/DropdownMenu/menuItemHover.d.ts.map +1 -0
  93. package/dist/DropdownMenu/menuItemHover.js +33 -0
  94. package/dist/DropdownMenu/menuItemRoles.d.ts +8 -0
  95. package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -1
  96. package/dist/DropdownMenu/menuItemRoles.js +10 -1
  97. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  98. package/dist/DropdownMenu/renderDropdownItems.js +21 -6
  99. package/dist/Field/Field.d.ts +3 -1
  100. package/dist/Field/Field.d.ts.map +1 -1
  101. package/dist/Field/Field.js +4 -1
  102. package/dist/Field/FieldLabel.d.ts +2 -1
  103. package/dist/Field/FieldLabel.d.ts.map +1 -1
  104. package/dist/Field/FieldLabel.js +9 -5
  105. package/dist/Field/inputStyles.stylex.d.ts +1 -0
  106. package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
  107. package/dist/Field/inputStyles.stylex.js +1 -0
  108. package/dist/FieldStatus/FieldStatus.d.ts +10 -0
  109. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  110. package/dist/FieldStatus/FieldStatus.js +39 -3
  111. package/dist/FileInput/FileInput.d.ts +10 -2
  112. package/dist/FileInput/FileInput.d.ts.map +1 -1
  113. package/dist/FileInput/FileInput.js +93 -65
  114. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  115. package/dist/HoverCard/useHoverCard.js +10 -4
  116. package/dist/Icon/Icon.d.ts +15 -1
  117. package/dist/Icon/Icon.d.ts.map +1 -1
  118. package/dist/Icon/Icon.js +19 -3
  119. package/dist/Item/Item.d.ts +16 -3
  120. package/dist/Item/Item.d.ts.map +1 -1
  121. package/dist/Item/Item.js +31 -6
  122. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  123. package/dist/Layer/layerAnimations.stylex.js +10 -2
  124. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  125. package/dist/Lightbox/Lightbox.js +19 -13
  126. package/dist/List/List.d.ts.map +1 -1
  127. package/dist/List/List.js +6 -3
  128. package/dist/List/ListItem.d.ts +11 -1
  129. package/dist/List/ListItem.d.ts.map +1 -1
  130. package/dist/List/ListItem.js +2 -0
  131. package/dist/Markdown/Markdown.d.ts.map +1 -1
  132. package/dist/Markdown/Markdown.js +7 -0
  133. package/dist/MetadataList/MetadataList.d.ts +1 -1
  134. package/dist/MetadataList/MetadataList.d.ts.map +1 -1
  135. package/dist/MetadataList/MetadataList.js +4 -2
  136. package/dist/MultiSelector/MultiSelector.d.ts +9 -1
  137. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  138. package/dist/MultiSelector/MultiSelector.js +80 -42
  139. package/dist/NavMenu/NavHeadingMenuItem.js +1 -1
  140. package/dist/NumberInput/NumberInput.d.ts +10 -2
  141. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  142. package/dist/NumberInput/NumberInput.js +22 -17
  143. package/dist/Outline/Outline.js +2 -2
  144. package/dist/Pagination/Pagination.d.ts.map +1 -1
  145. package/dist/Pagination/Pagination.js +11 -3
  146. package/dist/PowerSearch/PowerSearch.d.ts +9 -2
  147. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  148. package/dist/PowerSearch/PowerSearch.js +2 -0
  149. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  150. package/dist/ProgressBar/ProgressBar.js +6 -1
  151. package/dist/RadioList/RadioList.d.ts +2 -2
  152. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  153. package/dist/RadioList/RadioListItem.js +5 -5
  154. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  155. package/dist/Resizable/ResizeHandle.js +49 -19
  156. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
  157. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  158. package/dist/SegmentedControl/SegmentedControlItem.js +5 -3
  159. package/dist/Selector/Selector.d.ts +8 -0
  160. package/dist/Selector/Selector.d.ts.map +1 -1
  161. package/dist/Selector/Selector.js +66 -17
  162. package/dist/SideNav/SideNav.d.ts.map +1 -1
  163. package/dist/SideNav/SideNav.js +5 -5
  164. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  165. package/dist/SideNav/SideNavCollapseButton.js +13 -10
  166. package/dist/SideNav/SideNavItem.d.ts +1 -1
  167. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  168. package/dist/SideNav/SideNavItem.js +12 -10
  169. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  170. package/dist/Skeleton/Skeleton.js +2 -2
  171. package/dist/Slider/Slider.d.ts.map +1 -1
  172. package/dist/Slider/Slider.js +87 -34
  173. package/dist/Spinner/Spinner.d.ts +3 -2
  174. package/dist/Spinner/Spinner.d.ts.map +1 -1
  175. package/dist/Spinner/Spinner.js +11 -2
  176. package/dist/Switch/Switch.d.ts +8 -1
  177. package/dist/Switch/Switch.d.ts.map +1 -1
  178. package/dist/Switch/Switch.js +171 -91
  179. package/dist/TabList/Tab.js +2 -2
  180. package/dist/TabList/TabList.d.ts +2 -11
  181. package/dist/TabList/TabList.d.ts.map +1 -1
  182. package/dist/TabList/TabList.js +5 -9
  183. package/dist/TabList/TabListContext.d.ts +0 -5
  184. package/dist/TabList/TabListContext.d.ts.map +1 -1
  185. package/dist/TabList/TabListContext.js +0 -5
  186. package/dist/TabList/TabMenu.js +1 -1
  187. package/dist/Table/BaseTable.d.ts.map +1 -1
  188. package/dist/Table/BaseTable.js +30 -4
  189. package/dist/Table/TableCell.js +3 -3
  190. package/dist/Table/TableHeaderCell.js +3 -3
  191. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  192. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +15 -11
  193. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  194. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +37 -27
  195. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  196. package/dist/Table/plugins/selection/useTableSelection.js +3 -0
  197. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  198. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +15 -7
  199. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  200. package/dist/Table/plugins/tree/useTableTreeData.js +29 -23
  201. package/dist/Table/types.d.ts +32 -0
  202. package/dist/Table/types.d.ts.map +1 -1
  203. package/dist/TextArea/TextArea.d.ts +10 -1
  204. package/dist/TextArea/TextArea.d.ts.map +1 -1
  205. package/dist/TextArea/TextArea.js +18 -19
  206. package/dist/TextInput/TextInput.d.ts +10 -2
  207. package/dist/TextInput/TextInput.d.ts.map +1 -1
  208. package/dist/TextInput/TextInput.js +20 -17
  209. package/dist/Thumbnail/Thumbnail.js +2 -2
  210. package/dist/TimeInput/TimeInput.d.ts +10 -2
  211. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  212. package/dist/TimeInput/TimeInput.js +17 -17
  213. package/dist/Timestamp/Timestamp.d.ts +31 -3
  214. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  215. package/dist/Timestamp/Timestamp.js +47 -76
  216. package/dist/Timestamp/formatInstant.d.ts +41 -0
  217. package/dist/Timestamp/formatInstant.d.ts.map +1 -0
  218. package/dist/Timestamp/formatInstant.js +177 -0
  219. package/dist/Timestamp/index.d.ts +1 -0
  220. package/dist/Timestamp/index.d.ts.map +1 -1
  221. package/dist/Timestamp/tooltipEntries.d.ts +56 -0
  222. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -0
  223. package/dist/Timestamp/tooltipEntries.js +163 -0
  224. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  225. package/dist/Toast/ToastViewport.js +61 -9
  226. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  227. package/dist/ToggleButton/ToggleButton.js +3 -1
  228. package/dist/Token/Token.d.ts +34 -4
  229. package/dist/Token/Token.d.ts.map +1 -1
  230. package/dist/Token/Token.js +85 -45
  231. package/dist/Token/TokenLink.d.ts +62 -0
  232. package/dist/Token/TokenLink.d.ts.map +1 -0
  233. package/dist/Token/TokenLink.js +76 -0
  234. package/dist/Token/index.d.ts +1 -1
  235. package/dist/Token/index.d.ts.map +1 -1
  236. package/dist/Tokenizer/Tokenizer.d.ts +9 -2
  237. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  238. package/dist/Tokenizer/Tokenizer.js +4 -2
  239. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  240. package/dist/Tooltip/useTooltip.js +17 -1
  241. package/dist/TopNav/TopNavItem.d.ts +3 -1
  242. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  243. package/dist/TopNav/TopNavItem.js +30 -3
  244. package/dist/TreeList/TreeList.d.ts.map +1 -1
  245. package/dist/TreeList/TreeList.js +1 -0
  246. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  247. package/dist/TreeList/TreeListBranches.js +8 -6
  248. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  249. package/dist/TreeList/TreeListItem.js +32 -14
  250. package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
  251. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  252. package/dist/Typeahead/BaseTypeahead.js +22 -11
  253. package/dist/Typeahead/Typeahead.d.ts +9 -2
  254. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  255. package/dist/Typeahead/Typeahead.js +11 -2
  256. package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
  257. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  258. package/dist/Typeahead/TypeaheadItem.js +20 -10
  259. package/dist/astryx.css +70 -30
  260. package/dist/astryx.umd.js +51 -51
  261. package/dist/astryx.umd.js.map +4 -4
  262. package/dist/hooks/index.d.ts +2 -0
  263. package/dist/hooks/index.d.ts.map +1 -1
  264. package/dist/hooks/index.js +1 -0
  265. package/dist/hooks/isRtlElement.d.ts +18 -0
  266. package/dist/hooks/isRtlElement.d.ts.map +1 -0
  267. package/dist/hooks/isRtlElement.js +27 -0
  268. package/dist/hooks/useAnnounce.d.ts +3 -0
  269. package/dist/hooks/useAnnounce.d.ts.map +1 -1
  270. package/dist/hooks/useAnnounce.js +39 -1
  271. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  272. package/dist/hooks/useFocusTrap.js +52 -11
  273. package/dist/hooks/useGridFocus.d.ts +6 -1
  274. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  275. package/dist/hooks/useGridFocus.js +12 -9
  276. package/dist/hooks/useHotkeys.d.ts +9 -0
  277. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  278. package/dist/hooks/useHotkeys.js +9 -0
  279. package/dist/hooks/useInputStatusIcon.d.ts +38 -0
  280. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -0
  281. package/dist/hooks/useInputStatusIcon.js +158 -0
  282. package/dist/hooks/useKeyboardHint.d.ts +1 -1
  283. package/dist/hooks/useKeyboardHint.d.ts.map +1 -1
  284. package/dist/hooks/useKeyboardHint.js +4 -2
  285. package/dist/hooks/useListFocus.d.ts +39 -1
  286. package/dist/hooks/useListFocus.d.ts.map +1 -1
  287. package/dist/hooks/useListFocus.js +54 -9
  288. package/dist/i18n/InternationalizationContext.d.ts +1 -0
  289. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  290. package/dist/i18n/InternationalizationContext.js +3 -0
  291. package/dist/i18n/InternationalizationProvider.d.ts +8 -1
  292. package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
  293. package/dist/i18n/InternationalizationProvider.js +5 -1
  294. package/dist/i18n/getLocaleDirection.d.ts +18 -0
  295. package/dist/i18n/getLocaleDirection.d.ts.map +1 -0
  296. package/dist/i18n/getLocaleDirection.js +29 -0
  297. package/dist/i18n/index.d.ts +6 -0
  298. package/dist/i18n/index.d.ts.map +1 -1
  299. package/dist/i18n/index.js +7 -1
  300. package/dist/i18n/useDirection.d.ts +2 -0
  301. package/dist/i18n/useDirection.d.ts.map +1 -0
  302. package/dist/i18n/useDirection.js +27 -0
  303. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  304. package/dist/theme/derivedVarRegistry.js +13 -0
  305. package/dist/utils/index.d.ts +1 -0
  306. package/dist/utils/index.d.ts.map +1 -1
  307. package/dist/utils/index.js +2 -1
  308. package/dist/utils/plainDate.d.ts +17 -0
  309. package/dist/utils/plainDate.d.ts.map +1 -1
  310. package/dist/utils/plainDate.js +23 -4
  311. package/dist/utils/rtlStyles.d.ts +24 -0
  312. package/dist/utils/rtlStyles.d.ts.map +1 -0
  313. package/dist/utils/rtlStyles.js +27 -0
  314. package/locales/en.json +112 -0
  315. package/locales/pseudo.json +84 -0
  316. package/package.json +6 -1
  317. package/src/Avatar/Avatar.doc.mjs +50 -3
  318. package/src/Avatar/Avatar.test.tsx +218 -0
  319. package/src/Avatar/Avatar.tsx +285 -79
  320. package/src/Avatar/AvatarStatusDot.doc.mjs +4 -4
  321. package/src/Avatar/AvatarStatusDot.tsx +6 -4
  322. package/src/AvatarGroup/AvatarGroup.doc.mjs +2 -0
  323. package/src/AvatarGroup/AvatarGroup.test.tsx +162 -2
  324. package/src/AvatarGroup/AvatarGroup.tsx +58 -3
  325. package/src/AvatarGroup/AvatarGroupOverflow.tsx +5 -2
  326. package/src/Banner/Banner.tsx +6 -6
  327. package/src/Breadcrumbs/BreadcrumbItem.tsx +10 -10
  328. package/src/Breadcrumbs/Breadcrumbs.test.tsx +52 -1
  329. package/src/Breadcrumbs/index.ts +4 -0
  330. package/src/Button/Button.doc.mjs +0 -4
  331. package/src/Button/Button.test.tsx +16 -0
  332. package/src/Button/Button.tsx +6 -4
  333. package/src/Calendar/Calendar.test.tsx +170 -20
  334. package/src/Calendar/Calendar.tsx +73 -16
  335. package/src/Calendar/dayCellUtils.test.ts +18 -18
  336. package/src/Calendar/dayCellUtils.ts +2 -2
  337. package/src/Calendar/styles.ts +33 -39
  338. package/src/Card/Card.tsx +1 -1
  339. package/src/Carousel/Carousel.tsx +25 -6
  340. package/src/Chat/ChatComposer.doc.mjs +2 -2
  341. package/src/Chat/ChatComposerInput.doc.mjs +1 -1
  342. package/src/Chat/ChatComposerInput.tsx +2 -2
  343. package/src/Chat/ChatDictationButton.tsx +7 -1
  344. package/src/Chat/ChatLayout.tsx +4 -4
  345. package/src/Chat/ChatMessageBubble.tsx +15 -10
  346. package/src/Chat/ChatSystemMessage.test.tsx +5 -0
  347. package/src/Chat/ChatToolCalls.tsx +9 -2
  348. package/src/Chat/useTriggerMenu.tsx +1 -1
  349. package/src/CheckboxInput/CheckboxInput.doc.mjs +6 -0
  350. package/src/CheckboxInput/CheckboxInput.test.tsx +23 -0
  351. package/src/CheckboxInput/CheckboxInput.tsx +21 -6
  352. package/src/CheckboxList/CheckboxList.doc.mjs +6 -0
  353. package/src/CheckboxList/CheckboxList.test.tsx +94 -3
  354. package/src/CheckboxList/CheckboxListItem.tsx +17 -10
  355. package/src/Citation/Citation.doc.mjs +4 -3
  356. package/src/Citation/Citation.test.tsx +97 -0
  357. package/src/Citation/Citation.tsx +43 -11
  358. package/src/CodeBlock/CodeBlock.test.tsx +17 -0
  359. package/src/CodeBlock/CodeBlock.tsx +38 -11
  360. package/src/Collapsible/Collapsible.doc.mjs +1 -0
  361. package/src/Collapsible/Collapsible.test.tsx +7 -0
  362. package/src/Collapsible/Collapsible.tsx +16 -6
  363. package/src/CommandPalette/CommandPalette.test.tsx +95 -1
  364. package/src/CommandPalette/CommandPalette.tsx +41 -2
  365. package/src/CommandPalette/CommandPaletteItem.tsx +1 -1
  366. package/src/ContextMenu/ContextMenu.test.tsx +59 -2
  367. package/src/ContextMenu/ContextMenu.tsx +22 -11
  368. package/src/ContextMenu/index.ts +5 -0
  369. package/src/DateInput/DateInput.doc.mjs +25 -0
  370. package/src/DateInput/DateInput.test.tsx +230 -0
  371. package/src/DateInput/DateInput.tsx +51 -29
  372. package/src/DateRangeInput/DateRangeInput.doc.mjs +16 -0
  373. package/src/DateRangeInput/DateRangeInput.test.tsx +140 -0
  374. package/src/DateRangeInput/DateRangeInput.tsx +42 -30
  375. package/src/DateTimeInput/DateTimeInput.doc.mjs +22 -1
  376. package/src/DateTimeInput/DateTimeInput.test.tsx +112 -0
  377. package/src/DateTimeInput/DateTimeInput.tsx +44 -39
  378. package/src/Divider/Divider.test.tsx +32 -8
  379. package/src/Divider/Divider.tsx +14 -1
  380. package/src/DropdownMenu/DropdownMenu.test.tsx +60 -0
  381. package/src/DropdownMenu/DropdownMenu.tsx +31 -12
  382. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +8 -2
  383. package/src/DropdownMenu/DropdownMenuItem.tsx +9 -3
  384. package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +4 -12
  385. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +9 -10
  386. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +9 -3
  387. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +20 -2
  388. package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +88 -0
  389. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +635 -0
  390. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +553 -0
  391. package/src/DropdownMenu/index.ts +16 -0
  392. package/src/DropdownMenu/menuItemHover.ts +39 -0
  393. package/src/DropdownMenu/menuItemRoles.ts +9 -0
  394. package/src/DropdownMenu/renderDropdownItems.tsx +29 -15
  395. package/src/Field/Field.tsx +14 -9
  396. package/src/Field/FieldLabel.test.tsx +25 -0
  397. package/src/Field/FieldLabel.tsx +12 -3
  398. package/src/Field/inputStyles.stylex.ts +2 -0
  399. package/src/FieldStatus/FieldStatus.test.tsx +72 -0
  400. package/src/FieldStatus/FieldStatus.tsx +48 -2
  401. package/src/FileInput/FileInput.doc.mjs +23 -2
  402. package/src/FileInput/FileInput.test.tsx +104 -13
  403. package/src/FileInput/FileInput.tsx +106 -79
  404. package/src/HoverCard/useHoverCard.tsx +15 -3
  405. package/src/Icon/Icon.doc.mjs +13 -0
  406. package/src/Icon/Icon.test.tsx +89 -0
  407. package/src/Icon/Icon.tsx +51 -3
  408. package/src/Item/Item.doc.mjs +5 -0
  409. package/src/Item/Item.test.tsx +88 -0
  410. package/src/Item/Item.tsx +57 -11
  411. package/src/Layer/layerAnimations.stylex.ts +23 -0
  412. package/src/Lightbox/Lightbox.tsx +15 -7
  413. package/src/List/List.test.tsx +17 -29
  414. package/src/List/List.tsx +6 -1
  415. package/src/List/ListItem.doc.mjs +12 -0
  416. package/src/List/ListItem.tsx +13 -0
  417. package/src/Markdown/Markdown.test.tsx +21 -0
  418. package/src/Markdown/Markdown.tsx +4 -0
  419. package/src/MetadataList/MetadataList.test.tsx +24 -0
  420. package/src/MetadataList/MetadataList.tsx +6 -2
  421. package/src/MultiSelector/MultiSelector.doc.mjs +20 -0
  422. package/src/MultiSelector/MultiSelector.test.tsx +402 -0
  423. package/src/MultiSelector/MultiSelector.tsx +120 -59
  424. package/src/NavMenu/NavHeadingMenuItem.tsx +1 -1
  425. package/src/NumberInput/NumberInput.doc.mjs +21 -0
  426. package/src/NumberInput/NumberInput.test.tsx +71 -0
  427. package/src/NumberInput/NumberInput.tsx +35 -26
  428. package/src/Outline/Outline.test.tsx +12 -12
  429. package/src/Outline/Outline.tsx +2 -2
  430. package/src/Pagination/Pagination.tsx +11 -3
  431. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  432. package/src/PowerSearch/PowerSearch.test.tsx +23 -0
  433. package/src/PowerSearch/PowerSearch.tsx +10 -1
  434. package/src/ProgressBar/ProgressBar.test.tsx +34 -0
  435. package/src/ProgressBar/ProgressBar.tsx +16 -1
  436. package/src/RadioList/RadioList.doc.mjs +6 -0
  437. package/src/RadioList/RadioList.test.tsx +26 -4
  438. package/src/RadioList/RadioList.tsx +2 -2
  439. package/src/RadioList/RadioListItem.tsx +12 -5
  440. package/src/Resizable/ResizeHandle.test.tsx +94 -0
  441. package/src/Resizable/ResizeHandle.tsx +70 -20
  442. package/src/SegmentedControl/SegmentedControl.test.tsx +28 -0
  443. package/src/SegmentedControl/SegmentedControlItem.tsx +22 -2
  444. package/src/Selector/Selector.doc.mjs +15 -0
  445. package/src/Selector/Selector.test.tsx +370 -0
  446. package/src/Selector/Selector.tsx +88 -20
  447. package/src/SideNav/SideNav.tsx +1 -8
  448. package/src/SideNav/SideNavCollapseButton.tsx +9 -7
  449. package/src/SideNav/SideNavItem.tsx +5 -3
  450. package/src/Skeleton/Skeleton.test.tsx +16 -0
  451. package/src/Skeleton/Skeleton.tsx +13 -1
  452. package/src/Slider/Slider.doc.mjs +6 -0
  453. package/src/Slider/Slider.test.tsx +240 -12
  454. package/src/Slider/Slider.tsx +96 -23
  455. package/src/Spinner/Spinner.test.tsx +10 -8
  456. package/src/Spinner/Spinner.tsx +15 -5
  457. package/src/Switch/Switch.doc.mjs +142 -42
  458. package/src/Switch/Switch.test.tsx +111 -0
  459. package/src/Switch/Switch.tsx +149 -32
  460. package/src/TabList/Tab.tsx +2 -2
  461. package/src/TabList/TabList.doc.mjs +0 -6
  462. package/src/TabList/TabList.test.tsx +3 -15
  463. package/src/TabList/TabList.tsx +6 -17
  464. package/src/TabList/TabListContext.ts +0 -6
  465. package/src/TabList/TabMenu.tsx +2 -2
  466. package/src/Table/BaseTable.tsx +34 -2
  467. package/src/Table/Table.doc.mjs +11 -0
  468. package/src/Table/Table.test.tsx +85 -0
  469. package/src/Table/TableCell.tsx +3 -3
  470. package/src/Table/TableHeaderCell.tsx +3 -3
  471. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +9 -6
  472. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +19 -0
  473. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +27 -18
  474. package/src/Table/plugins/selection/useTableSelection.test.tsx +61 -3
  475. package/src/Table/plugins/selection/useTableSelection.tsx +3 -0
  476. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +38 -9
  477. package/src/Table/plugins/tree/useTableTreeData.tsx +17 -13
  478. package/src/Table/types.ts +33 -0
  479. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  480. package/src/TextArea/TextArea.doc.mjs +21 -0
  481. package/src/TextArea/TextArea.test.tsx +23 -0
  482. package/src/TextArea/TextArea.tsx +27 -26
  483. package/src/TextInput/TextInput.doc.mjs +21 -0
  484. package/src/TextInput/TextInput.test.tsx +150 -0
  485. package/src/TextInput/TextInput.tsx +28 -25
  486. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  487. package/src/Thumbnail/Thumbnail.tsx +1 -1
  488. package/src/TimeInput/TimeInput.doc.mjs +21 -0
  489. package/src/TimeInput/TimeInput.test.tsx +23 -0
  490. package/src/TimeInput/TimeInput.tsx +24 -25
  491. package/src/Timestamp/Timestamp.doc.mjs +32 -6
  492. package/src/Timestamp/Timestamp.test.tsx +371 -0
  493. package/src/Timestamp/Timestamp.tsx +96 -93
  494. package/src/Timestamp/formatInstant.ts +218 -0
  495. package/src/Timestamp/index.ts +4 -0
  496. package/src/Timestamp/tooltipEntries.test.ts +552 -0
  497. package/src/Timestamp/tooltipEntries.ts +210 -0
  498. package/src/Toast/ToastViewport.test.tsx +218 -8
  499. package/src/Toast/ToastViewport.tsx +71 -12
  500. package/src/ToggleButton/ToggleButton.test.tsx +24 -0
  501. package/src/ToggleButton/ToggleButton.tsx +19 -1
  502. package/src/Token/Token.test.tsx +166 -15
  503. package/src/Token/Token.tsx +104 -47
  504. package/src/Token/TokenLink.tsx +107 -0
  505. package/src/Token/index.ts +1 -1
  506. package/src/Tokenizer/Tokenizer.doc.mjs +23 -1
  507. package/src/Tokenizer/Tokenizer.test.tsx +23 -0
  508. package/src/Tokenizer/Tokenizer.tsx +14 -2
  509. package/src/Tooltip/Tooltip.test.tsx +46 -0
  510. package/src/Tooltip/useTooltip.tsx +20 -0
  511. package/src/TopNav/TopNav.test.tsx +93 -2
  512. package/src/TopNav/TopNavItem.doc.mjs +3 -3
  513. package/src/TopNav/TopNavItem.tsx +35 -6
  514. package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +1 -1
  515. package/src/TreeList/TreeList.doc.mjs +6 -0
  516. package/src/TreeList/TreeList.test.tsx +50 -10
  517. package/src/TreeList/TreeList.tsx +5 -0
  518. package/src/TreeList/TreeListBranches.tsx +8 -6
  519. package/src/TreeList/TreeListItem.tsx +33 -11
  520. package/src/Typeahead/BaseTypeahead.tsx +42 -15
  521. package/src/Typeahead/Typeahead.doc.mjs +13 -0
  522. package/src/Typeahead/Typeahead.test.tsx +112 -0
  523. package/src/Typeahead/Typeahead.tsx +17 -1
  524. package/src/Typeahead/TypeaheadItem.tsx +2 -1
  525. package/src/__tests__/forcedColors.ts +60 -0
  526. package/src/__tests__/inputWidthContract.test.tsx +62 -0
  527. package/src/doc-mjs.d.ts +19 -0
  528. package/src/hooks/index.ts +6 -0
  529. package/src/hooks/isRtlElement.ts +27 -0
  530. package/src/hooks/useAnnounce.test.tsx +75 -2
  531. package/src/hooks/useAnnounce.ts +47 -1
  532. package/src/hooks/useFocusTrap.test.tsx +104 -0
  533. package/src/hooks/useFocusTrap.ts +61 -12
  534. package/src/hooks/useGridFocus.doc.mjs +3 -3
  535. package/src/hooks/useGridFocus.test.tsx +41 -0
  536. package/src/hooks/useGridFocus.ts +21 -10
  537. package/src/hooks/useHotkeys.ts +9 -0
  538. package/src/hooks/useInputStatusIcon.test.tsx +162 -0
  539. package/src/hooks/useInputStatusIcon.tsx +224 -0
  540. package/src/hooks/useKeyboardHint.test.tsx +22 -0
  541. package/src/hooks/useKeyboardHint.tsx +6 -2
  542. package/src/hooks/useListFocus.doc.mjs +25 -3
  543. package/src/hooks/useListFocus.test.tsx +153 -1
  544. package/src/hooks/useListFocus.ts +102 -9
  545. package/src/i18n/InternationalizationContext.ts +4 -0
  546. package/src/i18n/InternationalizationProvider.tsx +12 -2
  547. package/src/i18n/__tests__/getLocaleDirection.test.ts +40 -0
  548. package/src/i18n/__tests__/useDirection.test.tsx +66 -0
  549. package/src/i18n/getLocaleDirection.ts +29 -0
  550. package/src/i18n/index.ts +6 -0
  551. package/src/i18n/useDirection.ts +29 -0
  552. package/src/reset.css +0 -26
  553. package/src/reset.test.ts +60 -0
  554. package/src/theme/derivedVarRegistry.test.ts +1 -0
  555. package/src/theme/derivedVarRegistry.ts +6 -0
  556. package/src/theme/generateThemeRules.test.ts +42 -0
  557. package/src/utils/index.ts +2 -0
  558. package/src/utils/plainDate.test.ts +24 -6
  559. package/src/utils/plainDate.ts +18 -4
  560. package/src/utils/rtlStyles.ts +27 -0
@@ -9,11 +9,35 @@
9
9
  * SYNC: When Item component changes, update tests to match new behavior
10
10
  */
11
11
 
12
+ import {useRef} from 'react';
12
13
  import {describe, it, expect, vi} from 'vitest';
13
14
  import {render, screen} from '@testing-library/react';
14
15
  import userEvent from '@testing-library/user-event';
15
16
  import {Item} from './Item';
16
17
 
18
+ /**
19
+ * Item in delegation mode: `interactiveRef` points at a nested control that
20
+ * owns the row's keyboard access and action. The row is an enlarged tap target
21
+ * that forwards surface clicks to that control (useClickableContainer).
22
+ */
23
+ function DelegatingItem({onToggle}: {onToggle?: () => void}) {
24
+ const ref = useRef<HTMLInputElement>(null);
25
+ return (
26
+ <Item
27
+ label="Row"
28
+ interactiveRef={ref}
29
+ startContent={
30
+ <input
31
+ ref={ref}
32
+ type="checkbox"
33
+ aria-label="Pick row"
34
+ onChange={onToggle}
35
+ />
36
+ }
37
+ />
38
+ );
39
+ }
40
+
17
41
  describe('Item', () => {
18
42
  // ===========================================================================
19
43
  // Basic rendering
@@ -190,6 +214,70 @@ describe('Item', () => {
190
214
  expect(buttons).toHaveLength(1);
191
215
  });
192
216
 
217
+ // ===========================================================================
218
+ // Interactive — interactiveRef (delegation to a nested control)
219
+ // ===========================================================================
220
+
221
+ it('renders no invisible button in interactiveRef (delegation) mode', () => {
222
+ const {container} = render(<DelegatingItem />);
223
+ // The nested control provides keyboard access — the row must not add a
224
+ // second focusable control for the same action (WCAG 4.1.2).
225
+ expect(container.querySelector('button')).not.toBeInTheDocument();
226
+ });
227
+
228
+ it('keeps the nested control as the only tab stop in interactiveRef mode', async () => {
229
+ const user = userEvent.setup();
230
+ render(<DelegatingItem />);
231
+ await user.tab();
232
+ expect(screen.getByRole('checkbox')).toHaveFocus();
233
+ // Next tab leaves the item entirely — the row itself is not focusable.
234
+ await user.tab();
235
+ expect(screen.getByRole('checkbox')).not.toHaveFocus();
236
+ expect(document.body).toHaveFocus();
237
+ });
238
+
239
+ it('delegates a row-surface click to the interactive control', async () => {
240
+ const user = userEvent.setup();
241
+ const onToggle = vi.fn();
242
+ render(<DelegatingItem onToggle={onToggle} />);
243
+ // Clicking the label (row surface) is forwarded to the checkbox.
244
+ await user.click(screen.getByText('Row'));
245
+ expect(onToggle).toHaveBeenCalledTimes(1);
246
+ });
247
+
248
+ it('does not double-fire when the interactive control itself is clicked', async () => {
249
+ const user = userEvent.setup();
250
+ const onToggle = vi.fn();
251
+ render(<DelegatingItem onToggle={onToggle} />);
252
+ await user.click(screen.getByRole('checkbox'));
253
+ // The row must not re-forward the control's own click back to it.
254
+ expect(onToggle).toHaveBeenCalledTimes(1);
255
+ });
256
+
257
+ it('ignores onClick when interactiveRef is set (delegation wins, single tab stop)', async () => {
258
+ const user = userEvent.setup();
259
+ const onClick = vi.fn();
260
+ function ItemWithBoth() {
261
+ const ref = useRef<HTMLInputElement>(null);
262
+ return (
263
+ <Item
264
+ label="Row"
265
+ onClick={onClick}
266
+ interactiveRef={ref}
267
+ startContent={
268
+ <input ref={ref} type="checkbox" aria-label="Pick row" />
269
+ }
270
+ />
271
+ );
272
+ }
273
+ const {container} = render(<ItemWithBoth />);
274
+ // No invisible button (onClick is ignored in delegation mode)...
275
+ expect(container.querySelector('button')).not.toBeInTheDocument();
276
+ // ...and the checkbox is still the sole tab stop.
277
+ await user.tab();
278
+ expect(screen.getByRole('checkbox')).toHaveFocus();
279
+ });
280
+
193
281
  // ===========================================================================
194
282
  // Interactive — href (invisible anchor pattern)
195
283
  // ===========================================================================
package/src/Item/Item.tsx CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  /**
6
6
  * @file Item.tsx
7
- * @input Uses React, ReactNode, StyleXStyles, theme tokens
7
+ * @input Uses React, ReactNode, StyleXStyles, theme tokens, useClickableContainer
8
8
  * @output Exports Item component, ItemProps type
9
9
  * @position Core layout primitive; consumed by index.ts, tested by Item.test.tsx
10
10
  *
@@ -16,7 +16,7 @@
16
16
  * - /packages/cli/templates/blocks/components/Item/ (showcase blocks)
17
17
  */
18
18
 
19
- import type {ReactNode} from 'react';
19
+ import {useRef, type ReactNode} from 'react';
20
20
  import * as stylex from '@stylexjs/stylex';
21
21
  import {
22
22
  colorVars,
@@ -27,9 +27,11 @@ import {
27
27
  typeScaleVars,
28
28
  } from '../theme/tokens.stylex';
29
29
  import type {BaseProps} from '../BaseProps';
30
- import {mergeProps} from '../utils';
30
+ import {mergeProps, mergeRefs} from '../utils';
31
31
  import {computeTargetAndRel} from '../Link/computeTargetAndRel';
32
32
  import {useLinkComponent} from '../Link/useLinkComponent';
33
+ import {useClickableContainer} from '../hooks/useClickableContainer';
34
+ import {useDevWarning} from '../hooks/useDevWarning';
33
35
  import {themeProps} from '../utils/themeProps';
34
36
 
35
37
  // =============================================================================
@@ -108,6 +110,20 @@ export interface ItemProps extends BaseProps<HTMLElement> {
108
110
  */
109
111
  onClick?: (event: React.MouseEvent) => void;
110
112
 
113
+ /**
114
+ * Ref to a nested control inside the item (e.g. a checkbox in
115
+ * `startContent`) that already provides the item's keyboard access and
116
+ * action. When set, the item becomes an enlarged click/tap target that
117
+ * delegates surface clicks to that control via the `useClickableContainer`
118
+ * pattern: it renders no invisible button/anchor, so the row adds no second
119
+ * tab stop (WCAG 4.1.2 — one focusable control per option). Clicks on the
120
+ * control itself, and on any other nested interactive element, are left to
121
+ * that element. Mutually exclusive with `onClick`/`href` — when
122
+ * `interactiveRef` is set those are ignored (the nested control is the sole
123
+ * action).
124
+ */
125
+ interactiveRef?: React.RefObject<HTMLElement | null>;
126
+
111
127
  /**
112
128
  * Link URL. Makes the item a link via an invisible anchor element.
113
129
  */
@@ -349,6 +365,7 @@ export function Item({
349
365
  labelLines,
350
366
  descriptionLines,
351
367
  onClick,
368
+ interactiveRef,
352
369
  href,
353
370
  target: targetFromProps,
354
371
  rel: relFromProps,
@@ -363,7 +380,29 @@ export function Item({
363
380
  ...restProps
364
381
  }: ItemProps) {
365
382
  const LinkComponent = useLinkComponent();
366
- const isInteractive = onClick != null || href != null;
383
+
384
+ // Delegation mode: the row is an enlarged click/tap target for a nested
385
+ // control (e.g. a checkbox) that owns the keyboard access and action. The
386
+ // control is the row's only tab stop; the row proxies surface clicks to it.
387
+ const isDelegate = interactiveRef != null;
388
+ const containerRef = useRef<HTMLElement | null>(null);
389
+ // Only onClick is needed: onMouseUp handles middle-click href navigation,
390
+ // which delegation mode never has (href is ignored here).
391
+ const {onClick: delegatedOnClick} = useClickableContainer({
392
+ containerRef,
393
+ interactiveRef: interactiveRef ?? undefined,
394
+ disabled: isDisabled,
395
+ });
396
+
397
+ useDevWarning(
398
+ 'Item',
399
+ '`interactiveRef` is mutually exclusive with `onClick`/`href`. In ' +
400
+ 'delegation mode the row only forwards clicks to the referenced control, ' +
401
+ 'so `onClick`/`href` are ignored. Drop one of them.',
402
+ isDelegate && (onClick != null || href != null),
403
+ );
404
+
405
+ const isInteractive = onClick != null || href != null || isDelegate;
367
406
  const {target, rel} = computeTargetAndRel(targetFromProps, relFromProps);
368
407
  // When a semantic role is provided (e.g. "menuitem"), a parent component
369
408
  // handles keyboard access. Skip the invisible button/anchor and put
@@ -441,7 +480,10 @@ export function Item({
441
480
  <span {...stylex.props(styles.startContent)}>{startContent}</span>
442
481
  )}
443
482
 
444
- {hasParentRole ? (
483
+ {hasParentRole || isDelegate ? (
484
+ // Delegation mode (and parent-role mode) put the label in a plain span:
485
+ // keyboard access lives on the nested control, so no invisible
486
+ // button/anchor is rendered and the row adds no second tab stop.
445
487
  <span
446
488
  {...stylex.props(
447
489
  styles.content,
@@ -497,7 +539,9 @@ export function Item({
497
539
 
498
540
  return (
499
541
  <Component
500
- ref={ref as React.Ref<never>}
542
+ ref={
543
+ (isDelegate ? mergeRefs(ref, containerRef) : ref) as React.Ref<never>
544
+ }
501
545
  {...restProps}
502
546
  aria-selected={(allowsAriaSelected && isSelected) || undefined}
503
547
  // aria-selected is invalid on roles that don't permit it (listitem, a
@@ -527,11 +571,13 @@ export function Item({
527
571
  )}
528
572
  role={role}
529
573
  onClick={
530
- hasParentRole
531
- ? onClick
532
- : isInteractive
533
- ? handleContainerClick
534
- : undefined
574
+ isDelegate
575
+ ? delegatedOnClick
576
+ : hasParentRole
577
+ ? onClick
578
+ : isInteractive
579
+ ? handleContainerClick
580
+ : undefined
535
581
  }>
536
582
  {innerContent}
537
583
  </Component>
@@ -61,6 +61,27 @@ const enterStart = stylex.keyframes({
61
61
  to: {opacity: 1, transform: 'translateX(0) scale(1)'},
62
62
  });
63
63
 
64
+ // RTL: the horizontal entrance nudge is a physical translateX, so it must
65
+ // mirror under RTL — a layer on the inline-end/start side must slide in from
66
+ // the correct physical side following the reading flow. Only the horizontal
67
+ // (translateX) keyframes need mirroring; the vertical enterAbove/enterBelow
68
+ // (translateY) entrances are direction-neutral and are left as-is.
69
+ const enterEndRtl = stylex.keyframes({
70
+ from: {
71
+ opacity: 0,
72
+ transform: `translateX(${spacingVars['--spacing-2']}) scale(0.95)`,
73
+ },
74
+ to: {opacity: 1, transform: 'translateX(0) scale(1)'},
75
+ });
76
+
77
+ const enterStartRtl = stylex.keyframes({
78
+ from: {
79
+ opacity: 0,
80
+ transform: `translateX(calc(-1 * ${spacingVars['--spacing-2']})) scale(0.95)`,
81
+ },
82
+ to: {opacity: 1, transform: 'translateX(0) scale(1)'},
83
+ });
84
+
64
85
  const animationBase = {
65
86
  animationDuration: durationVars['--duration-fast-max'],
66
87
  animationTimingFunction: easeVars['--ease-standard'],
@@ -95,6 +116,7 @@ export const layerAnimations = stylex.create({
95
116
  end: {
96
117
  animationName: {
97
118
  default: enterEnd,
119
+ ':is([dir="rtl"] *)': enterEndRtl,
98
120
  '@media (prefers-reduced-motion: reduce)': 'none',
99
121
  },
100
122
  ...animationBase,
@@ -102,6 +124,7 @@ export const layerAnimations = stylex.create({
102
124
  start: {
103
125
  animationName: {
104
126
  default: enterStart,
127
+ ':is([dir="rtl"] *)': enterStartRtl,
105
128
  '@media (prefers-reduced-motion: reduce)': 'none',
106
129
  },
107
130
  ...animationBase,
@@ -32,7 +32,7 @@ import {IconButton} from '../IconButton';
32
32
  import {useAnnounce} from '../hooks/useAnnounce';
33
33
  import {useScrollLock} from '../hooks/useScrollLock';
34
34
  import {useIsomorphicLayoutEffect} from '../hooks/useIsomorphicLayoutEffect';
35
- import {mergeProps, mergeRefs} from '../utils';
35
+ import {mergeProps, mergeRefs, rtlStyles} from '../utils';
36
36
  import type {BaseProps} from '../BaseProps';
37
37
  import {themeProps} from '../utils/themeProps';
38
38
  import {useTranslator} from '../i18n';
@@ -199,7 +199,7 @@ const styles = stylex.create({
199
199
  closeButton: {
200
200
  position: 'absolute',
201
201
  top: spacingVars['--spacing-3'],
202
- right: spacingVars['--spacing-3'],
202
+ insetInlineEnd: spacingVars['--spacing-3'],
203
203
  zIndex: 1,
204
204
  },
205
205
  navButton: {
@@ -209,15 +209,15 @@ const styles = stylex.create({
209
209
  zIndex: 1,
210
210
  },
211
211
  navPrev: {
212
- left: spacingVars['--spacing-3'],
212
+ insetInlineStart: spacingVars['--spacing-3'],
213
213
  },
214
214
  navNext: {
215
- right: spacingVars['--spacing-3'],
215
+ insetInlineEnd: spacingVars['--spacing-3'],
216
216
  },
217
217
  counter: {
218
218
  position: 'absolute',
219
219
  top: spacingVars['--spacing-3'],
220
- left: spacingVars['--spacing-3'],
220
+ insetInlineStart: spacingVars['--spacing-3'],
221
221
  color: colorVars['--color-on-dark'],
222
222
  fontSize: typeScaleVars['--text-body-size'],
223
223
  lineHeight: typeScaleVars['--text-body-leading'],
@@ -532,7 +532,11 @@ export function Lightbox({
532
532
  {isGallery && (
533
533
  <div {...stylex.props(styles.navButton, styles.navPrev)}>
534
534
  <IconButton
535
- icon={<Icon icon="chevronLeft" size="sm" color="inherit" />}
535
+ icon={
536
+ <span {...stylex.props(rtlStyles.mirror)}>
537
+ <Icon icon="chevronLeft" size="sm" color="inherit" />
538
+ </span>
539
+ }
536
540
  label={t('@astryx.lightbox.previous')}
537
541
  variant="ghost"
538
542
  isDisabled={!canPrev}
@@ -587,7 +591,11 @@ export function Lightbox({
587
591
  {isGallery && (
588
592
  <div {...stylex.props(styles.navButton, styles.navNext)}>
589
593
  <IconButton
590
- icon={<Icon icon="chevronRight" size="sm" color="inherit" />}
594
+ icon={
595
+ <span {...stylex.props(rtlStyles.mirror)}>
596
+ <Icon icon="chevronRight" size="sm" color="inherit" />
597
+ </span>
598
+ }
591
599
  label={t('@astryx.lightbox.next')}
592
600
  variant="ghost"
593
601
  isDisabled={!canNext}
@@ -136,35 +136,23 @@ describe('List', () => {
136
136
  expect(container.querySelector('ul')).toBeInTheDocument();
137
137
  });
138
138
 
139
- it('adds role="list" when listStyle is none (Safari fix)', () => {
140
- const {container} = render(
141
- <List>
142
- <ListItem label="Item" />
143
- </List>,
144
- );
145
- const ul = container.querySelector('ul');
146
- expect(ul).toHaveAttribute('role', 'list');
147
- });
148
-
149
- it('does not add role="list" when listStyle is disc', () => {
150
- const {container} = render(
151
- <List listStyle="disc">
152
- <ListItem label="Item" />
153
- </List>,
154
- );
155
- const ul = container.querySelector('ul');
156
- expect(ul).not.toHaveAttribute('role');
157
- });
158
-
159
- it('does not add role="list" when listStyle is decimal', () => {
160
- const {container} = render(
161
- <List listStyle="decimal">
162
- <ListItem label="Item" />
163
- </List>,
164
- );
165
- const ol = container.querySelector('ol');
166
- expect(ol).not.toHaveAttribute('role');
167
- });
139
+ it.each(['none', 'disc', 'circle', 'decimal'] as const)(
140
+ 'adds an explicit role="list" when listStyle is %s (Safari fix)',
141
+ listStyle => {
142
+ // The base list style always sets list-style-type: none (markers are
143
+ // custom-rendered by ListItem), so Safari/VoiceOver drops the implicit
144
+ // list role for EVERY variant — the explicit role must always be there.
145
+ render(
146
+ <List listStyle={listStyle}>
147
+ <ListItem label="Item 1" />
148
+ <ListItem label="Item 2" />
149
+ </List>,
150
+ );
151
+ const list = screen.getByRole('list');
152
+ expect(list).toHaveAttribute('role', 'list');
153
+ expect(screen.getAllByRole('listitem')).toHaveLength(2);
154
+ },
155
+ );
168
156
 
169
157
  it('renders items as <li> elements', () => {
170
158
  const {container} = render(
package/src/List/List.tsx CHANGED
@@ -168,7 +168,12 @@ export function List({
168
168
  data-testid={testId}
169
169
  aria-labelledby={header != null ? headerId : undefined}
170
170
  {...(isOrdered && start != null && start !== 1 ? {start} : {})}
171
- {...(listStyle === 'none' && !isOrdered ? {role: 'list'} : {})}
171
+ // The base list style always sets list-style-type: none (markers are
172
+ // custom-rendered by ListItem), and Safari/VoiceOver drops implicit
173
+ // list semantics for lists styled with list-style: none. The explicit
174
+ // role restores "list, N items" announcements for every listStyle
175
+ // variant.
176
+ role="list"
172
177
  {...mergeProps(
173
178
  themeProps('list', {density, listStyle}),
174
179
  stylex.props(
@@ -59,6 +59,11 @@ export const docs = {
59
59
  type: '(e: MouseEvent) => void',
60
60
  description: 'Click handler; enables the invisible button pattern.',
61
61
  },
62
+ {
63
+ name: 'interactiveRef',
64
+ type: 'RefObject<HTMLElement | null>',
65
+ description: 'Ref to a nested control (e.g. a checkbox in startContent) that owns the item\'s keyboard access and action. The row becomes an enlarged click/tap target that delegates surface clicks to it (useClickableContainer) and renders no invisible button/anchor, so the row adds no second tab stop (WCAG 4.1.2). Mutually exclusive with onClick/href — those are ignored when set.',
66
+ },
62
67
  {
63
68
  name: 'href',
64
69
  type: 'string',
@@ -121,6 +126,11 @@ export const docsZh = {
121
126
  type: '(e: MouseEvent) => void',
122
127
  description: '点击处理函数;启用隐形按钮模式。',
123
128
  },
129
+ {
130
+ name: 'interactiveRef',
131
+ type: 'RefObject<HTMLElement | null>',
132
+ description: '指向嵌套控件(如 startContent 中的复选框)的 ref,该控件承载项目的键盘访问和操作。行成为更大的点击/触摸目标,将表面点击委托给该控件(useClickableContainer),且不渲染不可见按钮/锚点,因此行不会增加第二个 Tab 停留点(WCAG 4.1.2)。与 onClick/href 互斥——设置后二者将被忽略。',
133
+ },
124
134
  {
125
135
  name: 'href',
126
136
  type: 'string',
@@ -162,6 +172,8 @@ export const docsDense = {
162
172
  startContent: 'Content before label area (e.g. icon, avatar).',
163
173
  endContent: 'Content after label area (e.g. badge, chevron).',
164
174
  onClick: 'Click handler; enables invisible button pattern.',
175
+ interactiveRef:
176
+ 'Ref to a nested control that owns the item\'s keyboard access/action; row delegates surface clicks to it (useClickableContainer), no invisible button/anchor, no second tab stop (WCAG 4.1.2). Mutually exclusive with onClick/href.',
165
177
  href: 'Link URL; enables invisible anchor pattern.',
166
178
  target: 'Link target attribute, only when href provided. target="_blank" auto-adds noopener noreferrer.',
167
179
  rel: 'Link relationship tokens. noopener noreferrer are merged for target="_blank".',
@@ -75,6 +75,17 @@ export interface ListItemProps extends BaseProps<HTMLLIElement> {
75
75
  */
76
76
  onClick?: (e: React.MouseEvent) => void;
77
77
 
78
+ /**
79
+ * Ref to a nested control inside the item (e.g. a checkbox in
80
+ * `startContent`) that already provides the item's keyboard access and
81
+ * action. When set, the item becomes an enlarged click/tap target that
82
+ * delegates surface clicks to that control via the `useClickableContainer`
83
+ * pattern: it renders no invisible button/anchor, so the row adds no second
84
+ * tab stop (WCAG 4.1.2 — one focusable control per option). Mutually
85
+ * exclusive with `onClick`/`href` — when set those are ignored.
86
+ */
87
+ interactiveRef?: React.RefObject<HTMLElement | null>;
88
+
78
89
  /**
79
90
  * URL for link items. Renders an invisible anchor element.
80
91
  * Automatically enables hover/press styles when provided.
@@ -199,6 +210,7 @@ export function ListItem({
199
210
  startContent,
200
211
  endContent,
201
212
  onClick,
213
+ interactiveRef,
202
214
  href,
203
215
  target,
204
216
  rel,
@@ -239,6 +251,7 @@ export function ListItem({
239
251
  description={description}
240
252
  endContent={endContent}
241
253
  onClick={onClick}
254
+ interactiveRef={interactiveRef}
242
255
  href={href}
243
256
  target={target as '_blank' | '_self'}
244
257
  rel={rel}
@@ -236,6 +236,27 @@ describe('Markdown', () => {
236
236
  expect(img!.getAttribute('src')).toBe('image.png');
237
237
  });
238
238
 
239
+ it('uses the components.image override for a standalone (block) image', () => {
240
+ // A standalone image line parses as a block image; its render path must
241
+ // honor components.image just like the inline image path does.
242
+ render(
243
+ <Markdown
244
+ components={{
245
+ image: ({src, alt}) => (
246
+ <span data-testid="custom-image" data-src={src}>
247
+ {alt}
248
+ </span>
249
+ ),
250
+ }}>
251
+ {'![alt text](image.png)'}
252
+ </Markdown>,
253
+ );
254
+ expect(document.querySelector('img')).not.toBeInTheDocument();
255
+ const custom = screen.getByTestId('custom-image');
256
+ expect(custom).toHaveTextContent('alt text');
257
+ expect(custom.getAttribute('data-src')).toBe('image.png');
258
+ });
259
+
239
260
  it('shifts heading levels with headingLevelStart', () => {
240
261
  render(<Markdown headingLevelStart={3}>{'# Heading 1'}</Markdown>);
241
262
  expect(screen.getByText('Heading 1').tagName).toBe('H3');
@@ -1523,6 +1523,10 @@ function renderBlock(
1523
1523
  </div>
1524
1524
  );
1525
1525
  }
1526
+ const ImageComp = components?.image;
1527
+ if (ImageComp) {
1528
+ return <ImageComp key={index} src={safeSrc} alt={node.alt} />;
1529
+ }
1526
1530
  return (
1527
1531
  <div
1528
1532
  key={index}
@@ -5,6 +5,7 @@ import {render, screen} from '@testing-library/react';
5
5
  import userEvent from '@testing-library/user-event';
6
6
  import {MetadataList} from './MetadataList';
7
7
  import {MetadataListItem} from './MetadataListItem';
8
+ import {InternationalizationProvider} from '../i18n';
8
9
 
9
10
  describe('MetadataList', () => {
10
11
  it('renders a description list with items', () => {
@@ -87,6 +88,29 @@ describe('MetadataList', () => {
87
88
  expect(screen.queryByText('B')).not.toBeInTheDocument();
88
89
  });
89
90
 
91
+ it('localizes the show more / show less labels through the i18n catalog', async () => {
92
+ const user = userEvent.setup();
93
+
94
+ render(
95
+ <InternationalizationProvider
96
+ locale="fr"
97
+ overrides={{
98
+ fr: {
99
+ '@astryx.metadataList.showMore': 'Afficher plus',
100
+ '@astryx.metadataList.showLess': 'Afficher moins',
101
+ },
102
+ }}>
103
+ <MetadataList maxNumOfItems={1}>
104
+ <MetadataListItem label="A">1</MetadataListItem>
105
+ <MetadataListItem label="B">2</MetadataListItem>
106
+ </MetadataList>
107
+ </InternationalizationProvider>,
108
+ );
109
+
110
+ await user.click(screen.getByText('Afficher plus'));
111
+ expect(screen.getByText('Afficher moins')).toBeInTheDocument();
112
+ });
113
+
90
114
  it('does not show toggle in horizontal mode even with maxNumOfItems', () => {
91
115
  render(
92
116
  <MetadataList orientation="horizontal" maxNumOfItems={1}>
@@ -4,7 +4,7 @@
4
4
 
5
5
  /**
6
6
  * @file MetadataList.tsx
7
- * @input Uses React, ReactNode, StyleXStyles, theme tokens, MetadataListContext
7
+ * @input Uses React, ReactNode, StyleXStyles, theme tokens, MetadataListContext, i18n (useTranslator)
8
8
  * @output Exports MetadataList component, MetadataListProps, MetadataListColumns types
9
9
  * @position Core implementation; consumed by index.ts, tested by MetadataList.test.tsx
10
10
  *
@@ -31,6 +31,7 @@ import {
31
31
  import type {BaseProps} from '../BaseProps';
32
32
  import {mergeProps} from '../utils';
33
33
  import {themeProps} from '../utils/themeProps';
34
+ import {useTranslator} from '../i18n';
34
35
 
35
36
  // =============================================================================
36
37
  // Types
@@ -197,6 +198,7 @@ export function MetadataList({
197
198
  const labelConfig = label ?? (isMultiColumn ? LABEL_TOP : LABEL_START);
198
199
  const [isShowAll, setIsShowAll] = useState(false);
199
200
  const contentId = useId();
201
+ const t = useTranslator();
200
202
 
201
203
  const contextValue = useMemo(
202
204
  () => ({
@@ -283,7 +285,9 @@ export function MetadataList({
283
285
  aria-expanded={isShowAll}
284
286
  onClick={() => setIsShowAll(prev => !prev)}
285
287
  {...stylex.props(styles.toggleButton)}>
286
- {isShowAll ? 'Show less' : 'Show more'}
288
+ {isShowAll
289
+ ? t('@astryx.metadataList.showLess')
290
+ : t('@astryx.metadataList.showMore')}
287
291
  </button>
288
292
  )}
289
293
  </div>
@@ -21,6 +21,11 @@ export const docs = {
21
21
  theming: {
22
22
  targets: [
23
23
  {className: 'astryx-multi-selector', visualProps: ['size', 'status']},
24
+ {className: 'astryx-multi-selector-clear-icon'},
25
+ {
26
+ className: 'astryx-multi-selector-indicator-icon',
27
+ states: ['state'],
28
+ },
24
29
  ],
25
30
  },
26
31
  components: [
@@ -155,12 +160,25 @@ export const docs = {
155
160
  type: "{type: 'error' | 'warning' | 'success', message?: string}",
156
161
  description: 'Validation status with an optional message.',
157
162
  },
163
+ {
164
+ name: 'statusVariant',
165
+ type: "'attached' | 'detached'",
166
+ description:
167
+ 'How the status message is placed relative to the input. attached overlaps directly below the input (bordered treatment); detached floats below as a separate element with spacing.',
168
+ default: "'attached'",
169
+ },
158
170
  {
159
171
  name: 'renderOption',
160
172
  type: '(option: MultiSelectorOptionData) => ReactNode',
161
173
  description:
162
174
  'Custom render function for each selectable option in the dropdown. Not called for dividers, sections, or the select-all row.',
163
175
  },
176
+ {
177
+ name: 'width',
178
+ type: 'SizeValue',
179
+ description:
180
+ 'Width of the field (number = pixels, string used as-is, e.g. "100%"). Sizes the whole field (label, control, and status) so they stay aligned.',
181
+ },
164
182
  {
165
183
  name: 'xstyle',
166
184
  type: 'StyleXStyles',
@@ -250,6 +268,7 @@ export const docsZh = {
250
268
  isRequired: '将字段标记为必填。',
251
269
  isLoading: '在触发器中显示加载旋转器。',
252
270
  status: '带可选消息的验证状态。',
271
+ statusVariant: '状态消息的放置方式:attached 直接叠加在输入框下方;detached 作为独立元素浮于下方并留有间距。',
253
272
  renderOption:
254
273
  '每个可选选项的自定义渲染函数。不会用于分隔线、分组或全选行。',
255
274
  xstyle: '布局自定义的 StyleX 样式,必须是 stylex.create() 值。',
@@ -375,6 +394,7 @@ export const docsDense = {
375
394
  isRequired: 'marks required',
376
395
  isLoading: 'spinner in trigger',
377
396
  status: 'validation status w/ optional message',
397
+ statusVariant: 'How status message is placed: attached overlaps below input; detached floats below w/ spacing.',
378
398
  renderOption:
379
399
  'custom render fn per selectable option; not dividers/sections/select-all',
380
400
  xstyle: 'StyleX layout styles; stylex.create() only',