@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
@@ -0,0 +1,224 @@
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
+
45
+ /**
46
+ * Maps each status type to its glyph. Shared so every input shows the same icon
47
+ * for a given status, matching the detached message box's leading icon.
48
+ */
49
+ const STATUS_ICON: Record<InputStatusType, IconName> = {
50
+ warning: 'warning',
51
+ error: 'error',
52
+ success: 'success',
53
+ };
54
+
55
+ /**
56
+ * Accessible-name i18n keys for the focusable status button, keyed by type.
57
+ */
58
+ const STATUS_BUTTON_LABEL_KEY: Record<InputStatusType, string> = {
59
+ warning: '@astryx.input.statusButton.warning',
60
+ error: '@astryx.input.statusButton.error',
61
+ success: '@astryx.input.statusButton.success',
62
+ };
63
+
64
+ const styles = stylex.create({
65
+ // Contain the anchor so the tooltip positions against the icon, and keep the
66
+ // glyph vertically centered within the input control.
67
+ iconAnchor: {
68
+ display: 'inline-flex',
69
+ alignItems: 'center',
70
+ },
71
+ // The tooltip-variant status affordance is a real button so it is keyboard
72
+ // focusable and tappable. Strip the native chrome and show only a focus ring.
73
+ statusButton: {
74
+ display: 'inline-flex',
75
+ alignItems: 'center',
76
+ justifyContent: 'center',
77
+ padding: 0,
78
+ margin: 0,
79
+ border: 'none',
80
+ background: 'none',
81
+ color: 'inherit',
82
+ cursor: 'pointer',
83
+ borderRadius: radiusVars['--radius-full'],
84
+ outlineWidth: {default: null, ':focus-visible': '2px'},
85
+ outlineStyle: {default: null, ':focus-visible': 'solid'},
86
+ outlineColor: {
87
+ default: null,
88
+ ':focus-visible': colorVars['--color-accent'],
89
+ },
90
+ outlineOffset: {default: null, ':focus-visible': '2px'},
91
+ },
92
+ });
93
+
94
+ export interface UseInputStatusIconOptions {
95
+ /** The input's status, or undefined when there is none. */
96
+ status?: InputStatus;
97
+ /** How the status is presented relative to the input. */
98
+ statusVariant?: FieldStatusVariant;
99
+ /** Whether the input is inside an InputGroup (which owns status rendering). */
100
+ isInGroup?: boolean;
101
+ /** Size of the on-field icon. @default 'md' */
102
+ size?: IconSize;
103
+ }
104
+
105
+ export interface UseInputStatusIconReturn {
106
+ /**
107
+ * The on-field status affordance to render inside the input container —
108
+ * the status icon (plain, for attached) or a focusable info-tip button plus
109
+ * its tooltip layer (for the `tooltip` variant). `null` when no icon should
110
+ * render (no status, `detached` variant, or inside a group).
111
+ */
112
+ statusIcon: ReactNode;
113
+ /**
114
+ * ID to add to the input's `aria-describedby` when — and only when — the
115
+ * status is surfaced through a rendered tooltip layer (the `tooltip` variant
116
+ * with a message). It is `undefined` in every other case (no status, no
117
+ * message, `attached`/`detached`, or inside a group), so call-sites can drop
118
+ * it straight into their described-by list without a dangling reference: the
119
+ * id is present exactly when its tooltip element is in the DOM.
120
+ */
121
+ describedBy: string | undefined;
122
+ }
123
+
124
+ /**
125
+ * Builds the on-field status icon and its accessibility wiring for a bordered
126
+ * input. See the file header for the per-variant behavior.
127
+ */
128
+ export function useInputStatusIcon({
129
+ status,
130
+ statusVariant = 'attached',
131
+ isInGroup = false,
132
+ size = 'md',
133
+ }: UseInputStatusIconOptions): UseInputStatusIconReturn {
134
+ const t = useTranslator();
135
+ const isTooltipVariant = statusVariant === 'tooltip';
136
+ const hasTooltip = isTooltipVariant && !!status?.message;
137
+
138
+ // Touch tap-to-toggle. Hover is unavailable on touch, and useTooltip
139
+ // suppresses simulated hover there, so on touch a tap controls visibility.
140
+ // `undefined` leaves the tooltip uncontrolled (pointer + keyboard drive it);
141
+ // a boolean takes control for the current open/close cycle.
142
+ const [tapOpen, setTapOpen] = useState<boolean | undefined>(undefined);
143
+
144
+ const tooltip = useTooltip({
145
+ placement: 'above',
146
+ isEnabled: hasTooltip,
147
+ isOpen: tapOpen,
148
+ });
149
+
150
+ // Re-hand control back to hover/focus once a tap-opened tooltip is dismissed
151
+ // (Escape/outside interaction), so subsequent pointer/keyboard use behaves
152
+ // normally.
153
+ const handleButtonBlur = useCallback(() => {
154
+ setTapOpen(undefined);
155
+ }, []);
156
+
157
+ const handleButtonClick = useCallback(() => {
158
+ // Only take control on touch/hover-less devices. On hover-capable devices
159
+ // pointer hover and keyboard focus already drive the tooltip (uncontrolled),
160
+ // so a click-toggle would fight them; leave it uncontrolled there.
161
+ if (
162
+ typeof window === 'undefined' ||
163
+ typeof window.matchMedia !== 'function' ||
164
+ !window.matchMedia('(hover: none)').matches
165
+ ) {
166
+ return;
167
+ }
168
+ setTapOpen(prev => (prev === true ? false : true));
169
+ }, []);
170
+
171
+ // Dismiss a tap-opened tooltip on Escape and return to the uncontrolled
172
+ // state (useTooltip owns Escape for the uncontrolled case, but while we hold
173
+ // control via isOpen it does not, so mirror it here).
174
+ useEffect(() => {
175
+ if (tapOpen !== true) {
176
+ return;
177
+ }
178
+ const onKeyDown = (e: KeyboardEvent) => {
179
+ if (e.key === 'Escape') {
180
+ setTapOpen(undefined);
181
+ }
182
+ };
183
+ document.addEventListener('keydown', onKeyDown);
184
+ return () => document.removeEventListener('keydown', onKeyDown);
185
+ }, [tapOpen]);
186
+
187
+ // Inside a group the group owns status rendering; the detached message box
188
+ // renders its own leading icon, so the on-field icon would duplicate it.
189
+ const shouldRenderIcon =
190
+ !!status && !isInGroup && statusVariant !== 'detached';
191
+
192
+ if (!shouldRenderIcon) {
193
+ return {statusIcon: null, describedBy: undefined};
194
+ }
195
+
196
+ const icon = (
197
+ <Icon icon={STATUS_ICON[status.type]} size={size} color={status.type} />
198
+ );
199
+
200
+ // Attached (and tooltip-without-message) variants: plain, non-interactive
201
+ // icon. The message box (attached) already carries the text.
202
+ if (!hasTooltip) {
203
+ return {statusIcon: icon, describedBy: undefined};
204
+ }
205
+
206
+ return {
207
+ statusIcon: (
208
+ <>
209
+ <button
210
+ type="button"
211
+ ref={tooltip.ref}
212
+ aria-label={t(STATUS_BUTTON_LABEL_KEY[status.type])}
213
+ aria-describedby={tooltip.describedBy}
214
+ onClick={handleButtonClick}
215
+ onBlur={handleButtonBlur}
216
+ {...stylex.props(styles.statusButton)}>
217
+ {icon}
218
+ </button>
219
+ {tooltip.renderTooltip(status.message)}
220
+ </>
221
+ ),
222
+ describedBy: tooltip.describedBy,
223
+ };
224
+ }
@@ -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',
@@ -19,6 +19,13 @@ export const docs = {
19
19
  default: "'[role=\"menuitem\"]'",
20
20
  required: false,
21
21
  },
