@astryxdesign/core 0.1.9 → 0.2.0-canary.0e01213

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 (625) 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/Center/Center.d.ts +20 -3
  33. package/dist/Center/Center.d.ts.map +1 -1
  34. package/dist/Center/Center.js +10 -2
  35. package/dist/Chat/ChatComposerInput.js +1 -1
  36. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  37. package/dist/Chat/ChatDictationButton.js +3 -1
  38. package/dist/Chat/ChatLayout.d.ts +7 -0
  39. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  40. package/dist/Chat/ChatLayout.js +16 -5
  41. package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
  42. package/dist/Chat/ChatMessageBubble.js +8 -8
  43. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  44. package/dist/Chat/ChatToolCalls.js +10 -3
  45. package/dist/Chat/useTriggerMenu.js +2 -2
  46. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  47. package/dist/CheckboxInput/CheckboxInput.js +6 -6
  48. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  49. package/dist/CheckboxList/CheckboxListItem.js +19 -4
  50. package/dist/Citation/Citation.d.ts +22 -1
  51. package/dist/Citation/Citation.d.ts.map +1 -1
  52. package/dist/Citation/Citation.js +20 -6
  53. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  54. package/dist/CodeBlock/CodeBlock.js +16 -10
  55. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  56. package/dist/Collapsible/Collapsible.js +3 -1
  57. package/dist/CommandPalette/CommandPalette.d.ts +1 -1
  58. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  59. package/dist/CommandPalette/CommandPalette.js +42 -3
  60. package/dist/CommandPalette/CommandPaletteItem.js +1 -1
  61. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  62. package/dist/ContextMenu/ContextMenu.js +23 -5
  63. package/dist/ContextMenu/index.d.ts +1 -0
  64. package/dist/ContextMenu/index.d.ts.map +1 -1
  65. package/dist/ContextMenu/index.js +3 -1
  66. package/dist/DateInput/DateInput.d.ts +10 -2
  67. package/dist/DateInput/DateInput.d.ts.map +1 -1
  68. package/dist/DateInput/DateInput.js +33 -19
  69. package/dist/DateRangeInput/DateRangeInput.d.ts +10 -2
  70. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  71. package/dist/DateRangeInput/DateRangeInput.js +23 -20
  72. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  73. package/dist/DateTimeInput/DateTimeInput.js +23 -18
  74. package/dist/Divider/Divider.d.ts +2 -2
  75. package/dist/Divider/Divider.d.ts.map +1 -1
  76. package/dist/Divider/Divider.js +13 -0
  77. package/dist/DropdownMenu/DropdownMenu.d.ts +6 -0
  78. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  79. package/dist/DropdownMenu/DropdownMenu.js +22 -5
  80. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +18 -12
  81. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  82. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +42 -64
  83. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  84. package/dist/DropdownMenu/DropdownMenuItem.js +5 -2
  85. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +8 -11
  86. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
  87. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +3 -1
  88. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +1 -1
  89. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  90. package/dist/DropdownMenu/DropdownMenuRadioItem.js +10 -3
  91. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +104 -0
  92. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -0
  93. package/dist/DropdownMenu/DropdownMenuSubMenu.js +435 -0
  94. package/dist/DropdownMenu/index.d.ts +10 -0
  95. package/dist/DropdownMenu/index.d.ts.map +1 -1
  96. package/dist/DropdownMenu/index.js +13 -0
  97. package/dist/DropdownMenu/menuItemHover.d.ts +24 -0
  98. package/dist/DropdownMenu/menuItemHover.d.ts.map +1 -0
  99. package/dist/DropdownMenu/menuItemHover.js +34 -0
  100. package/dist/DropdownMenu/menuItemRoles.d.ts +8 -0
  101. package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -1
  102. package/dist/DropdownMenu/menuItemRoles.js +10 -1
  103. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  104. package/dist/DropdownMenu/renderDropdownItems.js +27 -10
  105. package/dist/Field/Field.d.ts +3 -1
  106. package/dist/Field/Field.d.ts.map +1 -1
  107. package/dist/Field/Field.js +4 -1
  108. package/dist/Field/FieldLabel.d.ts +2 -1
  109. package/dist/Field/FieldLabel.d.ts.map +1 -1
  110. package/dist/Field/FieldLabel.js +9 -5
  111. package/dist/Field/inputStyles.stylex.d.ts +1 -0
  112. package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
  113. package/dist/Field/inputStyles.stylex.js +1 -0
  114. package/dist/FieldStatus/FieldStatus.d.ts +10 -0
  115. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  116. package/dist/FieldStatus/FieldStatus.js +51 -5
  117. package/dist/FileInput/FileInput.d.ts +10 -2
  118. package/dist/FileInput/FileInput.d.ts.map +1 -1
  119. package/dist/FileInput/FileInput.js +93 -65
  120. package/dist/Heading/Heading.d.ts.map +1 -1
  121. package/dist/Heading/Heading.js +3 -2
  122. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  123. package/dist/HoverCard/useHoverCard.js +10 -4
  124. package/dist/Icon/Icon.d.ts +15 -1
  125. package/dist/Icon/Icon.d.ts.map +1 -1
  126. package/dist/Icon/Icon.js +19 -3
  127. package/dist/Item/Item.d.ts +16 -3
  128. package/dist/Item/Item.d.ts.map +1 -1
  129. package/dist/Item/Item.js +31 -6
  130. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  131. package/dist/Layer/layerAnimations.stylex.js +10 -2
  132. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  133. package/dist/Lightbox/Lightbox.js +34 -15
  134. package/dist/List/List.d.ts.map +1 -1
  135. package/dist/List/List.js +6 -3
  136. package/dist/List/ListItem.d.ts +11 -1
  137. package/dist/List/ListItem.d.ts.map +1 -1
  138. package/dist/List/ListItem.js +2 -0
  139. package/dist/Markdown/Markdown.d.ts.map +1 -1
  140. package/dist/Markdown/Markdown.js +7 -0
  141. package/dist/MetadataList/MetadataList.d.ts +1 -1
  142. package/dist/MetadataList/MetadataList.d.ts.map +1 -1
  143. package/dist/MetadataList/MetadataList.js +4 -2
  144. package/dist/MultiSelector/MultiSelector.d.ts +9 -1
  145. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  146. package/dist/MultiSelector/MultiSelector.js +87 -45
  147. package/dist/NavMenu/NavHeadingMenuItem.js +1 -1
  148. package/dist/NumberInput/NumberInput.d.ts +10 -2
  149. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  150. package/dist/NumberInput/NumberInput.js +22 -17
  151. package/dist/Outline/Outline.js +2 -2
  152. package/dist/Pagination/Pagination.d.ts.map +1 -1
  153. package/dist/Pagination/Pagination.js +11 -3
  154. package/dist/PowerSearch/PowerSearch.d.ts +9 -2
  155. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  156. package/dist/PowerSearch/PowerSearch.js +2 -0
  157. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  158. package/dist/ProgressBar/ProgressBar.js +6 -1
  159. package/dist/RadioList/RadioList.d.ts +2 -2
  160. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  161. package/dist/RadioList/RadioListItem.js +5 -5
  162. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  163. package/dist/Resizable/ResizeHandle.js +49 -19
  164. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
  165. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  166. package/dist/SegmentedControl/SegmentedControlItem.js +5 -3
  167. package/dist/Selector/Selector.d.ts +8 -0
  168. package/dist/Selector/Selector.d.ts.map +1 -1
  169. package/dist/Selector/Selector.js +72 -19
  170. package/dist/SideNav/SideNav.d.ts.map +1 -1
  171. package/dist/SideNav/SideNav.js +5 -5
  172. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  173. package/dist/SideNav/SideNavCollapseButton.js +13 -10
  174. package/dist/SideNav/SideNavItem.d.ts +1 -1
  175. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  176. package/dist/SideNav/SideNavItem.js +12 -10
  177. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  178. package/dist/Skeleton/Skeleton.js +2 -2
  179. package/dist/Slider/Slider.d.ts.map +1 -1
  180. package/dist/Slider/Slider.js +87 -34
  181. package/dist/Spinner/Spinner.d.ts +3 -2
  182. package/dist/Spinner/Spinner.d.ts.map +1 -1
  183. package/dist/Spinner/Spinner.js +11 -2
  184. package/dist/Switch/Switch.d.ts +8 -1
  185. package/dist/Switch/Switch.d.ts.map +1 -1
  186. package/dist/Switch/Switch.js +171 -91
  187. package/dist/TabList/Tab.js +2 -2
  188. package/dist/TabList/TabList.d.ts +2 -11
  189. package/dist/TabList/TabList.d.ts.map +1 -1
  190. package/dist/TabList/TabList.js +5 -9
  191. package/dist/TabList/TabListContext.d.ts +0 -5
  192. package/dist/TabList/TabListContext.d.ts.map +1 -1
  193. package/dist/TabList/TabListContext.js +0 -5
  194. package/dist/TabList/TabMenu.js +1 -1
  195. package/dist/Table/BaseTable.d.ts.map +1 -1
  196. package/dist/Table/BaseTable.js +30 -4
  197. package/dist/Table/TableCell.js +3 -3
  198. package/dist/Table/TableHeaderCell.js +3 -3
  199. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  200. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +15 -11
  201. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts +18 -3
  202. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  203. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +58 -30
  204. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  205. package/dist/Table/plugins/selection/useTableSelection.js +3 -0
  206. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  207. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +15 -7
  208. package/dist/Table/plugins/tree/useTableTreeData.d.ts +11 -0
  209. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  210. package/dist/Table/plugins/tree/useTableTreeData.js +65 -25
  211. package/dist/Table/types.d.ts +32 -0
  212. package/dist/Table/types.d.ts.map +1 -1
  213. package/dist/Text/Text.d.ts +10 -1
  214. package/dist/Text/Text.d.ts.map +1 -1
  215. package/dist/Text/Text.js +25 -2
  216. package/dist/Text/index.d.ts +2 -2
  217. package/dist/Text/index.d.ts.map +1 -1
  218. package/dist/TextArea/TextArea.d.ts +10 -1
  219. package/dist/TextArea/TextArea.d.ts.map +1 -1
  220. package/dist/TextArea/TextArea.js +119 -52
  221. package/dist/TextInput/TextInput.d.ts +10 -2
  222. package/dist/TextInput/TextInput.d.ts.map +1 -1
  223. package/dist/TextInput/TextInput.js +20 -17
  224. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  225. package/dist/Thumbnail/Thumbnail.js +51 -45
  226. package/dist/TimeInput/TimeInput.d.ts +10 -2
  227. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  228. package/dist/TimeInput/TimeInput.js +17 -17
  229. package/dist/Timestamp/Timestamp.d.ts +31 -3
  230. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  231. package/dist/Timestamp/Timestamp.js +48 -77
  232. package/dist/Timestamp/formatInstant.d.ts +41 -0
  233. package/dist/Timestamp/formatInstant.d.ts.map +1 -0
  234. package/dist/Timestamp/formatInstant.js +177 -0
  235. package/dist/Timestamp/index.d.ts +1 -0
  236. package/dist/Timestamp/index.d.ts.map +1 -1
  237. package/dist/Timestamp/tooltipEntries.d.ts +56 -0
  238. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -0
  239. package/dist/Timestamp/tooltipEntries.js +163 -0
  240. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  241. package/dist/Toast/ToastViewport.js +61 -9
  242. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  243. package/dist/ToggleButton/ToggleButton.js +3 -1
  244. package/dist/Token/Token.d.ts +34 -4
  245. package/dist/Token/Token.d.ts.map +1 -1
  246. package/dist/Token/Token.js +85 -45
  247. package/dist/Token/TokenLink.d.ts +62 -0
  248. package/dist/Token/TokenLink.d.ts.map +1 -0
  249. package/dist/Token/TokenLink.js +76 -0
  250. package/dist/Token/index.d.ts +1 -1
  251. package/dist/Token/index.d.ts.map +1 -1
  252. package/dist/Tokenizer/Tokenizer.d.ts +9 -2
  253. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  254. package/dist/Tokenizer/Tokenizer.js +4 -2
  255. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  256. package/dist/Tooltip/useTooltip.js +17 -1
  257. package/dist/TopNav/TopNavItem.d.ts +3 -1
  258. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  259. package/dist/TopNav/TopNavItem.js +30 -3
  260. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  261. package/dist/TopNav/TopNavMegaMenu.js +9 -3
  262. package/dist/TreeList/TreeList.d.ts.map +1 -1
  263. package/dist/TreeList/TreeList.js +1 -0
  264. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  265. package/dist/TreeList/TreeListBranches.js +8 -6
  266. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  267. package/dist/TreeList/TreeListItem.js +32 -14
  268. package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
  269. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  270. package/dist/Typeahead/BaseTypeahead.js +22 -11
  271. package/dist/Typeahead/Typeahead.d.ts +9 -2
  272. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  273. package/dist/Typeahead/Typeahead.js +11 -2
  274. package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
  275. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  276. package/dist/Typeahead/TypeaheadItem.js +20 -10
  277. package/dist/astryx.css +115 -35
  278. package/dist/astryx.umd.js +51 -51
  279. package/dist/astryx.umd.js.map +4 -4
  280. package/dist/hooks/containerReveal.pool.stylex.d.ts +281 -0
  281. package/dist/hooks/containerReveal.pool.stylex.d.ts.map +1 -0
  282. package/dist/hooks/containerReveal.pool.stylex.js +281 -0
  283. package/dist/hooks/index.d.ts +4 -0
  284. package/dist/hooks/index.d.ts.map +1 -1
  285. package/dist/hooks/index.js +3 -1
  286. package/dist/hooks/isRtlElement.d.ts +18 -0
  287. package/dist/hooks/isRtlElement.d.ts.map +1 -0
  288. package/dist/hooks/isRtlElement.js +27 -0
  289. package/dist/hooks/useAnnounce.d.ts +3 -0
  290. package/dist/hooks/useAnnounce.d.ts.map +1 -1
  291. package/dist/hooks/useAnnounce.js +39 -1
  292. package/dist/hooks/useContainerReveal.d.ts +83 -0
  293. package/dist/hooks/useContainerReveal.d.ts.map +1 -0
  294. package/dist/hooks/useContainerReveal.js +123 -0
  295. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  296. package/dist/hooks/useFocusTrap.js +52 -11
  297. package/dist/hooks/useGridFocus.d.ts +6 -1
  298. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  299. package/dist/hooks/useGridFocus.js +12 -9
  300. package/dist/hooks/useHotkeys.d.ts +9 -0
  301. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  302. package/dist/hooks/useHotkeys.js +9 -0
  303. package/dist/hooks/useInputStatusIcon.d.ts +38 -0
  304. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -0
  305. package/dist/hooks/useInputStatusIcon.js +169 -0
  306. package/dist/hooks/useKeyboardHint.d.ts +1 -1
  307. package/dist/hooks/useKeyboardHint.d.ts.map +1 -1
  308. package/dist/hooks/useKeyboardHint.js +4 -2
  309. package/dist/hooks/useListFocus.d.ts +39 -1
  310. package/dist/hooks/useListFocus.d.ts.map +1 -1
  311. package/dist/hooks/useListFocus.js +54 -9
  312. package/dist/i18n/InternationalizationContext.d.ts +1 -0
  313. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  314. package/dist/i18n/InternationalizationContext.js +3 -0
  315. package/dist/i18n/InternationalizationProvider.d.ts +8 -1
  316. package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
  317. package/dist/i18n/InternationalizationProvider.js +5 -1
  318. package/dist/i18n/getLocaleDirection.d.ts +18 -0
  319. package/dist/i18n/getLocaleDirection.d.ts.map +1 -0
  320. package/dist/i18n/getLocaleDirection.js +29 -0
  321. package/dist/i18n/index.d.ts +6 -0
  322. package/dist/i18n/index.d.ts.map +1 -1
  323. package/dist/i18n/index.js +7 -1
  324. package/dist/i18n/useDirection.d.ts +2 -0
  325. package/dist/i18n/useDirection.d.ts.map +1 -0
  326. package/dist/i18n/useDirection.js +27 -0
  327. package/dist/theme/defineTheme.d.ts +6 -0
  328. package/dist/theme/defineTheme.d.ts.map +1 -1
  329. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  330. package/dist/theme/derivedVarRegistry.js +13 -0
  331. package/dist/theme/expandColorScale.d.ts +19 -2
  332. package/dist/theme/expandColorScale.d.ts.map +1 -1
  333. package/dist/theme/expandColorScale.js +38 -10
  334. package/dist/theme/index.d.ts +3 -3
  335. package/dist/theme/index.d.ts.map +1 -1
  336. package/dist/theme/types.d.ts +39 -2
  337. package/dist/theme/types.d.ts.map +1 -1
  338. package/dist/utils/index.d.ts +1 -0
  339. package/dist/utils/index.d.ts.map +1 -1
  340. package/dist/utils/index.js +2 -1
  341. package/dist/utils/plainDate.d.ts +17 -0
  342. package/dist/utils/plainDate.d.ts.map +1 -1
  343. package/dist/utils/plainDate.js +23 -4
  344. package/dist/utils/rtlStyles.d.ts +24 -0
  345. package/dist/utils/rtlStyles.d.ts.map +1 -0
  346. package/dist/utils/rtlStyles.js +27 -0
  347. package/locales/en.json +120 -0
  348. package/locales/pseudo.json +90 -0
  349. package/package.json +8 -3
  350. package/src/Avatar/Avatar.doc.mjs +50 -3
  351. package/src/Avatar/Avatar.test.tsx +218 -0
  352. package/src/Avatar/Avatar.tsx +285 -79
  353. package/src/Avatar/AvatarStatusDot.doc.mjs +4 -4
  354. package/src/Avatar/AvatarStatusDot.tsx +6 -4
  355. package/src/AvatarGroup/AvatarGroup.doc.mjs +2 -0
  356. package/src/AvatarGroup/AvatarGroup.test.tsx +162 -2
  357. package/src/AvatarGroup/AvatarGroup.tsx +58 -3
  358. package/src/AvatarGroup/AvatarGroupOverflow.tsx +5 -2
  359. package/src/Banner/Banner.tsx +6 -6
  360. package/src/Breadcrumbs/BreadcrumbItem.tsx +10 -10
  361. package/src/Breadcrumbs/Breadcrumbs.test.tsx +52 -1
  362. package/src/Breadcrumbs/index.ts +4 -0
  363. package/src/Button/Button.doc.mjs +0 -4
  364. package/src/Button/Button.test.tsx +16 -0
  365. package/src/Button/Button.tsx +6 -4
  366. package/src/Calendar/Calendar.test.tsx +170 -20
  367. package/src/Calendar/Calendar.tsx +73 -16
  368. package/src/Calendar/dayCellUtils.test.ts +18 -18
  369. package/src/Calendar/dayCellUtils.ts +2 -2
  370. package/src/Calendar/styles.ts +33 -39
  371. package/src/Card/Card.tsx +1 -1
  372. package/src/Carousel/Carousel.tsx +25 -6
  373. package/src/Center/Center.doc.mjs +38 -0
  374. package/src/Center/Center.test.tsx +75 -0
  375. package/src/Center/Center.tsx +37 -2
  376. package/src/Chat/ChatComposer.doc.mjs +2 -2
  377. package/src/Chat/ChatComposerInput.doc.mjs +1 -1
  378. package/src/Chat/ChatComposerInput.tsx +2 -2
  379. package/src/Chat/ChatDictationButton.tsx +7 -1
  380. package/src/Chat/ChatLayout.test.tsx +57 -0
  381. package/src/Chat/ChatLayout.tsx +26 -5
  382. package/src/Chat/ChatMessageBubble.tsx +15 -10
  383. package/src/Chat/ChatSystemMessage.test.tsx +5 -0
  384. package/src/Chat/ChatToolCalls.tsx +9 -2
  385. package/src/Chat/useTriggerMenu.tsx +1 -1
  386. package/src/CheckboxInput/CheckboxInput.doc.mjs +6 -0
  387. package/src/CheckboxInput/CheckboxInput.test.tsx +23 -0
  388. package/src/CheckboxInput/CheckboxInput.tsx +21 -6
  389. package/src/CheckboxList/CheckboxList.doc.mjs +6 -0
  390. package/src/CheckboxList/CheckboxList.test.tsx +94 -3
  391. package/src/CheckboxList/CheckboxListItem.tsx +17 -10
  392. package/src/Citation/Citation.doc.mjs +4 -3
  393. package/src/Citation/Citation.test.tsx +97 -0
  394. package/src/Citation/Citation.tsx +43 -11
  395. package/src/CodeBlock/CodeBlock.test.tsx +17 -0
  396. package/src/CodeBlock/CodeBlock.tsx +38 -11
  397. package/src/Collapsible/Collapsible.doc.mjs +1 -0
  398. package/src/Collapsible/Collapsible.test.tsx +7 -0
  399. package/src/Collapsible/Collapsible.tsx +16 -6
  400. package/src/CommandPalette/CommandPalette.test.tsx +95 -1
  401. package/src/CommandPalette/CommandPalette.tsx +41 -2
  402. package/src/CommandPalette/CommandPaletteItem.tsx +1 -1
  403. package/src/ContextMenu/ContextMenu.test.tsx +59 -2
  404. package/src/ContextMenu/ContextMenu.tsx +22 -11
  405. package/src/ContextMenu/index.ts +5 -0
  406. package/src/DateInput/DateInput.doc.mjs +25 -0
  407. package/src/DateInput/DateInput.test.tsx +230 -0
  408. package/src/DateInput/DateInput.tsx +51 -29
  409. package/src/DateRangeInput/DateRangeInput.doc.mjs +16 -0
  410. package/src/DateRangeInput/DateRangeInput.test.tsx +140 -0
  411. package/src/DateRangeInput/DateRangeInput.tsx +42 -30
  412. package/src/DateTimeInput/DateTimeInput.doc.mjs +22 -1
  413. package/src/DateTimeInput/DateTimeInput.test.tsx +112 -0
  414. package/src/DateTimeInput/DateTimeInput.tsx +44 -39
  415. package/src/Divider/Divider.test.tsx +32 -8
  416. package/src/Divider/Divider.tsx +14 -1
  417. package/src/DropdownMenu/DropdownMenu.doc.mjs +5 -2
  418. package/src/DropdownMenu/DropdownMenu.test.tsx +95 -0
  419. package/src/DropdownMenu/DropdownMenu.tsx +31 -12
  420. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +49 -66
  421. package/src/DropdownMenu/DropdownMenuItem.tsx +9 -3
  422. package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +4 -12
  423. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +9 -10
  424. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +19 -4
  425. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +84 -2
  426. package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +88 -0
  427. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +680 -0
  428. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +569 -0
  429. package/src/DropdownMenu/index.ts +16 -0
  430. package/src/DropdownMenu/menuItemHover.ts +40 -0
  431. package/src/DropdownMenu/menuItemRoles.ts +9 -0
  432. package/src/DropdownMenu/renderDropdownItems.tsx +40 -14
  433. package/src/Field/Field.doc.mjs +4 -0
  434. package/src/Field/Field.tsx +14 -9
  435. package/src/Field/FieldLabel.test.tsx +25 -0
  436. package/src/Field/FieldLabel.tsx +12 -3
  437. package/src/Field/inputStyles.stylex.ts +2 -0
  438. package/src/FieldStatus/FieldStatus.doc.mjs +1 -0
  439. package/src/FieldStatus/FieldStatus.test.tsx +133 -0
  440. package/src/FieldStatus/FieldStatus.tsx +61 -4
  441. package/src/FileInput/FileInput.doc.mjs +23 -2
  442. package/src/FileInput/FileInput.test.tsx +104 -13
  443. package/src/FileInput/FileInput.tsx +106 -79
  444. package/src/Heading/Heading.tsx +2 -1
  445. package/src/HoverCard/useHoverCard.tsx +15 -3
  446. package/src/Icon/Icon.doc.mjs +13 -0
  447. package/src/Icon/Icon.test.tsx +89 -0
  448. package/src/Icon/Icon.tsx +51 -3
  449. package/src/Item/Item.doc.mjs +5 -0
  450. package/src/Item/Item.test.tsx +88 -0
  451. package/src/Item/Item.tsx +57 -11
  452. package/src/Layer/layerAnimations.stylex.ts +23 -0
  453. package/src/Lightbox/Lightbox.test.tsx +33 -0
  454. package/src/Lightbox/Lightbox.tsx +30 -10
  455. package/src/List/List.test.tsx +17 -29
  456. package/src/List/List.tsx +6 -1
  457. package/src/List/ListItem.doc.mjs +12 -0
  458. package/src/List/ListItem.tsx +13 -0
  459. package/src/Markdown/Markdown.test.tsx +21 -0
  460. package/src/Markdown/Markdown.tsx +4 -0
  461. package/src/MetadataList/MetadataList.test.tsx +24 -0
  462. package/src/MetadataList/MetadataList.tsx +6 -2
  463. package/src/MultiSelector/MultiSelector.doc.mjs +20 -0
  464. package/src/MultiSelector/MultiSelector.test.tsx +436 -0
  465. package/src/MultiSelector/MultiSelector.tsx +133 -67
  466. package/src/NavMenu/NavHeadingMenuItem.tsx +1 -1
  467. package/src/NumberInput/NumberInput.doc.mjs +21 -0
  468. package/src/NumberInput/NumberInput.test.tsx +71 -0
  469. package/src/NumberInput/NumberInput.tsx +35 -26
  470. package/src/Outline/Outline.test.tsx +12 -12
  471. package/src/Outline/Outline.tsx +2 -2
  472. package/src/Pagination/Pagination.tsx +11 -3
  473. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  474. package/src/PowerSearch/PowerSearch.test.tsx +23 -0
  475. package/src/PowerSearch/PowerSearch.tsx +10 -1
  476. package/src/ProgressBar/ProgressBar.test.tsx +34 -0
  477. package/src/ProgressBar/ProgressBar.tsx +16 -1
  478. package/src/RadioList/RadioList.doc.mjs +6 -0
  479. package/src/RadioList/RadioList.test.tsx +26 -4
  480. package/src/RadioList/RadioList.tsx +2 -2
  481. package/src/RadioList/RadioListItem.tsx +12 -5
  482. package/src/Resizable/ResizeHandle.test.tsx +94 -0
  483. package/src/Resizable/ResizeHandle.tsx +70 -20
  484. package/src/SegmentedControl/SegmentedControl.test.tsx +28 -0
  485. package/src/SegmentedControl/SegmentedControlItem.tsx +22 -2
  486. package/src/Selector/Selector.doc.mjs +15 -0
  487. package/src/Selector/Selector.test.tsx +400 -0
  488. package/src/Selector/Selector.tsx +95 -23
  489. package/src/SideNav/SideNav.tsx +1 -8
  490. package/src/SideNav/SideNavCollapseButton.tsx +9 -7
  491. package/src/SideNav/SideNavItem.tsx +5 -3
  492. package/src/Skeleton/Skeleton.test.tsx +16 -0
  493. package/src/Skeleton/Skeleton.tsx +13 -1
  494. package/src/Slider/Slider.doc.mjs +6 -0
  495. package/src/Slider/Slider.test.tsx +240 -12
  496. package/src/Slider/Slider.tsx +96 -23
  497. package/src/Spinner/Spinner.test.tsx +10 -8
  498. package/src/Spinner/Spinner.tsx +15 -5
  499. package/src/Switch/Switch.doc.mjs +142 -42
  500. package/src/Switch/Switch.test.tsx +111 -0
  501. package/src/Switch/Switch.tsx +149 -32
  502. package/src/TabList/Tab.tsx +2 -2
  503. package/src/TabList/TabList.doc.mjs +0 -6
  504. package/src/TabList/TabList.test.tsx +3 -15
  505. package/src/TabList/TabList.tsx +6 -17
  506. package/src/TabList/TabListContext.ts +0 -6
  507. package/src/TabList/TabMenu.tsx +2 -2
  508. package/src/Table/BaseTable.tsx +34 -2
  509. package/src/Table/Table.doc.mjs +11 -0
  510. package/src/Table/Table.test.tsx +85 -0
  511. package/src/Table/TableCell.tsx +3 -3
  512. package/src/Table/TableHeaderCell.tsx +3 -3
  513. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +9 -6
  514. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +19 -0
  515. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +48 -21
  516. package/src/Table/plugins/selection/useTableSelection.test.tsx +61 -3
  517. package/src/Table/plugins/selection/useTableSelection.tsx +3 -0
  518. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +38 -9
  519. package/src/Table/plugins/tree/useTableTreeData.test.tsx +161 -2
  520. package/src/Table/plugins/tree/useTableTreeData.tsx +71 -14
  521. package/src/Table/types.ts +33 -0
  522. package/src/Table/useTableRowExpansion.doc.mjs +55 -2
  523. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  524. package/src/Table/useTableTreeData.doc.mjs +9 -0
  525. package/src/Text/Heading.doc.mjs +3 -3
  526. package/src/Text/Text.doc.mjs +1 -1
  527. package/src/Text/Text.test.tsx +42 -0
  528. package/src/Text/Text.tsx +25 -1
  529. package/src/Text/index.ts +2 -6
  530. package/src/TextArea/TextArea.doc.mjs +23 -2
  531. package/src/TextArea/TextArea.test.tsx +132 -4
  532. package/src/TextArea/TextArea.tsx +161 -67
  533. package/src/TextInput/TextInput.doc.mjs +21 -0
  534. package/src/TextInput/TextInput.test.tsx +150 -0
  535. package/src/TextInput/TextInput.tsx +28 -25
  536. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  537. package/src/Thumbnail/Thumbnail.test.tsx +24 -1
  538. package/src/Thumbnail/Thumbnail.tsx +28 -35
  539. package/src/TimeInput/TimeInput.doc.mjs +21 -0
  540. package/src/TimeInput/TimeInput.test.tsx +23 -0
  541. package/src/TimeInput/TimeInput.tsx +24 -25
  542. package/src/Timestamp/Timestamp.doc.mjs +32 -6
  543. package/src/Timestamp/Timestamp.test.tsx +371 -0
  544. package/src/Timestamp/Timestamp.tsx +96 -93
  545. package/src/Timestamp/formatInstant.ts +218 -0
  546. package/src/Timestamp/index.ts +4 -0
  547. package/src/Timestamp/tooltipEntries.test.ts +552 -0
  548. package/src/Timestamp/tooltipEntries.ts +210 -0
  549. package/src/Toast/ToastViewport.test.tsx +218 -8
  550. package/src/Toast/ToastViewport.tsx +71 -12
  551. package/src/ToggleButton/ToggleButton.test.tsx +24 -0
  552. package/src/ToggleButton/ToggleButton.tsx +19 -1
  553. package/src/Token/Token.test.tsx +166 -15
  554. package/src/Token/Token.tsx +104 -47
  555. package/src/Token/TokenLink.tsx +107 -0
  556. package/src/Token/index.ts +1 -1
  557. package/src/Tokenizer/Tokenizer.doc.mjs +23 -1
  558. package/src/Tokenizer/Tokenizer.test.tsx +23 -0
  559. package/src/Tokenizer/Tokenizer.tsx +14 -2
  560. package/src/Tooltip/Tooltip.test.tsx +46 -0
  561. package/src/Tooltip/useTooltip.tsx +20 -0
  562. package/src/TopNav/TopNav.test.tsx +93 -2
  563. package/src/TopNav/TopNavItem.doc.mjs +3 -3
  564. package/src/TopNav/TopNavItem.tsx +35 -6
  565. package/src/TopNav/TopNavMegaMenu.tsx +26 -2
  566. package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +1 -1
  567. package/src/TreeList/TreeList.doc.mjs +6 -0
  568. package/src/TreeList/TreeList.test.tsx +50 -10
  569. package/src/TreeList/TreeList.tsx +5 -0
  570. package/src/TreeList/TreeListBranches.tsx +8 -6
  571. package/src/TreeList/TreeListItem.tsx +33 -11
  572. package/src/Typeahead/BaseTypeahead.tsx +42 -15
  573. package/src/Typeahead/Typeahead.doc.mjs +13 -0
  574. package/src/Typeahead/Typeahead.test.tsx +112 -0
  575. package/src/Typeahead/Typeahead.tsx +17 -1
  576. package/src/Typeahead/TypeaheadItem.tsx +2 -1
  577. package/src/__tests__/babelPluginAddExtensions.test.ts +190 -0
  578. package/src/__tests__/forcedColors.ts +60 -0
  579. package/src/__tests__/inputWidthContract.test.tsx +62 -0
  580. package/src/doc-mjs.d.ts +19 -0
  581. package/src/hooks/containerReveal.pool.stylex.ts +464 -0
  582. package/src/hooks/index.ts +13 -0
  583. package/src/hooks/isRtlElement.ts +27 -0
  584. package/src/hooks/useAnnounce.test.tsx +75 -2
  585. package/src/hooks/useAnnounce.ts +47 -1
  586. package/src/hooks/useContainerReveal.test.tsx +116 -0
  587. package/src/hooks/useContainerReveal.ts +180 -0
  588. package/src/hooks/useFocusTrap.test.tsx +104 -0
  589. package/src/hooks/useFocusTrap.ts +61 -12
  590. package/src/hooks/useGridFocus.doc.mjs +3 -3
  591. package/src/hooks/useGridFocus.test.tsx +41 -0
  592. package/src/hooks/useGridFocus.ts +21 -10
  593. package/src/hooks/useHotkeys.ts +9 -0
  594. package/src/hooks/useInputStatusIcon.test.tsx +221 -0
  595. package/src/hooks/useInputStatusIcon.tsx +235 -0
  596. package/src/hooks/useKeyboardHint.test.tsx +22 -0
  597. package/src/hooks/useKeyboardHint.tsx +6 -2
  598. package/src/hooks/useListFocus.doc.mjs +25 -3
  599. package/src/hooks/useListFocus.test.tsx +153 -1
  600. package/src/hooks/useListFocus.ts +102 -9
  601. package/src/i18n/InternationalizationContext.ts +4 -0
  602. package/src/i18n/InternationalizationProvider.tsx +12 -2
  603. package/src/i18n/__tests__/getLocaleDirection.test.ts +40 -0
  604. package/src/i18n/__tests__/useDirection.test.tsx +66 -0
  605. package/src/i18n/getLocaleDirection.ts +29 -0
  606. package/src/i18n/index.ts +6 -0
  607. package/src/i18n/useDirection.ts +29 -0
  608. package/src/reset.css +0 -26
  609. package/src/reset.test.ts +60 -0
  610. package/src/theme/defineTheme.ts +6 -0
  611. package/src/theme/derivedVarRegistry.test.ts +1 -0
  612. package/src/theme/derivedVarRegistry.ts +6 -0
  613. package/src/theme/expandColorScale.test.ts +144 -0
  614. package/src/theme/expandColorScale.ts +53 -15
  615. package/src/theme/generateThemeRules.test.ts +42 -0
  616. package/src/theme/index.ts +4 -8
  617. package/src/theme/types.ts +43 -13
  618. package/src/utils/index.ts +2 -0
  619. package/src/utils/plainDate.test.ts +24 -6
  620. package/src/utils/plainDate.ts +18 -4
  621. package/src/utils/rtlStyles.ts +27 -0
  622. package/dist/Thumbnail/thumbnail.markers.stylex.d.ts +0 -9
  623. package/dist/Thumbnail/thumbnail.markers.stylex.d.ts.map +0 -1
  624. package/dist/Thumbnail/thumbnail.markers.stylex.js +0 -14
  625. package/src/Thumbnail/thumbnail.markers.stylex.ts +0 -12
