@astryxdesign/core 0.7.5 → 0.7.6-canary.bb18c81

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 (353) hide show
  1. package/dist/AlertDialog/AlertDialog.d.ts +1 -1
  2. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  3. package/dist/AlertDialog/AlertDialog.js +5 -5
  4. package/dist/Avatar/Avatar.d.ts.map +1 -1
  5. package/dist/Avatar/Avatar.js +12 -3
  6. package/dist/BottomSheet/BottomSheet.js +1 -1
  7. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  8. package/dist/BottomSheet/BottomSheetPanel.js +2 -1
  9. package/dist/BottomSheet/useSheetGestures.d.ts +12 -0
  10. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  11. package/dist/BottomSheet/useSheetGestures.js +30 -3
  12. package/dist/Button/Button.js +4 -4
  13. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  14. package/dist/CheckboxInput/CheckboxInput.js +6 -5
  15. package/dist/DateInput/DateInput.d.ts.map +1 -1
  16. package/dist/DateInput/DateInput.js +4 -1
  17. package/dist/DateInput/MonthScroller.d.ts.map +1 -1
  18. package/dist/DateInput/MonthScroller.js +101 -15
  19. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  20. package/dist/DateRangeInput/DateRangeInput.js +1 -1
  21. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  22. package/dist/DateTimeInput/DateTimeInput.js +4 -1
  23. package/dist/Dialog/Dialog.d.ts.map +1 -1
  24. package/dist/Dialog/Dialog.js +34 -9
  25. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  26. package/dist/Dialog/DialogHeader.js +5 -4
  27. package/dist/Divider/Divider.d.ts.map +1 -1
  28. package/dist/Divider/Divider.js +2 -1
  29. package/dist/Drawer/Drawer.d.ts.map +1 -1
  30. package/dist/Drawer/Drawer.js +5 -0
  31. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  32. package/dist/DropdownMenu/DropdownMenu.js +4 -1
  33. package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -1
  34. package/dist/DropdownMenu/MenuBottomSheetActionList.js +6 -5
  35. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts +10 -1
  36. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts.map +1 -1
  37. package/dist/DropdownMenu/lazyMenuBottomSheet.js +16 -2
  38. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  39. package/dist/DropdownMenu/renderDropdownItems.js +21 -16
  40. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  41. package/dist/EmptyState/EmptyState.js +2 -1
  42. package/dist/Field/FieldLabel.d.ts +1 -1
  43. package/dist/Field/FieldLabel.d.ts.map +1 -1
  44. package/dist/Field/FieldLabel.js +4 -4
  45. package/dist/Field/InputClearButton.js +3 -3
  46. package/dist/Field/PanelSearchInput.js +2 -2
  47. package/dist/FieldStatus/FieldStatus.js +2 -2
  48. package/dist/FileInput/FileInput.d.ts.map +1 -1
  49. package/dist/FileInput/FileInput.js +7 -1
  50. package/dist/Heading/Heading.js +1 -1
  51. package/dist/Icon/Icon.js +1 -1
  52. package/dist/Indicator/CheckIndicator.d.ts.map +1 -1
  53. package/dist/Indicator/CheckIndicator.js +2 -1
  54. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
  55. package/dist/Indicator/CheckboxIndicator.js +3 -1
  56. package/dist/Indicator/RadioIndicator.d.ts.map +1 -1
  57. package/dist/Indicator/RadioIndicator.js +3 -1
  58. package/dist/Item/Item.d.ts.map +1 -1
  59. package/dist/Item/Item.js +2 -1
  60. package/dist/Item/ItemSwipeLayer.js +1 -1
  61. package/dist/Layout/Layout.js +1 -1
  62. package/dist/Layout/LayoutContent.js +1 -1
  63. package/dist/Layout/LayoutFooter.js +1 -1
  64. package/dist/Layout/LayoutHeader.js +1 -1
  65. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  66. package/dist/Lightbox/Lightbox.js +5 -0
  67. package/dist/Lightbox/LightboxViewport.a11y.browser.spec.js +5 -4
  68. package/dist/List/List.js +1 -1
  69. package/dist/List/ListItem.js +3 -3
  70. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  71. package/dist/MobileNav/MobileNav.js +7 -2
  72. package/dist/MultiSelector/MultiSelector.d.ts +1 -1
  73. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  74. package/dist/MultiSelector/MultiSelector.js +5 -2
  75. package/dist/NumberInput/NumberInput.d.ts +3 -1
  76. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  77. package/dist/NumberInput/NumberInput.js +3 -3
  78. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  79. package/dist/ProgressBar/ProgressBar.js +16 -12
  80. package/dist/Resizable/useResizable.d.ts.map +1 -1
  81. package/dist/Resizable/useResizable.js +28 -2
  82. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  83. package/dist/SegmentedControl/SegmentedControlItem.js +4 -1
  84. package/dist/Selector/useSelectorPresentation.d.ts +8 -1
  85. package/dist/Selector/useSelectorPresentation.d.ts.map +1 -1
  86. package/dist/Selector/useSelectorPresentation.js +20 -4
  87. package/dist/Slider/Slider.d.ts.map +1 -1
  88. package/dist/Slider/Slider.js +7 -1
  89. package/dist/Spinner/Spinner.js +2 -2
  90. package/dist/Stack/Stack.js +1 -1
  91. package/dist/Stack/StackItem.js +1 -1
  92. package/dist/TabList/TabList.d.ts.map +1 -1
  93. package/dist/TabList/TabList.js +16 -1
  94. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  95. package/dist/Table/plugins/columnResize/useTableColumnResize.js +9 -1
  96. package/dist/Text/Text.js +1 -1
  97. package/dist/Text/useTruncation.d.ts.map +1 -1
  98. package/dist/Text/useTruncation.js +23 -5
  99. package/dist/TextArea/TextArea.d.ts +1 -1
  100. package/dist/TextArea/TextArea.d.ts.map +1 -1
  101. package/dist/TextArea/TextArea.js +13 -2
  102. package/dist/TextInput/TextInput.d.ts.map +1 -1
  103. package/dist/TextInput/TextInput.js +3 -3
  104. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  105. package/dist/Timestamp/Timestamp.js +2 -2
  106. package/dist/Timestamp/TimestampHoverCard.js +4 -4
  107. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  108. package/dist/Toast/ToastViewport.js +82 -51
  109. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  110. package/dist/Tokenizer/Tokenizer.js +13 -2
  111. package/dist/Tokenizer/TokenizerBottomSheet.js +3 -3
  112. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  113. package/dist/Toolbar/Toolbar.js +2 -1
  114. package/dist/Typeahead/Typeahead.d.ts +1 -1
  115. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  116. package/dist/Typeahead/Typeahead.js +9 -1
  117. package/dist/astryx.css +14 -1
  118. package/dist/gestures/PinchGestureRecognizer.d.ts +30 -0
  119. package/dist/gestures/PinchGestureRecognizer.d.ts.map +1 -0
  120. package/dist/gestures/PinchGestureRecognizer.js +125 -0
  121. package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts +70 -0
  122. package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts.map +1 -0
  123. package/dist/gestures/ScreenEdgePanGestureRecognizer.js +220 -0
  124. package/dist/gestures/SwipeGestureRecognizer.d.ts +36 -0
  125. package/dist/gestures/SwipeGestureRecognizer.d.ts.map +1 -0
  126. package/dist/gestures/SwipeGestureRecognizer.js +145 -0
  127. package/dist/gestures/VelocityTracker.d.ts +6 -2
  128. package/dist/gestures/VelocityTracker.d.ts.map +1 -1
  129. package/dist/gestures/VelocityTracker.js +7 -3
  130. package/dist/gestures/types.d.ts +11 -7
  131. package/dist/gestures/types.d.ts.map +1 -1
  132. package/dist/gestures/types.js +22 -2
  133. package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -1
  134. package/dist/hooks/useAdaptivePresentation.js +4 -1
  135. package/dist/hooks/useMenuHover.d.ts.map +1 -1
  136. package/dist/hooks/useMenuHover.js +12 -1
  137. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  138. package/dist/i18n/generated-locales/en.generated.js +3 -0
  139. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  140. package/dist/i18n/generated-locales/pseudo.generated.js +3 -0
  141. package/dist/motion/animator.d.ts +7 -1
  142. package/dist/motion/animator.d.ts.map +1 -1
  143. package/dist/motion/animator.js +119 -27
  144. package/dist/theme/defineTheme.js +1 -1
  145. package/dist/theme/domainTokens/domainTokenDefaults.d.ts +3 -0
  146. package/dist/theme/domainTokens/domainTokenDefaults.d.ts.map +1 -0
  147. package/dist/theme/domainTokens/domainTokenDefaults.js +19 -0
  148. package/dist/theme/domainTokens/index.d.ts +5 -4
  149. package/dist/theme/domainTokens/index.d.ts.map +1 -1
  150. package/dist/theme/domainTokens/index.js +5 -9
  151. package/dist/utils/focusReturn.d.ts +17 -1
  152. package/dist/utils/focusReturn.d.ts.map +1 -1
  153. package/dist/utils/focusReturn.js +11 -4
  154. package/dist/utils/getNodeText.d.ts +8 -0
  155. package/dist/utils/getNodeText.d.ts.map +1 -0
  156. package/dist/utils/getNodeText.js +37 -0
  157. package/dist/utils/interactionModality.d.ts +11 -2
  158. package/dist/utils/interactionModality.d.ts.map +1 -1
  159. package/dist/utils/interactionModality.js +58 -5
  160. package/dist/utils/presentation.d.ts +50 -0
  161. package/dist/utils/presentation.d.ts.map +1 -0
  162. package/dist/utils/presentation.js +125 -0
  163. package/dist/utils/touchPresentation.d.ts.map +1 -1
  164. package/dist/utils/touchPresentation.js +5 -3
  165. package/dist/utils/touchPresentation.stylex.d.ts +15 -0
  166. package/dist/utils/touchPresentation.stylex.d.ts.map +1 -0
  167. package/dist/utils/touchPresentation.stylex.js +33 -0
  168. package/locales/en.json +13 -1
  169. package/locales/pseudo.json +9 -0
  170. package/package.json +5 -4
  171. package/src/AlertDialog/AlertDialog.tsx +6 -5
  172. package/src/AlertDialog/__tests__/AlertDialog.a11y.chromium.spec.ts +18 -6
  173. package/src/Avatar/Avatar.spec.md +22 -22
  174. package/src/Avatar/Avatar.tsx +18 -1
  175. package/src/Avatar/__tests__/AvatarForcedColors.a11y.chromium.spec.ts +126 -0
  176. package/src/BottomSheet/BottomSheet.test.tsx +46 -0
  177. package/src/BottomSheet/BottomSheet.tsx +1 -1
  178. package/src/BottomSheet/BottomSheetPanel.tsx +2 -1
  179. package/src/BottomSheet/useSheetGestures.test.ts +52 -0
  180. package/src/BottomSheet/useSheetGestures.ts +37 -6
  181. package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.chromium.spec.ts +18 -6
  182. package/src/Button/Button.tsx +4 -4
  183. package/src/Calendar/Calendar.spec.md +87 -48
  184. package/src/Calendar/__tests__/Calendar.a11y.chromium.spec.ts +21 -7
  185. package/src/Carousel/__tests__/Carousel.a11y.chromium.spec.ts +21 -7
  186. package/src/CheckboxInput/CheckboxInput.tsx +6 -5
  187. package/src/Collapsible/Collapsible.spec.md +2 -0
  188. package/src/Collapsible/CollapsibleGroup.spec.md +57 -29
  189. package/src/Collapsible/__tests__/Collapsible.a11y.chromium.spec.ts +18 -7
  190. package/src/Collapsible/__tests__/CollapsibleGroupAccordion.a11y.chromium.spec.ts +18 -6
  191. package/src/ContextMenu/ContextMenu.test.tsx +35 -0
  192. package/src/DateInput/DateInput.test.tsx +53 -2
  193. package/src/DateInput/DateInput.tsx +15 -11
  194. package/src/DateInput/MonthScroller.tsx +106 -20
  195. package/src/DateInput/MonthScrollerSettle.test.tsx +165 -0
  196. package/src/DateInput/__tests__/MonthScrollerSteps.interaction.browser.spec.ts +312 -0
  197. package/src/DateRangeInput/DateRangeInput.test.tsx +46 -3
  198. package/src/DateRangeInput/DateRangeInput.tsx +16 -12
  199. package/src/DateTimeInput/DateTimeInput.spec.md +71 -48
  200. package/src/DateTimeInput/DateTimeInput.test.tsx +61 -0
  201. package/src/DateTimeInput/DateTimeInput.tsx +15 -11
  202. package/src/Dialog/Dialog.test.tsx +216 -2
  203. package/src/Dialog/Dialog.tsx +42 -9
  204. package/src/Dialog/DialogHeader.tsx +5 -4
  205. package/src/Dialog/__tests__/DialogSecondDocument.a11y.browser.spec.ts +202 -0
  206. package/src/Divider/Divider.tsx +2 -1
  207. package/src/Drawer/Drawer.spec.md +111 -80
  208. package/src/Drawer/Drawer.test.tsx +22 -0
  209. package/src/Drawer/Drawer.tsx +5 -0
  210. package/src/Drawer/DrawerHeader.spec.md +181 -0
  211. package/src/DropdownMenu/DropdownMenu.test.tsx +41 -0
  212. package/src/DropdownMenu/DropdownMenu.tsx +7 -1
  213. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +34 -0
  214. package/src/DropdownMenu/MenuBottomSheetActionList.tsx +6 -5
  215. package/src/DropdownMenu/__tests__/Menu.a11y.chromium.spec.ts +19 -7
  216. package/src/DropdownMenu/__tests__/MenuButton.a11y.chromium.spec.ts +21 -9
  217. package/src/DropdownMenu/lazyMenuBottomSheet.test.ts +53 -0
  218. package/src/DropdownMenu/lazyMenuBottomSheet.ts +23 -6
  219. package/src/DropdownMenu/renderDropdownItems.tsx +24 -19
  220. package/src/EmptyState/EmptyState.tsx +2 -1
  221. package/src/Field/FieldLabel.tsx +5 -4
  222. package/src/Field/InputClearButton.tsx +3 -3
  223. package/src/Field/PanelSearchInput.tsx +2 -2
  224. package/src/FieldStatus/FieldStatus.tsx +2 -2
  225. package/src/FileInput/FileInput.test.tsx +21 -0
  226. package/src/FileInput/FileInput.tsx +4 -1
  227. package/src/FormLayout/FormLayout.spec.md +7 -8
  228. package/src/Heading/Heading.tsx +1 -1
  229. package/src/Icon/Icon.tsx +1 -1
  230. package/src/Indicator/CheckIndicator.spec.md +47 -42
  231. package/src/Indicator/CheckIndicator.tsx +2 -1
  232. package/src/Indicator/CheckboxIndicator.spec.md +40 -37
  233. package/src/Indicator/CheckboxIndicator.tsx +3 -1
  234. package/src/Indicator/Indicator.spec.md +188 -0
  235. package/src/Indicator/RadioIndicator.tsx +3 -1
  236. package/src/Item/Item.tsx +2 -1
  237. package/src/Item/ItemSwipeLayer.tsx +1 -1
  238. package/src/Layout/Layout.spec.md +127 -131
  239. package/src/Layout/Layout.tsx +1 -1
  240. package/src/Layout/LayoutContent.spec.md +205 -0
  241. package/src/Layout/LayoutContent.tsx +1 -1
  242. package/src/Layout/LayoutFooter.spec.md +190 -0
  243. package/src/Layout/LayoutFooter.tsx +1 -1
  244. package/src/Layout/LayoutHeader.spec.md +193 -0
  245. package/src/Layout/LayoutHeader.tsx +1 -1
  246. package/src/Layout/LayoutPanel.spec.md +194 -0
  247. package/src/Layout/__tests__/Layout.a11y.chromium.spec.ts +18 -6
  248. package/src/Lightbox/Lightbox.test.tsx +18 -0
  249. package/src/Lightbox/Lightbox.tsx +5 -0
  250. package/src/Lightbox/LightboxViewport.a11y.browser.spec.ts +8 -4
  251. package/src/Link/__tests__/Link.a11y.chromium.spec.ts +18 -8
  252. package/src/List/List.tsx +1 -1
  253. package/src/List/ListItem.tsx +3 -3
  254. package/src/MobileNav/MobileNav.test.tsx +15 -0
  255. package/src/MobileNav/MobileNav.tsx +9 -1
  256. package/src/MobileNav/__tests__/MobileNavHeader.a11y.chromium.spec.ts +89 -0
  257. package/src/MoreMenu/MoreMenu.test.tsx +11 -0
  258. package/src/MultiSelector/MultiSelector.spec.md +7 -4
  259. package/src/MultiSelector/MultiSelector.test.tsx +118 -1
  260. package/src/MultiSelector/MultiSelector.tsx +6 -2
  261. package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +8 -1
  262. package/src/NavMenu/NavHeadingMenu.spec.md +1 -5
  263. package/src/NumberInput/NumberInput.doc.mjs +13 -7
  264. package/src/NumberInput/NumberInput.spec.md +59 -37
  265. package/src/NumberInput/NumberInput.tsx +13 -3
  266. package/src/NumberInput/__tests__/NumberInput.coarse-pointer.a11y.chromium.spec.ts +102 -0
  267. package/src/ProgressBar/ProgressBar.test.tsx +108 -32
  268. package/src/ProgressBar/ProgressBar.tsx +58 -10
  269. package/src/ProgressBar/__tests__/ProgressBar.a11y.chromium.spec.ts +21 -9
  270. package/src/ProgressBar/__tests__/ProgressBarMotion.a11y.chromium.spec.ts +831 -0
  271. package/src/Resizable/__tests__/ResizeHandle.a11y.chromium.spec.ts +21 -14
  272. package/src/Resizable/useResizable.test.ts +43 -0
  273. package/src/Resizable/useResizable.ts +32 -9
  274. package/src/SegmentedControl/SegmentedControl.test.tsx +84 -0
  275. package/src/SegmentedControl/SegmentedControlItem.tsx +11 -5
  276. package/src/Selector/Selector.test.tsx +90 -1
  277. package/src/Selector/__tests__/Selector.combobox.a11y.test.tsx +8 -1
  278. package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +8 -1
  279. package/src/Selector/useSelectorPresentation.ts +31 -3
  280. package/src/SideNav/SideNav.test.tsx +27 -0
  281. package/src/Slider/Slider.test.tsx +12 -0
  282. package/src/Slider/Slider.tsx +4 -1
  283. package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +19 -7
  284. package/src/Spinner/Spinner.tsx +2 -2
  285. package/src/Stack/Stack.tsx +1 -1
  286. package/src/Stack/StackItem.tsx +1 -1
  287. package/src/TabList/TabList.test.tsx +52 -1
  288. package/src/TabList/TabList.tsx +16 -1
  289. package/src/TabList/__tests__/TabPressPointer.interaction.browser.spec.ts +163 -0
  290. package/src/Table/__tests__/Table.a11y.chromium.spec.ts +18 -6
  291. package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +22 -0
  292. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +8 -4
  293. package/src/Text/Text.tsx +1 -1
  294. package/src/Text/useTruncation.test.tsx +128 -1
  295. package/src/Text/useTruncation.ts +23 -5
  296. package/src/TextArea/TextArea.spec.md +10 -13
  297. package/src/TextArea/TextArea.test.tsx +60 -0
  298. package/src/TextArea/TextArea.tsx +15 -1
  299. package/src/TextInput/TextInput.test.tsx +21 -0
  300. package/src/TextInput/TextInput.tsx +3 -1
  301. package/src/Timestamp/Timestamp.spec.md +6 -12
  302. package/src/Timestamp/Timestamp.test.tsx +30 -5
  303. package/src/Timestamp/Timestamp.tsx +6 -2
  304. package/src/Timestamp/TimestampHoverCard.tsx +4 -4
  305. package/src/Toast/Toast.spec.md +7 -10
  306. package/src/Toast/ToastViewport.test.tsx +575 -1
  307. package/src/Toast/ToastViewport.tsx +100 -50
  308. package/src/Tokenizer/Tokenizer.spec.md +5 -3
  309. package/src/Tokenizer/Tokenizer.test.tsx +68 -0
  310. package/src/Tokenizer/Tokenizer.tsx +14 -2
  311. package/src/Tokenizer/TokenizerBottomSheet.test.tsx +15 -4
  312. package/src/Tokenizer/TokenizerBottomSheet.tsx +3 -3
  313. package/src/Toolbar/Toolbar.spec.md +5 -6
  314. package/src/Toolbar/Toolbar.test.tsx +30 -0
  315. package/src/Toolbar/Toolbar.tsx +2 -1
  316. package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +18 -6
  317. package/src/Tooltip/__tests__/Tooltip.a11y.chromium.spec.ts +18 -7
  318. package/src/TopNav/TopNav.test.tsx +27 -0
  319. package/src/Typeahead/Typeahead.test.tsx +40 -0
  320. package/src/Typeahead/Typeahead.tsx +12 -1
  321. package/src/gestures/PinchGestureRecognizer.ts +144 -0
  322. package/src/gestures/ScreenEdgePanGestureRecognizer.ts +252 -0
  323. package/src/gestures/SwipeGestureRecognizer.ts +193 -0
  324. package/src/gestures/VelocityTracker.ts +10 -3
  325. package/src/gestures/__tests__/PanGestureRecognizer.test.ts +4 -4
  326. package/src/gestures/__tests__/PinchGestureRecognizer.test.ts +74 -0
  327. package/src/gestures/__tests__/ScreenEdgePanGestureRecognizer.test.ts +498 -0
  328. package/src/gestures/__tests__/SwipeGestureRecognizer.test.ts +108 -0
  329. package/src/gestures/__tests__/arenaPinchSwipeEdge.test.ts +188 -0
  330. package/src/gestures/__tests__/harness.ts +21 -2
  331. package/src/gestures/types.ts +23 -9
  332. package/src/hooks/useAdaptivePresentation.ts +4 -1
  333. package/src/hooks/useMenuHover.ts +15 -1
  334. package/src/i18n/generated-locales/en.generated.ts +4 -0
  335. package/src/i18n/generated-locales/pseudo.generated.ts +4 -0
  336. package/src/motion/README.md +11 -1
  337. package/src/motion/animator.test.ts +151 -3
  338. package/src/motion/animator.ts +149 -33
  339. package/src/theme/defineTheme.ts +1 -1
  340. package/src/theme/derivedVarRegistry.test.ts +4 -0
  341. package/src/theme/domainTokens/domainTokenDefaults.ts +19 -0
  342. package/src/theme/domainTokens/index.ts +6 -10
  343. package/src/utils/focusReturn.ts +17 -6
  344. package/src/utils/getNodeText.test.ts +24 -0
  345. package/src/utils/getNodeText.ts +37 -0
  346. package/src/utils/interactionModality.test.ts +45 -1
  347. package/src/utils/interactionModality.ts +64 -5
  348. package/src/utils/presentation.test.ts +380 -0
  349. package/src/utils/presentation.ts +180 -0
  350. package/src/utils/pressPaintPriority.test.ts +80 -0
  351. package/src/utils/touchPresentation.stylex.ts +43 -0
  352. package/src/utils/touchPresentation.test.ts +140 -1
  353. package/src/utils/touchPresentation.ts +5 -3
