@astryxdesign/core 0.3.0 → 0.4.0

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 (551) hide show
  1. package/CHANGELOG.md +156 -0
  2. package/dist/AlertDialog/AlertDialog.d.ts +10 -2
  3. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  4. package/dist/AlertDialog/AlertDialog.js +22 -3
  5. package/dist/AppShell/AppShell.d.ts +2 -2
  6. package/dist/AppShell/AppShell.d.ts.map +1 -1
  7. package/dist/AppShell/AppShell.js +40 -9
  8. package/dist/AspectRatio/AspectRatio.d.ts +13 -0
  9. package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
  10. package/dist/AspectRatio/AspectRatio.js +9 -0
  11. package/dist/Avatar/Avatar.d.ts.map +1 -1
  12. package/dist/Avatar/Avatar.js +33 -20
  13. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  14. package/dist/AvatarGroup/AvatarGroupOverflow.js +2 -3
  15. package/dist/Banner/Banner.d.ts.map +1 -1
  16. package/dist/Banner/Banner.js +15 -14
  17. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  18. package/dist/Breadcrumbs/BreadcrumbItem.js +21 -8
  19. package/dist/Breadcrumbs/index.d.ts +2 -1
  20. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  21. package/dist/Breadcrumbs/index.js +1 -0
  22. package/dist/Button/Button.d.ts.map +1 -1
  23. package/dist/Button/Button.js +11 -15
  24. package/dist/Calendar/Calendar.d.ts.map +1 -1
  25. package/dist/Calendar/Calendar.js +2 -1
  26. package/dist/Calendar/styles.d.ts +0 -2
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +0 -2
  29. package/dist/Carousel/Carousel.d.ts.map +1 -1
  30. package/dist/Carousel/Carousel.js +8 -12
  31. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  32. package/dist/Chat/ChatDictationButton.js +2 -1
  33. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  34. package/dist/Chat/ChatLayoutScrollButton.js +3 -1
  35. package/dist/Chat/ChatSendButton.d.ts.map +1 -1
  36. package/dist/Chat/ChatSendButton.js +4 -3
  37. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  38. package/dist/Chat/ChatToolCalls.js +17 -17
  39. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  40. package/dist/Chat/useTriggerMenu.js +2 -6
  41. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  42. package/dist/CheckboxInput/CheckboxInput.js +36 -125
  43. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  44. package/dist/ClickableCard/ClickableCard.js +3 -11
  45. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  46. package/dist/CodeBlock/CodeBlock.js +84 -67
  47. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  48. package/dist/Collapsible/Collapsible.js +35 -15
  49. package/dist/CommandPalette/CommandPaletteItem.d.ts +1 -1
  50. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  51. package/dist/CommandPalette/CommandPaletteItem.js +6 -3
  52. package/dist/CommandPalette/CommandPaletteList.js +1 -1
  53. package/dist/ComplexSelector/ComplexSelector.d.ts +1 -1
  54. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  55. package/dist/ComplexSelector/ComplexSelector.js +35 -33
  56. package/dist/ContextMenu/ContextMenu.d.ts +2 -2
  57. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  58. package/dist/ContextMenu/ContextMenu.js +1 -1
  59. package/dist/ContextMenu/index.d.ts +2 -1
  60. package/dist/ContextMenu/index.d.ts.map +1 -1
  61. package/dist/ContextMenu/index.js +1 -0
  62. package/dist/DateInput/DateInput.d.ts +9 -2
  63. package/dist/DateInput/DateInput.d.ts.map +1 -1
  64. package/dist/DateInput/DateInput.js +24 -23
  65. package/dist/DateRangeInput/DateRangeInput.d.ts +9 -2
  66. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  67. package/dist/DateRangeInput/DateRangeInput.js +11 -16
  68. package/dist/DateTimeInput/DateTimeInput.d.ts +9 -1
  69. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  70. package/dist/DateTimeInput/DateTimeInput.js +22 -17
  71. package/dist/Dialog/Dialog.d.ts.map +1 -1
  72. package/dist/Dialog/Dialog.js +4 -4
  73. package/dist/DropdownMenu/DropdownMenu.d.ts +33 -8
  74. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  75. package/dist/DropdownMenu/DropdownMenu.js +62 -20
  76. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +8 -11
  77. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  78. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +32 -31
  79. package/dist/DropdownMenu/DropdownMenuDivider.d.ts +25 -0
  80. package/dist/DropdownMenu/DropdownMenuDivider.d.ts.map +1 -0
  81. package/dist/DropdownMenu/DropdownMenuDivider.js +66 -0
  82. package/dist/DropdownMenu/DropdownMenuItem.d.ts +15 -2
  83. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  84. package/dist/DropdownMenu/DropdownMenuItem.js +20 -8
  85. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +5 -5
  86. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  87. package/dist/DropdownMenu/DropdownMenuRadioItem.js +24 -67
  88. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  89. package/dist/DropdownMenu/DropdownMenuSubMenu.js +2 -8
  90. package/dist/DropdownMenu/index.d.ts +2 -1
  91. package/dist/DropdownMenu/index.d.ts.map +1 -1
  92. package/dist/DropdownMenu/index.js +4 -0
  93. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  94. package/dist/DropdownMenu/renderDropdownItems.js +35 -27
  95. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  96. package/dist/EmptyState/EmptyState.js +9 -5
  97. package/dist/Field/InputClearButton.d.ts +16 -3
  98. package/dist/Field/InputClearButton.d.ts.map +1 -1
  99. package/dist/Field/InputClearButton.js +15 -4
  100. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  101. package/dist/HoverCard/useHoverCard.js +3 -19
  102. package/dist/Indicator/CheckIndicator.d.ts +25 -0
  103. package/dist/Indicator/CheckIndicator.d.ts.map +1 -0
  104. package/dist/Indicator/CheckIndicator.js +155 -0
  105. package/dist/Indicator/CheckboxIndicator.d.ts +22 -0
  106. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -0
  107. package/dist/Indicator/CheckboxIndicator.js +173 -0
  108. package/dist/Indicator/RadioIndicator.d.ts +23 -0
  109. package/dist/Indicator/RadioIndicator.d.ts.map +1 -0
  110. package/dist/Indicator/RadioIndicator.js +137 -0
  111. package/dist/Indicator/index.d.ts +24 -0
  112. package/dist/Indicator/index.d.ts.map +1 -0
  113. package/dist/Indicator/index.js +26 -0
  114. package/dist/Indicator/indicator.markers.stylex.d.ts +16 -0
  115. package/dist/Indicator/indicator.markers.stylex.d.ts.map +1 -0
  116. package/dist/Indicator/indicator.markers.stylex.js +21 -0
  117. package/dist/Indicator/indicatorRegistry.d.ts +71 -0
  118. package/dist/Indicator/indicatorRegistry.d.ts.map +1 -0
  119. package/dist/Indicator/indicatorRegistry.js +93 -0
  120. package/dist/Indicator/types.d.ts +167 -0
  121. package/dist/Indicator/types.d.ts.map +1 -0
  122. package/dist/Indicator/types.js +1 -0
  123. package/dist/Indicator/useIndicator.d.ts +18 -0
  124. package/dist/Indicator/useIndicator.d.ts.map +1 -0
  125. package/dist/Indicator/useIndicator.js +30 -0
  126. package/dist/Item/Item.d.ts.map +1 -1
  127. package/dist/Item/Item.js +4 -8
  128. package/dist/Layer/useLayer.d.ts +14 -0
  129. package/dist/Layer/useLayer.d.ts.map +1 -1
  130. package/dist/Layer/useLayer.js +25 -2
  131. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  132. package/dist/Lightbox/Lightbox.js +56 -53
  133. package/dist/Link/Link.d.ts.map +1 -1
  134. package/dist/Link/Link.js +4 -5
  135. package/dist/MobileNav/MobileNav.js +8 -8
  136. package/dist/MoreMenu/MoreMenu.d.ts +14 -1
  137. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  138. package/dist/MoreMenu/MoreMenu.js +4 -0
  139. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  140. package/dist/MultiSelector/MultiSelector.js +78 -75
  141. package/dist/NumberInput/NumberInput.d.ts +27 -1
  142. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  143. package/dist/NumberInput/NumberInput.js +230 -42
  144. package/dist/Outline/Outline.d.ts.map +1 -1
  145. package/dist/Outline/Outline.js +2 -3
  146. package/dist/Pagination/Pagination.d.ts.map +1 -1
  147. package/dist/Pagination/Pagination.js +61 -63
  148. package/dist/Popover/Popover.d.ts.map +1 -1
  149. package/dist/Popover/Popover.js +6 -8
  150. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  151. package/dist/PowerSearch/PowerSearch.js +1 -1
  152. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  153. package/dist/PowerSearch/PowerSearchEditPopover.js +7 -0
  154. package/dist/ProgressBar/ProgressBar.d.ts +11 -4
  155. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  156. package/dist/ProgressBar/ProgressBar.js +96 -6
  157. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  158. package/dist/RadioList/RadioListItem.js +29 -84
  159. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  160. package/dist/Resizable/ResizeHandle.js +2 -3
  161. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  162. package/dist/SegmentedControl/SegmentedControlItem.js +2 -3
  163. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  164. package/dist/SelectableCard/SelectableCard.js +3 -11
  165. package/dist/Selector/Selector.d.ts +2 -1
  166. package/dist/Selector/Selector.d.ts.map +1 -1
  167. package/dist/Selector/Selector.js +164 -94
  168. package/dist/Selector/hooks.d.ts +18 -6
  169. package/dist/Selector/hooks.d.ts.map +1 -1
  170. package/dist/Selector/hooks.js +57 -40
  171. package/dist/SideNav/SideNav.d.ts.map +1 -1
  172. package/dist/SideNav/SideNav.js +1 -1
  173. package/dist/SideNav/SideNavCollapseButton.d.ts +1 -1
  174. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  175. package/dist/SideNav/SideNavCollapseButton.js +29 -14
  176. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  177. package/dist/SideNav/SideNavHeading.js +56 -20
  178. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  179. package/dist/SideNav/SideNavItem.js +28 -22
  180. package/dist/Stack/stack.stylex.d.ts +3 -3
  181. package/dist/Switch/Switch.js +11 -10
  182. package/dist/TabList/Tab.d.ts.map +1 -1
  183. package/dist/TabList/Tab.js +2 -3
  184. package/dist/TabList/TabMenu.d.ts.map +1 -1
  185. package/dist/TabList/TabMenu.js +41 -19
  186. package/dist/Table/BaseTable.d.ts.map +1 -1
  187. package/dist/Table/BaseTable.js +8 -0
  188. package/dist/Table/index.d.ts +1 -1
  189. package/dist/Table/index.d.ts.map +1 -1
  190. package/dist/Table/index.js +1 -1
  191. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts +1 -1
  192. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  193. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +22 -18
  194. package/dist/Table/plugins/rowExpansion/index.d.ts +1 -1
  195. package/dist/Table/plugins/rowExpansion/index.d.ts.map +1 -1
  196. package/dist/Table/plugins/rowExpansion/index.js +1 -1
  197. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts +41 -76
  198. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  199. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +94 -345
  200. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  201. package/dist/Table/plugins/sortable/useTableSortable.js +23 -4
  202. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  203. package/dist/Table/plugins/tree/useTableTreeData.js +29 -33
  204. package/dist/Table/tableContextMenu.d.ts.map +1 -1
  205. package/dist/Table/tableContextMenu.js +2 -1
  206. package/dist/Table/types.d.ts +11 -0
  207. package/dist/Table/types.d.ts.map +1 -1
  208. package/dist/TextArea/TextArea.d.ts +10 -1
  209. package/dist/TextArea/TextArea.d.ts.map +1 -1
  210. package/dist/TextArea/TextArea.js +16 -6
  211. package/dist/TextInput/TextInput.d.ts +10 -1
  212. package/dist/TextInput/TextInput.d.ts.map +1 -1
  213. package/dist/TextInput/TextInput.js +13 -19
  214. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  215. package/dist/Thumbnail/Thumbnail.js +32 -26
  216. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  217. package/dist/TimeInput/TimeInput.js +7 -15
  218. package/dist/Timestamp/TimestampHoverCard.d.ts.map +1 -1
  219. package/dist/Timestamp/TimestampHoverCard.js +16 -34
  220. package/dist/Token/Token.d.ts.map +1 -1
  221. package/dist/Token/Token.js +20 -11
  222. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  223. package/dist/Tokenizer/Tokenizer.js +4 -2
  224. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  225. package/dist/Tooltip/useTooltip.js +3 -19
  226. package/dist/TopNav/TopNav.d.ts.map +1 -1
  227. package/dist/TopNav/TopNav.js +6 -5
  228. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  229. package/dist/TopNav/TopNavHeading.js +54 -16
  230. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  231. package/dist/TopNav/TopNavItem.js +3 -9
  232. package/dist/TopNav/TopNavMegaMenu.d.ts +7 -0
  233. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  234. package/dist/TopNav/TopNavMegaMenu.js +146 -60
  235. package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
  236. package/dist/TopNav/TopNavMegaMenuItem.js +22 -3
  237. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  238. package/dist/TopNav/TopNavMenu.js +83 -38
  239. package/dist/TreeList/TreeList.d.ts.map +1 -1
  240. package/dist/TreeList/TreeList.js +13 -0
  241. package/dist/TreeList/TreeListBranches.d.ts +1 -1
  242. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  243. package/dist/TreeList/TreeListBranches.js +14 -4
  244. package/dist/TreeList/TreeListItem.d.ts +11 -1
  245. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  246. package/dist/TreeList/TreeListItem.js +57 -30
  247. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  248. package/dist/Typeahead/BaseTypeahead.js +16 -8
  249. package/dist/astryx.css +74 -85
  250. package/dist/astryx.umd.js +56 -56
  251. package/dist/astryx.umd.js.map +4 -4
  252. package/dist/hooks/containerReveal.stylex.d.ts +61 -0
  253. package/dist/hooks/containerReveal.stylex.d.ts.map +1 -0
  254. package/dist/hooks/containerReveal.stylex.js +63 -0
  255. package/dist/hooks/index.d.ts +3 -0
  256. package/dist/hooks/index.d.ts.map +1 -1
  257. package/dist/hooks/index.js +2 -0
  258. package/dist/hooks/useClipboard.d.ts +59 -0
  259. package/dist/hooks/useClipboard.d.ts.map +1 -0
  260. package/dist/hooks/useClipboard.js +106 -0
  261. package/dist/hooks/useContainerReveal.d.ts +14 -8
  262. package/dist/hooks/useContainerReveal.d.ts.map +1 -1
  263. package/dist/hooks/useContainerReveal.js +28 -69
  264. package/dist/hooks/useIndicatorFocusRing.d.ts +28 -0
  265. package/dist/hooks/useIndicatorFocusRing.d.ts.map +1 -0
  266. package/dist/hooks/useIndicatorFocusRing.js +96 -0
  267. package/dist/hooks/useLongPress.d.ts.map +1 -1
  268. package/dist/hooks/useLongPress.js +12 -2
  269. package/dist/hooks/useScrollLock.d.ts.map +1 -1
  270. package/dist/hooks/useScrollLock.js +33 -18
  271. package/dist/hooks/useStreamingText.d.ts +18 -3
  272. package/dist/hooks/useStreamingText.d.ts.map +1 -1
  273. package/dist/hooks/useStreamingText.js +47 -7
  274. package/dist/hooks/useTreeFocus.d.ts.map +1 -1
  275. package/dist/hooks/useTreeFocus.js +9 -3
  276. package/dist/hooks/useTypeahead.d.ts +3 -2
  277. package/dist/hooks/useTypeahead.d.ts.map +1 -1
  278. package/dist/hooks/useTypeahead.js +19 -7
  279. package/dist/index.d.ts +1 -0
  280. package/dist/index.d.ts.map +1 -1
  281. package/dist/index.js +1 -0
  282. package/dist/theme/defineTheme.d.ts +16 -0
  283. package/dist/theme/defineTheme.d.ts.map +1 -1
  284. package/dist/theme/defineTheme.js +8 -0
  285. package/dist/theme/derivedVarRegistry.d.ts +8 -0
  286. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  287. package/dist/theme/derivedVarRegistry.js +14 -13
  288. package/dist/theme/expandColorScale.d.ts +3 -1
  289. package/dist/theme/expandColorScale.d.ts.map +1 -1
  290. package/dist/theme/expandColorScale.js +17 -7
  291. package/dist/theme/expandTypeScale.d.ts.map +1 -1
  292. package/dist/theme/expandTypeScale.js +17 -0
  293. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  294. package/dist/theme/generateThemeRules.js +11 -0
  295. package/dist/theme/index.d.ts +1 -0
  296. package/dist/theme/index.d.ts.map +1 -1
  297. package/dist/utils/focusOutline.stylex.d.ts +107 -0
  298. package/dist/utils/focusOutline.stylex.d.ts.map +1 -0
  299. package/dist/utils/focusOutline.stylex.js +130 -0
  300. package/dist/utils/focusReturn.d.ts +24 -0
  301. package/dist/utils/focusReturn.d.ts.map +1 -0
  302. package/dist/utils/focusReturn.js +29 -0
  303. package/dist/utils/index.d.ts +1 -0
  304. package/dist/utils/index.d.ts.map +1 -1
  305. package/dist/utils/index.js +1 -0
  306. package/dist/utils/mergeRefs.d.ts.map +1 -1
  307. package/dist/utils/mergeRefs.js +7 -5
  308. package/dist/utils/rtlStyles.d.ts +2 -2
  309. package/dist/utils/rtlStyles.js +2 -2
  310. package/dist/utils/themeProps.d.ts +20 -1
  311. package/dist/utils/themeProps.d.ts.map +1 -1
  312. package/dist/utils/themeProps.js +8 -2
  313. package/locales/en.json +12 -0
  314. package/locales/pseudo.json +9 -0
  315. package/package.json +7 -2
  316. package/src/AlertDialog/AlertDialog.doc.mjs +16 -3
  317. package/src/AlertDialog/AlertDialog.test.tsx +208 -1
  318. package/src/AlertDialog/AlertDialog.tsx +21 -3
  319. package/src/AppShell/AppShell.doc.mjs +11 -0
  320. package/src/AppShell/AppShell.test.tsx +139 -0
  321. package/src/AppShell/AppShell.tsx +32 -22
  322. package/src/AspectRatio/AspectRatio.doc.mjs +27 -3
  323. package/src/AspectRatio/AspectRatio.test.tsx +30 -0
  324. package/src/AspectRatio/AspectRatio.tsx +13 -0
  325. package/src/Avatar/Avatar.doc.mjs +2 -13
  326. package/src/Avatar/Avatar.test.tsx +33 -8
  327. package/src/Avatar/Avatar.tsx +52 -50
  328. package/src/AvatarGroup/AvatarGroupOverflow.tsx +2 -9
  329. package/src/Banner/Banner.tsx +9 -6
  330. package/src/Breadcrumbs/BreadcrumbItem.tsx +16 -6
  331. package/src/Breadcrumbs/index.ts +5 -1
  332. package/src/Button/Button.tsx +21 -41
  333. package/src/Calendar/Calendar.tsx +2 -1
  334. package/src/Calendar/styles.ts +0 -8
  335. package/src/Carousel/Carousel.doc.mjs +3 -0
  336. package/src/Carousel/Carousel.tsx +10 -6
  337. package/src/Chat/Chat.doc.mjs +2 -0
  338. package/src/Chat/ChatDictationButton.tsx +7 -1
  339. package/src/Chat/ChatLayoutScrollButton.test.tsx +22 -0
  340. package/src/Chat/ChatLayoutScrollButton.tsx +8 -1
  341. package/src/Chat/ChatSendButton.tsx +2 -3
  342. package/src/Chat/ChatToolCalls.test.tsx +35 -0
  343. package/src/Chat/ChatToolCalls.tsx +36 -16
  344. package/src/Chat/useTriggerMenu.tsx +2 -5
  345. package/src/CheckboxInput/CheckboxInput.doc.mjs +4 -2
  346. package/src/CheckboxInput/CheckboxInput.test.tsx +115 -0
  347. package/src/CheckboxInput/CheckboxInput.tsx +45 -202
  348. package/src/ClickableCard/ClickableCard.tsx +6 -11
  349. package/src/CodeBlock/CodeBlock.doc.mjs +3 -0
  350. package/src/CodeBlock/CodeBlock.test.tsx +91 -0
  351. package/src/CodeBlock/CodeBlock.tsx +57 -80
  352. package/src/Collapsible/Collapsible.tsx +22 -18
  353. package/src/CommandPalette/CommandPalette.test.tsx +29 -0
  354. package/src/CommandPalette/CommandPaletteItem.tsx +7 -4
  355. package/src/CommandPalette/CommandPaletteList.tsx +2 -2
  356. package/src/ComplexSelector/ComplexSelector.doc.mjs +42 -0
  357. package/src/ComplexSelector/ComplexSelector.test.tsx +64 -0
  358. package/src/ComplexSelector/ComplexSelector.tsx +46 -29
  359. package/src/ContextMenu/ContextMenu.doc.mjs +1 -1
  360. package/src/ContextMenu/ContextMenu.test.tsx +41 -0
  361. package/src/ContextMenu/ContextMenu.tsx +3 -3
  362. package/src/ContextMenu/index.ts +5 -1
  363. package/src/DateInput/DateInput.doc.mjs +18 -3
  364. package/src/DateInput/DateInput.test.tsx +93 -11
  365. package/src/DateInput/DateInput.tsx +41 -19
  366. package/src/DateRangeInput/DateRangeInput.doc.mjs +10 -2
  367. package/src/DateRangeInput/DateRangeInput.test.tsx +110 -14
  368. package/src/DateRangeInput/DateRangeInput.tsx +24 -12
  369. package/src/DateTimeInput/DateTimeInput.doc.mjs +16 -2
  370. package/src/DateTimeInput/DateTimeInput.test.tsx +75 -0
  371. package/src/DateTimeInput/DateTimeInput.tsx +35 -8
  372. package/src/Dialog/Dialog.tsx +2 -9
  373. package/src/DropdownMenu/DropdownMenu.doc.mjs +5 -9
  374. package/src/DropdownMenu/DropdownMenu.test.tsx +534 -4
  375. package/src/DropdownMenu/DropdownMenu.tsx +113 -52
  376. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +27 -30
  377. package/src/DropdownMenu/DropdownMenuDivider.doc.mjs +34 -0
  378. package/src/DropdownMenu/DropdownMenuDivider.tsx +81 -0
  379. package/src/DropdownMenu/DropdownMenuItem.doc.mjs +39 -4
  380. package/src/DropdownMenu/DropdownMenuItem.tsx +48 -10
  381. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +30 -76
  382. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +25 -19
  383. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +44 -14
  384. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +1 -5
  385. package/src/DropdownMenu/index.ts +8 -1
  386. package/src/DropdownMenu/renderDropdownItems.tsx +33 -34
  387. package/src/EmptyState/EmptyState.doc.mjs +4 -0
  388. package/src/EmptyState/EmptyState.test.tsx +62 -0
  389. package/src/EmptyState/EmptyState.tsx +16 -6
  390. package/src/Field/Field.doc.mjs +1 -0
  391. package/src/Field/InputClearButton.test.tsx +117 -0
  392. package/src/Field/InputClearButton.tsx +30 -3
  393. package/src/HoverCard/useHoverCard.tsx +2 -23
  394. package/src/Icon/Icon.doc.mjs +2 -1
  395. package/src/Indicator/CheckIndicator.tsx +174 -0
  396. package/src/Indicator/CheckboxIndicator.tsx +257 -0
  397. package/src/Indicator/Indicator.doc.mjs +344 -0
  398. package/src/Indicator/Indicator.test.tsx +393 -0
  399. package/src/Indicator/RadioIndicator.tsx +206 -0
  400. package/src/Indicator/index.ts +45 -0
  401. package/src/Indicator/indicator.markers.stylex.ts +19 -0
  402. package/src/Indicator/indicatorRegistry.test.tsx +106 -0
  403. package/src/Indicator/indicatorRegistry.ts +120 -0
  404. package/src/Indicator/types.ts +186 -0
  405. package/src/Indicator/useIndicator.ts +39 -0
  406. package/src/Item/Item.doc.mjs +1 -1
  407. package/src/Item/Item.tsx +7 -14
  408. package/src/Layer/useLayer.doc.mjs +2 -2
  409. package/src/Layer/useLayer.test.tsx +85 -0
  410. package/src/Layer/useLayer.tsx +39 -1
  411. package/src/Lightbox/Lightbox.doc.mjs +1 -1
  412. package/src/Lightbox/Lightbox.tsx +43 -38
  413. package/src/Link/Link.tsx +4 -11
  414. package/src/MobileNav/MobileNav.tsx +3 -3
  415. package/src/MoreMenu/MoreMenu.doc.mjs +31 -0
  416. package/src/MoreMenu/MoreMenu.test.tsx +68 -1
  417. package/src/MoreMenu/MoreMenu.tsx +19 -0
  418. package/src/MultiSelector/MultiSelector.doc.mjs +9 -1
  419. package/src/MultiSelector/MultiSelector.test.tsx +255 -16
  420. package/src/MultiSelector/MultiSelector.tsx +103 -99
  421. package/src/NumberInput/NumberInput.doc.mjs +265 -50
  422. package/src/NumberInput/NumberInput.test.tsx +726 -21
  423. package/src/NumberInput/NumberInput.tsx +353 -59
  424. package/src/Outline/Outline.tsx +2 -5
  425. package/src/Pagination/Pagination.doc.mjs +1 -1
  426. package/src/Pagination/Pagination.test.tsx +22 -7
  427. package/src/Pagination/Pagination.tsx +38 -35
  428. package/src/Popover/Popover.tsx +6 -7
  429. package/src/PowerSearch/PowerSearch.tsx +1 -1
  430. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +42 -0
  431. package/src/PowerSearch/PowerSearchEditPopover.tsx +7 -0
  432. package/src/ProgressBar/ProgressBar.doc.mjs +27 -5
  433. package/src/ProgressBar/ProgressBar.test.tsx +224 -1
  434. package/src/ProgressBar/ProgressBar.tsx +124 -22
  435. package/src/RadioList/RadioList.doc.mjs +4 -2
  436. package/src/RadioList/RadioListItem.tsx +31 -130
  437. package/src/Resizable/ResizeHandle.tsx +2 -9
  438. package/src/SegmentedControl/SegmentedControlItem.tsx +2 -9
  439. package/src/SelectableCard/SelectableCard.tsx +4 -9
  440. package/src/Selector/Selector.doc.mjs +7 -4
  441. package/src/Selector/Selector.test.tsx +935 -25
  442. package/src/Selector/Selector.tsx +189 -106
  443. package/src/Selector/hooks.ts +77 -50
  444. package/src/SideNav/SideNav.test.tsx +46 -0
  445. package/src/SideNav/SideNav.tsx +1 -0
  446. package/src/SideNav/SideNavCollapseButton.tsx +15 -10
  447. package/src/SideNav/SideNavHeading.tsx +48 -10
  448. package/src/SideNav/SideNavItem.tsx +20 -12
  449. package/src/Stack/Stack.doc.mjs +110 -0
  450. package/src/StatusDot/StatusDot.doc.mjs +12 -3
  451. package/src/Switch/Switch.test.tsx +32 -0
  452. package/src/Switch/Switch.tsx +8 -8
  453. package/src/TabList/Tab.tsx +2 -9
  454. package/src/TabList/TabMenu.tsx +14 -21
  455. package/src/Table/BaseTable.tsx +11 -0
  456. package/src/Table/index.ts +1 -4
  457. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +30 -14
  458. package/src/Table/plugins/rowExpansion/index.ts +1 -4
  459. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +125 -175
  460. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +115 -458
  461. package/src/Table/plugins/sortable/useTableSortable.tsx +2 -6
  462. package/src/Table/plugins/tree/useTableTreeData.tsx +43 -24
  463. package/src/Table/tableContextMenu.test.tsx +27 -0
  464. package/src/Table/tableContextMenu.tsx +1 -0
  465. package/src/Table/types.ts +11 -0
  466. package/src/Table/useTableGroupedRows.doc.mjs +1 -1
  467. package/src/Table/useTableRowExpansion.doc.mjs +43 -66
  468. package/src/TextArea/TextArea.doc.mjs +42 -2
  469. package/src/TextArea/TextArea.test.tsx +219 -0
  470. package/src/TextArea/TextArea.tsx +34 -7
  471. package/src/TextInput/TextInput.doc.mjs +18 -2
  472. package/src/TextInput/TextInput.test.tsx +189 -0
  473. package/src/TextInput/TextInput.tsx +25 -31
  474. package/src/Thumbnail/Thumbnail.tsx +2 -9
  475. package/src/TimeInput/TimeInput.doc.mjs +2 -2
  476. package/src/TimeInput/TimeInput.test.tsx +30 -3
  477. package/src/TimeInput/TimeInput.tsx +9 -26
  478. package/src/Timestamp/Timestamp.doc.mjs +4 -3
  479. package/src/Timestamp/TimestampHoverCard.tsx +13 -35
  480. package/src/Token/Token.tsx +5 -28
  481. package/src/Tokenizer/Tokenizer.doc.mjs +2 -2
  482. package/src/Tokenizer/Tokenizer.test.tsx +45 -3
  483. package/src/Tokenizer/Tokenizer.tsx +10 -2
  484. package/src/Tooltip/useTooltip.tsx +2 -23
  485. package/src/TopNav/TopNav.tsx +1 -3
  486. package/src/TopNav/TopNavHeading.tsx +42 -13
  487. package/src/TopNav/TopNavItem.tsx +3 -21
  488. package/src/TopNav/TopNavMegaMenu.test.tsx +433 -33
  489. package/src/TopNav/TopNavMegaMenu.tsx +118 -53
  490. package/src/TopNav/TopNavMegaMenuItem.tsx +2 -9
  491. package/src/TopNav/TopNavMenu.tsx +34 -37
  492. package/src/TreeList/TreeList.doc.mjs +5 -0
  493. package/src/TreeList/TreeList.test.tsx +206 -7
  494. package/src/TreeList/TreeList.tsx +22 -0
  495. package/src/TreeList/TreeListBranches.tsx +25 -2
  496. package/src/TreeList/TreeListItem.tsx +89 -52
  497. package/src/Typeahead/BaseTypeahead.tsx +19 -6
  498. package/src/Typeahead/Typeahead.doc.mjs +1 -0
  499. package/src/Typeahead/Typeahead.test.tsx +76 -3
  500. package/src/hooks/containerReveal.stylex.ts +102 -0
  501. package/src/hooks/index.ts +4 -0
  502. package/src/hooks/useClipboard.doc.mjs +87 -0
  503. package/src/hooks/useClipboard.test.tsx +151 -0
  504. package/src/hooks/useClipboard.ts +135 -0
  505. package/src/hooks/useContainerReveal.doc.mjs +7 -7
  506. package/src/hooks/useContainerReveal.test.tsx +42 -62
  507. package/src/hooks/useContainerReveal.ts +36 -91
  508. package/src/hooks/useImageMode.test.ts +1 -1
  509. package/src/hooks/useIndicatorFocusRing.tsx +120 -0
  510. package/src/hooks/useLongPress.test.tsx +18 -0
  511. package/src/hooks/useLongPress.ts +12 -2
  512. package/src/hooks/useScrollLock.test.ts +72 -0
  513. package/src/hooks/useScrollLock.ts +51 -19
  514. package/src/hooks/useStreamingText.test.ts +46 -1
  515. package/src/hooks/useStreamingText.ts +49 -7
  516. package/src/hooks/useTreeFocus.test.tsx +50 -0
  517. package/src/hooks/useTreeFocus.ts +14 -3
  518. package/src/hooks/useTypeahead.test.tsx +150 -0
  519. package/src/hooks/useTypeahead.ts +24 -9
  520. package/src/i18n/InternationalizationProvider.doc.mjs +7 -2
  521. package/src/index.ts +1 -0
  522. package/src/theme/defineTheme.ts +24 -0
  523. package/src/theme/derivedVarRegistry.test.ts +21 -1
  524. package/src/theme/derivedVarRegistry.ts +19 -6
  525. package/src/theme/expandColorScale.test.ts +14 -0
  526. package/src/theme/expandColorScale.ts +24 -9
  527. package/src/theme/expandTypeScale.test.ts +43 -0
  528. package/src/theme/expandTypeScale.ts +17 -0
  529. package/src/theme/generateThemeRules.test.ts +61 -20
  530. package/src/theme/generateThemeRules.ts +11 -0
  531. package/src/theme/index.ts +13 -0
  532. package/src/utils/focusOutline.stylex.ts +163 -0
  533. package/src/utils/focusReturn.test.ts +42 -0
  534. package/src/utils/focusReturn.ts +31 -0
  535. package/src/utils/index.ts +1 -0
  536. package/src/utils/mergeRefs.test.ts +48 -0
  537. package/src/utils/mergeRefs.ts +12 -6
  538. package/src/utils/rtlStyles.ts +2 -2
  539. package/src/utils/themeProps.ts +26 -4
  540. package/dist/CheckboxInput/checkbox.markers.stylex.d.ts +0 -9
  541. package/dist/CheckboxInput/checkbox.markers.stylex.d.ts.map +0 -1
  542. package/dist/CheckboxInput/checkbox.markers.stylex.js +0 -14
  543. package/dist/RadioList/radio.markers.stylex.d.ts +0 -7
  544. package/dist/RadioList/radio.markers.stylex.d.ts.map +0 -1
  545. package/dist/RadioList/radio.markers.stylex.js +0 -12
  546. package/dist/hooks/containerReveal.pool.stylex.d.ts +0 -303
  547. package/dist/hooks/containerReveal.pool.stylex.d.ts.map +0 -1
  548. package/dist/hooks/containerReveal.pool.stylex.js +0 -292
  549. package/src/CheckboxInput/checkbox.markers.stylex.ts +0 -12
  550. package/src/RadioList/radio.markers.stylex.ts +0 -10
  551. package/src/hooks/containerReveal.pool.stylex.ts +0 -571
