@astryxdesign/core 0.1.1 → 0.1.2-canary.04e71b2

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 (631) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/README.md +131 -13
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +11 -5
  5. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  6. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  7. package/dist/AvatarGroup/AvatarGroupOverflow.js +4 -1
  8. package/dist/Banner/Banner.d.ts +7 -0
  9. package/dist/Banner/Banner.d.ts.map +1 -1
  10. package/dist/Banner/Banner.js +9 -2
  11. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +1 -1
  12. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  13. package/dist/Breadcrumbs/BreadcrumbItem.js +24 -5
  14. package/dist/Button/Button.d.ts +12 -2
  15. package/dist/Button/Button.d.ts.map +1 -1
  16. package/dist/Button/Button.js +53 -17
  17. package/dist/Calendar/Calendar.d.ts +5 -4
  18. package/dist/Calendar/Calendar.d.ts.map +1 -1
  19. package/dist/Calendar/Calendar.js +82 -43
  20. package/dist/Calendar/hooks/index.d.ts +0 -2
  21. package/dist/Calendar/hooks/index.d.ts.map +1 -1
  22. package/dist/Calendar/hooks/index.js +1 -2
  23. package/dist/Calendar/index.d.ts +3 -3
  24. package/dist/Calendar/index.d.ts.map +1 -1
  25. package/dist/Calendar/index.js +1 -1
  26. package/dist/Calendar/styles.d.ts +3 -9
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +3 -11
  29. package/dist/Carousel/Carousel.d.ts.map +1 -1
  30. package/dist/Carousel/Carousel.js +17 -3
  31. package/dist/Center/Center.d.ts +11 -1
  32. package/dist/Center/Center.d.ts.map +1 -1
  33. package/dist/Center/Center.js +9 -3
  34. package/dist/Chat/ChatMessageList.d.ts +14 -1
  35. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  36. package/dist/Chat/ChatMessageList.js +2 -0
  37. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  38. package/dist/Chat/useTriggerMenu.js +4 -1
  39. package/dist/CheckboxInput/CheckboxInput.d.ts +23 -2
  40. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  41. package/dist/CheckboxInput/CheckboxInput.js +51 -9
  42. package/dist/CheckboxList/CheckboxList.d.ts +13 -1
  43. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  44. package/dist/CheckboxList/CheckboxList.js +39 -9
  45. package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
  46. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  47. package/dist/Citation/Citation.d.ts.map +1 -1
  48. package/dist/Citation/Citation.js +3 -3
  49. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  50. package/dist/CodeBlock/CodeBlock.js +49 -37
  51. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  52. package/dist/CommandPalette/CommandPalette.js +5 -5
  53. package/dist/ContextMenu/ContextMenu.d.ts +4 -5
  54. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  55. package/dist/ContextMenu/ContextMenu.js +93 -11
  56. package/dist/DateInput/DateInput.d.ts +23 -1
  57. package/dist/DateInput/DateInput.d.ts.map +1 -1
  58. package/dist/DateInput/DateInput.js +59 -8
  59. package/dist/DateRangeInput/DateRangeInput.d.ts +23 -1
  60. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  61. package/dist/DateRangeInput/DateRangeInput.js +42 -9
  62. package/dist/DateTimeInput/DateTimeInput.d.ts +35 -2
  63. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  64. package/dist/DateTimeInput/DateTimeInput.js +78 -15
  65. package/dist/Dialog/Dialog.d.ts.map +1 -1
  66. package/dist/Dialog/Dialog.js +12 -0
  67. package/dist/DropdownMenu/DropdownMenu.d.ts +1 -8
  68. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  69. package/dist/DropdownMenu/DropdownMenu.js +42 -14
  70. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  71. package/dist/EmptyState/EmptyState.js +7 -1
  72. package/dist/Field/Field.d.ts +19 -2
  73. package/dist/Field/Field.d.ts.map +1 -1
  74. package/dist/Field/Field.js +4 -0
  75. package/dist/Field/FieldLabel.d.ts +21 -2
  76. package/dist/Field/FieldLabel.d.ts.map +1 -1
  77. package/dist/Field/FieldLabel.js +41 -26
  78. package/dist/FileInput/FileInput.d.ts +22 -1
  79. package/dist/FileInput/FileInput.d.ts.map +1 -1
  80. package/dist/FileInput/FileInput.js +56 -10
  81. package/dist/Grid/Grid.d.ts +11 -1
  82. package/dist/Grid/Grid.d.ts.map +1 -1
  83. package/dist/Grid/Grid.js +8 -0
  84. package/dist/HoverCard/HoverCard.d.ts +2 -2
  85. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  86. package/dist/HoverCard/HoverCard.js +18 -6
  87. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  88. package/dist/HoverCard/useHoverCard.js +7 -3
  89. package/dist/InputGroup/InputGroup.d.ts +2 -2
  90. package/dist/InputGroup/InputGroup.d.ts.map +1 -1
  91. package/dist/InputGroup/InputGroup.js +14 -5
  92. package/dist/InputGroup/InputGroupContext.d.ts +4 -0
  93. package/dist/InputGroup/InputGroupContext.d.ts.map +1 -1
  94. package/dist/InputGroup/InputGroupContext.js +2 -2
  95. package/dist/InputGroup/index.d.ts +1 -0
  96. package/dist/InputGroup/index.d.ts.map +1 -1
  97. package/dist/Kbd/Kbd.d.ts.map +1 -1
  98. package/dist/Kbd/Kbd.js +34 -2
  99. package/dist/Layer/anchorName.d.ts +20 -0
  100. package/dist/Layer/anchorName.d.ts.map +1 -0
  101. package/dist/Layer/anchorName.js +36 -0
  102. package/dist/Layer/layerAnimations.stylex.d.ts +4 -0
  103. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  104. package/dist/Layer/layerAnimations.stylex.js +8 -4
  105. package/dist/Layer/useLayer.d.ts +29 -0
  106. package/dist/Layer/useLayer.d.ts.map +1 -1
  107. package/dist/Layer/useLayer.js +55 -11
  108. package/dist/Layout/Layout.d.ts +4 -11
  109. package/dist/Layout/Layout.d.ts.map +1 -1
  110. package/dist/Layout/Layout.js +10 -11
  111. package/dist/Layout/LayoutFooter.d.ts +2 -2
  112. package/dist/Layout/LayoutFooter.d.ts.map +1 -1
  113. package/dist/Layout/LayoutHeader.d.ts +2 -2
  114. package/dist/Layout/LayoutHeader.d.ts.map +1 -1
  115. package/dist/Layout/LayoutPanel.d.ts +2 -2
  116. package/dist/Layout/LayoutPanel.d.ts.map +1 -1
  117. package/dist/Layout/padding.stylex.d.ts +51 -0
  118. package/dist/Layout/padding.stylex.d.ts.map +1 -1
  119. package/dist/Layout/padding.stylex.js +63 -0
  120. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  121. package/dist/Lightbox/Lightbox.js +4 -2
  122. package/dist/Link/Link.d.ts +17 -2
  123. package/dist/Link/Link.d.ts.map +1 -1
  124. package/dist/Link/Link.js +20 -9
  125. package/dist/Link/LinkProvider.d.ts +1 -0
  126. package/dist/Link/LinkProvider.d.ts.map +1 -1
  127. package/dist/Link/LinkProvider.js +1 -0
  128. package/dist/Markdown/Markdown.d.ts.map +1 -1
  129. package/dist/Markdown/Markdown.js +18 -3
  130. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  131. package/dist/MobileNav/MobileNav.js +13 -0
  132. package/dist/MoreMenu/MoreMenu.d.ts +1 -7
  133. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  134. package/dist/MoreMenu/MoreMenu.js +0 -2
  135. package/dist/MultiSelector/MultiSelector.d.ts +25 -2
  136. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  137. package/dist/MultiSelector/MultiSelector.js +103 -22
  138. package/dist/MultiSelector/hooks.d.ts +12 -1
  139. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  140. package/dist/MultiSelector/hooks.js +14 -1
  141. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  142. package/dist/NavMenu/NavHeadingMenu.js +34 -3
  143. package/dist/NumberInput/NumberInput.d.ts +23 -2
  144. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  145. package/dist/NumberInput/NumberInput.js +62 -12
  146. package/dist/Pagination/Pagination.d.ts +1 -1
  147. package/dist/Pagination/Pagination.d.ts.map +1 -1
  148. package/dist/Pagination/Pagination.js +46 -28
  149. package/dist/Popover/usePopover.d.ts +25 -2
  150. package/dist/Popover/usePopover.d.ts.map +1 -1
  151. package/dist/Popover/usePopover.js +18 -6
  152. package/dist/PowerSearch/PowerSearch.d.ts +11 -1
  153. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  154. package/dist/PowerSearch/PowerSearch.js +6 -1
  155. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  156. package/dist/ProgressBar/ProgressBar.js +3 -5
  157. package/dist/RadioList/RadioList.d.ts +20 -2
  158. package/dist/RadioList/RadioList.d.ts.map +1 -1
  159. package/dist/RadioList/RadioList.js +105 -9
  160. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  161. package/dist/RadioList/RadioListItem.js +13 -2
  162. package/dist/SegmentedControl/SegmentedControl.d.ts +13 -2
  163. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  164. package/dist/SegmentedControl/SegmentedControl.js +78 -44
  165. package/dist/SegmentedControl/SegmentedControlContext.d.ts +7 -0
  166. package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  167. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  168. package/dist/SegmentedControl/SegmentedControlItem.js +13 -2
  169. package/dist/Selector/Selector.d.ts +22 -1
  170. package/dist/Selector/Selector.d.ts.map +1 -1
  171. package/dist/Selector/Selector.js +84 -23
  172. package/dist/Selector/hooks.d.ts +8 -1
  173. package/dist/Selector/hooks.d.ts.map +1 -1
  174. package/dist/Selector/hooks.js +14 -1
  175. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  176. package/dist/SideNav/SideNavItem.js +1 -1
  177. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  178. package/dist/Skeleton/Skeleton.js +8 -2
  179. package/dist/Slider/Slider.d.ts +21 -0
  180. package/dist/Slider/Slider.d.ts.map +1 -1
  181. package/dist/Slider/Slider.js +38 -11
  182. package/dist/Spinner/Spinner.d.ts.map +1 -1
  183. package/dist/Spinner/Spinner.js +1 -1
  184. package/dist/Stack/Stack.d.ts +38 -23
  185. package/dist/Stack/Stack.d.ts.map +1 -1
  186. package/dist/Stack/Stack.js +25 -1
  187. package/dist/Stack/StackItem.d.ts +12 -23
  188. package/dist/Stack/StackItem.d.ts.map +1 -1
  189. package/dist/Stack/StackItem.js +8 -1
  190. package/dist/Switch/Switch.d.ts +23 -2
  191. package/dist/Switch/Switch.d.ts.map +1 -1
  192. package/dist/Switch/Switch.js +41 -11
  193. package/dist/TabList/Tab.d.ts.map +1 -1
  194. package/dist/TabList/Tab.js +6 -0
  195. package/dist/TabList/TabList.d.ts +16 -4
  196. package/dist/TabList/TabList.d.ts.map +1 -1
  197. package/dist/TabList/TabList.js +86 -10
  198. package/dist/TabList/TabListContext.d.ts +5 -0
  199. package/dist/TabList/TabListContext.d.ts.map +1 -1
  200. package/dist/TabList/TabListContext.js +5 -0
  201. package/dist/TabList/TabMenu.d.ts.map +1 -1
  202. package/dist/TabList/TabMenu.js +6 -1
  203. package/dist/Table/BaseTable.d.ts.map +1 -1
  204. package/dist/Table/BaseTable.js +38 -16
  205. package/dist/Table/Table.d.ts.map +1 -1
  206. package/dist/Table/Table.js +36 -7
  207. package/dist/Table/TableCell.d.ts +8 -1
  208. package/dist/Table/TableCell.d.ts.map +1 -1
  209. package/dist/Table/TableCell.js +10 -10
  210. package/dist/Table/TableHeaderCell.d.ts +8 -1
  211. package/dist/Table/TableHeaderCell.d.ts.map +1 -1
  212. package/dist/Table/TableHeaderCell.js +16 -16
  213. package/dist/Table/TableRow.d.ts +6 -1
  214. package/dist/Table/TableRow.d.ts.map +1 -1
  215. package/dist/Table/TableRow.js +15 -7
  216. package/dist/Table/index.d.ts +4 -1
  217. package/dist/Table/index.d.ts.map +1 -1
  218. package/dist/Table/index.js +3 -2
  219. package/dist/Table/plugins/pagination/paginateData.d.ts +2 -1
  220. package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
  221. package/dist/Table/plugins/pagination/paginateData.js +8 -3
  222. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  223. package/dist/Table/plugins/pagination/useTablePagination.js +6 -1
  224. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  225. package/dist/Table/plugins/sortable/useTableSortable.js +59 -3
  226. package/dist/Table/plugins/stickyColumns/index.d.ts +3 -0
  227. package/dist/Table/plugins/stickyColumns/index.d.ts.map +1 -0
  228. package/dist/Table/plugins/stickyColumns/index.js +3 -0
  229. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts +25 -0
  230. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -0
  231. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +369 -0
  232. package/dist/Table/tableContextMenu.d.ts +33 -0
  233. package/dist/Table/tableContextMenu.d.ts.map +1 -0
  234. package/dist/Table/tableContextMenu.js +133 -0
  235. package/dist/Table/types.d.ts +161 -5
  236. package/dist/Table/types.d.ts.map +1 -1
  237. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  238. package/dist/Table/useBaseTablePlugins.js +1 -1
  239. package/dist/Text/text.stylex.d.ts +2 -2
  240. package/dist/Text/text.stylex.js +2 -2
  241. package/dist/TextArea/TextArea.d.ts +22 -1
  242. package/dist/TextArea/TextArea.d.ts.map +1 -1
  243. package/dist/TextArea/TextArea.js +41 -9
  244. package/dist/TextInput/TextInput.d.ts +23 -2
  245. package/dist/TextInput/TextInput.d.ts.map +1 -1
  246. package/dist/TextInput/TextInput.js +55 -10
  247. package/dist/TimeInput/TimeInput.d.ts +23 -1
  248. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  249. package/dist/TimeInput/TimeInput.js +53 -10
  250. package/dist/Timestamp/Timestamp.d.ts +1 -1
  251. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  252. package/dist/Timestamp/Timestamp.js +28 -3
  253. package/dist/Toast/Toast.d.ts.map +1 -1
  254. package/dist/Toast/Toast.js +14 -0
  255. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  256. package/dist/Toast/ToastViewport.js +132 -3
  257. package/dist/ToggleButton/ToggleButton.d.ts +7 -9
  258. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  259. package/dist/ToggleButton/ToggleButton.js +34 -57
  260. package/dist/Tokenizer/Tokenizer.d.ts +12 -1
  261. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  262. package/dist/Tokenizer/Tokenizer.js +27 -5
  263. package/dist/Toolbar/Toolbar.d.ts +3 -3
  264. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  265. package/dist/Toolbar/Toolbar.js +51 -7
  266. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  267. package/dist/Tooltip/useTooltip.js +57 -9
  268. package/dist/TreeList/TreeList.d.ts +1 -1
  269. package/dist/TreeList/TreeList.d.ts.map +1 -1
  270. package/dist/TreeList/TreeList.js +72 -2
  271. package/dist/TreeList/TreeListItem.d.ts +11 -1
  272. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  273. package/dist/TreeList/TreeListItem.js +51 -12
  274. package/dist/TreeList/treeListItem.markers.stylex.d.ts +16 -0
  275. package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +1 -0
  276. package/dist/TreeList/treeListItem.markers.stylex.js +22 -0
  277. package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
  278. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  279. package/dist/Typeahead/BaseTypeahead.js +67 -6
  280. package/dist/Typeahead/Typeahead.d.ts +24 -1
  281. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  282. package/dist/Typeahead/Typeahead.js +29 -5
  283. package/dist/VisuallyHidden/VisuallyHidden.d.ts +61 -0
  284. package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
  285. package/dist/VisuallyHidden/VisuallyHidden.js +65 -0
  286. package/dist/VisuallyHidden/index.d.ts +11 -0
  287. package/dist/VisuallyHidden/index.d.ts.map +1 -0
  288. package/dist/VisuallyHidden/index.js +13 -0
  289. package/dist/astryx.css +40 -1
  290. package/dist/astryx.umd.js +147 -0
  291. package/dist/astryx.umd.js.map +7 -0
  292. package/dist/hooks/focusableSelector.d.ts +22 -0
  293. package/dist/hooks/focusableSelector.d.ts.map +1 -0
  294. package/dist/hooks/focusableSelector.js +24 -0
  295. package/dist/hooks/index.d.ts +12 -2
  296. package/dist/hooks/index.d.ts.map +1 -1
  297. package/dist/hooks/index.js +7 -2
  298. package/dist/hooks/useAnnounce.d.ts +40 -0
  299. package/dist/hooks/useAnnounce.d.ts.map +1 -0
  300. package/dist/hooks/useAnnounce.js +142 -0
  301. package/dist/hooks/useClickableContainer.d.ts +16 -1
  302. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  303. package/dist/hooks/useClickableContainer.js +16 -4
  304. package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
  305. package/dist/hooks/useEntryAnimation.js +4 -4
  306. package/dist/hooks/useFocusTrap.d.ts +18 -0
  307. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  308. package/dist/hooks/useFocusTrap.js +87 -3
  309. package/dist/hooks/useGridFocus.d.ts +88 -1
  310. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  311. package/dist/hooks/useGridFocus.js +261 -62
  312. package/dist/hooks/useKeyboardHint.d.ts +82 -0
  313. package/dist/hooks/useKeyboardHint.d.ts.map +1 -0
  314. package/dist/hooks/useKeyboardHint.js +220 -0
  315. package/dist/hooks/useListFocus.d.ts +78 -7
  316. package/dist/hooks/useListFocus.d.ts.map +1 -1
  317. package/dist/hooks/useListFocus.js +305 -52
  318. package/dist/hooks/useLongPress.d.ts +21 -0
  319. package/dist/hooks/useLongPress.d.ts.map +1 -0
  320. package/dist/hooks/useLongPress.js +89 -0
  321. package/dist/hooks/useTreeFocus.d.ts +157 -0
  322. package/dist/hooks/useTreeFocus.d.ts.map +1 -0
  323. package/dist/hooks/useTreeFocus.js +337 -0
  324. package/dist/hooks/useTypeahead.d.ts +63 -0
  325. package/dist/hooks/useTypeahead.d.ts.map +1 -0
  326. package/dist/hooks/useTypeahead.js +117 -0
  327. package/dist/index.d.ts +1 -0
  328. package/dist/index.d.ts.map +1 -1
  329. package/dist/index.js +1 -0
  330. package/dist/theme/generateThemeRules.js +1 -1
  331. package/dist/theme/syntax/SyntaxTheme.d.ts +1 -1
  332. package/dist/theme/syntax/SyntaxTheme.js +1 -1
  333. package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
  334. package/dist/theme/syntax/defineSyntaxTheme.js +2 -2
  335. package/dist/theme/syntax/presets.d.ts +1 -1
  336. package/dist/theme/syntax/presets.d.ts.map +1 -1
  337. package/dist/theme/syntax/presets.js +1 -1
  338. package/dist/theme/syntax/tokens.d.ts +1 -1
  339. package/dist/theme/syntax/tokens.js +1 -1
  340. package/dist/theme/types.d.ts +1 -1
  341. package/dist/utils/dateTypes.d.ts +12 -0
  342. package/dist/utils/dateTypes.d.ts.map +1 -1
  343. package/dist/utils/dateTypes.js +41 -1
  344. package/dist/utils/index.d.ts +4 -0
  345. package/dist/utils/index.d.ts.map +1 -1
  346. package/dist/utils/index.js +3 -1
  347. package/dist/utils/inputAria.d.ts +29 -0
  348. package/dist/utils/inputAria.d.ts.map +1 -0
  349. package/dist/utils/inputAria.js +30 -0
  350. package/dist/utils/timeParser.d.ts.map +1 -1
  351. package/dist/utils/timeParser.js +5 -2
  352. package/package.json +15 -5
  353. package/src/Avatar/Avatar.test.tsx +37 -0
  354. package/src/Avatar/Avatar.tsx +10 -5
  355. package/src/AvatarGroup/AvatarGroupOverflow.tsx +3 -0
  356. package/src/Banner/Banner.doc.mjs +8 -0
  357. package/src/Banner/Banner.test.tsx +16 -7
  358. package/src/Banner/Banner.tsx +9 -2
  359. package/src/Breadcrumbs/BreadcrumbItem.tsx +25 -7
  360. package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -10
  361. package/src/Button/Button.doc.mjs +6 -0
  362. package/src/Button/Button.test.tsx +105 -11
  363. package/src/Button/Button.tsx +76 -21
  364. package/src/Calendar/Calendar.doc.mjs +4 -4
  365. package/src/Calendar/Calendar.test.tsx +168 -30
  366. package/src/Calendar/Calendar.tsx +121 -66
  367. package/src/Calendar/hooks/index.ts +0 -6
  368. package/src/Calendar/index.ts +1 -3
  369. package/src/Calendar/styles.ts +4 -9
  370. package/src/Carousel/Carousel.doc.mjs +7 -1
  371. package/src/Carousel/Carousel.test.tsx +20 -0
  372. package/src/Carousel/Carousel.tsx +16 -1
  373. package/src/Center/Center.doc.mjs +12 -2
  374. package/src/Center/Center.tsx +28 -3
  375. package/src/Chat/ChatComposer.doc.mjs +3 -0
  376. package/src/Chat/ChatComposerDrawer.doc.mjs +12 -0
  377. package/src/Chat/ChatMessageList.doc.mjs +8 -0
  378. package/src/Chat/ChatMessageList.test.tsx +18 -0
  379. package/src/Chat/ChatMessageList.tsx +16 -0
  380. package/src/Chat/useTriggerMenu.tsx +3 -0
  381. package/src/CheckboxInput/CheckboxInput.doc.mjs +18 -0
  382. package/src/CheckboxInput/CheckboxInput.test.tsx +172 -27
  383. package/src/CheckboxInput/CheckboxInput.tsx +78 -6
  384. package/src/CheckboxList/CheckboxList.doc.mjs +9 -0
  385. package/src/CheckboxList/CheckboxList.test.tsx +169 -16
  386. package/src/CheckboxList/CheckboxList.tsx +56 -3
  387. package/src/CheckboxList/CheckboxListContext.tsx +7 -0
  388. package/src/Citation/Citation.doc.mjs +10 -0
  389. package/src/Citation/Citation.test.tsx +117 -0
  390. package/src/Citation/Citation.tsx +9 -1
  391. package/src/ClickableCard/ClickableCard.doc.mjs +15 -0
  392. package/src/CodeBlock/CodeBlock.test.tsx +115 -0
  393. package/src/CodeBlock/CodeBlock.tsx +61 -33
  394. package/src/CommandPalette/CommandPalette.test.tsx +47 -1
  395. package/src/CommandPalette/CommandPalette.tsx +9 -21
  396. package/src/ContextMenu/ContextMenu.doc.mjs +4 -4
  397. package/src/ContextMenu/ContextMenu.test.tsx +155 -1
  398. package/src/ContextMenu/ContextMenu.tsx +113 -16
  399. package/src/DateInput/DateInput.doc.mjs +281 -56
  400. package/src/DateInput/DateInput.test.tsx +188 -2
  401. package/src/DateInput/DateInput.tsx +89 -10
  402. package/src/DateRangeInput/DateRangeInput.doc.mjs +251 -46
  403. package/src/DateRangeInput/DateRangeInput.test.tsx +182 -20
  404. package/src/DateRangeInput/DateRangeInput.tsx +63 -5
  405. package/src/DateTimeInput/DateTimeInput.doc.mjs +368 -67
  406. package/src/DateTimeInput/DateTimeInput.test.tsx +279 -11
  407. package/src/DateTimeInput/DateTimeInput.tsx +125 -17
  408. package/src/Dialog/Dialog.doc.mjs +2 -2
  409. package/src/Dialog/Dialog.tsx +12 -0
  410. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -8
  411. package/src/DropdownMenu/DropdownMenu.test.tsx +56 -42
  412. package/src/DropdownMenu/DropdownMenu.tsx +52 -18
  413. package/src/EmptyState/EmptyState.test.tsx +4 -2
  414. package/src/EmptyState/EmptyState.tsx +6 -2
  415. package/src/Field/Field.test.tsx +32 -0
  416. package/src/Field/Field.tsx +23 -5
  417. package/src/Field/FieldLabel.tsx +55 -17
  418. package/src/FileInput/FileInput.doc.mjs +17 -0
  419. package/src/FileInput/FileInput.test.tsx +283 -49
  420. package/src/FileInput/FileInput.tsx +76 -4
  421. package/src/Grid/Grid.doc.mjs +14 -4
  422. package/src/Grid/Grid.tsx +20 -0
  423. package/src/HoverCard/HoverCard.doc.mjs +3 -0
  424. package/src/HoverCard/HoverCard.test.tsx +189 -2
  425. package/src/HoverCard/HoverCard.tsx +20 -16
  426. package/src/HoverCard/useHoverCard.tsx +13 -10
  427. package/src/Icon/Icon.doc.mjs +8 -0
  428. package/src/InputGroup/InputGroup.doc.mjs +2 -2
  429. package/src/InputGroup/InputGroup.test.tsx +98 -60
  430. package/src/InputGroup/InputGroup.tsx +21 -4
  431. package/src/InputGroup/InputGroupContext.ts +9 -4
  432. package/src/InputGroup/index.ts +1 -0
  433. package/src/Kbd/Kbd.test.tsx +22 -4
  434. package/src/Kbd/Kbd.tsx +37 -12
  435. package/src/Layer/anchorName.test.ts +146 -0
  436. package/src/Layer/anchorName.ts +46 -0
  437. package/src/Layer/layerAnimations.stylex.ts +20 -4
  438. package/src/Layer/useLayer.doc.mjs +12 -2
  439. package/src/Layer/useLayer.tsx +100 -15
  440. package/src/Layout/Layout.doc.mjs +1 -1
  441. package/src/Layout/Layout.tsx +8 -16
  442. package/src/Layout/LayoutFooter.doc.mjs +4 -4
  443. package/src/Layout/LayoutFooter.tsx +3 -3
  444. package/src/Layout/LayoutHeader.doc.mjs +4 -4
  445. package/src/Layout/LayoutHeader.tsx +3 -3
  446. package/src/Layout/LayoutPanel.tsx +3 -3
  447. package/src/Layout/padding.stylex.ts +52 -0
  448. package/src/Lightbox/Lightbox.doc.mjs +9 -2
  449. package/src/Lightbox/Lightbox.test.tsx +53 -5
  450. package/src/Lightbox/Lightbox.tsx +9 -4
  451. package/src/Link/Link.doc.mjs +14 -0
  452. package/src/Link/Link.test.tsx +52 -2
  453. package/src/Link/Link.tsx +42 -7
  454. package/src/Link/LinkProvider.tsx +1 -0
  455. package/src/List/List.doc.mjs +7 -0
  456. package/src/Markdown/Markdown.doc.mjs +2 -0
  457. package/src/Markdown/Markdown.test.tsx +28 -26
  458. package/src/Markdown/Markdown.tsx +21 -6
  459. package/src/MetadataList/MetadataListItem.doc.mjs +4 -0
  460. package/src/MobileNav/MobileNav.doc.mjs +8 -8
  461. package/src/MobileNav/MobileNav.tsx +13 -0
  462. package/src/MobileNav/MobileNavReopen.test.tsx +118 -0
  463. package/src/MoreMenu/MoreMenu.doc.mjs +2 -17
  464. package/src/MoreMenu/MoreMenu.tsx +0 -9
  465. package/src/MultiSelector/MultiSelector.doc.mjs +25 -0
  466. package/src/MultiSelector/MultiSelector.test.tsx +375 -8
  467. package/src/MultiSelector/MultiSelector.tsx +145 -21
  468. package/src/MultiSelector/hooks.ts +27 -0
  469. package/src/NavMenu/NavHeadingMenu.test.tsx +77 -1
  470. package/src/NavMenu/NavHeadingMenu.tsx +50 -3
  471. package/src/NumberInput/NumberInput.doc.mjs +17 -0
  472. package/src/NumberInput/NumberInput.test.tsx +242 -40
  473. package/src/NumberInput/NumberInput.tsx +89 -11
  474. package/src/OverflowList/OverflowList.doc.mjs +17 -0
  475. package/src/Pagination/Pagination.doc.mjs +3 -3
  476. package/src/Pagination/Pagination.test.tsx +250 -14
  477. package/src/Pagination/Pagination.tsx +54 -29
  478. package/src/Popover/usePopover.doc.mjs +7 -3
  479. package/src/Popover/usePopover.tsx +49 -6
  480. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  481. package/src/PowerSearch/PowerSearch.test.tsx +97 -1
  482. package/src/PowerSearch/PowerSearch.tsx +16 -3
  483. package/src/ProgressBar/ProgressBar.test.tsx +45 -27
  484. package/src/ProgressBar/ProgressBar.tsx +3 -4
  485. package/src/RadioList/RadioList.doc.mjs +9 -0
  486. package/src/RadioList/RadioList.test.tsx +252 -16
  487. package/src/RadioList/RadioList.tsx +166 -10
  488. package/src/RadioList/RadioListItem.tsx +14 -2
  489. package/src/Resizable/Resizable.doc.mjs +1 -1
  490. package/src/Section/Section.doc.mjs +11 -0
  491. package/src/SegmentedControl/SegmentedControl.doc.mjs +9 -0
  492. package/src/SegmentedControl/SegmentedControl.test.tsx +214 -42
  493. package/src/SegmentedControl/SegmentedControl.tsx +102 -54
  494. package/src/SegmentedControl/SegmentedControlContext.ts +7 -0
  495. package/src/SegmentedControl/SegmentedControlItem.tsx +14 -1
  496. package/src/SelectableCard/SelectableCard.doc.mjs +15 -0
  497. package/src/Selector/Selector.doc.mjs +25 -0
  498. package/src/Selector/Selector.test.tsx +322 -15
  499. package/src/Selector/Selector.tsx +126 -40
  500. package/src/Selector/hooks.ts +23 -0
  501. package/src/SideNav/SideNav.test.tsx +16 -0
  502. package/src/SideNav/SideNavItem.tsx +2 -4
  503. package/src/Skeleton/Skeleton.doc.mjs +11 -1
  504. package/src/Skeleton/Skeleton.test.tsx +25 -0
  505. package/src/Skeleton/Skeleton.tsx +11 -1
  506. package/src/Slider/Slider.doc.mjs +19 -0
  507. package/src/Slider/Slider.test.tsx +158 -18
  508. package/src/Slider/Slider.tsx +54 -6
  509. package/src/Spinner/Spinner.tsx +7 -1
  510. package/src/Stack/Stack.doc.mjs +77 -0
  511. package/src/Stack/Stack.test.tsx +55 -5
  512. package/src/Stack/Stack.tsx +73 -24
  513. package/src/Stack/StackItem.test.tsx +23 -0
  514. package/src/Stack/StackItem.tsx +20 -23
  515. package/src/Switch/Switch.doc.mjs +15 -0
  516. package/src/Switch/Switch.test.tsx +162 -27
  517. package/src/Switch/Switch.tsx +61 -21
  518. package/src/TabList/Tab.tsx +6 -0
  519. package/src/TabList/TabList.doc.mjs +12 -0
  520. package/src/TabList/TabList.test.tsx +208 -0
  521. package/src/TabList/TabList.tsx +102 -12
  522. package/src/TabList/TabListContext.ts +7 -3
  523. package/src/TabList/TabMenu.tsx +6 -3
  524. package/src/Table/BaseTable.tsx +72 -41
  525. package/src/Table/Table.doc.mjs +14 -0
  526. package/src/Table/Table.test.tsx +39 -30
  527. package/src/Table/Table.tsx +27 -1
  528. package/src/Table/TableCell.tsx +25 -23
  529. package/src/Table/TableHeaderCell.tsx +28 -29
  530. package/src/Table/TableRow.tsx +37 -7
  531. package/src/Table/index.ts +6 -2
  532. package/src/Table/plugins/pagination/paginateData.ts +10 -3
  533. package/src/Table/plugins/pagination/useTablePagination.test.tsx +16 -0
  534. package/src/Table/plugins/pagination/useTablePagination.tsx +8 -1
  535. package/src/Table/plugins/sortable/useTableSortable.test.tsx +91 -2
  536. package/src/Table/plugins/sortable/useTableSortable.tsx +53 -1
  537. package/src/Table/plugins/stickyColumns/index.ts +4 -0
  538. package/src/Table/plugins/stickyColumns/useTableStickyColumns.test.tsx +163 -0
  539. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +407 -0
  540. package/src/Table/tableContextMenu.test.tsx +157 -0
  541. package/src/Table/tableContextMenu.tsx +142 -0
  542. package/src/Table/types.ts +175 -4
  543. package/src/Table/useBaseTablePlugins.ts +1 -0
  544. package/src/Table/useTableColumnResize.doc.mjs +96 -0
  545. package/src/Table/useTableStickyColumns.doc.mjs +59 -0
  546. package/src/Text/Heading.doc.mjs +15 -2
  547. package/src/Text/Text.doc.mjs +7 -1
  548. package/src/Text/text.stylex.ts +2 -2
  549. package/src/TextArea/TextArea.doc.mjs +17 -0
  550. package/src/TextArea/TextArea.test.tsx +167 -35
  551. package/src/TextArea/TextArea.tsx +62 -15
  552. package/src/TextInput/TextInput.doc.mjs +17 -0
  553. package/src/TextInput/TextInput.test.tsx +168 -34
  554. package/src/TextInput/TextInput.tsx +77 -9
  555. package/src/TimeInput/TimeInput.doc.mjs +335 -60
  556. package/src/TimeInput/TimeInput.test.tsx +144 -7
  557. package/src/TimeInput/TimeInput.tsx +74 -5
  558. package/src/Timestamp/Timestamp.doc.mjs +9 -0
  559. package/src/Timestamp/Timestamp.test.tsx +44 -18
  560. package/src/Timestamp/Timestamp.tsx +30 -10
  561. package/src/Toast/Toast.tsx +14 -0
  562. package/src/Toast/ToastViewport.test.tsx +204 -0
  563. package/src/Toast/ToastViewport.tsx +167 -21
  564. package/src/ToggleButton/ToggleButton.doc.mjs +9 -3
  565. package/src/ToggleButton/ToggleButton.test.tsx +39 -18
  566. package/src/ToggleButton/ToggleButton.tsx +46 -83
  567. package/src/Tokenizer/Tokenizer.doc.mjs +15 -0
  568. package/src/Tokenizer/Tokenizer.test.tsx +105 -1
  569. package/src/Tokenizer/Tokenizer.tsx +41 -2
  570. package/src/Toolbar/Toolbar.doc.mjs +65 -5
  571. package/src/Toolbar/Toolbar.test.tsx +109 -6
  572. package/src/Toolbar/Toolbar.tsx +59 -5
  573. package/src/Tooltip/Tooltip.test.tsx +81 -0
  574. package/src/Tooltip/useTooltip.tsx +57 -11
  575. package/src/TreeList/TreeList.test.tsx +295 -6
  576. package/src/TreeList/TreeList.tsx +73 -1
  577. package/src/TreeList/TreeListItem.tsx +51 -5
  578. package/src/TreeList/treeListItem.markers.stylex.ts +20 -0
  579. package/src/Typeahead/BaseTypeahead.tsx +86 -9
  580. package/src/Typeahead/Typeahead.doc.mjs +136 -30
  581. package/src/Typeahead/Typeahead.test.tsx +331 -1
  582. package/src/Typeahead/Typeahead.tsx +52 -1
  583. package/src/VisuallyHidden/VisuallyHidden.doc.mjs +80 -0
  584. package/src/VisuallyHidden/VisuallyHidden.test.tsx +76 -0
  585. package/src/VisuallyHidden/VisuallyHidden.tsx +113 -0
  586. package/src/VisuallyHidden/index.ts +15 -0
  587. package/src/hooks/focusableSelector.ts +25 -0
  588. package/src/hooks/index.ts +28 -2
  589. package/src/hooks/useAnnounce.test.tsx +101 -0
  590. package/src/hooks/useAnnounce.ts +168 -0
  591. package/src/hooks/useClickableContainer.ts +16 -4
  592. package/src/hooks/useEntryAnimation.ts +16 -4
  593. package/src/hooks/useFocusTrap.test.tsx +132 -0
  594. package/src/hooks/useFocusTrap.ts +98 -4
  595. package/src/hooks/useGridFocus.doc.mjs +40 -2
  596. package/src/hooks/useGridFocus.test.tsx +132 -0
  597. package/src/hooks/useGridFocus.ts +343 -67
  598. package/src/hooks/useImageMode.test.ts +133 -0
  599. package/src/hooks/useKeyboardHint.doc.mjs +101 -0
  600. package/src/hooks/useKeyboardHint.test.tsx +70 -0
  601. package/src/hooks/useKeyboardHint.tsx +332 -0
  602. package/src/hooks/useListFocus.doc.mjs +50 -10
  603. package/src/hooks/useListFocus.test.tsx +375 -0
  604. package/src/hooks/useListFocus.ts +395 -57
  605. package/src/hooks/useLongPress.test.tsx +134 -0
  606. package/src/hooks/useLongPress.ts +119 -0
  607. package/src/hooks/useTreeFocus.doc.mjs +140 -0
  608. package/src/hooks/useTreeFocus.test.tsx +214 -0
  609. package/src/hooks/useTreeFocus.ts +559 -0
  610. package/src/hooks/useTypeahead.test.tsx +109 -0
  611. package/src/hooks/useTypeahead.ts +183 -0
  612. package/src/index.ts +1 -0
  613. package/src/theme/derivedVarRegistry.test.ts +4 -0
  614. package/src/theme/generateThemeRules.test.ts +7 -8
  615. package/src/theme/generateThemeRules.ts +1 -1
  616. package/src/theme/syntax/SyntaxTheme.tsx +1 -1
  617. package/src/theme/syntax/defineSyntaxTheme.ts +20 -11
  618. package/src/theme/syntax/presets.ts +5 -2
  619. package/src/theme/syntax/tokens.ts +1 -1
  620. package/src/theme/types.ts +1 -1
  621. package/src/utils/dateTypes.test.ts +40 -0
  622. package/src/utils/dateTypes.ts +40 -0
  623. package/src/utils/index.ts +9 -0
  624. package/src/utils/inputAria.test.ts +39 -0
  625. package/src/utils/inputAria.ts +58 -0
  626. package/src/utils/timeParser.test.ts +10 -0
  627. package/src/utils/timeParser.ts +5 -2
  628. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts +0 -57
  629. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts.map +0 -1
  630. package/dist/Calendar/hooks/useCalendarRovingTabindex.js +0 -96
  631. package/src/Calendar/hooks/useCalendarRovingTabindex.ts +0 -118