@@ -69,13 +69,17 @@
69
69
  * Chromium starts it after the block.
70
70
  *
71
71
  * Promotion: Mobile Safari runs an animation whose start time is set from
72
- * script on its compositor at its first commit only if the element already
73
- * has a compositing layer; without one, a block right after that commit
74
- * freezes it (iOS 26.2 simulator: 2 of 7 runs kept moving). Every animation
75
- * the animator creates, and every `set`, therefore promotes its channels in
76
- * the same task: inline `will-change` on the element, merged with the
77
- * element's own. That is enough (5 of 5, and 4 of 4 through the animator);
78
- * promotion ends PROMOTION_LINGER_MS after the last motion.
72
+ * script on its compositor at its first commit only if the element (or the
73
+ * pseudo-element) already has a compositing layer; without one, a block
74
+ * right after that commit freezes it (iOS 26.2 simulator: 2 of 7 runs kept
75
+ * moving, 0 of 5 on a ::before). Every animation the animator creates, and
76
+ * every `set`, therefore promotes its channels in the same task: inline
77
+ * `will-change` on the element, merged with the element's own, or the custom
78
+ * property `--astryx-motion-will-change-<pseudo>` on the host for a
79
+ * pseudo-element, whose style opts in with
80
+ * `will-change: var(--astryx-motion-will-change-before, auto)`. That is
81
+ * enough (5 of 5, and 4 of 4 through the animator); promotion ends
82
+ * PROMOTION_LINGER_MS after the last motion.
79
83
  *