@@ -171,6 +171,28 @@ describe('BaseTypeahead', () => {
171
171
  });
172
172
  });
173
173
 
174
+ it('exposes the empty state as a themeable target', async () => {
175
+ const {container} = render(
176
+ <BaseTypeahead
177
+ searchSource={fruitSource}
178
+ value={null}
179
+ onChange={() => {}}
180
+ debounceMs={0}
181
+ emptySearchResultsText="No results found"
182
+ />,
183
+ );
184
+ const input = screen.getByRole('combobox');
185
+ fireEvent.change(input, {target: {value: 'zzzzz'}});
186
+
187
+ await waitFor(() => {
188
+ const emptyState = container.querySelector(
189
+ '.astryx-typeahead-empty-state',
190
+ );
191
+ expect(emptyState).not.toBeNull();
192
+ expect(emptyState).toHaveTextContent('No results found');
193
+ });
194
+ });
195
+
174
196
  describe('empty results active descendant (#4059)', () => {
175
197
  it('does not set aria-activedescendant when search has 0 results', async () => {
176
198
  render(
@@ -198,6 +220,45 @@ describe('BaseTypeahead', () => {
198
220
  });
199
221
  });
200
222
 
223
+ describe('IME composition guard (#4828)', () => {
224
+ it('does not select the highlighted result on a composing Enter', async () => {
225
+ const onChange = vi.fn();
226
+ render(
227
+ <BaseTypeahead
228
+ searchSource={fruitSource}
229
+ value={null}
230
+ onChange={onChange}
231
+ debounceMs={0}
232
+ />,
233
+ );
234
+ const input = screen.getByRole('combobox');
235
+ fireEvent.change(input, {target: {value: 'App'}});
236
+ await waitFor(() => {
237
+ expect(input).toHaveAttribute('aria-expanded', 'true');
238
+ });
239
+
240
+ // The browser fires this composing keydown for the Enter that commits
241
+ // an IME candidate (isComposing: true, or legacy keyCode 229) before
242
+ // compositionend writes the pending syllable into the input. Without
243
+ // the guard this both selects the highlighted result AND clears the
244
+ // input via handleSelect, so the syllable that compositionend then
245
+ // writes lands in an emptied field instead of being part of the word.
246
+ fireEvent.keyDown(input, {key: 'Enter', isComposing: true});
247
+ expect(onChange).not.toHaveBeenCalled();
248
+ expect(input).toHaveValue('App');
249
+
250
+ fireEvent.keyDown(input, {key: 'Enter', keyCode: 229});
251
+ expect(onChange).not.toHaveBeenCalled();
252
+ expect(input).toHaveValue('App');
253
+
254
+ // A real, non-composing Enter still selects normally.
255
+ fireEvent.keyDown(input, {key: 'Enter'});
256
+ expect(onChange).toHaveBeenCalledWith(
257
+ expect.objectContaining({label: 'Apple'}),
258
+ );
259
+ });
260
+ });
261
+
201
262
  it('disables input when isDisabled', () => {
202
263
  render(
203
264
  <BaseTypeahead
@@ -1038,11 +1099,16 @@ describe('Typeahead disabledMessage', () => {
1038
1099
  });
1039
1100
  });
1040
1101
 
1041
-
1042
1102
  describe('Typeahead statusVariant forwarding', () => {
1043
1103
  it('defaults to attached (status renders with data-variant="attached")', () => {
1044
1104
  const {container} = render(
1045
- <Typeahead label="Fruit" searchSource={fruitSource} value={null} onChange={() => {}} status={{type: 'error', message: 'Required'}} />,
1105
+ <Typeahead
1106
+ label="Fruit"
1107
+ searchSource={fruitSource}
1108
+ value={null}
1109
+ onChange={() => {}}
1110
+ status={{type: 'error', message: 'Required'}}
1111
+ />,
1046
1112
  );
1047
1113
  expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
1048
1114
  'data-variant',
@@ -1052,7 +1118,14 @@ describe('Typeahead statusVariant forwarding', () => {
1052
1118
 
1053
1119
  it('forwards statusVariant="detached" to the underlying Field status', () => {
1054
1120
  const {container} = render(
1055
- <Typeahead label="Fruit" searchSource={fruitSource} value={null} onChange={() => {}} status={{type: 'error', message: 'Required'}} statusVariant="detached" />,
1121
+ <Typeahead
1122
+ label="Fruit"
1123
+ searchSource={fruitSource}
1124
+ value={null}
1125
+ onChange={() => {}}
1126
+ status={{type: 'error', message: 'Required'}}
1127
+ statusVariant="detached"
1128
+ />,
1056
1129
  );
1057
1130
  expect(container.querySelector('.astryx-field-status')).toHaveAttribute(
1058
1131
  'data-variant',
@@ -0,0 +1,102 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file containerReveal.stylex.ts
5
+ * @input Uses StyleX, theme tokens
6
+ * @output The container style that publishes the reveal state, and the four
7
+ * content style blocks (reveal / conceal, each with a layout-preserved
8
+ * variant) that read it.
9
+ * @position Internal to useContainerReveal.
10
+ *
11
+ * HOW THE SCOPING WORKS: the container declares its own reveal state as
12
+ * inherited custom properties on itself, and content reads them. Because a
13
+ * nested container re-declares the same properties on itself, its subtree sees
14
+ * the inner value and never the ancestor's — nesting isolation falls out of the
15
+ * cascade, with one static style block for any number of containers.
16
+ *
17
+ * SYNC: When the block shape changes, update useContainerReveal.ts.
18
+ */
19
+
20
+ import * as stylex from '@stylexjs/stylex';
21
+ import {durationVars, easeVars} from '../theme/tokens.stylex';
22
+
23
+ const REST_DELAY = '0s, ' + durationVars['--duration-fast'];
24
+
25
+ export const styles = stylex.create({
26
+ container: {
27
+ '--_reveal-opacity': {
28
+ default: 0,
29
+ ':hover': {'@media (hover: hover)': 1},
30
+ ':focus-within': 1,
31
+ '@media (any-pointer: coarse)': 1,
32
+ },
33
+ '--_reveal-position': {
34
+ default: 'absolute',
35
+ ':hover': {'@media (hover: hover)': 'static'},
36
+ ':focus-within': 'static',
37
+ '@media (any-pointer: coarse)': 'static',
38
+ },
39
+ // The position flip is discrete, so it transitions with allow-discrete and
40
+ // a state-conditional delay: 0 on entry (flips into flow immediately, then
41
+ // fades in) and the fade duration on exit (stays in flow until the fade
42
+ // finishes, then snaps out) — without this the exit would snap out of flow
43
+ // at full opacity and flicker.
44
+ '--_reveal-delay': {
45
+ default: REST_DELAY,
46
+ ':hover': {'@media (hover: hover)': '0s, 0s'},
47
+ ':focus-within': '0s, 0s',
48
+ '@media (any-pointer: coarse)': '0s, 0s',
49
+ },
50
+ // Conceal is a mouse-only visual swap, so it reads hover alone: no
51
+ // :focus-within (a keyboard user must never watch content vanish) and no
52
+ // coarse-pointer branch (it stays visible on touch).
53
+ '--_conceal-opacity': {
54
+ default: 1,
55
+ ':hover': {'@media (hover: hover)': 0},
56
+ },
57
+ },
58
+ // The fallbacks make content spread outside a reveal container fail visible
59
+ // rather than invisible.
60
+ reveal: {
61
+ transitionProperty: 'opacity, position',
62
+ transitionDuration: {
63
+ default: durationVars['--duration-fast'] + ', 0s',
64
+ '@media (prefers-reduced-motion: reduce)': '0s, 0s',
65
+ },
66
+ transitionTimingFunction: easeVars['--ease-standard'],
67
+ transitionBehavior: 'allow-discrete',
68
+ transitionDelay: {
69
+ default: 'var(--_reveal-delay, 0s, 0s)',
70
+ '@media (prefers-reduced-motion: reduce)': '0s, 0s',
71
+ },
72
+ opacity: 'var(--_reveal-opacity, 1)',
73
+ position: 'var(--_reveal-position, static)',
74
+ },
75
+ revealLayoutPreserved: {
76
+ transitionProperty: 'opacity',
77
+ transitionDuration: {
78
+ default: durationVars['--duration-fast'],
79
+ '@media (prefers-reduced-motion: reduce)': '0s',
80
+ },
81
+ transitionTimingFunction: easeVars['--ease-standard'],
82
+ opacity: 'var(--_reveal-opacity, 1)',
83
+ },
84
+ conceal: {
85
+ transitionProperty: 'opacity',
86
+ transitionDuration: {
87
+ default: durationVars['--duration-fast'],
88
+ '@media (prefers-reduced-motion: reduce)': '0s',
89
+ },
90
+ transitionTimingFunction: easeVars['--ease-standard'],
91
+ opacity: 'var(--_conceal-opacity, 1)',
92
+ },
93
+ concealLayoutPreserved: {
94
+ transitionProperty: 'opacity',
95
+ transitionDuration: {
96
+ default: durationVars['--duration-fast'],
97
+ '@media (prefers-reduced-motion: reduce)': '0s',
98
+ },
99
+ transitionTimingFunction: easeVars['--ease-standard'],
100
+ opacity: 'var(--_conceal-opacity, 1)',
101
+ },
102
+ });
@@ -17,6 +17,9 @@ export type {UseFocusTrapOptions, UseFocusTrapReturn} from './useFocusTrap';
17
17
  export {useAnnounce} from './useAnnounce';
18
18
  export type {AnnounceFn, AnnouncePoliteness} from './useAnnounce';
19
19
 
20
+ export {useClipboard} from './useClipboard';
21
+ export type {UseClipboardOptions, UseClipboardReturn} from './useClipboard';
22
+
20
23
  export {useGridFocus} from './useGridFocus';
21
24
  export type {UseGridFocusOptions, UseGridFocusReturn} from './useGridFocus';
22
25
 
@@ -92,6 +95,7 @@ export {useLongPress} from './useLongPress';
92
95
  export type {UseLongPressOptions, UseLongPressHandlers} from './useLongPress';
93
96
 
94
97
  export {useDevWarning} from './useDevWarning';
98
+ export {useIndicatorFocusRing} from './useIndicatorFocusRing';
95
99
 
96
100
  export {useContainerReveal} from './useContainerReveal';
97
101
  export type {
@@ -0,0 +1,87 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('@astryxdesign/cli/authoring').HookDoc} */
4
+ export const docs = {
5
+ name: 'useClipboard',
6
+ displayName: 'useClipboard',
7
+ keywords: [
8
+ 'clipboard', 'copy', 'copied', 'paste', 'writeText', 'copy-to-clipboard',
9
+ 'copy button', 'announce', 'a11y',
10
+ ],
11
+ params: [
12
+ {
13
+ name: 'options',
14
+ type: 'UseClipboardOptions',
15
+ description: 'Configuration object.',
16
+ required: false,
17
+ },
18
+ {
19
+ name: 'options.announce',
20
+ type: 'string',
21
+ description:
22
+ 'Message announced to a polite live region on a successful copy. A swapped aria-label alone is not reliably announced, so pass the localized confirmation (e.g. "Copied") to have it spoken. Omit to skip the announcement.',
23
+ required: false,
24
+ },
25
+ {
26
+ name: 'options.resetAfterMs',
27
+ type: 'number',
28
+ description:
29
+ 'Milliseconds isCopied stays true after a successful copy before reverting.',
30
+ default: '2000',
31
+ required: false,
32
+ },
33
+ ],
34
+ returns: [
35
+ {
36
+ name: 'copy',
37
+ type: '(text: string) => Promise<boolean>',
38
+ description:
39
+ 'Writes text to the clipboard. On success flips isCopied to true, announces the configured message, restarts the reset timer, and resolves true. A clipboard rejection is a silent no-op that leaves the copied state unchanged and resolves false.',
40
+ },
41
+ {
42
+ name: 'isCopied',
43
+ type: 'boolean',
44
+ description:
45
+ 'True for resetAfterMs after the most recent successful copy, then reverts. Drive the copied confirmation (e.g. a copy → check icon flip) off this.',
46
+ },
47
+ ],
48
+ usage: {
49
+ description:
50
+ 'Copy-to-clipboard behavior: the clipboard write, a transient isCopied flag with its own reset timer, and an optional polite screen-reader announcement. Extracted so every copy affordance is a thin control over one implementation instead of re-deriving the timer and announcement. Rapid re-copies restart the reset timer so the confirmation always lasts the full duration, and the timer is cleaned up on unmount. CodeBlock and Timestamp build their built-in copy buttons on it; reach for it directly when building a copy affordance that is not a plain icon button (a menu item, a labeled text button, a copy-on-click value chip).',
51
+ bestPractices: [
52
+ { guidance: true, description: 'Drive the copied confirmation (copy → check icon, label swap) off the returned isCopied flag rather than tracking your own state.' },
53
+ { guidance: true, description: 'Pass a localized announce message so the copy is spoken by screen readers; swapping the button aria-label alone is not reliably announced.' },
54
+ { guidance: true, description: 'For the common compact icon copy button, render a ghost IconButton with a "Copy" tooltip and wire onClick to copy(); the tooltip stays "Copy" and the icon flip is the confirmation.' },
55
+ { guidance: false, description: 'Track a separate copied useState alongside the hook; isCopied already reflects the copied window and resets itself.' },
56
+ ],
57
+ },
58
+ relatedComponents: ['CodeBlock', 'Timestamp', 'IconButton'],
59
+ relatedHooks: ['useAnnounce'],
60
+ importPath: '@astryxdesign/core/hooks',
61
+ category: 'interaction',
62
+ };
63
+
64
+ /** @type {import('@astryxdesign/cli/authoring').HookTranslationDoc} */
65
+ export const docsDense = {
66
+ description:
67
+ 'Copy-to-clipboard behavior: clipboard write + transient isCopied flag with reset timer + optional polite SR announcement. One implementation shared by copy affordances instead of re-deriving timer/announce. Rapid re-copies restart the timer; timer cleaned up on unmount. CodeBlock/Timestamp build their copy buttons on it.',
68
+ paramDescriptions: {
69
+ options: 'config object.',
70
+ 'options.announce': 'polite live-region message on successful copy (e.g. "Copied"); aria-label swap alone is not reliably announced. Omit to skip.',
71
+ 'options.resetAfterMs': 'ms isCopied stays true after a copy before reverting.',
72
+ },
73
+ returnDescriptions: {
74
+ copy: '(text) => Promise<boolean>: writes text, flips isCopied, announces, restarts reset timer, resolves true; silent no-op resolving false on rejection.',
75
+ isCopied: 'true for resetAfterMs after the last successful copy; drive the copy → check confirmation off it.',
76
+ },
77
+ usage: {
78
+ description:
79
+ 'Copy-to-clipboard behavior (write + isCopied flag with reset timer + optional polite SR announce). Thin controls over one implementation. Rapid re-copies restart the timer; cleaned up on unmount. Use directly for copy affordances that are not plain icon buttons (menu item, labeled text button, value chip).',
80
+ bestPractices: [
81
+ { guidance: true, description: 'Drive the copy → check confirmation off the returned isCopied, not your own state.' },
82
+ { guidance: true, description: 'Pass a localized announce so the copy is spoken (aria-label swap alone is not reliably announced).' },
83
+ { guidance: true, description: 'For a compact icon copy button, use a ghost IconButton with a "Copy" tooltip and wire onClick to copy(); the tooltip stays "Copy" and the icon flip is the confirmation.' },
84
+ { guidance: false, description: 'Track a separate copied useState alongside the hook.' },
85
+ ],
86
+ },
87
+ };
@@ -0,0 +1,151 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file useClipboard.test.tsx
5
+ * @input Uses vitest, @testing-library/react, useClipboard hook
6
+ * @output Unit tests for the useClipboard copy behavior
7
+ * @position Testing; validates useClipboard.ts
8
+ *
9
+ * SYNC: When useClipboard.ts changes, update tests to match new behavior
10
+ */
11
+
12
+ import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
13
+ import {act, renderHook, waitFor} from '@testing-library/react';
14
+ import {useClipboard} from './useClipboard';
15
+ import {__resetLiveRegionsForTest} from './useAnnounce';
16
+
17
+ function politeRegion(): HTMLElement | null {
18
+ return document.querySelector('[data-astryx-live-region="polite"]');
19
+ }
20
+
21
+ describe('useClipboard', () => {
22
+ beforeEach(() => {
23
+ // jsdom does not implement the async Clipboard API.
24
+ Object.assign(navigator, {
25
+ clipboard: {writeText: vi.fn().mockResolvedValue(undefined)},
26
+ });
27
+ });
28
+
29
+ afterEach(() => {
30
+ __resetLiveRegionsForTest();
31
+ vi.restoreAllMocks();
32
+ });
33
+
34
+ it('writes the given text to the clipboard and resolves true', async () => {
35
+ const {result} = renderHook(() => useClipboard());
36
+ let outcome: boolean | undefined;
37
+ await act(async () => {
38
+ outcome = await result.current.copy('hello world');
39
+ });
40
+ expect(navigator.clipboard.writeText).toHaveBeenCalledWith('hello world');
41
+ expect(outcome).toBe(true);
42
+ });
43
+
44
+ it('flips isCopied to true after a successful copy', async () => {
45
+ const {result} = renderHook(() => useClipboard());
46
+ expect(result.current.isCopied).toBe(false);
47
+ await act(async () => {
48
+ await result.current.copy('x');
49
+ });
50
+ expect(result.current.isCopied).toBe(true);
51
+ });
52
+
53
+ it('reverts isCopied after resetAfterMs', async () => {
54
+ vi.useFakeTimers();
55
+ try {
56
+ const {result} = renderHook(() => useClipboard({resetAfterMs: 1000}));
57
+ await act(async () => {
58
+ await result.current.copy('x');
59
+ });
60
+ expect(result.current.isCopied).toBe(true);
61
+ act(() => {
62
+ vi.advanceTimersByTime(1000);
63
+ });
64
+ expect(result.current.isCopied).toBe(false);
65
+ } finally {
66
+ vi.useRealTimers();
67
+ }
68
+ });
69
+
70
+ it('keeps isCopied true for the full window after a rapid re-copy', async () => {
71
+ vi.useFakeTimers();
72
+ try {
73
+ const {result} = renderHook(() => useClipboard({resetAfterMs: 2000}));
74
+ await act(async () => {
75
+ await result.current.copy('x');
76
+ });
77
+ expect(result.current.isCopied).toBe(true);
78
+
79
+ // Re-copy partway through the window.
80
+ act(() => {
81
+ vi.advanceTimersByTime(1500);
82
+ });
83
+ await act(async () => {
84
+ await result.current.copy('x');
85
+ });
86
+
87
+ // 600ms after the second copy (2.1s after the first): the first copy's
88
+ // timer must not have reverted it early.
89
+ act(() => {
90
+ vi.advanceTimersByTime(600);
91
+ });
92
+ expect(result.current.isCopied).toBe(true);
93
+
94
+ // It resets 2s after the most recent copy.
95
+ act(() => {
96
+ vi.advanceTimersByTime(1400);
97
+ });
98
+ expect(result.current.isCopied).toBe(false);
99
+ } finally {
100
+ vi.useRealTimers();
101
+ }
102
+ });
103
+
104
+ it('announces the configured message to a polite live region', async () => {
105
+ const {result} = renderHook(() => useClipboard({announce: 'Copied'}));
106
+ await act(async () => {
107
+ await result.current.copy('x');
108
+ });
109
+ await waitFor(() => {
110
+ expect(politeRegion()).toHaveTextContent('Copied');
111
+ });
112
+ });
113
+
114
+ it('does not announce when no announce message is configured', async () => {
115
+ const {result} = renderHook(() => useClipboard());
116
+ await act(async () => {
117
+ await result.current.copy('x');
118
+ });
119
+ // No announce option → no live region is ever created.
120
+ expect(politeRegion()).toBeNull();
121
+ });
122
+
123
+ it('is a silent no-op that resolves false when the clipboard rejects', async () => {
124
+ (
125
+ navigator.clipboard.writeText as ReturnType<typeof vi.fn>
126
+ ).mockRejectedValue(new Error('denied'));
127
+ const {result} = renderHook(() => useClipboard({announce: 'Copied'}));
128
+ let outcome: boolean | undefined;
129
+ await act(async () => {
130
+ outcome = await result.current.copy('x');
131
+ });
132
+ expect(outcome).toBe(false);
133
+ expect(result.current.isCopied).toBe(false);
134
+ expect(politeRegion()).toBeNull();
135
+ });
136
+
137
+ it('clears a pending reset timer on unmount', async () => {
138
+ vi.useFakeTimers();
139
+ const clearSpy = vi.spyOn(globalThis, 'clearTimeout');
140
+ try {
141
+ const {result, unmount} = renderHook(() => useClipboard());
142
+ await act(async () => {
143
+ await result.current.copy('x');
144
+ });
145
+ unmount();
146
+ expect(clearSpy).toHaveBeenCalled();
147
+ } finally {
148
+ vi.useRealTimers();
149
+ }
150
+ });
151
+ });
@@ -0,0 +1,135 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useClipboard.ts
7
+ * @input Uses React state/effect/callback; navigator.clipboard; useAnnounce
8
+ * @output Exports useClipboard hook plus its options/return types
9
+ * @position Core behavior hook; owns the copy-to-clipboard behavior shared by
10
+ * CodeBlock and Timestamp — the clipboard write, the transient "copied"
11
+ * flag with its reset timer, and the polite screen-reader announcement — so
12
+ * each call site is a thin control over one implementation instead of
13
+ * re-deriving the timer/announce details independently.
14
+ *
15
+ * SYNC: When modified, update these files to stay in sync:
16
+ * - /packages/core/src/hooks/index.ts
17
+ * - /packages/core/src/hooks/useClipboard.doc.mjs
18
+ * - /packages/core/src/hooks/useClipboard.test.tsx
19
+ */
20
+
21
+ import {useCallback, useEffect, useRef, useState} from 'react';
22
+ import {useAnnounce} from './useAnnounce';
23
+
24
+ /**
25
+ * How long `isCopied` stays true after a successful copy before reverting.
26
+ * Long enough for the copied confirmation (e.g. a copy → check icon flip) to
27
+ * register without lingering.
28
+ */
29
+ const DEFAULT_RESET_AFTER_MS = 2000;
30
+
31
+ /**
32
+ * Options for {@link useClipboard}.
33
+ */
34
+ export interface UseClipboardOptions {
35
+ /**
36
+ * Message announced to a polite live region on a successful copy. Swapping a
37
+ * control's `aria-label` alone is not reliably announced by screen readers,
38
+ * so pass the localized confirmation (e.g. "Copied") to have it spoken.
39
+ * Omit to skip the announcement.
40
+ */
41
+ announce?: string;
42
+ /**
43
+ * Milliseconds `isCopied` stays true after a successful copy before it
44
+ * reverts to false.
45
+ * @default 2000
46
+ */
47
+ resetAfterMs?: number;
48
+ }
49
+
50
+ /**
51
+ * Return value of {@link useClipboard}.
52
+ */
53
+ export interface UseClipboardReturn {
54
+ /**
55
+ * Write `text` to the clipboard. On success, flips `isCopied` to true,
56
+ * announces the configured message, and (re)starts the reset timer;
57
+ * resolves `true`. A clipboard rejection is a silent no-op that leaves the
58
+ * copied state unchanged and resolves `false`.
59
+ */
60
+ copy: (text: string) => Promise<boolean>;
61
+ /** True for `resetAfterMs` after the most recent successful copy. */
62
+ isCopied: boolean;
63
+ }
64
+
65
+ /**
66
+ * Copy-to-clipboard behavior: the clipboard write, a transient `isCopied`
67
+ * flag with its own reset timer, and an optional polite screen-reader
68
+ * announcement — the block otherwise re-derived at every copy affordance.
69
+ *
70
+ * Per [API Conventions → Behaviors: Hooks Over Wrappers], the behavior is a
71
+ * hook and the control (an icon button, a menu item, a value chip) is a thin
72
+ * shell over it. Rapid re-copies restart the reset timer so the confirmation
73
+ * always lasts the full duration, and the timer is cleaned up on unmount.
74
+ *
75
+ * @example
76
+ * ```
77
+ * function CopyButton({text}: {text: string}) {
78
+ * const {copy, isCopied} = useClipboard({announce: 'Copied'});
79
+ * return (
80
+ * <IconButton
81
+ * label={isCopied ? 'Copied' : 'Copy'}
82
+ * tooltip="Copy"
83
+ * icon={<Icon icon={isCopied ? 'check' : 'copy'} />}
84
+ * onClick={() => void copy(text)}
85
+ * />
86
+ * );
87
+ * }
88
+ * ```
89
+ */
90
+ export function useClipboard(
91
+ options: UseClipboardOptions = {},
92
+ ): UseClipboardReturn {
93
+ const {announce: announceMessage, resetAfterMs = DEFAULT_RESET_AFTER_MS} =
94
+ options;
95
+ const announce = useAnnounce();
96
+ const [isCopied, setIsCopied] = useState(false);
97
+ const resetTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
98
+
99
+ // Clear a pending "copied" reset when the consumer unmounts.
100
+ useEffect(() => {
101
+ return () => {
102
+ if (resetTimerRef.current != null) {
103
+ clearTimeout(resetTimerRef.current);
104
+ }
105
+ };
106
+ }, []);
107
+
108
+ const copy = useCallback(
109
+ async (text: string): Promise<boolean> => {
110
+ try {
111
+ await navigator.clipboard.writeText(text);
112
+ setIsCopied(true);
113
+ if (announceMessage) {
114
+ announce(announceMessage);
115
+ }
116
+ // Restart the reset timer on every copy — otherwise a rapid re-copy
117
+ // is reverted early by the previous copy's timer.
118
+ if (resetTimerRef.current != null) {
119
+ clearTimeout(resetTimerRef.current);
120
+ }
121
+ resetTimerRef.current = setTimeout(() => {
122
+ resetTimerRef.current = null;
123
+ setIsCopied(false);
124
+ }, resetAfterMs);
125
+ return true;
126
+ } catch {
127
+ // Clipboard failures leave the copied state unchanged.
128
+ return false;
129
+ }
130
+ },
131
+ [announce, announceMessage, resetAfterMs],
132
+ );
133
+
134
+ return {copy, isCopied};
135
+ }
@@ -15,7 +15,7 @@ export const docs = {
15
15
  {
16
16
  name: 'options.isEnabled',
17
17
  type: 'boolean',
18
- description: 'When false the hook is inert: no marker is applied and content getters return no styles, so content is always shown. Lets a component gate reveal on its own prop (e.g. revealOn === "hover").',
18
+ description: 'When false the hook is inert: the container gets no styles and content getters return no styles, so content is always shown. Read on every render, so a component can flip it after mount (e.g. revealOn === "hover").',
19
19
  default: 'true',
20
20
  required: false,
21
21
  },
@@ -34,11 +34,11 @@ export const docs = {
34
34
  ],
35
35
  usage: {
36
36
  description:
37
- 'A headless hover/focus reveal primitive. Gives a container a scoped trigger that reveals (or conceals) content inside it when the container is hovered or receives keyboard focus: the classic "row actions appear on hover" pattern. The reveal is CSS-only: no hover state lives in React and hovering never triggers a re-render. The caller authors no StyleX for the reveal itself; the hook hands out a scoped marker and matching styles, so nested containers never leak hover/focus into one another. Accessible by construction: revealed content is visually hidden at rest with position and opacity (never display:none), so it stays mounted, keeps its place in the tab order, and is announced to assistive technology; it reveals on :focus-within so keyboard users see it when tabbing in, stays visible on touch (never gated behind hover on coarse pointers), and honors prefers-reduced-motion.',
37
+ 'A headless hover/focus reveal primitive. Gives a container a scoped trigger that reveals (or conceals) content inside it when the container is hovered or receives keyboard focus: the classic "row actions appear on hover" pattern. The reveal is CSS-only: no hover state lives in React and hovering never triggers a re-render. The caller authors no StyleX for the reveal itself; the hook hands out the container and content styles, and a nested container shadows its ancestor, so nested containers never leak hover/focus into one another. Accessible by construction: revealed content is visually hidden at rest with position and opacity (never display:none), so it stays mounted, keeps its place in the tab order, and is announced to assistive technology; it reveals on :focus-within so keyboard users see it when tabbing in, stays visible on touch (never gated behind hover on coarse pointers), and honors prefers-reduced-motion.',
38
38
  bestPractices: [
39
39
  { guidance: true, description: 'Destructure getContainerProps and getContentRevealProps; spread getContainerProps() on the container (via mergeProps with your own stylex.props) and getContentRevealProps() on the content to reveal.' },
40
40
  { guidance: true, description: 'Use for secondary affordances: reveal-on-hover row actions (edit/copy/remove on list or table rows) and overlay controls on a card or media tile (e.g. Thumbnail\'s remove button).' },
41
- { guidance: true, description: 'Gate the reveal with isEnabled when a consumer prop decides whether content is revealed on hover or always shown.' },
41
+ { guidance: true, description: 'Gate the reveal with isEnabled when a consumer prop decides whether content is revealed on hover or always shown; it can change at any time.' },
42
42
  { guidance: true, description: 'Pass isLayoutPreserved for absolutely-positioned or overlay content to reserve its box and avoid layout shift when it appears.' },
43
43
  { guidance: false, description: 'Use it to hide content that must always be discoverable; keep essential actions visible instead of gating them behind hover.' },
44
44
  ],
@@ -52,10 +52,10 @@ export const docs = {
52
52
  /** @type {import('@astryxdesign/cli/authoring').HookTranslationDoc} */
53
53
  export const docsDense = {
54
54
  description:
55
- 'Headless hover/focus reveal primitive. Gives a container a scoped trigger that reveals (or conceals) content inside it on hover / keyboard focus: the "row actions appear on hover" pattern. CSS-only: no hover state in React, no re-render on hover. Caller authors no StyleX; hook hands out a scoped marker + matching styles, so nested containers never leak hover/focus. Accessible: revealed content visually hidden at rest via position + opacity (never display:none), stays mounted + in tab order + announced; reveals on :focus-within, stays visible on touch, honors prefers-reduced-motion.',
55
+ 'Headless hover/focus reveal primitive. Gives a container a scoped trigger that reveals (or conceals) content inside it on hover / keyboard focus: the "row actions appear on hover" pattern. CSS-only: no hover state in React, no re-render on hover. Caller authors no StyleX; hook hands out container + content styles; a nested container shadows its ancestor, so nested containers never leak hover/focus. Accessible: revealed content visually hidden at rest via position + opacity (never display:none), stays mounted + in tab order + announced; reveals on :focus-within, stays visible on touch, honors prefers-reduced-motion.',
56
56
  paramDescriptions: {
57
57
  options: 'config for reveal container. optional.',
58
- 'options.isEnabled': 'when false hook is inert: no marker, content getters return no styles, content always shown. Lets component gate reveal on its own prop.',
58
+ 'options.isEnabled': 'when false hook is inert: no container styles, content getters return no styles, content always shown. Read every render, so it can flip after mount.',
59
59
  },
60
60
  returnDescriptions: {
61
61
  getContainerProps: 'spread onto container whose hover/focus-within drives reveal.',
@@ -63,11 +63,11 @@ export const docsDense = {
63
63
  },
64
64
  usage: {
65
65
  description:
66
- 'Headless hover/focus reveal primitive. Gives a container a scoped trigger that reveals (or conceals) content inside it on hover / keyboard focus: the "row actions appear on hover" pattern. CSS-only: no hover state in React, no re-render on hover. Caller authors no StyleX; hook hands out a scoped marker + matching styles, so nested containers never leak hover/focus. Accessible: revealed content visually hidden at rest via position + opacity (never display:none), stays mounted + in tab order + announced; reveals on :focus-within, stays visible on touch, honors prefers-reduced-motion.',
66
+ 'Headless hover/focus reveal primitive. Gives a container a scoped trigger that reveals (or conceals) content inside it on hover / keyboard focus: the "row actions appear on hover" pattern. CSS-only: no hover state in React, no re-render on hover. Caller authors no StyleX; hook hands out container + content styles; a nested container shadows its ancestor, so nested containers never leak hover/focus. Accessible: revealed content visually hidden at rest via position + opacity (never display:none), stays mounted + in tab order + announced; reveals on :focus-within, stays visible on touch, honors prefers-reduced-motion.',
67
67
  bestPractices: [
68
68
  { guidance: true, description: 'Destructure getContainerProps + getContentRevealProps; spread getContainerProps() on container (via mergeProps w/ your stylex.props) + getContentRevealProps() on content to reveal.' },
69
69
  { guidance: true, description: 'Use for secondary affordances: reveal-on-hover row actions (edit/copy/remove on list / table rows) + overlay controls on card / media tile (e.g. Thumbnail remove button).' },
70
- { guidance: true, description: 'Gate reveal w/ isEnabled when a consumer prop decides revealed-on-hover vs always shown.' },
70
+ { guidance: true, description: 'Gate reveal w/ isEnabled when a consumer prop decides revealed-on-hover vs always shown; can change at any time.' },
71
71
  { guidance: true, description: 'Pass isLayoutPreserved for absolutely-positioned / overlay content to reserve its box + avoid layout shift.' },
72
72
  { guidance: false, description: 'Use to hide content that must always be discoverable; keep essential actions visible instead of gating behind hover.' },
73
73
  ],