@@ -23,10 +23,12 @@ const NO_DISABLED: number[] = [];
23
23
  function Grid({
24
24
  disabled = NO_DISABLED,
25
25
  seed = 0,
26
+ dir,
26
27
  ...opts
27
28
  }: {
28
29
  disabled?: number[];
29
30
  seed?: number;
31
+ dir?: 'ltr' | 'rtl';
30
32
  } & Partial<Parameters<typeof useGridFocus>[0]>) {
31
33
  const {gridRef, handleKeyDown, handleFocus} = useGridFocus<HTMLDivElement>({
32
34
  columns: 3,
@@ -41,6 +43,7 @@ function Grid({
41
43
  <div
42
44
  ref={gridRef}
43
45
  role="grid"
46
+ dir={dir}
44
47
  onKeyDown={handleKeyDown}
45
48
  onFocus={handleFocus}>
46
49
  {Array.from({length: 9}, (_, i) => (
@@ -130,3 +133,41 @@ describe('useGridFocus roving tabindex (hasRovingTabIndex)', () => {
130
133
  expect(screen.getByTestId('cell-1')).toHaveAttribute('tabindex', '-1');
131
134
  });
132
135
  });
136
+
137
+ describe('useGridFocus RTL auto-detection (WCAG 1.3.2)', () => {
138
+ // jsdom reflects the `dir` attribute into computed style only on the element
139
+ // that carries it, so tests set dir="rtl" on the grid container itself — the
140
+ // element the hook reads via gridRef.
141
+
142
+ it('auto-detects dir="rtl": ArrowLeft moves forward (next cell)', () => {
143
+ render(<Grid seed={1} dir="rtl" />);
144
+ const grid = screen.getByRole('grid');
145
+ screen.getByTestId('cell-1').focus();
146
+ fireEvent.keyDown(grid, {key: 'ArrowLeft'});
147
+ expect(screen.getByTestId('cell-2')).toHaveFocus();
148
+ });
149
+
150
+ it('auto-detects dir="rtl": ArrowRight moves backward (previous cell)', () => {
151
+ render(<Grid seed={1} dir="rtl" />);
152
+ const grid = screen.getByRole('grid');
153
+ screen.getByTestId('cell-1').focus();
154
+ fireEvent.keyDown(grid, {key: 'ArrowRight'});
155
+ expect(screen.getByTestId('cell-0')).toHaveFocus();
156
+ });
157
+
158
+ it('leaves vertical navigation unaffected under dir="rtl"', () => {
159
+ render(<Grid seed={0} dir="rtl" />);
160
+ const grid = screen.getByRole('grid');
161
+ screen.getByTestId('cell-0').focus();
162
+ fireEvent.keyDown(grid, {key: 'ArrowDown'});
163
+ expect(screen.getByTestId('cell-3')).toHaveFocus();
164
+ });
165
+
166
+ it('explicit isRtl={false} overrides a dir="rtl" container', () => {
167
+ render(<Grid seed={1} dir="rtl" isRtl={false} />);
168
+ const grid = screen.getByRole('grid');
169
+ screen.getByTestId('cell-1').focus();
170
+ fireEvent.keyDown(grid, {key: 'ArrowRight'});
171
+ expect(screen.getByTestId('cell-2')).toHaveFocus();
172
+ });
173
+ });
@@ -4,9 +4,12 @@
4
4
 
5
5
  /**
6
6
  * @file useGridFocus.ts
7
- * @input Uses React useCallback, useRef, useIsomorphicLayoutEffect
7
+ * @input Uses React useCallback, useRef, useIsomorphicLayoutEffect,
8
+ * isRtlElement
8
9
  * @output Exports useGridFocus hook for grid keyboard navigation
9
- * @position Core hook; used by Calendar for date grid navigation
10
+ * @position Core hook; used by Calendar for date grid navigation. Auto-detects
11
+ * RTL from the container's computed direction so horizontal arrow keys
12
+ * follow visual direction (WCAG 1.3.2).
10
13
  *
11
14
  * SYNC: When modified, update:
12
15
  * - /packages/core/src/hooks/index.ts
@@ -14,6 +17,7 @@
14
17
  */
15
18
 
16
19
  import {useCallback, useRef} from 'react';
20
+ import {isRtlElement} from './isRtlElement';
17
21
  import {useIsomorphicLayoutEffect} from './useIsomorphicLayoutEffect';
18
22
 
19
23
  /**
@@ -88,7 +92,12 @@ export interface UseGridFocusOptions {
88
92
  /**
89
93
  * Whether the grid is in a right-to-left context. When true, ArrowLeft and
90
94
  * ArrowRight are swapped so horizontal navigation follows visual direction.
91
- * @default false
95
+ *
96
+ * When omitted, the direction is auto-detected from the container's computed
97
+ * `direction` (read lazily on keydown, only for horizontal arrow keys), so
98
+ * grids inside `dir="rtl"` subtrees flip automatically. Pass an explicit
99
+ * boolean to override detection.
100
+ * @default undefined (auto-detect from the container)
92
101
  */
93
102
  isRtl?: boolean;
94
103
 
@@ -217,7 +226,7 @@ export function useGridFocus<T extends HTMLElement = HTMLElement>(
217
226
  onNavigateAfter,
218
227
  onPageUp,
219
228
  onPageDown,
220
- isRtl = false,
229
+ isRtl,
221
230
  hasRovingTabIndex = false,
222
231
  } = options;
223
232
 
@@ -455,13 +464,15 @@ export function useGridFocus<T extends HTMLElement = HTMLElement>(
455
464
 
456
465
  // In RTL, ArrowLeft/ArrowRight are swapped so horizontal navigation
457
466
  // follows visual direction. Vertical keys (Up/Down) are unaffected.
467
+ // Direction is resolved lazily — getComputedStyle runs only when a
468
+ // horizontal arrow key is actually pressed (SSR-safe, no layout thrash
469
+ // on unrelated keys) — and an explicit `isRtl` always wins.
458
470
  let key = e.key;
459
- if (isRtl) {
460
- if (key === 'ArrowLeft') {
461
- key = 'ArrowRight';
462
- } else if (key === 'ArrowRight') {
463
- key = 'ArrowLeft';
464
- }
471
+ if (
472
+ (key === 'ArrowLeft' || key === 'ArrowRight') &&
473
+ (isRtl ?? isRtlElement(gridRef.current))
474
+ ) {
475
+ key = key === 'ArrowLeft' ? 'ArrowRight' : 'ArrowLeft';
465
476
  }
466
477
 
467
478
  switch (key) {
@@ -155,6 +155,15 @@ function matchesCombo(
155
155
  * definitions live in a ref, so re-renders update behavior without
156
156
  * re-subscribing. First matching hotkey wins per event.
157
157
  *
158
+ * Accessibility (WCAG 2.1.4 — Character Key Shortcuts): a shortcut whose
159
+ * combo is a single unmodified character (letter, digit, punctuation or
160
+ * symbol — e.g. 'c' or '/') is easily triggered by accident by speech-input
161
+ * users and keyboard users with motor impairments. If you register one, you
162
+ * MUST also provide at least one of: (a) a way to turn the shortcut off,
163
+ * (b) a way to remap it to include a modifier, or (c) scope it so it is only
164
+ * active while the relevant component has focus. Prefer `mod`-based combos
165
+ * where possible; the `isDisabled` flag can back a user-facing off switch.
166
+ *
158
167
  * @param hotkeys - Shortcut registrations to listen for.
159
168
  *
160
169
  * @example
@@ -0,0 +1,221 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import {describe, it, expect} from 'vitest';
4
+ import type {ReactElement} from 'react';
5
+ import {render, screen} from '@testing-library/react';
6
+ import {TextInput} from '../TextInput/TextInput';
7
+ import {TextArea} from '../TextArea/TextArea';
8
+ import {NumberInput} from '../NumberInput/NumberInput';
9
+ import {DateInput} from '../DateInput/DateInput';
10
+ import {DateRangeInput} from '../DateRangeInput/DateRangeInput';
11
+ import {TimeInput} from '../TimeInput/TimeInput';
12
+ import {FileInput} from '../FileInput/FileInput';
13
+ import type {FieldStatusVariant} from '../FieldStatus/FieldStatus';
14
+
15
+ // Every id referenced by an aria-describedby must resolve to an element in the
16
+ // document (WCAG 1.3.1) — a described-by pointing at a non-rendered node is a
17
+ // dangling reference. The tooltip variant is the interesting case: it swaps the
18
+ // message box for a tooltip layer, so the referenced ids change per variant.
19
+ function expectNoDanglingDescribedBy(root: HTMLElement) {
20
+ for (const el of Array.from(root.querySelectorAll('[aria-describedby]'))) {
21
+ const value = el.getAttribute('aria-describedby') ?? '';
22
+ for (const id of value.split(/\s+/).filter(Boolean)) {
23
+ expect(
24
+ document.getElementById(id),
25
+ `dangling aria-describedby id "${id}" on <${el.tagName.toLowerCase()}>`,
26
+ ).not.toBeNull();
27
+ }
28
+ }
29
+ }
30
+
31
+ const VARIANTS: FieldStatusVariant[] = ['attached', 'detached', 'tooltip'];
32
+
33
+ const INPUTS: [string, (variant: FieldStatusVariant) => ReactElement][] = [
34
+ [
35
+ 'TextInput',
36
+ v => (
37
+ <TextInput
38
+ label="Field"
39
+ value=""
40
+ onChange={() => {}}
41
+ status={{type: 'error', message: 'Message'}}
42
+ statusVariant={v}
43
+ />
44
+ ),
45
+ ],
46
+ [
47
+ 'TextArea',
48
+ v => (
49
+ <TextArea
50
+ label="Field"
51
+ value=""
52
+ onChange={() => {}}
53
+ status={{type: 'error', message: 'Message'}}
54
+ statusVariant={v}
55
+ />
56
+ ),
57
+ ],
58
+ [
59
+ 'NumberInput',
60
+ v => (
61
+ <NumberInput
62
+ label="Field"
63
+ value={undefined}
64
+ onChange={() => {}}
65
+ status={{type: 'error', message: 'Message'}}
66
+ statusVariant={v}
67
+ />
68
+ ),
69
+ ],
70
+ [
71
+ 'DateInput',
72
+ v => (
73
+ <DateInput
74
+ label="Field"
75
+ value={undefined}
76
+ onChange={() => {}}
77
+ status={{type: 'error', message: 'Message'}}
78
+ statusVariant={v}
79
+ />
80
+ ),
81
+ ],
82
+ [
83
+ 'DateRangeInput',
84
+ v => (
85
+ <DateRangeInput
86
+ label="Field"
87
+ value={null}
88
+ onChange={() => {}}
89
+ status={{type: 'error', message: 'Message'}}
90
+ statusVariant={v}
91
+ />
92
+ ),
93
+ ],
94
+ [
95
+ 'TimeInput',
96
+ v => (
97
+ <TimeInput
98
+ label="Field"
99
+ value={undefined}
100
+ onChange={() => {}}
101
+ status={{type: 'error', message: 'Message'}}
102
+ statusVariant={v}
103
+ />
104
+ ),
105
+ ],
106
+ [
107
+ 'FileInput',
108
+ v => (
109
+ <FileInput
110
+ label="Field"
111
+ value={null}
112
+ onChange={() => {}}
113
+ status={{type: 'error', message: 'Message'}}
114
+ statusVariant={v}
115
+ />
116
+ ),
117
+ ],
118
+ ];
119
+
120
+ describe('useInputStatusIcon — no dangling aria-describedby (WCAG 1.3.1)', () => {
121
+ for (const [name, make] of INPUTS) {
122
+ for (const variant of VARIANTS) {
123
+ it(`${name} / statusVariant="${variant}"`, () => {
124
+ const {container} = render(make(variant));
125
+ expectNoDanglingDescribedBy(container);
126
+ });
127
+ }
128
+ }
129
+
130
+ it('tooltip variant links the input to the rendered tooltip element', () => {
131
+ render(
132
+ <TextInput
133
+ label="Field"
134
+ value=""
135
+ onChange={() => {}}
136
+ status={{type: 'error', message: 'Message'}}
137
+ statusVariant="tooltip"
138
+ />,
139
+ );
140
+ const input = screen.getByRole('textbox');
141
+ const tooltip = screen.getByRole('tooltip', {hidden: true});
142
+ expect(input.getAttribute('aria-describedby')).toContain(tooltip.id);
143
+ });
144
+
145
+ it('does not render a tooltip (or reference one) when there is no message', () => {
146
+ // No message → the tooltip variant renders no tooltip layer and no
147
+ // described-by should point at a non-existent status element.
148
+ const {container} = render(
149
+ <TextInput
150
+ label="Field"
151
+ value=""
152
+ onChange={() => {}}
153
+ status={{type: 'error'}}
154
+ statusVariant="tooltip"
155
+ />,
156
+ );
157
+ expect(
158
+ screen.queryByRole('tooltip', {hidden: true}),
159
+ ).not.toBeInTheDocument();
160
+ expectNoDanglingDescribedBy(container);
161
+ });
162
+ });
163
+
164
+ describe('useInputStatusIcon — input-status-icon theme target', () => {
165
+ // The stable theme target lands on the on-field status glyph itself, so a
166
+ // theme can restyle (e.g. resize) just this icon via `defineTheme`. It is
167
+ // rendered by the attached and tooltip variants; detached suppresses the
168
+ // on-field icon in favour of the detached message box's leading glyph.
169
+ const getStatusIcon = (root: HTMLElement): HTMLElement => {
170
+ const icon = root.querySelector('.astryx-input-status-icon');
171
+ if (icon == null) {
172
+ throw new Error('status icon not found');
173
+ }
174
+ return icon as HTMLElement;
175
+ };
176
+
177
+ for (const variant of ['attached', 'tooltip'] as const) {
178
+ it(`renders the target on the on-field icon (statusVariant="${variant}")`, () => {
179
+ const {container} = render(
180
+ <TextInput
181
+ label="Field"
182
+ value=""
183
+ onChange={() => {}}
184
+ status={{type: 'warning', message: 'Message'}}
185
+ statusVariant={variant}
186
+ />,
187
+ );
188
+ const icon = getStatusIcon(container);
189
+ expect(icon).toHaveClass('astryx-input-status-icon');
190
+ expect(icon).toHaveClass('astryx-icon');
191
+ expect(icon).toHaveAttribute('data-size', 'md');
192
+ expect(icon).toHaveAttribute('data-status', 'warning');
193
+ });
194
+ }
195
+
196
+ it('reflects the status type per status', () => {
197
+ const {container} = render(
198
+ <TextInput
199
+ label="Field"
200
+ value=""
201
+ onChange={() => {}}
202
+ status={{type: 'success', message: 'Message'}}
203
+ statusVariant="attached"
204
+ />,
205
+ );
206
+ expect(getStatusIcon(container)).toHaveAttribute('data-status', 'success');
207
+ });
208
+
209
+ it('does not render the on-field target for the detached variant', () => {
210
+ const {container} = render(
211
+ <TextInput
212
+ label="Field"
213
+ value=""
214
+ onChange={() => {}}
215
+ status={{type: 'error', message: 'Message'}}
216
+ statusVariant="detached"
217
+ />,
218
+ );
219
+ expect(container.querySelector('.astryx-input-status-icon')).toBeNull();
220
+ });
221
+ });
@@ -0,0 +1,235 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useInputStatusIcon.tsx
7
+ * @input Input status, statusVariant, and InputGroup awareness
8
+ * @output The on-field status icon element (with tooltip) and aria-describedby wiring
9
+ * @position Shared hook for bordered inputs that render a status affordance inside the control
10
+ *
11
+ * SYNC: When modified, update:
12
+ * - /packages/core/src/hooks/index.ts (exports)
13
+ *
14
+ * Centralizes the on-field status icon across the bordered inputs (TextInput,
15
+ * TextArea, NumberInput, DateInput, DateTimeInput, DateRangeInput, TimeInput,
16
+ * FileInput) so the three statusVariants behave consistently:
17
+ *
18
+ * - `attached` → icon sits inside the control; the message box below carries
19
+ * the text.
20
+ * - `detached` → the detached message box renders its OWN leading icon, so the
21
+ * on-field icon is suppressed here to avoid a duplicate glyph.
22
+ * - `tooltip` → no message box renders; the status is surfaced through an
23
+ * info-tip on the on-field icon. The icon is a real focusable `<button>` so
24
+ * the status is reachable by every user, not just those with hover or a
25
+ * screen reader:
26
+ * - Keyboard (no AT): the button is in the tab order (WCAG 2.1.1) with a
27
+ * visible focus ring (WCAG 2.4.7); focusing it opens the tooltip.
28
+ * - Pointer: hover opens it (guarded to fine pointers).
29
+ * - Touch: hover is unavailable, so a tap toggles the tooltip open/closed.
30
+ * - Assistive tech: the button has an accessible name (its status type,
31
+ * WCAG 4.1.2) and is described by the message via `aria-describedby`.
32
+ * - Dismissible with Escape and hoverable (WCAG 1.4.13) via useTooltip.
33
+ */
34
+
35
+ import {useCallback, useEffect, useState} from 'react';
36
+ import type {ReactNode} from 'react';
37
+ import * as stylex from '@stylexjs/stylex';
38
+ import {Icon, type IconName, type IconSize} from '../Icon';
39
+ import type {FieldStatusVariant} from '../FieldStatus/FieldStatus';
40
+ import type {InputStatus, InputStatusType} from '../Field/types';
41
+ import {useTooltip} from '../Tooltip';
42
+ import {useTranslator} from '../i18n';
43
+ import {colorVars, radiusVars} from '../theme/tokens.stylex';
44
+ import {themeProps} from '../utils/themeProps';
45
+
46
+ /**
47
+ * Maps each status type to its glyph. Shared so every input shows the same icon
48
+ * for a given status, matching the detached message box's leading icon.
49
+ */
50
+ const STATUS_ICON: Record<InputStatusType, IconName> = {
51
+ warning: 'warning',
52
+ error: 'error',
53
+ success: 'success',
54
+ };
55
+
56
+ /**
57
+ * Accessible-name i18n keys for the focusable status button, keyed by type.
58
+ */
59
+ const STATUS_BUTTON_LABEL_KEY: Record<InputStatusType, string> = {
60
+ warning: '@astryx.input.statusButton.warning',
61
+ error: '@astryx.input.statusButton.error',
62
+ success: '@astryx.input.statusButton.success',
63
+ };
64
+
65
+ const styles = stylex.create({
66
+ // Contain the anchor so the tooltip positions against the icon, and keep the
67
+ // glyph vertically centered within the input control.
68
+ iconAnchor: {
69
+ display: 'inline-flex',
70
+ alignItems: 'center',
71
+ },
72
+ // The tooltip-variant status affordance is a real button so it is keyboard
73
+ // focusable and tappable. Strip the native chrome and show only a focus ring.
74
+ statusButton: {
75
+ display: 'inline-flex',
76
+ alignItems: 'center',
77
+ justifyContent: 'center',
78
+ padding: 0,
79
+ margin: 0,
80
+ border: 'none',
81
+ background: 'none',
82
+ color: 'inherit',
83
+ cursor: 'pointer',
84
+ borderRadius: radiusVars['--radius-full'],
85
+ outlineWidth: {default: null, ':focus-visible': '2px'},
86
+ outlineStyle: {default: null, ':focus-visible': 'solid'},
87
+ outlineColor: {
88
+ default: null,
89
+ ':focus-visible': colorVars['--color-accent'],
90
+ },
91
+ outlineOffset: {default: null, ':focus-visible': '2px'},
92
+ },
93
+ });
94
+
95
+ export interface UseInputStatusIconOptions {
96
+ /** The input's status, or undefined when there is none. */
97
+ status?: InputStatus;
98
+ /** How the status is presented relative to the input. */
99
+ statusVariant?: FieldStatusVariant;
100
+ /** Whether the input is inside an InputGroup (which owns status rendering). */
101
+ isInGroup?: boolean;
102
+ /** Size of the on-field icon. @default 'md' */
103
+ size?: IconSize;
104
+ }
105
+
106
+ export interface UseInputStatusIconReturn {
107
+ /**
108
+ * The on-field status affordance to render inside the input container —
109
+ * the status icon (plain, for attached) or a focusable info-tip button plus
110
+ * its tooltip layer (for the `tooltip` variant). `null` when no icon should
111
+ * render (no status, `detached` variant, or inside a group).
112
+ */
113
+ statusIcon: ReactNode;
114
+ /**
115
+ * ID to add to the input's `aria-describedby` when — and only when — the
116
+ * status is surfaced through a rendered tooltip layer (the `tooltip` variant
117
+ * with a message). It is `undefined` in every other case (no status, no
118
+ * message, `attached`/`detached`, or inside a group), so call-sites can drop
119
+ * it straight into their described-by list without a dangling reference: the
120
+ * id is present exactly when its tooltip element is in the DOM.
121
+ */
122
+ describedBy: string | undefined;
123
+ }
124
+
125
+ /**
126
+ * Builds the on-field status icon and its accessibility wiring for a bordered
127
+ * input. See the file header for the per-variant behavior.
128
+ */
129
+ export function useInputStatusIcon({
130
+ status,
131
+ statusVariant = 'attached',
132
+ isInGroup = false,
133
+ size = 'md',
134
+ }: UseInputStatusIconOptions): UseInputStatusIconReturn {
135
+ const t = useTranslator();
136
+ const isTooltipVariant = statusVariant === 'tooltip';
137
+ const hasTooltip = isTooltipVariant && !!status?.message;
138
+
139
+ // Touch tap-to-toggle. Hover is unavailable on touch, and useTooltip
140
+ // suppresses simulated hover there, so on touch a tap controls visibility.
141
+ // `undefined` leaves the tooltip uncontrolled (pointer + keyboard drive it);
142
+ // a boolean takes control for the current open/close cycle.
143
+ const [tapOpen, setTapOpen] = useState<boolean | undefined>(undefined);
144
+
145
+ const tooltip = useTooltip({
146
+ placement: 'above',
147
+ isEnabled: hasTooltip,
148
+ isOpen: tapOpen,
149
+ });
150
+
151
+ // Re-hand control back to hover/focus once a tap-opened tooltip is dismissed
152
+ // (Escape/outside interaction), so subsequent pointer/keyboard use behaves
153
+ // normally.
154
+ const handleButtonBlur = useCallback(() => {
155
+ setTapOpen(undefined);
156
+ }, []);
157
+
158
+ const handleButtonClick = useCallback(() => {
159
+ // Only take control on touch/hover-less devices. On hover-capable devices
160
+ // pointer hover and keyboard focus already drive the tooltip (uncontrolled),
161
+ // so a click-toggle would fight them; leave it uncontrolled there.
162
+ if (
163
+ typeof window === 'undefined' ||
164
+ typeof window.matchMedia !== 'function' ||
165
+ !window.matchMedia('(hover: none)').matches
166
+ ) {
167
+ return;
168
+ }
169
+ setTapOpen(prev => (prev === true ? false : true));
170
+ }, []);
171
+
172
+ // Dismiss a tap-opened tooltip on Escape and return to the uncontrolled
173
+ // state (useTooltip owns Escape for the uncontrolled case, but while we hold
174
+ // control via isOpen it does not, so mirror it here).
175
+ useEffect(() => {
176
+ if (tapOpen !== true) {
177
+ return;
178
+ }
179
+ const onKeyDown = (e: KeyboardEvent) => {
180
+ if (e.key === 'Escape') {
181
+ setTapOpen(undefined);
182
+ }
183
+ };
184
+ document.addEventListener('keydown', onKeyDown);
185
+ return () => document.removeEventListener('keydown', onKeyDown);
186
+ }, [tapOpen]);
187
+
188
+ // Inside a group the group owns status rendering; the detached message box
189
+ // renders its own leading icon, so the on-field icon would duplicate it.
190
+ const shouldRenderIcon =
191
+ !!status && !isInGroup && statusVariant !== 'detached';
192
+
193
+ if (!shouldRenderIcon) {
194
+ return {statusIcon: null, describedBy: undefined};
195
+ }
196
+
197
+ const icon = (
198
+ <Icon
199
+ icon={STATUS_ICON[status.type]}
200
+ size={size}
201
+ color={status.type}
202
+ // Stable theme target on the status glyph itself, so a theme can restyle
203
+ // just this icon (color, size) — and each status — via `defineTheme`.
204
+ // Same-element rules in @layer astryx-theme win over the icon's own base
205
+ // width/height/fontSize, which a field-level target could not reach.
206
+ // Shared by the attached and tooltip variants across all bordered inputs.
207
+ {...themeProps('input-status-icon', {size, status: status.type})}
208
+ />
209
+ );
210
+
211
+ // Attached (and tooltip-without-message) variants: plain, non-interactive
212
+ // icon. The message box (attached) already carries the text.
213
+ if (!hasTooltip) {
214
+ return {statusIcon: icon, describedBy: undefined};
215
+ }
216
+
217
+ return {
218
+ statusIcon: (
219
+ <>
220
+ <button
221
+ type="button"
222
+ ref={tooltip.ref}
223
+ aria-label={t(STATUS_BUTTON_LABEL_KEY[status.type])}
224
+ aria-describedby={tooltip.describedBy}
225
+ onClick={handleButtonClick}
226
+ onBlur={handleButtonBlur}
227
+ {...stylex.props(styles.statusButton)}>
228
+ {icon}
229
+ </button>
230
+ {tooltip.renderTooltip(status.message)}
231
+ </>
232
+ ),
233
+ describedBy: tooltip.describedBy,
234
+ };
235
+ }
@@ -9,6 +9,7 @@
9
9
  import {render} from '@testing-library/react';
10
10
  import {describe, expect, it} from 'vitest';
11
11
  import {useKeyboardHint, type KeyboardHintOrientation} from './useKeyboardHint';
12
+ import {InternationalizationProvider} from '../i18n';
12
13
 
13
14
  function TestHint({orientation}: {orientation?: KeyboardHintOrientation}) {
14
15
  const {hintElement} = useKeyboardHint({orientation});
@@ -67,4 +68,25 @@ describe('useKeyboardHint', () => {
67
68
 
68
69
  expect(hint.className).toContain('useKeyboardHint__styles.hint');
69
70
  });
71
+
72
+ it('renders the "to navigate" label from the i18n catalog', () => {
73
+ const {container} = render(<TestHint />);
74
+ const hint = container.querySelector('[popover="manual"]') as HTMLElement;
75
+
76
+ expect(hint.textContent).toContain('to navigate');
77
+ });
78
+
79
+ it('localizes the "to navigate" label through the i18n catalog', () => {
80
+ const {container} = render(
81
+ <InternationalizationProvider
82
+ locale="fr"
83
+ overrides={{fr: {'@astryx.keyboardHint.toNavigate': 'pour naviguer'}}}>
84
+ <TestHint />
85
+ </InternationalizationProvider>,
86
+ );
87
+ const hint = container.querySelector('[popover="manual"]') as HTMLElement;
88
+
89
+ expect(hint.textContent).toContain('pour naviguer');
90
+ expect(hint.textContent).not.toContain('to navigate');
91
+ });
70
92
  });
@@ -4,7 +4,7 @@
4
4
 
5
5
  /**
6
6
  * @file useKeyboardHint.tsx
7
- * @input Uses React, StyleX, Kbd, useLayer
7
+ * @input Uses React, StyleX, Kbd, useLayer, i18n (useTranslator)
8
8
  * @output Exports useKeyboardHint hook — ephemeral arrow-key navigation hint
9
9
  * @position Core hook; shows sighted keyboard users how to navigate composite
10
10
  * widgets that use roving tabindex (single Tab stop, arrows inside)
@@ -27,6 +27,7 @@ import {
27
27
  } from '../theme/tokens.stylex';
28
28
  import {Kbd} from '../Kbd';
29
29
  import {useLayer} from '../Layer/useLayer';
30
+ import {useTranslator} from '../i18n';
30
31
 
31
32
  // ---------------------------------------------------------------------------
32
33
  // Types
@@ -179,6 +180,7 @@ export function useKeyboardHint(
179
180
  isEnabled = true,
180
181
  } = options;
181
182
 
183
+ const t = useTranslator();
182
184
  const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
183
185
  const dismissedRef = useRef(false);
184
186
  const isVisibleRef = useRef(false);
@@ -316,7 +318,9 @@ export function useKeyboardHint(
316
318
  const hintElement = layer.render(
317
319
  <span aria-hidden="true">
318
320
  {arrowContent}
319
- <span {...stylex.props(styles.label)}>to navigate</span>
321
+ <span {...stylex.props(styles.label)}>
322
+ {t('@astryx.keyboardHint.toNavigate')}
323
+ </span>
320
324
  </span>,
321
325
  {
322
326
  placement: 'below',