22
+ {
23
+ name: 'options.boundarySelector',
24
+ type: 'string',
25
+ description:
26
+ "Selector identifying a list boundary, for lists that contain nested lists of the same kind (e.g. a menu with submenu flyouts). When set, item collection and key handling are scoped to this level's own container. Typically '[role=\"menu\"]'.",
27
+ required: false,
28
+ },
22
29
  {
23
30
  name: 'options.wrap',
24
31
  type: 'boolean',
@@ -49,8 +56,8 @@ export const docs = {
49
56
  {
50
57
  name: 'options.isRtl',
51
58
  type: 'boolean',
52
- description: 'Whether the list is in a right-to-left context. When true, ArrowLeft/ArrowRight are swapped for horizontal navigation so it follows visual direction.',
53
- default: 'false',
59
+ description: 'Whether the list is in a right-to-left context. When true, ArrowLeft/ArrowRight are swapped for horizontal navigation so it follows visual direction. When omitted, auto-detected from the container computed direction on keydown.',
60
+ default: "undefined (auto-detect from the container's computed direction)",
54
61
  required: false,
55
62
  },
56
63
  {
@@ -99,6 +106,18 @@ export const docs = {
99
106
  type: '() => boolean',
100
107
  description: 'Focus the last enabled item. Returns true when an item was focused.',
101
108
  },
109
+ {
110
+ name: 'ownsEvent',
111
+ type: '(e: React.KeyboardEvent) => boolean',
112
+ description:
113
+ "Whether a key event belongs to this list level rather than a nested list sharing the same boundarySelector. Always true when no boundarySelector is set. Use to guard consumer-added key handling (Enter/Space, typeahead).",
114
+ },
115
+ {
116
+ name: 'getItems',
117
+ type: '() => HTMLElement[]',
118
+ description:
119
+ "This level's focusable items in DOM order (already scoped by boundarySelector). Build typeahead targets from this instead of re-querying.",
120
+ },
102
121
  ],
103
122
  usage: {
104
123
  description:
@@ -123,11 +142,12 @@ export const docsDense = {
123
142
  paramDescriptions: {
124
143
  options: 'config for list focus behavior. All fields optional.',
125
144
  'options.itemSelector': 'selector for focusable items in list.',
145
+ 'options.boundarySelector': "boundary selector for lists that contain nested lists (e.g. submenu flyouts); scopes items + key handling to this level.",
126
146
  'options.wrap': 'whether arrow navigation wraps around at ends.',
127
147
  'options.onEscape': 'callback when Escape key pressed (e.g. close menu).',
128
148
  'options.orientation': "navigation orientation. 'horizontal' uses ArrowLeft/ArrowRight, 'vertical' uses ArrowUp/ArrowDown, 'both' accepts all four arrows.",
129
149
  'options.hasHomeEnd': 'whether Home/End jump to first/last enabled item.',
130
- 'options.isRtl': 'when true, ArrowLeft/ArrowRight swap for horizontal nav (RTL).',
150
+ 'options.isRtl': 'ArrowLeft/ArrowRight swap for horizontal nav (RTL). default: auto-detect from container computed direction; explicit boolean wins.',
131
151
  'options.hasRovingTabIndex': 'opt into roving-tabindex ownership: hook stamps + repairs a single tab stop across items.',
132
152
  'options.hasCaretGuard': "when true, don't steal arrow keys from a nested text input/textarea mid-line or from a contenteditable.",
133
153
  },
@@ -138,6 +158,8 @@ export const docsDense = {
138
158
  focusItem: 'focus specific item by index (clamped to valid range).',
139
159
  focusFirst: 'focus first enabled item; returns true when focused.',
140
160
  focusLast: 'focus last enabled item; returns true when focused.',
161
+ ownsEvent: 'whether a key event is this level\'s vs a nested list\'s (boundarySelector); always true without one.',
162
+ getItems: "this level's focusable items in DOM order, scoped by boundarySelector.",
141
163
  },
142
164
  usage: {
143
165
  description:
@@ -3,13 +3,15 @@
3
3
  /**
4
4
  * @file useListFocus.test.tsx
5
5
  * @input Uses vitest, @testing-library/react, useListFocus hook
6
- * @output Unit tests for useListFocus disabled-item skipping + navigation
6
+ * @output Unit tests for useListFocus disabled-item skipping, navigation, and
7
+ * RTL auto-detection
7
8
  * @position Testing; validates useListFocus.ts keyboard navigation
8
9
  *
9
10
  * SYNC: When useListFocus.ts changes, update tests to match new behavior
10
11
  */
11
12
 
12
13
  import {describe, it, expect} from 'vitest';
14
+ import type {KeyboardEvent as ReactKeyboardEvent} from 'react';
13
15
  import {render, screen, fireEvent} from '@testing-library/react';
14
16
  import {useListFocus} from './useListFocus';
15
17
 
@@ -373,3 +375,153 @@ describe('useListFocus shortcut passthrough', () => {
373
375
  expect(screen.getByTestId('A')).toHaveFocus();
374
376
  });
375
377
  });
378
+
379
+ /**
380
+ * A horizontal list (menubar-like) for RTL direction tests. jsdom reflects the
381
+ * `dir` attribute into computed style only on the element that carries it, so
382
+ * `dir` is set on the list container itself — the element the hook reads via
383
+ * listRef.
384
+ */
385
+ function HorizontalMenu({dir, isRtl}: {dir?: 'ltr' | 'rtl'; isRtl?: boolean}) {
386
+ const {listRef, handleKeyDown} = useListFocus<HTMLDivElement>({
387
+ orientation: 'horizontal',
388
+ isRtl,
389
+ });
390
+ const items = ['One', 'Two', 'Three'];
391
+ return (
392
+ <div ref={listRef} role="menu" dir={dir} onKeyDown={handleKeyDown}>
393
+ {items.map(label => (
394
+ <div key={label} role="menuitem" tabIndex={-1} data-testid={label}>
395
+ {label}
396
+ </div>
397
+ ))}
398
+ </div>
399
+ );
400
+ }
401
+
402
+ describe('useListFocus RTL auto-detection (WCAG 1.3.2)', () => {
403
+ it('auto-detects dir="rtl": ArrowLeft moves to the next item', () => {
404
+ render(<HorizontalMenu dir="rtl" />);
405
+ const menu = screen.getByRole('menu');
406
+ screen.getByTestId('One').focus();
407
+ fireEvent.keyDown(menu, {key: 'ArrowLeft'});
408
+ expect(screen.getByTestId('Two')).toHaveFocus();
409
+ });
410
+
411
+ it('auto-detects dir="rtl": ArrowRight moves to the previous item', () => {
412
+ render(<HorizontalMenu dir="rtl" />);
413
+ const menu = screen.getByRole('menu');
414
+ screen.getByTestId('Two').focus();
415
+ fireEvent.keyDown(menu, {key: 'ArrowRight'});
416
+ expect(screen.getByTestId('One')).toHaveFocus();
417
+ });
418
+
419
+ it('stays LTR without a direction: ArrowRight moves to the next item', () => {
420
+ render(<HorizontalMenu />);
421
+ const menu = screen.getByRole('menu');
422
+ screen.getByTestId('One').focus();
423
+ fireEvent.keyDown(menu, {key: 'ArrowRight'});
424
+ expect(screen.getByTestId('Two')).toHaveFocus();
425
+ });
426
+
427
+ it('explicit isRtl={false} overrides a dir="rtl" container', () => {
428
+ render(<HorizontalMenu dir="rtl" isRtl={false} />);
429
+ const menu = screen.getByRole('menu');
430
+ screen.getByTestId('One').focus();
431
+ fireEvent.keyDown(menu, {key: 'ArrowRight'});
432
+ expect(screen.getByTestId('Two')).toHaveFocus();
433
+ });
434
+
435
+ it('explicit isRtl={true} flips arrows without a dir attribute', () => {
436
+ render(<HorizontalMenu isRtl />);
437
+ const menu = screen.getByRole('menu');
438
+ screen.getByTestId('One').focus();
439
+ fireEvent.keyDown(menu, {key: 'ArrowLeft'});
440
+ expect(screen.getByTestId('Two')).toHaveFocus();
441
+ });
442
+ });
443
+
444
+ // A menu whose second item contains a NESTED role="menu" (mirrors an inline
445
+ // submenu flyout). Exercises boundarySelector: item scoping + event ownership.
446
+ function NestedMenu() {
447
+ const {listRef, handleKeyDown, ownsEvent} = useListFocus<HTMLDivElement>({
448
+ boundarySelector: '[role="menu"]',
449
+ wrap: false,
450
+ });
451
+ return (
452
+ <div ref={listRef} role="menu" onKeyDown={handleKeyDown}>
453
+ <div role="menuitem" tabIndex={-1} data-testid="Outer1">
454
+ Outer1
455
+ </div>
456
+ <div role="menuitem" tabIndex={-1} data-testid="Outer2">
457
+ Outer2
458
+ {/* Nested submenu rendered inline (like a popover flyout). */}
459
+ <div role="menu" data-testid="inner-menu">
460
+ <div role="menuitem" tabIndex={-1} data-testid="Inner1">
461
+ Inner1
462
+ </div>
463
+ <div role="menuitem" tabIndex={-1} data-testid="Inner2">
464
+ Inner2
465
+ </div>
466
+ </div>
467
+ </div>
468
+ <div role="menuitem" tabIndex={-1} data-testid="Outer3">
469
+ Outer3
470
+ </div>
471
+ {/* Surface ownership for assertions. */}
472
+ <input
473
+ data-testid="probe-owns"
474
+ onKeyDown={e => {
475
+ e.currentTarget.setAttribute('data-owns', String(ownsEvent(e)));
476
+ }}
477
+ />
478
+ </div>
479
+ );
480
+ }
481
+
482
+ // Variant with the ownsEvent probe placed INSIDE the nested menu, so a key
483
+ // event from it reports as not-owned by the outer list.
484
+ function NestedMenuWithInnerProbe() {
485
+ const {listRef, handleKeyDown, ownsEvent} = useListFocus<HTMLDivElement>({
486
+ boundarySelector: '[role="menu"]',
487
+ wrap: false,
488
+ });
489
+ const report = (e: ReactKeyboardEvent<HTMLInputElement>) => {
490
+ e.currentTarget.setAttribute('data-owns', String(ownsEvent(e)));
491
+ };
492
+ return (
493
+ <div ref={listRef} role="menu" onKeyDown={handleKeyDown}>
494
+ <div role="menuitem" tabIndex={-1} data-testid="Outer1">
495
+ Outer1
496
+ </div>
497
+ <div role="menu" data-testid="inner-menu">
498
+ <input data-testid="inner-probe" onKeyDown={report} />
499
+ </div>
500
+ </div>
501
+ );
502
+ }
503
+
504
+ describe('useListFocus boundarySelector (nested lists)', () => {
505
+ it("ArrowDown skips over a nested list's items to the next own item", () => {
506
+ render(<NestedMenu />);
507
+ const menu = screen.getAllByRole('menu')[0];
508
+ screen.getByTestId('Outer2').focus();
509
+ fireEvent.keyDown(menu, {key: 'ArrowDown'});
510
+ // Lands on Outer3, not Inner1 (which is inside the nested menu).
511
+ expect(screen.getByTestId('Outer3')).toHaveFocus();
512
+ });
513
+
514
+ it('ownsEvent is true for an event originating at this level', () => {
515
+ render(<NestedMenu />);
516
+ const ownProbe = screen.getByTestId('probe-owns');
517
+ fireEvent.keyDown(ownProbe, {key: 'ArrowDown'});
518
+ expect(ownProbe).toHaveAttribute('data-owns', 'true');
519
+ });
520
+
521
+ it('ownsEvent is false for an event from inside a nested list', () => {
522
+ render(<NestedMenuWithInnerProbe />);
523
+ const innerProbe = screen.getByTestId('inner-probe');
524
+ fireEvent.keyDown(innerProbe, {key: 'ArrowDown'});
525
+ expect(innerProbe).toHaveAttribute('data-owns', 'false');
526
+ });
527
+ });