@@ -0,0 +1,133 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file useImageMode.test.ts
5
+ * @input Uses vitest, @testing-library/react, useImageMode hook
6
+ * @output Unit tests for useImageMode hook
7
+ * @position Testing; validates useImageMode.ts pixel-sampling + CORS behavior
8
+ *
9
+ * SYNC: When useImageMode.ts changes, update tests to match new behavior
10
+ */
11
+
12
+ import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
13
+ import {renderHook, waitFor, act} from '@testing-library/react';
14
+ import {useImageMode} from './useImageMode';
15
+
16
+ // jsdom has no fetch/createImageBitmap/OffscreenCanvas — stub them per test so
17
+ // we can exercise the sampling path and the cross-origin failure path.
18
+
19
+ /** Fill the sampled canvas with a flat RGB so perceptualLightness is deterministic. */
20
+ function stubSamplingPipeline(rgb: [number, number, number]) {
21
+ // fetch resolves to a blob (same-origin / CORS-enabled image)
22
+ vi.stubGlobal(
23
+ 'fetch',
24
+ vi.fn(async () => ({blob: async () => ({}) as Blob})),
25
+ );
26
+ vi.stubGlobal(
27
+ 'createImageBitmap',
28
+ vi.fn(async () => ({width: 100, height: 100}) as ImageBitmap),
29
+ );
30
+ const [r, g, b] = rgb;
31
+ class FakeOffscreenCanvas {
32
+ constructor(
33
+ public width: number,
34
+ public height: number,
35
+ ) {}
36
+ getContext() {
37
+ return {
38
+ drawImage: vi.fn(),
39
+ getImageData: (_x: number, _y: number, w: number, h: number) => {
40
+ const data = new Uint8ClampedArray(w * h * 4);
41
+ for (let i = 0; i < data.length; i += 4) {
42
+ data[i] = r;
43
+ data[i + 1] = g;
44
+ data[i + 2] = b;
45
+ data[i + 3] = 255;
46
+ }
47
+ return {data};
48
+ },
49
+ };
50
+ }
51
+ }
52
+ vi.stubGlobal('OffscreenCanvas', FakeOffscreenCanvas);
53
+ }
54
+
55
+ /** Simulate a cross-origin image with no CORS headers: fetch rejects. */
56
+ function stubCorsBlockedFetch() {
57
+ vi.stubGlobal(
58
+ 'fetch',
59
+ vi.fn(async () => {
60
+ throw new TypeError(
61
+ 'Failed to fetch: No Access-Control-Allow-Origin header',
62
+ );
63
+ }),
64
+ );
65
+ vi.stubGlobal('createImageBitmap', vi.fn());
66
+ vi.stubGlobal('OffscreenCanvas', class {});
67
+ }
68
+
69
+ describe('useImageMode', () => {
70
+ beforeEach(() => {
71
+ vi.restoreAllMocks();
72
+ });
73
+ afterEach(() => {
74
+ vi.unstubAllGlobals();
75
+ });
76
+
77
+ it('returns the fallback while no src is provided', () => {
78
+ const {result} = renderHook(() => useImageMode(null, {fallback: 'dark'}));
79
+ expect(result.current).toBe('dark');
80
+ });
81
+
82
+ it('samples a dark image and reports "dark"', async () => {
83
+ stubSamplingPipeline([10, 10, 10]);
84
+ const {result} = renderHook(() =>
85
+ useImageMode('data:image/png;base64,AAAA'),
86
+ );
87
+ await waitFor(() => expect(result.current).toBe('dark'));
88
+ });
89
+
90
+ it('samples a light image and reports "light"', async () => {
91
+ stubSamplingPipeline([240, 240, 240]);
92
+ const {result} = renderHook(() =>
93
+ useImageMode('data:image/png;base64,AAAA'),
94
+ );
95
+ await waitFor(() => expect(result.current).toBe('light'));
96
+ });
97
+
98
+ // ── Reproduction for BB-001 (cluster C6) ────────────────────────────────
99
+ // A cross-origin image with no `Access-Control-Allow-Origin` header cannot be
100
+ // fetched with `mode: 'cors'`, so the APCA sampling pipeline never runs and the
101
+ // hook silently stays on its fallback. This is why image-backed Thumbnail
102
+ // overlay/remove-button contrast is unreliable for hosted demo examples that
103
+ // point `src` at a non-CORS CDN URL.
104
+ it('stays on fallback when a cross-origin image cannot be sampled (CORS)', async () => {
105
+ stubCorsBlockedFetch();
106
+ const {result} = renderHook(() =>
107
+ useImageMode(
108
+ 'https://lookaside.facebook.com/assets/astryx/moody-scene-vertical-2.png',
109
+ {fallback: null},
110
+ ),
111
+ );
112
+ // Give the rejected fetch + state update a chance to flush.
113
+ await act(async () => {
114
+ await new Promise(r => setTimeout(r, 0));
115
+ });
116
+ expect(result.current).toBeNull();
117
+ });
118
+
119
+ it('requests the image with CORS mode (the constraint that fails for non-CORS hosts)', async () => {
120
+ const fetchSpy = vi.fn(async () => {
121
+ throw new TypeError('CORS');
122
+ });
123
+ vi.stubGlobal('fetch', fetchSpy);
124
+ vi.stubGlobal('createImageBitmap', vi.fn());
125
+ vi.stubGlobal('OffscreenCanvas', class {});
126
+ renderHook(() => useImageMode('https://cross-origin.example/x.png'));
127
+ await waitFor(() => expect(fetchSpy).toHaveBeenCalled());
128
+ expect(fetchSpy).toHaveBeenCalledWith(
129
+ 'https://cross-origin.example/x.png',
130
+ {mode: 'cors'},
131
+ );
132
+ });
133
+ });
@@ -0,0 +1,101 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../docs-types').HookDoc} */
4
+ export const docs = {
5
+ name: 'useKeyboardHint',
6
+ displayName: 'useKeyboardHint',
7
+ keywords: ['keyboard', 'hint', 'arrow', 'navigation', 'roving', 'tabindex', 'focus', 'discoverability', 'toolbar', 'tabs', 'segmented', 'affordance', 'a11y'],
8
+ params: [
9
+ {
10
+ name: 'options',
11
+ type: 'UseKeyboardHintOptions',
12
+ description: 'Configuration object for the keyboard hint.',
13
+ required: false,
14
+ },
15
+ {
16
+ name: 'options.orientation',
17
+ type: "'horizontal' | 'vertical' | 'both'",
18
+ description: 'Which arrow-key axis the composite navigates, controlling which arrow icons the hint shows (← → for horizontal, ↑ ↓ for vertical, all four for both).',
19
+ default: "'horizontal'",
20
+ required: false,
21
+ },
22
+ {
23
+ name: 'options.dismissAfterMs',
24
+ type: 'number',
25
+ description: 'Milliseconds before the hint auto-dismisses after appearing.',
26
+ default: '3000',
27
+ required: false,
28
+ },
29
+ {
30
+ name: 'options.isEnabled',
31
+ type: 'boolean',
32
+ description: 'Whether the hint is enabled. Set to false to suppress it for a specific instance (e.g. a disabled or read-only widget).',
33
+ default: 'true',
34
+ required: false,
35
+ },
36
+ ],
37
+ returns: [
38
+ {
39
+ name: 'hintElement',
40
+ type: 'ReactNode',
41
+ description: 'The popover hint element to render inside the composite container (as the last child). Portals to the top layer via popover="manual", renders arrow keys with Kbd, and manages its own visibility — render it unconditionally.',
42
+ },
43
+ {
44
+ name: 'onFocus',
45
+ type: '(e: React.FocusEvent) => void',
46
+ description: 'Attach to the container onFocus. Shows the hint on the first keyboard-focus (:focus-visible) entry from outside the composite.',
47
+ },
48
+ {
49
+ name: 'onBlur',
50
+ type: '(e: React.FocusEvent) => void',
51
+ description: 'Attach to the container onBlur. Hides the hint when focus leaves the composite entirely, and re-anchors when focus moves within.',
52
+ },
53
+ {
54
+ name: 'onKeyDown',
55
+ type: '(e: React.KeyboardEvent) => void',
56
+ description: 'Attach to the container onKeyDown. Dismisses the hint on the first arrow press (the user has discovered the interaction). Never prevents default or stops propagation.',
57
+ },
58
+ ],
59
+ usage: {
60
+ description:
61
+ 'Shows an ephemeral "← → to navigate" hint anchored to the focused item the first time a roving-tabindex composite (Toolbar, TabList, SegmentedControl, etc.) receives keyboard focus. It teaches sighted keyboard users that arrow keys move within the group. The hint renders arrow keys with Kbd in the top layer (popover="manual") and is CSS-anchor-positioned to the focused element, so overflow containers never clip it. It auto-dismisses on the first arrow press, on timeout, or on blur, and does not re-show for that instance. Toolbar, TabList, and SegmentedControl wire this in automatically — reach for the hook directly only when building a custom roving-tabindex widget.',
62
+ bestPractices: [
63
+ { guidance: true, description: 'Compose the returned onFocus/onKeyDown with your existing focus handlers rather than replacing them — call onKeyDown first (it only dismisses, never prevents), then your navigation handler.' },
64
+ { guidance: true, description: 'Render hintElement as the last child of the composite container; it is position:fixed in the top layer and aria-hidden, so it never affects layout or the accessibility tree.' },
65
+ { guidance: true, description: 'Match orientation to the arrow keys your widget actually responds to so the hint shows the correct icons.' },
66
+ { guidance: false, description: 'Use for single controls or widgets without roving-tabindex navigation — the hint only makes sense where arrows move focus within a group.' },
67
+ ],
68
+ },
69
+ relatedComponents: ['Toolbar', 'TabList', 'SegmentedControl'],
70
+ relatedHooks: ['useListFocus', 'useGridFocus', 'useTreeFocus'],
71
+ importPath: '@astryxdesign/core/hooks',
72
+ category: 'focus',
73
+ };
74
+
75
+ /** @type {import('../docs-types').HookTranslationDoc} */
76
+ export const docsDense = {
77
+ description:
78
+ 'Ephemeral "← → to navigate" hint anchored to the focused item on first keyboard focus of a roving-tabindex composite (Toolbar/TabList/SegmentedControl). Teaches sighted keyboard users that arrows move within the group. Kbd-rendered arrows in a top-layer popover="manual", CSS-anchor-positioned (never clipped by overflow), aria-hidden. Auto-dismisses on first arrow press, timeout, or blur; never re-shows for that instance.',
79
+ paramDescriptions: {
80
+ options: 'config for the keyboard hint.',
81
+ 'options.orientation': "arrow axis: 'horizontal' (← →), 'vertical' (↑ ↓), 'both' (all four). Controls shown icons.",
82
+ 'options.dismissAfterMs': 'ms before auto-dismiss after appearing.',
83
+ 'options.isEnabled': 'whether the hint is enabled; false suppresses it (e.g. disabled/read-only widget).',
84
+ },
85
+ returnDescriptions: {
86
+ hintElement: 'popover hint to render as last child of the container. Kbd-rendered arrows, top-layer, self-managing — render unconditionally.',
87
+ onFocus: 'container onFocus: shows hint on first :focus-visible entry from outside.',
88
+ onBlur: 'container onBlur: hides on leaving the composite; re-anchors on internal moves.',
89
+ onKeyDown: 'container onKeyDown: dismisses on first arrow press. Never prevents default.',
90
+ },
91
+ usage: {
92
+ description:
93
+ 'Ephemeral "← → to navigate" hint on first keyboard focus of a roving-tabindex composite. Top-layer anchored popover, aria-hidden. Auto-dismisses on first arrow press, timeout, or blur; no re-show. Toolbar/TabList/SegmentedControl wire it in automatically.',
94
+ bestPractices: [
95
+ { guidance: true, description: 'Compose returned onFocus/onKeyDown with existing handlers (call onKeyDown first — it only dismisses).' },
96
+ { guidance: true, description: 'Render hintElement as last child; top-layer + aria-hidden, no layout/a11y impact.' },
97
+ { guidance: true, description: 'Match orientation to the arrows the widget responds to.' },
98
+ { guidance: false, description: 'Use for single controls / non-roving widgets.' },
99
+ ],
100
+ },
101
+ };
@@ -0,0 +1,70 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file useKeyboardHint.test.tsx
5
+ * @input Uses React Testing Library, useKeyboardHint
6
+ * @output Unit tests for keyboard hint rendering
7
+ */
8
+
9
+ import {render} from '@testing-library/react';
10
+ import {describe, expect, it} from 'vitest';
11
+ import {useKeyboardHint, type KeyboardHintOrientation} from './useKeyboardHint';
12
+
13
+ function TestHint({orientation}: {orientation?: KeyboardHintOrientation}) {
14
+ const {hintElement} = useKeyboardHint({orientation});
15
+ return <div>{hintElement}</div>;
16
+ }
17
+
18
+ function getKeyLabels(container: HTMLElement): (string | null)[] {
19
+ return Array.from(container.querySelectorAll('.astryx-kbd')).map(key =>
20
+ key.getAttribute('aria-label'),
21
+ );
22
+ }
23
+
24
+ function getKeyText(container: HTMLElement): string[] {
25
+ return Array.from(container.querySelectorAll('kbd')).map(
26
+ key => key.textContent ?? '',
27
+ );
28
+ }
29
+
30
+ describe('useKeyboardHint', () => {
31
+ it('renders horizontal arrow keys with Kbd', () => {
32
+ const {container} = render(<TestHint orientation="horizontal" />);
33
+
34
+ expect(getKeyLabels(container)).toEqual(['Left arrow', 'Right arrow']);
35
+ expect(getKeyText(container)).toEqual(['←', '→']);
36
+ });
37
+
38
+ it('renders vertical arrow keys with Kbd', () => {
39
+ const {container} = render(<TestHint orientation="vertical" />);
40
+
41
+ expect(getKeyLabels(container)).toEqual(['Up arrow', 'Down arrow']);
42
+ expect(getKeyText(container)).toEqual(['↑', '↓']);
43
+ });
44
+
45
+ it('renders all arrow keys for both-axis navigation', () => {
46
+ const {container} = render(<TestHint orientation="both" />);
47
+
48
+ expect(getKeyLabels(container)).toEqual([
49
+ 'Left arrow',
50
+ 'Right arrow',
51
+ 'Up arrow',
52
+ 'Down arrow',
53
+ ]);
54
+ expect(getKeyText(container)).toEqual(['←', '→', '↑', '↓']);
55
+ });
56
+
57
+ it('positions the hint farther from the anchor', () => {
58
+ const {container} = render(<TestHint />);
59
+ const hint = container.querySelector('[popover="manual"]') as HTMLElement;
60
+
61
+ expect(hint.style.marginBlockStart).toBe('var(--spacing-2)');
62
+ });
63
+
64
+ it('keeps the hint surface padding after useLayer reset styles', () => {
65
+ const {container} = render(<TestHint />);
66
+ const hint = container.querySelector('[popover="manual"]') as HTMLElement;
67
+
68
+ expect(hint.className).toContain('useKeyboardHint__styles.hint');
69
+ });
70
+ });
@@ -0,0 +1,332 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useKeyboardHint.tsx
7
+ * @input Uses React, StyleX, Kbd, useLayer
8
+ * @output Exports useKeyboardHint hook — ephemeral arrow-key navigation hint
9
+ * @position Core hook; shows sighted keyboard users how to navigate composite
10
+ * widgets that use roving tabindex (single Tab stop, arrows inside)
11
+ *
12
+ * SYNC: When modified, update:
13
+ * - /packages/core/src/hooks/index.ts
14
+ * - /packages/core/src/hooks/useKeyboardHint.doc.mjs
15
+ * - /apps/storybook/stories/useKeyboardHint.stories.tsx
16
+ * - /packages/cli/templates/blocks/components/Hooks/useKeyboardHintHookUsage.tsx
17
+ */
18
+
19
+ import React, {useCallback, useEffect, useRef, type ReactNode} from 'react';
20
+ import * as stylex from '@stylexjs/stylex';
21
+ import {
22
+ colorVars,
23
+ radiusVars,
24
+ shadowVars,
25
+ spacingVars,
26
+ typeScaleVars,
27
+ } from '../theme/tokens.stylex';
28
+ import {Kbd} from '../Kbd';
29
+ import {useLayer} from '../Layer/useLayer';
30
+
31
+ // ---------------------------------------------------------------------------
32
+ // Types
33
+ // ---------------------------------------------------------------------------
34
+
35
+ export type KeyboardHintOrientation = 'horizontal' | 'vertical' | 'both';
36
+
37
+ export interface UseKeyboardHintOptions {
38
+ /**
39
+ * Orientation of the arrow-key navigation. Controls which arrow icons are
40
+ * shown in the hint badge.
41
+ * - `'horizontal'` → ← →
42
+ * - `'vertical'` → ↑ ↓
43
+ * - `'both'` → ← → ↑ ↓
44
+ * @default 'horizontal'
45
+ */
46
+ orientation?: KeyboardHintOrientation;
47
+
48
+ /**
49
+ * Milliseconds before the hint auto-dismisses after appearing.
50
+ * @default 3000
51
+ */
52
+ dismissAfterMs?: number;
53
+
54
+ /**
55
+ * Whether the hint is enabled. Set to false to disable for a specific
56
+ * instance (e.g. when the widget is read-only or the user has dismissed
57
+ * globally).
58
+ * @default true
59
+ */
60
+ isEnabled?: boolean;
61
+ }
62
+
63
+ export interface UseKeyboardHintReturn {
64
+ /**
65
+ * The popover hint element to render inside your component tree (portals to
66
+ * top layer via `popover="manual"`). Render unconditionally — it manages its
67
+ * own visibility.
68
+ */
69
+ hintElement: ReactNode;
70
+
71
+ /**
72
+ * Attach to the composite container's `onFocus`. Shows the hint on the first
73
+ * keyboard-focus (`:focus-visible`) entry from outside.
74
+ */
75
+ onFocus: (e: React.FocusEvent) => void;
76
+
77
+ /**
78
+ * Attach to the composite container's `onBlur`. Hides the hint when focus
79
+ * leaves the composite entirely.
80
+ */
81
+ onBlur: (e: React.FocusEvent) => void;
82
+
83
+ /**
84
+ * Attach to the composite container's `onKeyDown`. Dismisses the hint on the
85
+ * first arrow press (the user discovered the interaction).
86
+ */
87
+ onKeyDown: (e: React.KeyboardEvent) => void;
88
+ }
89
+
90
+ // ---------------------------------------------------------------------------
91
+ // Styles
92
+ // ---------------------------------------------------------------------------
93
+
94
+ const ARROW_KEYS = new Set(['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']);
95
+
96
+ const ARROW_HINT_KEYS: Record<
97
+ KeyboardHintOrientation,
98
+ ReadonlyArray<string>
99
+ > = {
100
+ horizontal: ['left', 'right'],
101
+ vertical: ['up', 'down'],
102
+ both: ['left', 'right', 'up', 'down'],
103
+ };
104
+
105
+ const styles = stylex.create({
106
+ hint: {
107
+ // Top layer + anchor positioned
108
+ position: 'fixed',
109
+ inset: 'auto',
110
+ margin: 0,
111
+ border: 'none',
112
+
113
+ // Surface
114
+ backgroundColor: colorVars['--color-background-popover'],
115
+ borderRadius: radiusVars['--radius-element'],
116
+ boxShadow: shadowVars['--shadow-low'],
117
+ paddingBlockStart: spacingVars['--spacing-1'],
118
+ paddingBlockEnd: spacingVars['--spacing-1'],
119
+ paddingInlineStart: spacingVars['--spacing-2'],
120
+ paddingInlineEnd: spacingVars['--spacing-2'],
121
+
122
+ // Typography
123
+ fontSize: typeScaleVars['--text-supporting-size'],
124
+ lineHeight: typeScaleVars['--text-supporting-leading'],
125
+ color: colorVars['--color-text-secondary'],
126
+ whiteSpace: 'nowrap',
127
+
128
+ // Animation
129
+ opacity: {
130
+ default: 0,
131
+ ':popover-open': 1,
132
+ },
133
+ transitionProperty: 'opacity, display, overlay',
134
+ transitionDuration: '150ms',
135
+ transitionBehavior: 'allow-discrete',
136
+
137
+ // Don't capture pointer events (hint floats above content)
138
+ pointerEvents: 'none',
139
+ },
140
+ keys: {
141
+ display: 'inline-flex',
142
+ alignItems: 'center',
143
+ gap: spacingVars['--spacing-1'],
144
+ },
145
+ label: {
146
+ marginInlineStart: spacingVars['--spacing-1'],
147
+ },
148
+ });
149
+
150
+ // ---------------------------------------------------------------------------
151
+ // Hook
152
+ // ---------------------------------------------------------------------------
153
+
154
+ /**
155
+ * Shows an ephemeral visual hint ("← → to navigate") anchored to the focused
156
+ * item when a composite widget first receives keyboard focus. Teaches sighted
157
+ * keyboard users that arrows navigate within the group.
158
+ *
159
+ * The hint renders in the top layer (popover="manual") and is CSS-anchor-
160
+ * positioned to the currently focused element, so it is never clipped by
161
+ * overflow containers. It auto-dismisses on first arrow press, timeout, or
162
+ * blur, and does not re-show for that instance.
163
+ *
164
+ * @example
165
+ * ```
166
+ * const hint = useKeyboardHint({orientation: 'horizontal'});
167
+ * <div role="toolbar" onFocus={hint.onFocus} onBlur={hint.onBlur} onKeyDown={hint.onKeyDown}>
168
+ * {children}
169
+ * {hint.hintElement}
170
+ * </div>
171
+ * ```
172
+ */
173
+ export function useKeyboardHint(
174
+ options: UseKeyboardHintOptions = {},
175
+ ): UseKeyboardHintReturn {
176
+ const {
177
+ orientation = 'horizontal',
178
+ dismissAfterMs = 3000,
179
+ isEnabled = true,
180
+ } = options;
181
+
182
+ const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
183
+ const dismissedRef = useRef(false);
184
+ const isVisibleRef = useRef(false);
185
+ const layerAnchorRef = useRef<(el: HTMLElement | null) => void>(() => {});
186
+
187
+ const clearDismissTimeout = useCallback(() => {
188
+ if (timeoutRef.current) {
189
+ clearTimeout(timeoutRef.current);
190
+ timeoutRef.current = null;
191
+ }
192
+ }, []);
193
+
194
+ const handleLayerShow = useCallback(() => {
195
+ isVisibleRef.current = true;
196
+ }, []);
197
+
198
+ const handleLayerHide = useCallback(() => {
199
+ isVisibleRef.current = false;
200
+ clearDismissTimeout();
201
+ layerAnchorRef.current(null);
202
+ }, [clearDismissTimeout]);
203
+
204
+ const layer = useLayer({
205
+ mode: 'context',
206
+ onShow: handleLayerShow,
207
+ onHide: handleLayerHide,
208
+ });
209
+ layerAnchorRef.current = layer.ref;
210
+
211
+ // Hide + mark dismissed (won't re-show for this instance)
212
+ const dismiss = useCallback(() => {
213
+ dismissedRef.current = true;
214
+ clearDismissTimeout();
215
+ layer.hide();
216
+ isVisibleRef.current = false;
217
+ layerAnchorRef.current(null);
218
+ }, [clearDismissTimeout, layer]);
219
+
220
+ // Show the layer anchored to the focused element
221
+ const show = useCallback(
222
+ (anchor: HTMLElement) => {
223
+ if (dismissedRef.current || !isEnabled) {
224
+ return;
225
+ }
226
+
227
+ layerAnchorRef.current(anchor);
228
+ layer.show();
229
+
230
+ clearDismissTimeout();
231
+ timeoutRef.current = setTimeout(() => {
232
+ dismiss();
233
+ }, dismissAfterMs);
234
+ },
235
+ [clearDismissTimeout, dismiss, dismissAfterMs, isEnabled, layer],
236
+ );
237
+
238
+ // Cleanup on unmount
239
+ useEffect(
240
+ () => () => {
241
+ clearDismissTimeout();
242
+ layerAnchorRef.current(null);
243
+ },
244
+ [clearDismissTimeout],
245
+ );
246
+
247
+ // --- Handlers ---
248
+
249
+ const onFocus = useCallback(
250
+ (e: React.FocusEvent) => {
251
+ if (dismissedRef.current || !isEnabled) {
252
+ return;
253
+ }
254
+ // Only show on keyboard focus (focus-visible)
255
+ const target = e.target as HTMLElement;
256
+ if (!target.matches(':focus-visible')) {
257
+ return;
258
+ }
259
+ // Only show when focus enters from outside the container
260
+ const container = e.currentTarget as HTMLElement;
261
+ if (
262
+ e.relatedTarget instanceof Node &&
263
+ container.contains(e.relatedTarget)
264
+ ) {
265
+ return;
266
+ }
267
+ show(target);
268
+ },
269
+ [show, isEnabled],
270
+ );
271
+
272
+ const onBlur = useCallback(
273
+ (e: React.FocusEvent) => {
274
+ if (!isVisibleRef.current) {
275
+ return;
276
+ }
277
+ const container = e.currentTarget as HTMLElement;
278
+ // Only dismiss when focus leaves the container entirely
279
+ if (
280
+ e.relatedTarget instanceof Node &&
281
+ container.contains(e.relatedTarget)
282
+ ) {
283
+ // Focus moved within — re-anchor to the new target
284
+ if (!dismissedRef.current && e.relatedTarget instanceof HTMLElement) {
285
+ layerAnchorRef.current(e.relatedTarget);
286
+ }
287
+ return;
288
+ }
289
+ dismiss();
290
+ },
291
+ [dismiss],
292
+ );
293
+
294
+ const onKeyDown = useCallback(
295
+ (e: React.KeyboardEvent) => {
296
+ if (!isVisibleRef.current) {
297
+ return;
298
+ }
299
+ if (ARROW_KEYS.has(e.key)) {
300
+ dismiss();
301
+ }
302
+ },
303
+ [dismiss],
304
+ );
305
+
306
+ // --- Render the hint element ---
307
+
308
+ const arrowContent = (
309
+ <span {...stylex.props(styles.keys)}>
310
+ {ARROW_HINT_KEYS[orientation].map(key => (
311
+ <Kbd key={key} keys={key} />
312
+ ))}
313
+ </span>
314
+ );
315
+
316
+ const hintElement = layer.render(
317
+ <span aria-hidden="true">
318
+ {arrowContent}
319
+ <span {...stylex.props(styles.label)}>to navigate</span>
320
+ </span>,
321
+ {
322
+ placement: 'below',
323
+ alignment: 'start',
324
+ xstyle: styles.hint,
325
+ style: {
326
+ marginBlockStart: spacingVars['--spacing-2'],
327
+ },
328
+ },
329
+ );
330
+
331
+ return {hintElement, onFocus, onBlur, onKeyDown};
332
+ }