80
84
  * Known limits, iOS Safari (measured on the iOS 26.2 simulator):
81
85
  * - a block in the task that starts a motion delays it: it starts when the
@@ -125,6 +129,14 @@
125
129
  * Motion policy above, a cross-fade on a coarse pointer or after a touch
126
130
  * release (`touchRelease`) and at once elsewhere (DR2, DR3).
127
131
  *
132
+ * A pseudo-element (`options.pseudoElement`, a dialog's `::backdrop`) has no
133
+ * inline style: its end value is held by a static filling animation (after a
134
+ * curve ends, and after `set`) until the next animation replaces it, and for
135
+ * as long as the pseudo-element exists. The browser makes a `::backdrop`
136
+ * again each time its dialog opens, without the hold, so a presentation
137
+ * calls `set` with its starting value right after `showModal()`, in the same
138
+ * task: a `set` before it lands on the backdrop the open replaces.
139
+ *
128
140
  * SSR-safe: nothing touches `window` or `document` at module scope, and an
129
141
  * animator created without an element only keeps state until one is attached.
130
142
  *
@@ -285,7 +297,7 @@ function format(channel, value) {
285
297
  const shared = new WeakMap();
286
298
 
287
299
  /**
288
- * The one animator for `element`, created on
300
+ * The one animator for `element` (and `options.pseudoElement`), created on
289
301
  * first use. Code that animates one element from several places (a swipe on
290
302
  * x, a removal on y) shares it, so their curves compose in one `translate`
291
303
  * track and retargeting one axis leaves the other on its curve; two
@@ -294,10 +306,16 @@ const shared = new WeakMap();
294
306
  * elsewhere or `dispose` it: it lives as long as the element.
295
307
  */
296
308
  export function animatorFor(element, options = {}) {
297
- let animator = shared.get(element);
309
+ let byPseudo = shared.get(element);
310
+ if (byPseudo == null) {
311
+ byPseudo = new Map();
312
+ shared.set(element, byPseudo);
313
+ }
314
+ const key = options.pseudoElement ?? '';
315
+ let animator = byPseudo.get(key);
298
316
  if (animator == null) {
299
317
  animator = createAnimator(element, options);
300
- shared.set(element, animator);
318
+ byPseudo.set(key, animator);
301
319
  }
302
320
  return animator;
303
321
  }
@@ -413,6 +431,7 @@ export function createAnimator(element, options = {}) {
413
431
  const requestFrame = options.requestFrame ?? (typeof requestAnimationFrame === 'function' ? callback => {
414
432
  requestAnimationFrame(() => callback());
415
433
  } : null);
434
+ const pseudoElement = options.pseudoElement;
416
435
  // Reduced Motion reads the person's settings and nothing else: no option
417
436
  // overrides them (design:reduced-motion DR4).
418
437
  const prefersReducedMotion = defaultPrefersReducedMotion;
@@ -463,6 +482,8 @@ export function createAnimator(element, options = {}) {
463
482
  /** The stylesheet's will-change when there is no inline value. */
464
483
  let sheetWillChange = '';
465
484
  let demoteTimer = null;
485
+ /** A pseudo-element is promoted through a custom property on its host. */
486
+ const promotionProperty = pseudoElement == null ? null : `--astryx-motion-will-change-${pseudoElement.replace(/^:+/, '')}`;
466
487
  /** Channels whose identity the last node held inline (see `attach`). */
467
488
  let heldIdentity = new Set();
468
489
 
@@ -533,7 +554,7 @@ export function createAnimator(element, options = {}) {
533
554
 
534
555
  const finalValue = property => segments.get(property)?.target ?? resting[property];
535
556
  const writeInline = (channel, value) => {
536
- if (target == null) {
557
+ if (target == null || pseudoElement != null) {
537
558
  return;
538
559
  }
539
560
  touched.add(channel);
@@ -588,14 +609,36 @@ export function createAnimator(element, options = {}) {
588
609
  }
589
610
  return times;
590
611
  };
612
+
613
+ /** A pseudo-element's resting value, held by a static filling animation. */
614
+ const hold = (channel, time) => {
615
+ if (target == null) {
616
+ return;
617
+ }
618
+ const value = format(channel, finalValue);
619
+ const animation = target.animate([{
620
+ [channel]: value
621
+ }, {
622
+ [channel]: value
623
+ }], {
624
+ duration: 1,
625
+ fill: 'both',
626
+ pseudoElement
627
+ });
628
+ animation.startTime = time;
629
+ running.set(channel, animation);
630
+ };
591
631
  const rebuild = (channel, time) => {
592
632
  const previous = running.get(channel);
593
633
  running.delete(channel);
594
634
  clearTimeout(retireTimers.get(channel));
595
635
  retireTimers.delete(channel);
596
- // The element's inline style holds the end state, so its old animation
597
- // can go first.
598
- previous?.cancel();
636
+ // An element's inline style holds the end state, so its old animation can
637
+ // go first. A pseudo-element has none: its old animation keeps holding the
638
+ // value until the new one exists (same task, no frame in between).
639
+ if (pseudoElement == null) {
640
+ previous?.cancel();
641
+ }
599
642
  const properties = CHANNELS[channel];
600
643
  // Settled curves become resting values.
601
644
  for (const property of properties) {
@@ -612,6 +655,10 @@ export function createAnimator(element, options = {}) {
612
655
  return;
613
656
  }
614
657
  if (moving.length === 0) {
658
+ if (pseudoElement != null) {
659
+ hold(channel, time);
660
+ }
661
+ previous?.cancel();
615
662
  return;
616
663
  }
617
664
  // A channel rebuilt by an explicit call (set, rebase, a retarget of
@@ -624,6 +671,10 @@ export function createAnimator(element, options = {}) {
624
671
  const from = waiting?.provisional ?? time;
625
672
  const end = Math.min(from + MAX_DURATION_MS, Math.max(...moving.map(p => segments.get(p).start + segments.get(p).duration)));
626
673
  if (end <= from) {
674
+ if (pseudoElement != null) {
675
+ hold(channel, time);
676
+ }
677
+ previous?.cancel();
627
678
  return;
628
679
  }
629
680
  if (callGroup != null) {
@@ -702,7 +753,10 @@ export function createAnimator(element, options = {}) {
702
753
  // The main thread drops the animation once it runs after the end; the
703
754
  // inline style already holds the same values, so that changes no pixel.
704
755
  fill: 'both',
705
- easing: 'linear'
756
+ easing: 'linear',
757
+ ...(pseudoElement != null ? {
758
+ pseudoElement
759
+ } : {})
706
760
  });
707
761
  if (waiting != null) {
708
762
  // From rest: no start time, so the browser starts the animation when
@@ -723,7 +777,9 @@ export function createAnimator(element, options = {}) {
723
777
 
724
778
  /**
725
779
  * Once the main thread runs after the curve's end, the animation leaves the
726
- * running state: the element's inline style already shows the end values.
780
+ * running state: an element's inline style already shows the end values; a
781
+ * pseudo-element's end value moves to a static holding animation, created
782
+ * before the long one goes.
727
783
  */
728
784
  const scheduleRetire = (channel, animation, end) => {
729
785
  clearTimeout(retireTimers.get(channel));
@@ -740,6 +796,12 @@ export function createAnimator(element, options = {}) {
740
796
  return;
741
797
  }
742
798
  running.delete(channel);
799
+ // A pseudo-element holds its resting value, unless the stylesheet
800
+ // takes it back now (a borrowed dip that has ended).
801
+ const handsBack = channel === 'opacity' && borrowedOpacity != null && ![...landings].some(landing => landing.fadeIn?.isBorrowed === true);
802
+ if (pseudoElement != null && !handsBack) {
803
+ hold(channel, now());
804
+ }
743
805
  animation.cancel();
744
806
  releaseIdentity(channel);
745
807
  flushReleases();
@@ -750,20 +812,30 @@ export function createAnimator(element, options = {}) {
750
812
  /** The element's own (stylesheet) opacity, read once when a dip starts. */
751
813
  const ownOpacity = () => {
752
814
  const view = target?.ownerDocument.defaultView;
753
- const value = target == null ? NaN : Number.parseFloat(view?.getComputedStyle(target).getPropertyValue('opacity') ?? '');
815
+ const value = target == null ? NaN : Number.parseFloat(view?.getComputedStyle(target, pseudoElement ?? null).getPropertyValue('opacity') ?? '');
754
816
  return Number.isFinite(value) ? Math.min(1, Math.max(0, value)) : 1;
755
817
  };
756
818
 
757
819
  /**
758
820
  * Whether the animator holds an opacity of its own on the element: an inline
759
- * value it wrote (a `set` over a stylesheet that hides or dims it). A dip
760
- * returns to that value.
821
+ * value it wrote, or a pseudo-element's holding animation (a `set` over a
822
+ * stylesheet that hides or dims it). A dip returns to that value.
761
823
  */
762
- const writesOwnOpacity = () => (target?.style.getPropertyValue('opacity') ?? '') !== '';
824
+ const writesOwnOpacity = () => pseudoElement == null ? (target?.style.getPropertyValue('opacity') ?? '') !== '' : running.has('opacity');
763
825
 
764
- /** Gives the element's opacity back to its stylesheet: its inline value goes. */
826
+ /**
827
+ * Gives the element's opacity back to its stylesheet: an element's inline
828
+ * value goes; a pseudo-element, which has no inline style, loses the
829
+ * animation holding its value.
830
+ */
765
831
  const releaseOwnOpacity = () => {
766
- target?.style.removeProperty('opacity');
832
+ if (pseudoElement == null) {
833
+ target?.style.removeProperty('opacity');
834
+ return;
835
+ }
836
+ const held = running.get('opacity');
837
+ running.delete('opacity');
838
+ held?.cancel();
767
839
  };
768
840
 
769
841
  /**
@@ -844,6 +916,9 @@ export function createAnimator(element, options = {}) {
844
916
  releaseOwnOpacity();
845
917
  return;
846
918
  }
919
+ if (pseudoElement != null) {
920
+ return;
921
+ }
847
922
  if (target.style.getPropertyValue(channel) !== IDENTITY_TEXT[channel]) {
848
923
  return;
849
924
  }
@@ -903,9 +978,17 @@ export function createAnimator(element, options = {}) {
903
978
 
904
979
  // -- promotion -------------------------------------------------------------
905
980
 
906
- /** Writes the promoted channels as will-change. */
981
+ /** Writes the promoted channels as will-change (or the pseudo's property). */
907
982
  const writePromotion = element => {
908
983
  const list = [...promoted].join(', ');
984
+ if (promotionProperty != null) {
985
+ if (list === '') {
986
+ element.style.removeProperty(promotionProperty);
987
+ } else {
988
+ element.style.setProperty(promotionProperty, list);
989
+ }
990
+ return;
991
+ }
909
992
  if (list === '') {
910
993
  element.style.willChange = ownWillChange ?? '';
911
994
  ownWillChange = null;
@@ -924,7 +1007,7 @@ export function createAnimator(element, options = {}) {
924
1007
  };
925
1008
 
926
1009
  /**
927
- * Gives the element a compositing layer for
1010
+ * Gives the element (or its pseudo-element) a compositing layer for
928
1011
  * `channel` from the task that starts the motion: Mobile Safari runs an
929
1012
  * animation whose start time is set from script on its compositor at its
930
1013
  * first commit only if the element already has a layer.
@@ -1747,7 +1830,11 @@ export function createAnimator(element, options = {}) {
1747
1830
  }
1748
1831
  // After the landing's commit, which can promote the element again.
1749
1832
  demote();
1833
+ // A pseudo-element holds an identity with a static animation, which the
1834
+ // cancel below drops: note the channels to hold again on the next node
1835
+ // (never a borrowed opacity, which is the stylesheet's).
1750
1836
  const isOpacityBorrowed = borrowedOpacity != null || [...landings].some(landing => landing.restoreIsBorrowed === true);
1837
+ const pseudoHeld = pseudoElement == null || target == null ? null : new Set(Object.keys(CHANNELS).filter(channel => running.has(channel) && !(channel === 'opacity' && isOpacityBorrowed) && CHANNELS[channel].every(p => !segments.has(p) && resting[p] === IDENTITY[p])));
1751
1838
  for (const animation of running.values()) {
1752
1839
  animation.cancel();
1753
1840
  }
@@ -1767,7 +1854,7 @@ export function createAnimator(element, options = {}) {
1767
1854
  // differs, kept through a detach for the node that comes next.
1768
1855
  if (target != null) {
1769
1856
  const outgoing = target;
1770
- heldIdentity = new Set(Object.keys(CHANNELS).filter(channel => outgoing.style.getPropertyValue(channel) === IDENTITY_TEXT[channel]));
1857
+ heldIdentity = pseudoHeld ?? new Set(Object.keys(CHANNELS).filter(channel => outgoing.style.getPropertyValue(channel) === IDENTITY_TEXT[channel]));
1771
1858
  }
1772
1859
  const held = heldIdentity;
1773
1860
  target = next;
@@ -1792,8 +1879,13 @@ export function createAnimator(element, options = {}) {
1792
1879
  } else if (held.has(channel)) {
1793
1880
  // An identity the animator held inline over a stylesheet that
1794
1881
  // differs goes onto the new node too (writeInline keeps it only
1795
- // where the new node's stylesheet still differs).
1796
- writeInline(channel, property => resting[property]);
1882
+ // where the new node's stylesheet still differs); a pseudo-element's
1883
+ // is held again.
1884
+ if (pseudoElement != null) {
1885
+ hold(channel, time);
1886
+ } else {
1887
+ writeInline(channel, property => resting[property]);
1888
+ }
1797
1889
  }
1798
1890
  }
1799
1891
  flushReleases();
@@ -68,7 +68,7 @@ import { resolveOnMedia } from "./onMediaTokens.js";
68
68
  import { colorDefaults, spacingDefaults, sizeDefaults, borderDefaults, focusDefaults, radiusDefaults, shadowDefaults, durationDefaults, easeDefaults, typographyDefaults, textSizeDefaults, fontWeightDefaults, typeScaleDefaults } from "./tokens.stylex.js";
69
69
  import { resolveThemeValues } from "./resolveThemeValues.js";
70
70
  import { normalizeThemeAdaptations, resolveThemeAdaptationRules, resolveThemeGenerativeAxes } from "./themeAdaptations.js";
71
- import { domainTokenDefaults } from "./domainTokens/index.js";
71
+ import { domainTokenDefaults } from "./domainTokens/domainTokenDefaults.js";
72
72
  import { registerTheme } from "./themeRegistry.js";
73
73
  import { resolveLocalTokenContract } from "./localTokens.js";
74
74
 
@@ -0,0 +1,3 @@
1
+ /** All domain token defaults merged — used by defineTheme for validation */
2
+ export declare const domainTokenDefaults: Record<string, string>;
3
+ //# sourceMappingURL=domainTokenDefaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domainTokenDefaults.d.ts","sourceRoot":"","sources":["../../../src/theme/domainTokens/domainTokenDefaults.ts"],"names":[],"mappings":"AAcA,4EAA4E;AAC5E,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGtD,CAAC"}
@@ -0,0 +1,19 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file domainTokens/domainTokenDefaults.ts
5
+ * @input syntaxTokenDefaults, dataTokenDefaults
6
+ * @output domainTokenDefaults
7
+ * @position Merges the domain token defaults for defineTheme. Kept out of the
8
+ * domainTokens barrel because defineTheme sits in a lazily imported module
9
+ * graph, which imports no barrel (pinned by scripts/core-single-bundle.test.mjs).
10
+ */
11
+
12
+ import { syntaxTokenDefaults } from "../syntax/tokens.js";
13
+ import { dataTokenDefaults } from "./dataTokens.js";
14
+
15
+ /** All domain token defaults merged — used by defineTheme for validation */
16
+ export const domainTokenDefaults = {
17
+ ...syntaxTokenDefaults,
18
+ ...dataTokenDefaults
19
+ };
@@ -4,17 +4,18 @@
4
4
  * @output domainTokenDefaults, DomainTokenName
5
5
  * @position Token aggregator; consumed by defineTheme for validation + autocomplete
6
6
  *
7
- * Merges all domain token defaults into a single map for defineTheme.
8
- * Actual token definitions live in their own directories (syntax/, dataviz/).
7
+ * Re-exports only. The merged map lives in domainTokenDefaults.ts so that
8
+ * defineTheme, inside a lazily imported module graph, imports it without a
9
+ * barrel. Actual token definitions live in their own directories (syntax/,
10
+ * dataviz/).
9
11
  */
10
12
  export { syntaxTokenDefaults } from '../syntax/tokens.js';
11
13
  export type { SyntaxTokenName } from '../syntax/tokens.js';
12
14
  export { dataTokenDefaults } from './dataTokens.js';
13
15
  export type { DataTokenName } from './dataTokens.js';
16
+ export { domainTokenDefaults } from './domainTokenDefaults.js';
14
17
  import type { SyntaxTokenName } from '../syntax/tokens.js';
15
18
  import type { DataTokenName } from './dataTokens.js';
16
- /** All domain token defaults merged — used by defineTheme for validation */
17
- export declare const domainTokenDefaults: Record<string, string>;
18
19
  /** Union of all domain token names */
19
20
  export type DomainTokenName = SyntaxTokenName | DataTokenName;
20
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/theme/domainTokens/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,OAAO,EAAC,mBAAmB,EAAC,MAAM,kBAAkB,CAAC;AACrD,YAAY,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAEtD,OAAO,EAAC,iBAAiB,EAAC,MAAM,cAAc,CAAC;AAC/C,YAAY,EAAC,aAAa,EAAC,MAAM,cAAc,CAAC;AAIhD,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,cAAc,CAAC;AAEhD,4EAA4E;AAC5E,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGtD,CAAC;AAEF,sCAAsC;AACtC,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/theme/domainTokens/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAEH,OAAO,EAAC,mBAAmB,EAAC,MAAM,kBAAkB,CAAC;AACrD,YAAY,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAEtD,OAAO,EAAC,iBAAiB,EAAC,MAAM,cAAc,CAAC;AAC/C,YAAY,EAAC,aAAa,EAAC,MAAM,cAAc,CAAC;AAEhD,OAAO,EAAC,mBAAmB,EAAC,MAAM,uBAAuB,CAAC;AAE1D,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,cAAc,CAAC;AAEhD,sCAAsC;AACtC,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG,aAAa,CAAC"}
@@ -6,18 +6,14 @@
6
6
  * @output domainTokenDefaults, DomainTokenName
7
7
  * @position Token aggregator; consumed by defineTheme for validation + autocomplete
8
8
  *
9
- * Merges all domain token defaults into a single map for defineTheme.
10
- * Actual token definitions live in their own directories (syntax/, dataviz/).
9
+ * Re-exports only. The merged map lives in domainTokenDefaults.ts so that
10
+ * defineTheme, inside a lazily imported module graph, imports it without a
11
+ * barrel. Actual token definitions live in their own directories (syntax/,
12
+ * dataviz/).
11
13
  */
12
14
 
13
15
  export { syntaxTokenDefaults } from "../syntax/tokens.js";
14
16
  export { dataTokenDefaults } from "./dataTokens.js";
15
- import { syntaxTokenDefaults } from "../syntax/tokens.js";
16
- import { dataTokenDefaults } from "./dataTokens.js";
17
- /** All domain token defaults merged — used by defineTheme for validation */
18
- export const domainTokenDefaults = {
19
- ...syntaxTokenDefaults,
20
- ...dataTokenDefaults
21
- };
17
+ export { domainTokenDefaults } from "./domainTokenDefaults.js";
22
18
 
23
19
  /** Union of all domain token names */
@@ -1,3 +1,15 @@
1
+ /**
2
+ * @file focusReturn.ts
3
+ * @input The document's current active element and the shared interaction
4
+ * modality.
5
+ * @output Whether focus is currently "detached" — on nothing in particular —
6
+ * and a focus move whose ring follows the last input.
7
+ * @position Utility; used by overlay triggers (e.g. DateInput, DateTimeInput)
8
+ * that return focus to their control when the overlay closes, but only when
9
+ * the dismiss did not already move focus somewhere the user chose, and by
10
+ * overlays (Dialog, DropdownMenu) that move focus programmatically.
11
+ */
12
+ import { type InteractionModality } from './interactionModality.js';
1
13
  /**
2
14
  * True when focus rests on nothing in particular — no active element, or the
3
15
  * document body / root element.
@@ -21,8 +33,12 @@ export declare function isFocusDetached(doc?: Document): boolean;
21
33
  * autofocus target, the control that opened a Dialog, or a menu trigger the
22
34
  * caller renders. Pass `options` (for example `preventScroll`) as to `focus()`.
23
35
  *
36
+ * `modality` defaults to the page's last input. A caller moving focus inside a
37
+ * second document (a same-origin iframe, a Document Picture-in-Picture window)
38
+ * passes that document's, which it tracks with `trackInteractionModalityIn`.
39
+ *
24
40
  * Make sure modality tracking is active (`useInteractionModalityTracking()`)
25
41
  * in the component that calls this.
26
42
  */
27
- export declare function focusForLastInput(element: HTMLElement, options?: FocusOptions): void;
43
+ export declare function focusForLastInput(element: HTMLElement, options?: FocusOptions, modality?: InteractionModality): void;
28
44
  //# sourceMappingURL=focusReturn.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"focusReturn.d.ts","sourceRoot":"","sources":["../../src/utils/focusReturn.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,GAAG,GAAE,QAAmB,GAAG,OAAO,CAKjE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,YAAY,GACrB,IAAI,CAwBN"}
1
+ {"version":3,"file":"focusReturn.d.ts","sourceRoot":"","sources":["../../src/utils/focusReturn.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAEH,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,uBAAuB,CAAC;AAE/B;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,GAAG,GAAE,QAAmB,GAAG,OAAO,CAKjE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,YAAY,EACtB,QAAQ,GAAE,mBAA8C,GACvD,IAAI,CA2BN"}
@@ -41,14 +41,21 @@ export function isFocusDetached(doc = document) {
41
41
  * autofocus target, the control that opened a Dialog, or a menu trigger the
42
42
  * caller renders. Pass `options` (for example `preventScroll`) as to `focus()`.
43
43
  *
44
+ * `modality` defaults to the page's last input. A caller moving focus inside a
45
+ * second document (a same-origin iframe, a Document Picture-in-Picture window)
46
+ * passes that document's, which it tracks with `trackInteractionModalityIn`.
47
+ *
44
48
  * Make sure modality tracking is active (`useInteractionModalityTracking()`)
45
49
  * in the component that calls this.
46
50
  */
47
- export function focusForLastInput(element, options) {
51
+ export function focusForLastInput(element, options, modality = getInteractionModality()) {
48
52
  // A text-entry field paints its own focus treatment to show where typing
49
- // goes, and an extra blur there can run validation or mark it touched.
50
- const isTextEntry = element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement || element.isContentEditable;
51
- if (isTextEntry || getInteractionModality() !== 'pointer') {
53
+ // goes, and an extra blur there can run validation or mark it touched. The
54
+ // field's own window: one in an iframe or a Picture-in-Picture window is
55
+ // never an instance of the page's constructors.
56
+ const view = element.ownerDocument.defaultView ?? window;
57
+ const isTextEntry = element instanceof view.HTMLInputElement || element instanceof view.HTMLTextAreaElement || element instanceof view.HTMLSelectElement || element.isContentEditable;
58
+ if (isTextEntry || modality !== 'pointer') {
52
59
  if (options === undefined) {
53
60
  element.focus();
54
61
  } else {
@@ -0,0 +1,8 @@
1
+ import type { ReactNode } from 'react';
2
+ /**
3
+ * The plain text a node renders through strings, numbers and element
4
+ * children, joined with spaces. Text a component renders internally, from
5
+ * props other than `children`, is not reachable and yields `''`.
6
+ */
7
+ export declare function getNodeText(node: ReactNode): string;
8
+ //# sourceMappingURL=getNodeText.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getNodeText.d.ts","sourceRoot":"","sources":["../../src/utils/getNodeText.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAErC;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAkBnD"}
@@ -0,0 +1,37 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file getNodeText.ts
5
+ * @input Receives a React node
6
+ * @output Exports getNodeText, the node's plain text
7
+ * @position Shared utility; consumed where a rendered node also needs a
8
+ * spoken name (Toast announcements, Table column-resize handle names)
9
+ */
10
+
11
+ import { isValidElement } from 'react';
12
+ /**
13
+ * The plain text a node renders through strings, numbers and element
14
+ * children, joined with spaces. Text a component renders internally, from
15
+ * props other than `children`, is not reachable and yields `''`.
16
+ */
17
+ export function getNodeText(node) {
18
+ if (node == null || typeof node === 'boolean') {
19
+ return '';
20
+ }
21
+ if (typeof node === 'string') {
22
+ return node;
23
+ }
24
+ if (typeof node === 'number') {
25
+ return String(node);
26
+ }
27
+ if (Array.isArray(node)) {
28
+ return node.map(getNodeText).filter(Boolean).join(' ');
29
+ }
30
+ if (/*#__PURE__*/isValidElement(node)) {
31
+ const {
32
+ children
33
+ } = node.props;
34
+ return getNodeText(children);
35
+ }
36
+ return '';
37
+ }
@@ -2,8 +2,17 @@ export type InteractionModality = 'keyboard' | 'pointer';
2
2
  declare function trackInteractionModality(): () => void;
3
3
  /** Activate shared modality tracking when the first consumer mounts. */
4
4
  export declare function useInteractionModalityTracking(): void;
5
- /** How the user last interacted with the page. */
6
- export declare function getInteractionModality(): InteractionModality;
5
+ /**
6
+ * Track a second document while a consumer there is mounted: a component an
7
+ * app renders into a same-origin iframe or a Document Picture-in-Picture
8
+ * window gets input there that never reaches the page's listeners. The first
9
+ * consumer installs the document's listener pair and the last one's cleanup
10
+ * removes it. Read the value with `getInteractionModality(doc)`. The page's
11
+ * own document keeps its document-lifetime listeners.
12
+ */
13
+ export declare function trackInteractionModalityIn(targetDocument: Document): () => void;
14
+ /** How the user last interacted with the page, or with `targetDocument`. */
15
+ export declare function getInteractionModality(targetDocument?: Document): InteractionModality;
7
16
  /** Test-only: restore the initial state between cases. */
8
17
  export declare function __resetInteractionModalityForTest(): void;
9
18
  /** Test-only: activate the tracker without rendering a hook. */
@@ -1 +1 @@
1
- {"version":3,"file":"interactionModality.d.ts","sourceRoot":"","sources":["../../src/utils/interactionModality.ts"],"names":[],"mappings":"AAkCA,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,SAAS,CAAC;AA8CzD,iBAAS,wBAAwB,IAAI,MAAM,IAAI,CAsB9C;AAED,wEAAwE;AACxE,wBAAgB,8BAA8B,IAAI,IAAI,CAErD;AAED,kDAAkD;AAClD,wBAAgB,sBAAsB,IAAI,mBAAmB,CAI5D;AAED,0DAA0D;AAC1D,wBAAgB,iCAAiC,IAAI,IAAI,CAIxD;AAED,gEAAgE;AAChE,eAAO,MAAM,yCAAyC,iCAC5B,CAAC"}
1
+ {"version":3,"file":"interactionModality.d.ts","sourceRoot":"","sources":["../../src/utils/interactionModality.ts"],"names":[],"mappings":"AAwCA,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,SAAS,CAAC;AAgDzD,iBAAS,wBAAwB,IAAI,MAAM,IAAI,CAsB9C;AAED,wEAAwE;AACxE,wBAAgB,8BAA8B,IAAI,IAAI,CAErD;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,cAAc,EAAE,QAAQ,GACvB,MAAM,IAAI,CAkCZ;AAED,4EAA4E;AAC5E,wBAAgB,sBAAsB,CACpC,cAAc,CAAC,EAAE,QAAQ,GACxB,mBAAmB,CAOrB;AAED,0DAA0D;AAC1D,wBAAgB,iCAAiC,IAAI,IAAI,CAIxD;AAED,gEAAgE;AAChE,eAAO,MAAM,yCAAyC,iCAC5B,CAAC"}
@@ -6,7 +6,9 @@
6
6
  * @file interactionModality.ts
7
7
  * @input Global pointerdown/keydown events
8
8
  * @output `useInteractionModalityTracking()` activates document-lifetime tracking;
9
- * `getInteractionModality()` reads how the user last interacted
9
+ * `getInteractionModality()` reads how the user last interacted;
10
+ * `trackInteractionModalityIn(doc)` and `getInteractionModality(doc)` do the
11
+ * same for a second document
10
12
  * @position Internal document-wide singleton; used where `:focus-visible` alone
11
13
  * is too broad
12
14
  *
@@ -26,8 +28,12 @@
26
28
  * The store lives on `document`, so duplicate Astryx bundles share one state
27
29
  * value and one listener pair. The first mounted consumer activates tracking;
28
30
  * the listeners then remain for the document lifetime so input during a gap
29
- * between consumers is not lost. Importing this module has no DOM side effects,
30
- * and server rendering keeps the keyboard-safe default.
31
+ * between consumers is not lost. A second document (a same-origin iframe, a
32
+ * Document Picture-in-Picture window) gets its own store when a component
33
+ * tracks it, since input there never reaches the page's listeners; its
34
+ * listeners stay only while a consumer there is mounted. Importing this module
35
+ * has no DOM side effects, and server rendering keeps the keyboard-safe
36
+ * default.
31
37
  */
32
38
  import { useEffect } from 'react';
33
39
  const interactionModalityStoreKey = Symbol.for('@astryxdesign/core/interaction-modality/v1');
@@ -89,8 +95,55 @@ export function useInteractionModalityTracking() {
89
95
  useEffect(() => trackInteractionModality(), []);
90
96
  }
91
97
 
92
- /** How the user last interacted with the page. */
93
- export function getInteractionModality() {
98
+ /**
99
+ * Track a second document while a consumer there is mounted: a component an
100
+ * app renders into a same-origin iframe or a Document Picture-in-Picture
101
+ * window gets input there that never reaches the page's listeners. The first
102
+ * consumer installs the document's listener pair and the last one's cleanup
103
+ * removes it. Read the value with `getInteractionModality(doc)`. The page's
104
+ * own document keeps its document-lifetime listeners.
105
+ */
106
+ export function trackInteractionModalityIn(targetDocument) {
107
+ if (typeof document !== 'undefined' && targetDocument === document) {
108
+ return trackInteractionModality();
109
+ }
110
+ const store = getStore(targetDocument);
111
+ store.consumers = (store.consumers ?? 0) + 1;
112
+ if (store.consumers === 1) {
113
+ store.isListening = true;
114
+ targetDocument.addEventListener('pointerdown', store.onPointerDown, {
115
+ capture: true,
116
+ passive: true
117
+ });
118
+ targetDocument.addEventListener('keydown', store.onKeyDown, {
119
+ capture: true,
120
+ passive: true
121
+ });
122
+ }
123
+ let isReleased = false;
124
+ return () => {
125
+ if (isReleased) {
126
+ return;
127
+ }
128
+ isReleased = true;
129
+ store.consumers = (store.consumers ?? 1) - 1;
130
+ if (store.consumers === 0) {
131
+ store.isListening = false;
132
+ targetDocument.removeEventListener('pointerdown', store.onPointerDown, {
133
+ capture: true
134
+ });
135
+ targetDocument.removeEventListener('keydown', store.onKeyDown, {
136
+ capture: true
137
+ });
138
+ }
139
+ };
140
+ }
141
+
142
+ /** How the user last interacted with the page, or with `targetDocument`. */
143
+ export function getInteractionModality(targetDocument) {
144
+ if (targetDocument != null) {
145
+ return getStore(targetDocument).modality;
146
+ }
94
147
  return typeof document === 'undefined' ? 'keyboard' : getStore(document).modality;
95
148
  }
96
149