@astryxdesign/core 0.7.5 → 0.7.6-canary.0ef60dd

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 (559) 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/Badge/Badge.d.ts.map +1 -1
  7. package/dist/Badge/Badge.js +7 -1
  8. package/dist/BottomSheet/BottomSheet.js +1 -1
  9. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  10. package/dist/BottomSheet/BottomSheetPanel.js +2 -1
  11. package/dist/BottomSheet/useSheetGestures.d.ts +12 -0
  12. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  13. package/dist/BottomSheet/useSheetGestures.js +30 -3
  14. package/dist/Button/Button.js +4 -4
  15. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  16. package/dist/CheckboxInput/CheckboxInput.js +6 -5
  17. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  18. package/dist/ClickableCard/ClickableCard.js +37 -4
  19. package/dist/DateInput/DateInput.d.ts.map +1 -1
  20. package/dist/DateInput/DateInput.js +12 -4
  21. package/dist/DateInput/MonthScroller.d.ts.map +1 -1
  22. package/dist/DateInput/MonthScroller.js +101 -15
  23. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  24. package/dist/DateInput/TouchDateField.js +8 -3
  25. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  26. package/dist/DateRangeInput/DateRangeInput.js +1 -1
  27. package/dist/DateTimeInput/DateTimeInput.d.ts +5 -2
  28. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  29. package/dist/DateTimeInput/DateTimeInput.js +23 -12
  30. package/dist/DateTimeInput/NativeTimeSegment.d.ts.map +1 -1
  31. package/dist/DateTimeInput/NativeTimeSegment.js +5 -4
  32. package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
  33. package/dist/DateTimeInput/TouchDateTimeField.js +4 -3
  34. package/dist/Dialog/Dialog.d.ts.map +1 -1
  35. package/dist/Dialog/Dialog.js +34 -9
  36. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  37. package/dist/Dialog/DialogHeader.js +5 -4
  38. package/dist/Divider/Divider.d.ts.map +1 -1
  39. package/dist/Divider/Divider.js +2 -1
  40. package/dist/Drawer/Drawer.d.ts +1 -0
  41. package/dist/Drawer/Drawer.d.ts.map +1 -1
  42. package/dist/Drawer/Drawer.js +7 -2
  43. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  44. package/dist/DropdownMenu/DropdownMenu.js +7 -3
  45. package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -1
  46. package/dist/DropdownMenu/MenuBottomSheetActionList.js +6 -5
  47. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts +20 -2
  48. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts.map +1 -1
  49. package/dist/DropdownMenu/lazyMenuBottomSheet.js +31 -3
  50. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  51. package/dist/DropdownMenu/renderDropdownItems.js +21 -16
  52. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  53. package/dist/EmptyState/EmptyState.js +2 -1
  54. package/dist/Field/FieldLabel.d.ts +1 -1
  55. package/dist/Field/FieldLabel.d.ts.map +1 -1
  56. package/dist/Field/FieldLabel.js +4 -4
  57. package/dist/Field/InputClearButton.js +3 -3
  58. package/dist/Field/PanelSearchInput.js +2 -2
  59. package/dist/FieldStatus/FieldStatus.js +2 -2
  60. package/dist/FileInput/FileInput.d.ts.map +1 -1
  61. package/dist/FileInput/FileInput.js +7 -1
  62. package/dist/Heading/Heading.js +1 -1
  63. package/dist/Icon/Icon.js +1 -1
  64. package/dist/Indicator/CheckIndicator.d.ts.map +1 -1
  65. package/dist/Indicator/CheckIndicator.js +2 -1
  66. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
  67. package/dist/Indicator/CheckboxIndicator.js +3 -1
  68. package/dist/Indicator/RadioIndicator.d.ts.map +1 -1
  69. package/dist/Indicator/RadioIndicator.js +3 -1
  70. package/dist/Item/Item.d.ts.map +1 -1
  71. package/dist/Item/Item.js +2 -1
  72. package/dist/Item/ItemSwipeLayer.js +1 -1
  73. package/dist/Layer/popoverOperations.d.ts +27 -0
  74. package/dist/Layer/popoverOperations.d.ts.map +1 -0
  75. package/dist/Layer/popoverOperations.js +159 -0
  76. package/dist/Layer/useLayer.d.ts +1 -1
  77. package/dist/Layer/useLayer.d.ts.map +1 -1
  78. package/dist/Layer/useLayer.js +93 -59
  79. package/dist/Layout/Layout.js +1 -1
  80. package/dist/Layout/LayoutContent.js +1 -1
  81. package/dist/Layout/LayoutFooter.js +1 -1
  82. package/dist/Layout/LayoutHeader.js +1 -1
  83. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  84. package/dist/Lightbox/Lightbox.js +5 -0
  85. package/dist/Lightbox/LightboxViewport.a11y.browser.spec.js +5 -4
  86. package/dist/List/List.js +1 -1
  87. package/dist/List/ListItem.js +3 -3
  88. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  89. package/dist/MobileNav/MobileNav.js +7 -2
  90. package/dist/MultiSelector/CreateRow.a11y.chromium.spec.js +7 -6
  91. package/dist/MultiSelector/MultiSelector.d.ts +1 -1
  92. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  93. package/dist/MultiSelector/MultiSelector.js +26 -10
  94. package/dist/NumberInput/NumberInput.d.ts +3 -1
  95. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  96. package/dist/NumberInput/NumberInput.js +3 -3
  97. package/dist/NumberInput/numberParser.d.ts +3 -0
  98. package/dist/NumberInput/numberParser.d.ts.map +1 -1
  99. package/dist/NumberInput/numberParser.js +7 -44
  100. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  101. package/dist/ProgressBar/ProgressBar.js +16 -12
  102. package/dist/Resizable/useResizable.d.ts.map +1 -1
  103. package/dist/Resizable/useResizable.js +28 -2
  104. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  105. package/dist/SegmentedControl/SegmentedControlItem.js +4 -1
  106. package/dist/Selector/useSelectorPresentation.d.ts +8 -1
  107. package/dist/Selector/useSelectorPresentation.d.ts.map +1 -1
  108. package/dist/Selector/useSelectorPresentation.js +20 -4
  109. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  110. package/dist/Skeleton/Skeleton.js +1 -1
  111. package/dist/Slider/Slider.d.ts.map +1 -1
  112. package/dist/Slider/Slider.js +10 -2
  113. package/dist/Spinner/Spinner.d.ts.map +1 -1
  114. package/dist/Spinner/Spinner.js +3 -3
  115. package/dist/Stack/Stack.js +1 -1
  116. package/dist/Stack/StackItem.js +1 -1
  117. package/dist/TabList/TabList.d.ts.map +1 -1
  118. package/dist/TabList/TabList.js +16 -1
  119. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  120. package/dist/Table/plugins/columnResize/useTableColumnResize.js +9 -1
  121. package/dist/Text/Text.js +1 -1
  122. package/dist/Text/useTruncation.d.ts.map +1 -1
  123. package/dist/Text/useTruncation.js +23 -5
  124. package/dist/TextArea/TextArea.d.ts +1 -1
  125. package/dist/TextArea/TextArea.d.ts.map +1 -1
  126. package/dist/TextArea/TextArea.js +13 -2
  127. package/dist/TextInput/TextInput.d.ts.map +1 -1
  128. package/dist/TextInput/TextInput.js +3 -3
  129. package/dist/TimeInput/TimeInput.d.ts +6 -2
  130. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  131. package/dist/TimeInput/TimeInput.js +21 -14
  132. package/dist/TimeInput/TouchTimeField.d.ts.map +1 -1
  133. package/dist/TimeInput/TouchTimeField.js +5 -4
  134. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  135. package/dist/Timestamp/Timestamp.js +2 -2
  136. package/dist/Timestamp/TimestampHoverCard.js +4 -4
  137. package/dist/Toast/Toast.d.ts.map +1 -1
  138. package/dist/Toast/Toast.js +60 -20
  139. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  140. package/dist/Toast/ToastViewport.js +82 -51
  141. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  142. package/dist/Tokenizer/Tokenizer.js +13 -2
  143. package/dist/Tokenizer/TokenizerBottomSheet.js +3 -3
  144. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  145. package/dist/Toolbar/Toolbar.js +2 -1
  146. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  147. package/dist/TopNav/TopNavMegaMenu.js +3 -0
  148. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  149. package/dist/TopNav/TopNavMenu.js +1 -0
  150. package/dist/Typeahead/Typeahead.d.ts +1 -1
  151. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  152. package/dist/Typeahead/Typeahead.js +9 -1
  153. package/dist/astryx.css +20 -4
  154. package/dist/gestures/GestureArena.d.ts +6 -0
  155. package/dist/gestures/GestureArena.d.ts.map +1 -1
  156. package/dist/gestures/GestureArena.js +39 -0
  157. package/dist/gestures/GestureHost.d.ts +352 -0
  158. package/dist/gestures/GestureHost.d.ts.map +1 -0
  159. package/dist/gestures/GestureHost.js +1773 -0
  160. package/dist/gestures/GestureRecognizer.d.ts +10 -0
  161. package/dist/gestures/GestureRecognizer.d.ts.map +1 -1
  162. package/dist/gestures/GestureRecognizer.js +16 -0
  163. package/dist/gestures/LongPressGestureRecognizer.d.ts +1 -0
  164. package/dist/gestures/LongPressGestureRecognizer.d.ts.map +1 -1
  165. package/dist/gestures/LongPressGestureRecognizer.js +3 -0
  166. package/dist/gestures/PanGestureRecognizer.d.ts +1 -0
  167. package/dist/gestures/PanGestureRecognizer.d.ts.map +1 -1
  168. package/dist/gestures/PanGestureRecognizer.js +3 -0
  169. package/dist/gestures/PinchGestureRecognizer.d.ts +31 -0
  170. package/dist/gestures/PinchGestureRecognizer.d.ts.map +1 -0
  171. package/dist/gestures/PinchGestureRecognizer.js +128 -0
  172. package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts +71 -0
  173. package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts.map +1 -0
  174. package/dist/gestures/ScreenEdgePanGestureRecognizer.js +223 -0
  175. package/dist/gestures/SwipeGestureRecognizer.d.ts +37 -0
  176. package/dist/gestures/SwipeGestureRecognizer.d.ts.map +1 -0
  177. package/dist/gestures/SwipeGestureRecognizer.js +148 -0
  178. package/dist/gestures/TapGestureRecognizer.d.ts +1 -0
  179. package/dist/gestures/TapGestureRecognizer.d.ts.map +1 -1
  180. package/dist/gestures/TapGestureRecognizer.js +3 -0
  181. package/dist/gestures/VelocityTracker.d.ts +6 -2
  182. package/dist/gestures/VelocityTracker.d.ts.map +1 -1
  183. package/dist/gestures/VelocityTracker.js +7 -3
  184. package/dist/gestures/haptics.d.ts +65 -0
  185. package/dist/gestures/haptics.d.ts.map +1 -0
  186. package/dist/gestures/haptics.js +182 -0
  187. package/dist/gestures/nativeScroll.d.ts +42 -0
  188. package/dist/gestures/nativeScroll.d.ts.map +1 -0
  189. package/dist/gestures/nativeScroll.js +228 -0
  190. package/dist/gestures/touchAction.d.ts +60 -0
  191. package/dist/gestures/touchAction.d.ts.map +1 -0
  192. package/dist/gestures/touchAction.js +157 -0
  193. package/dist/gestures/touchActionRule.d.ts +34 -0
  194. package/dist/gestures/touchActionRule.d.ts.map +1 -0
  195. package/dist/gestures/touchActionRule.js +140 -0
  196. package/dist/gestures/types.d.ts +18 -7
  197. package/dist/gestures/types.d.ts.map +1 -1
  198. package/dist/gestures/types.js +22 -2
  199. package/dist/hooks/index.d.ts +1 -1
  200. package/dist/hooks/index.d.ts.map +1 -1
  201. package/dist/hooks/modalAnnounce.d.ts.map +1 -1
  202. package/dist/hooks/modalAnnounce.js +107 -61
  203. package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -1
  204. package/dist/hooks/useAdaptivePresentation.js +4 -1
  205. package/dist/hooks/useAnnounce.d.ts +101 -42
  206. package/dist/hooks/useAnnounce.d.ts.map +1 -1
  207. package/dist/hooks/useAnnounce.js +161 -75
  208. package/dist/hooks/useAnnounceRenderedText.d.ts +11 -3
  209. package/dist/hooks/useAnnounceRenderedText.d.ts.map +1 -1
  210. package/dist/hooks/useAnnounceRenderedText.js +5 -3
  211. package/dist/hooks/useBusyAnnouncement.d.ts.map +1 -1
  212. package/dist/hooks/useBusyAnnouncement.js +6 -12
  213. package/dist/hooks/useMenuHover.d.ts.map +1 -1
  214. package/dist/hooks/useMenuHover.js +12 -1
  215. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  216. package/dist/i18n/generated-locales/en.generated.js +5 -0
  217. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  218. package/dist/i18n/generated-locales/pseudo.generated.js +5 -0
  219. package/dist/motion/animator.d.ts +7 -1
  220. package/dist/motion/animator.d.ts.map +1 -1
  221. package/dist/motion/animator.js +119 -27
  222. package/dist/theme/defineTheme.d.ts +19 -5
  223. package/dist/theme/defineTheme.d.ts.map +1 -1
  224. package/dist/theme/defineTheme.js +64 -4
  225. package/dist/theme/domainTokens/domainTokenDefaults.d.ts +3 -0
  226. package/dist/theme/domainTokens/domainTokenDefaults.d.ts.map +1 -0
  227. package/dist/theme/domainTokens/domainTokenDefaults.js +19 -0
  228. package/dist/theme/domainTokens/index.d.ts +5 -4
  229. package/dist/theme/domainTokens/index.d.ts.map +1 -1
  230. package/dist/theme/domainTokens/index.js +5 -9
  231. package/dist/theme/index.d.ts +1 -0
  232. package/dist/theme/index.d.ts.map +1 -1
  233. package/dist/theme/motionKnobs.d.ts +56 -0
  234. package/dist/theme/motionKnobs.d.ts.map +1 -0
  235. package/dist/theme/motionKnobs.js +48 -0
  236. package/dist/theme/resolveThemeValues.d.ts +11 -4
  237. package/dist/theme/resolveThemeValues.d.ts.map +1 -1
  238. package/dist/theme/resolveThemeValues.js +40 -4
  239. package/dist/theme/themeAdaptations.d.ts +7 -3
  240. package/dist/theme/themeAdaptations.d.ts.map +1 -1
  241. package/dist/theme/themeAdaptations.js +36 -7
  242. package/dist/theme/themeMotion.d.ts +96 -0
  243. package/dist/theme/themeMotion.d.ts.map +1 -0
  244. package/dist/theme/themeMotion.js +177 -0
  245. package/dist/theme/tokenReferences.d.ts +31 -0
  246. package/dist/theme/tokenReferences.d.ts.map +1 -0
  247. package/dist/theme/tokenReferences.js +132 -0
  248. package/dist/theme/tokens.d.ts +1 -0
  249. package/dist/theme/tokens.d.ts.map +1 -1
  250. package/dist/theme/tokens.js +9 -129
  251. package/dist/theme/tokens.stylex.d.ts +12 -0
  252. package/dist/theme/tokens.stylex.d.ts.map +1 -1
  253. package/dist/theme/tokens.stylex.js +12 -0
  254. package/dist/utils/focusReturn.d.ts +17 -1
  255. package/dist/utils/focusReturn.d.ts.map +1 -1
  256. package/dist/utils/focusReturn.js +11 -4
  257. package/dist/utils/getNodeText.d.ts +8 -0
  258. package/dist/utils/getNodeText.d.ts.map +1 -0
  259. package/dist/utils/getNodeText.js +37 -0
  260. package/dist/utils/interactionModality.d.ts +11 -2
  261. package/dist/utils/interactionModality.d.ts.map +1 -1
  262. package/dist/utils/interactionModality.js +58 -5
  263. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  264. package/dist/utils/interactionOverlay.stylex.js +17 -8
  265. package/dist/utils/plainDate.d.ts +2 -0
  266. package/dist/utils/plainDate.d.ts.map +1 -1
  267. package/dist/utils/plainDate.js +46 -3
  268. package/dist/utils/presentation.d.ts +50 -0
  269. package/dist/utils/presentation.d.ts.map +1 -0
  270. package/dist/utils/presentation.js +125 -0
  271. package/dist/utils/pressFeedback.d.ts.map +1 -1
  272. package/dist/utils/pressFeedback.js +20 -1
  273. package/dist/utils/primaryClick.d.ts +11 -0
  274. package/dist/utils/primaryClick.d.ts.map +1 -0
  275. package/dist/utils/primaryClick.js +25 -0
  276. package/dist/utils/timeFormat.d.ts +47 -0
  277. package/dist/utils/timeFormat.d.ts.map +1 -0
  278. package/dist/utils/timeFormat.js +240 -0
  279. package/dist/utils/touchPresentation.d.ts.map +1 -1
  280. package/dist/utils/touchPresentation.js +5 -3
  281. package/dist/utils/touchPresentation.stylex.d.ts +15 -0
  282. package/dist/utils/touchPresentation.stylex.d.ts.map +1 -0
  283. package/dist/utils/touchPresentation.stylex.js +33 -0
  284. package/dist/utils/unicodeDigits.d.ts +20 -0
  285. package/dist/utils/unicodeDigits.d.ts.map +1 -0
  286. package/dist/utils/unicodeDigits.js +70 -0
  287. package/locales/en.json +23 -3
  288. package/locales/pseudo.json +15 -0
  289. package/package.json +5 -4
  290. package/src/AlertDialog/AlertDialog.tsx +6 -5
  291. package/src/AlertDialog/__tests__/AlertDialog.a11y.chromium.spec.ts +18 -6
  292. package/src/Avatar/Avatar.spec.md +22 -22
  293. package/src/Avatar/Avatar.tsx +18 -1
  294. package/src/Avatar/__tests__/AvatarForcedColors.a11y.chromium.spec.ts +126 -0
  295. package/src/Badge/Badge.doc.mjs +1 -1
  296. package/src/Badge/Badge.tsx +19 -2
  297. package/src/Badge/__tests__/BadgeLargerText.a11y.chromium.spec.ts +260 -0
  298. package/src/BottomSheet/BottomSheet.test.tsx +46 -0
  299. package/src/BottomSheet/BottomSheet.tsx +1 -1
  300. package/src/BottomSheet/BottomSheetPanel.tsx +2 -1
  301. package/src/BottomSheet/useSheetGestures.test.ts +52 -0
  302. package/src/BottomSheet/useSheetGestures.ts +37 -6
  303. package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.chromium.spec.ts +18 -6
  304. package/src/Button/Button.tsx +4 -4
  305. package/src/Calendar/Calendar.spec.md +87 -48
  306. package/src/Calendar/__tests__/Calendar.a11y.chromium.spec.ts +21 -7
  307. package/src/Carousel/__tests__/Carousel.a11y.chromium.spec.ts +21 -7
  308. package/src/CheckboxInput/CheckboxInput.tsx +6 -5
  309. package/src/ClickableCard/ClickableCard.test.tsx +96 -0
  310. package/src/ClickableCard/ClickableCard.tsx +48 -4
  311. package/src/Collapsible/Collapsible.spec.md +2 -0
  312. package/src/Collapsible/CollapsibleGroup.spec.md +57 -29
  313. package/src/Collapsible/__tests__/Collapsible.a11y.chromium.spec.ts +18 -7
  314. package/src/Collapsible/__tests__/CollapsibleGroupAccordion.a11y.chromium.spec.ts +18 -6
  315. package/src/ComplexSelector/ComplexSelector.test.tsx +37 -0
  316. package/src/ContextMenu/ContextMenu.test.tsx +35 -0
  317. package/src/DateInput/DateInput.test.tsx +115 -3
  318. package/src/DateInput/DateInput.tsx +29 -14
  319. package/src/DateInput/DateInputTouch.test.tsx +76 -0
  320. package/src/DateInput/MonthScroller.tsx +106 -20
  321. package/src/DateInput/MonthScrollerSettle.test.tsx +165 -0
  322. package/src/DateInput/TouchDateField.tsx +14 -3
  323. package/src/DateInput/__tests__/MonthScrollerSteps.interaction.browser.spec.ts +312 -0
  324. package/src/DateRangeInput/DateRangeInput.test.tsx +46 -3
  325. package/src/DateRangeInput/DateRangeInput.tsx +16 -12
  326. package/src/DateTimeInput/DateTimeInput.doc.mjs +10 -8
  327. package/src/DateTimeInput/DateTimeInput.spec.md +75 -51
  328. package/src/DateTimeInput/DateTimeInput.test.tsx +81 -0
  329. package/src/DateTimeInput/DateTimeInput.tsx +46 -24
  330. package/src/DateTimeInput/NativeTimeSegment.tsx +4 -6
  331. package/src/DateTimeInput/TouchDateTimeField.tsx +3 -7
  332. package/src/Dialog/Dialog.test.tsx +216 -2
  333. package/src/Dialog/Dialog.tsx +42 -9
  334. package/src/Dialog/DialogHeader.tsx +5 -4
  335. package/src/Dialog/__tests__/DialogSecondDocument.a11y.browser.spec.ts +202 -0
  336. package/src/Divider/Divider.tsx +2 -1
  337. package/src/Drawer/Drawer.spec.md +111 -80
  338. package/src/Drawer/Drawer.test.tsx +35 -0
  339. package/src/Drawer/Drawer.tsx +13 -2
  340. package/src/Drawer/DrawerHeader.spec.md +181 -0
  341. package/src/Drawer/__tests__/DrawerNonModalPageScroll.a11y.chromium.spec.ts +267 -0
  342. package/src/DropdownMenu/DropdownMenu.test.tsx +41 -0
  343. package/src/DropdownMenu/DropdownMenu.tsx +10 -3
  344. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +34 -0
  345. package/src/DropdownMenu/MenuBottomSheetActionList.tsx +6 -5
  346. package/src/DropdownMenu/__tests__/Menu.a11y.chromium.spec.ts +19 -7
  347. package/src/DropdownMenu/__tests__/MenuButton.a11y.chromium.spec.ts +21 -9
  348. package/src/DropdownMenu/lazyMenuBottomSheet.test.ts +60 -0
  349. package/src/DropdownMenu/lazyMenuBottomSheet.ts +36 -7
  350. package/src/DropdownMenu/renderDropdownItems.tsx +24 -19
  351. package/src/EmptyState/EmptyState.tsx +2 -1
  352. package/src/Field/FieldLabel.tsx +5 -4
  353. package/src/Field/InputClearButton.tsx +3 -3
  354. package/src/Field/PanelSearchInput.tsx +2 -2
  355. package/src/FieldStatus/FieldStatus.tsx +2 -2
  356. package/src/FileInput/FileInput.test.tsx +21 -0
  357. package/src/FileInput/FileInput.tsx +4 -1
  358. package/src/FormLayout/FormLayout.spec.md +7 -8
  359. package/src/Heading/Heading.tsx +1 -1
  360. package/src/Icon/Icon.tsx +1 -1
  361. package/src/Indicator/CheckIndicator.spec.md +47 -42
  362. package/src/Indicator/CheckIndicator.tsx +2 -1
  363. package/src/Indicator/CheckboxIndicator.spec.md +40 -37
  364. package/src/Indicator/CheckboxIndicator.tsx +3 -1
  365. package/src/Indicator/Indicator.spec.md +188 -0
  366. package/src/Indicator/RadioIndicator.tsx +3 -1
  367. package/src/Item/Item.tsx +2 -1
  368. package/src/Item/ItemSwipeLayer.tsx +1 -1
  369. package/src/Kbd/Kbd.spec.md +81 -38
  370. package/src/Layer/__tests__/LayerBrowserHide.a11y.chromium.spec.ts +126 -0
  371. package/src/Layer/popoverOperations.test.ts +205 -0
  372. package/src/Layer/popoverOperations.ts +172 -0
  373. package/src/Layer/useLayer.test.tsx +348 -1
  374. package/src/Layer/useLayer.tsx +118 -71
  375. package/src/Layout/Layout.spec.md +127 -131
  376. package/src/Layout/Layout.tsx +1 -1
  377. package/src/Layout/LayoutContent.spec.md +205 -0
  378. package/src/Layout/LayoutContent.tsx +1 -1
  379. package/src/Layout/LayoutFooter.spec.md +190 -0
  380. package/src/Layout/LayoutFooter.tsx +1 -1
  381. package/src/Layout/LayoutHeader.spec.md +193 -0
  382. package/src/Layout/LayoutHeader.tsx +1 -1
  383. package/src/Layout/LayoutPanel.spec.md +194 -0
  384. package/src/Layout/__tests__/Layout.a11y.chromium.spec.ts +18 -6
  385. package/src/Lightbox/Lightbox.test.tsx +18 -0
  386. package/src/Lightbox/Lightbox.tsx +5 -0
  387. package/src/Lightbox/LightboxViewport.a11y.browser.spec.ts +8 -4
  388. package/src/Link/__tests__/Link.a11y.chromium.spec.ts +18 -8
  389. package/src/List/List.tsx +1 -1
  390. package/src/List/ListItem.spec.md +205 -0
  391. package/src/List/ListItem.tsx +3 -3
  392. package/src/MetadataList/MetadataList.spec.md +187 -0
  393. package/src/MetadataList/MetadataListItem.spec.md +165 -0
  394. package/src/MobileNav/MobileNav.test.tsx +15 -0
  395. package/src/MobileNav/MobileNav.tsx +9 -1
  396. package/src/MobileNav/__tests__/MobileNavHeader.a11y.chromium.spec.ts +89 -0
  397. package/src/MoreMenu/MoreMenu.test.tsx +11 -0
  398. package/src/MultiSelector/CreateRow.a11y.chromium.spec.ts +7 -8
  399. package/src/MultiSelector/MultiSelector.spec.md +7 -4
  400. package/src/MultiSelector/MultiSelector.test.tsx +270 -1
  401. package/src/MultiSelector/MultiSelector.tsx +25 -10
  402. package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +8 -1
  403. package/src/NavMenu/NavHeadingMenu.spec.md +1 -5
  404. package/src/NumberInput/NumberInput.doc.mjs +13 -7
  405. package/src/NumberInput/NumberInput.spec.md +59 -37
  406. package/src/NumberInput/NumberInput.tsx +13 -3
  407. package/src/NumberInput/__tests__/NumberInput.coarse-pointer.a11y.chromium.spec.ts +102 -0
  408. package/src/NumberInput/numberParser.ts +7 -45
  409. package/src/ProgressBar/ProgressBar.test.tsx +108 -32
  410. package/src/ProgressBar/ProgressBar.tsx +58 -10
  411. package/src/ProgressBar/__tests__/ProgressBar.a11y.chromium.spec.ts +21 -9
  412. package/src/ProgressBar/__tests__/ProgressBarMotion.a11y.chromium.spec.ts +831 -0
  413. package/src/Resizable/__tests__/ResizeHandle.a11y.chromium.spec.ts +21 -14
  414. package/src/Resizable/useResizable.test.ts +43 -0
  415. package/src/Resizable/useResizable.ts +32 -9
  416. package/src/SegmentedControl/SegmentedControl.test.tsx +84 -0
  417. package/src/SegmentedControl/SegmentedControlItem.tsx +11 -5
  418. package/src/Selector/Selector.test.tsx +90 -1
  419. package/src/Selector/__tests__/Selector.combobox.a11y.test.tsx +8 -1
  420. package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +8 -1
  421. package/src/Selector/useSelectorPresentation.ts +31 -3
  422. package/src/SideNav/SideNav.test.tsx +27 -0
  423. package/src/SideNav/__tests__/SideNavRailMotion.interaction.browser.spec.ts +176 -51
  424. package/src/Skeleton/Skeleton.tsx +3 -1
  425. package/src/Slider/Slider.test.tsx +31 -0
  426. package/src/Slider/Slider.tsx +8 -3
  427. package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +19 -7
  428. package/src/Spinner/Spinner.tsx +5 -3
  429. package/src/Stack/Stack.tsx +1 -1
  430. package/src/Stack/StackItem.tsx +1 -1
  431. package/src/TabList/TabList.test.tsx +58 -4
  432. package/src/TabList/TabList.tsx +16 -1
  433. package/src/TabList/__tests__/TabPressPointer.interaction.browser.spec.ts +163 -0
  434. package/src/Table/__tests__/Table.a11y.chromium.spec.ts +18 -6
  435. package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +22 -0
  436. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +8 -4
  437. package/src/Text/Text.tsx +1 -1
  438. package/src/Text/useTruncation.test.tsx +128 -1
  439. package/src/Text/useTruncation.ts +23 -5
  440. package/src/TextArea/TextArea.spec.md +10 -13
  441. package/src/TextArea/TextArea.test.tsx +60 -0
  442. package/src/TextArea/TextArea.tsx +15 -1
  443. package/src/TextInput/TextInput.test.tsx +21 -0
  444. package/src/TextInput/TextInput.tsx +3 -1
  445. package/src/TimeInput/NativeTimeInput.test.tsx +17 -0
  446. package/src/TimeInput/Presentation.test.tsx +21 -0
  447. package/src/TimeInput/TimeInput.doc.mjs +8 -8
  448. package/src/TimeInput/TimeInput.spec.md +45 -48
  449. package/src/TimeInput/TimeInput.test.tsx +76 -0
  450. package/src/TimeInput/TimeInput.tsx +36 -18
  451. package/src/TimeInput/TouchTimeField.tsx +4 -6
  452. package/src/Timestamp/Timestamp.spec.md +6 -12
  453. package/src/Timestamp/Timestamp.test.tsx +30 -5
  454. package/src/Timestamp/Timestamp.tsx +6 -2
  455. package/src/Timestamp/TimestampHoverCard.tsx +4 -4
  456. package/src/Toast/Toast.spec.md +14 -20
  457. package/src/Toast/Toast.tsx +104 -36
  458. package/src/Toast/ToastViewport.test.tsx +719 -1
  459. package/src/Toast/ToastViewport.tsx +100 -50
  460. package/src/Tokenizer/Tokenizer.spec.md +5 -3
  461. package/src/Tokenizer/Tokenizer.test.tsx +68 -0
  462. package/src/Tokenizer/Tokenizer.tsx +14 -2
  463. package/src/Tokenizer/TokenizerBottomSheet.test.tsx +15 -4
  464. package/src/Tokenizer/TokenizerBottomSheet.tsx +3 -3
  465. package/src/Toolbar/Toolbar.spec.md +5 -6
  466. package/src/Toolbar/Toolbar.test.tsx +30 -0
  467. package/src/Toolbar/Toolbar.tsx +2 -1
  468. package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +18 -6
  469. package/src/Tooltip/__tests__/Tooltip.a11y.chromium.spec.ts +18 -7
  470. package/src/TopNav/TopNav.test.tsx +27 -0
  471. package/src/TopNav/TopNavMegaMenu.test.tsx +21 -0
  472. package/src/TopNav/TopNavMegaMenu.tsx +4 -1
  473. package/src/TopNav/TopNavMenu.test.tsx +17 -0
  474. package/src/TopNav/TopNavMenu.tsx +1 -0
  475. package/src/Typeahead/Typeahead.test.tsx +40 -0
  476. package/src/Typeahead/Typeahead.tsx +12 -1
  477. package/src/__tests__/pressState.ts +8 -4
  478. package/src/gestures/GestureArena.ts +35 -0
  479. package/src/gestures/GestureHost.ts +2065 -0
  480. package/src/gestures/GestureRecognizer.ts +18 -0
  481. package/src/gestures/LongPressGestureRecognizer.ts +4 -0
  482. package/src/gestures/PanGestureRecognizer.ts +4 -0
  483. package/src/gestures/PinchGestureRecognizer.ts +148 -0
  484. package/src/gestures/ScreenEdgePanGestureRecognizer.ts +256 -0
  485. package/src/gestures/SwipeGestureRecognizer.ts +197 -0
  486. package/src/gestures/TapGestureRecognizer.ts +4 -0
  487. package/src/gestures/VelocityTracker.ts +10 -3
  488. package/src/gestures/__tests__/GestureHost.test.ts +2087 -0
  489. package/src/gestures/__tests__/PanGestureRecognizer.test.ts +4 -4
  490. package/src/gestures/__tests__/PinchGestureRecognizer.test.ts +74 -0
  491. package/src/gestures/__tests__/ScreenEdgePanGestureRecognizer.test.ts +498 -0
  492. package/src/gestures/__tests__/SwipeGestureRecognizer.test.ts +108 -0
  493. package/src/gestures/__tests__/arena.test.ts +5 -0
  494. package/src/gestures/__tests__/arenaPinchSwipeEdge.test.ts +193 -0
  495. package/src/gestures/__tests__/browser/build-fixture.mjs +46 -0
  496. package/src/gestures/__tests__/browser/fixture.ts +307 -0
  497. package/src/gestures/__tests__/browser/gestures.interaction.browser.spec.ts +1137 -0
  498. package/src/gestures/__tests__/browserNegotiation.test.ts +293 -0
  499. package/src/gestures/__tests__/haptics.test.ts +187 -0
  500. package/src/gestures/__tests__/harness.ts +21 -2
  501. package/src/gestures/__tests__/recognizerKind.test.ts +78 -0
  502. package/src/gestures/__tests__/touchActionRule.test.ts +54 -0
  503. package/src/gestures/haptics.ts +203 -0
  504. package/src/gestures/nativeScroll.ts +277 -0
  505. package/src/gestures/touchAction.ts +191 -0
  506. package/src/gestures/touchActionRule.ts +157 -0
  507. package/src/gestures/types.ts +30 -9
  508. package/src/hooks/index.ts +5 -1
  509. package/src/hooks/modalAnnounce.ts +129 -72
  510. package/src/hooks/useAdaptivePresentation.ts +4 -1
  511. package/src/hooks/useAnnounce.doc.mjs +35 -3
  512. package/src/hooks/useAnnounce.replace.test.tsx +516 -0
  513. package/src/hooks/useAnnounce.ts +209 -102
  514. package/src/hooks/useAnnounceRenderedText.adopters.test.tsx +10 -1
  515. package/src/hooks/useAnnounceRenderedText.ts +17 -3
  516. package/src/hooks/useBusyAnnouncement.ts +7 -10
  517. package/src/hooks/useMenuHover.ts +15 -1
  518. package/src/i18n/generated-locales/en.generated.ts +6 -0
  519. package/src/i18n/generated-locales/pseudo.generated.ts +6 -0
  520. package/src/motion/README.md +11 -1
  521. package/src/motion/animator.test.ts +151 -3
  522. package/src/motion/animator.ts +149 -33
  523. package/src/theme/Theme.doc.mjs +1 -1
  524. package/src/theme/defineTheme.ts +117 -8
  525. package/src/theme/derivedVarRegistry.test.ts +4 -0
  526. package/src/theme/domainTokens/domainTokenDefaults.ts +19 -0
  527. package/src/theme/domainTokens/index.ts +6 -10
  528. package/src/theme/expandMotionScale.test.ts +1 -1
  529. package/src/theme/index.ts +5 -0
  530. package/src/theme/motionKnobs.ts +82 -0
  531. package/src/theme/resolveThemeValues.ts +68 -6
  532. package/src/theme/themeAdaptations.ts +52 -12
  533. package/src/theme/themeMotion.test.ts +620 -0
  534. package/src/theme/themeMotion.ts +221 -0
  535. package/src/theme/tokenReferences.ts +149 -0
  536. package/src/theme/tokens.stylex.ts +10 -0
  537. package/src/theme/tokens.ts +15 -158
  538. package/src/utils/focusReturn.ts +17 -6
  539. package/src/utils/getNodeText.test.ts +24 -0
  540. package/src/utils/getNodeText.ts +37 -0
  541. package/src/utils/interactionModality.test.ts +45 -1
  542. package/src/utils/interactionModality.ts +64 -5
  543. package/src/utils/interactionOverlay.stylex.ts +12 -3
  544. package/src/utils/plainDate.test.ts +90 -0
  545. package/src/utils/plainDate.ts +58 -3
  546. package/src/utils/presentation.test.ts +380 -0
  547. package/src/utils/presentation.ts +180 -0
  548. package/src/utils/pressFeedback.test.ts +138 -0
  549. package/src/utils/pressFeedback.ts +34 -11
  550. package/src/utils/pressPaintPriority.test.ts +80 -0
  551. package/src/utils/primaryClick.test.ts +38 -0
  552. package/src/utils/primaryClick.ts +28 -0
  553. package/src/utils/timeFormat.test.ts +204 -0
  554. package/src/utils/timeFormat.ts +314 -0
  555. package/src/utils/touchPresentation.stylex.ts +43 -0
  556. package/src/utils/touchPresentation.test.ts +140 -1
  557. package/src/utils/touchPresentation.ts +5 -3
  558. package/src/utils/unicodeDigits.test.ts +44 -0
  559. package/src/utils/unicodeDigits.ts +71 -0
@@ -0,0 +1,203 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file haptics.ts
5
+ * @input A feedback kind, or a label element a real finger will tap
6
+ * @output Exports playHaptic (vibration where the platform has it),
7
+ * attachTapHaptic (iOS Safari's tap-moment light impact) and the support
8
+ * probes
9
+ * @position Haptics of packages/core/src/gestures; the kinds and the
10
+ * vibration patterns live in the constants table (motion/uikit/gestures.ts)
11
+ *
12
+ * The web has one haptics API, `navigator.vibrate`, and iOS Safari does not
13
+ * implement it. iOS Safari does play a haptic in exactly one place: when a
14
+ * REAL tap toggles an `<input type="checkbox" switch>`, by landing on the
15
+ * switch or on a `<label>` tied to it (and during a native drag-flip of the
16
+ * switch). That haptic is UIImpactFeedbackGenerator(.light). WebKit requires
17
+ * a user gesture AND a trusted event for it: a `label.click()` from script
18
+ * does not play it (WebKit bug 309082, May 2026), so nothing can fire a
19
+ * haptic later from JavaScript, and mid-drag haptics (detent snaps, picker
20
+ * ticks) cannot exist in iOS Safari.
21
+ *
22
+ * So there are two tools:
23
+ *
24
+ * - `attachTapHaptic(label)` puts a visually hidden switch inside a label the
25
+ * control renders as its tap target: every real tap on the label plays the
26
+ * light impact at the moment of the tap. The switch itself never takes the
27
+ * touch (`pointer-events: none`; WebKit marks a switch's `touchstart`
28
+ * handled, which would stop a pan starting on it from scrolling the page),
29
+ * and its own events never reach the page.
30
+ * - `playHaptic(kind)` vibrates where `navigator.vibrate` exists (Android
31
+ * browsers) with the kind's pattern, and does nothing elsewhere.
32
+ *
33
+ * UIKit's haptics follow the system setting, not Reduce Motion, so neither
34
+ * tool consults `prefers-reduced-motion`.
35
+ */
36
+
37
+ import {
38
+ HAPTIC_VIBRATION_PATTERNS,
39
+ type HapticFeedbackKind,
40
+ } from '../motion/uikit/gestures';
41
+
42
+ /** Marks the hidden switch, so click handlers can recognize and ignore it. */
43
+ export const HAPTIC_SWITCH_ATTRIBUTE = 'data-astryx-haptic-switch';
44
+
45
+ /** Whether this browser vibrates at all. */
46
+ export function canVibrate(): boolean {
47
+ return (
48
+ typeof navigator !== 'undefined' && typeof navigator.vibrate === 'function'
49
+ );
50
+ }
51
+
52
+ /** Whether this browser knows the switch control (Safari 17.4 and later). */
53
+ export function supportsSwitchControl(doc?: Document): boolean {
54
+ const target = doc ?? (typeof document === 'undefined' ? null : document);
55
+ if (target == null) {
56
+ return false;
57
+ }
58
+ const input = target.createElement('input');
59
+ input.type = 'checkbox';
60
+ return 'switch' in input;
61
+ }
62
+
63
+ /**
64
+ * Vibrate for a feedback kind where `navigator.vibrate` exists. Returns
65
+ * whether anything was played. Never plays on iOS, which has no vibration
66
+ * API; use `attachTapHaptic` for the tap moment there.
67
+ */
68
+ export function playHaptic(kind: HapticFeedbackKind): boolean {
69
+ if (!canVibrate()) {
70
+ return false;
71
+ }
72
+ return navigator.vibrate([...HAPTIC_VIBRATION_PATTERNS[kind].value]);
73
+ }
74
+
75
+ const VISUALLY_HIDDEN =
76
+ 'position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;' +
77
+ 'overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;' +
78
+ 'opacity:0;pointer-events:none;';
79
+
80
+ let nextLabelId = 0;
81
+
82
+ /** A native disabled control, or one marked `aria-disabled`. */
83
+ function isDisabledControl(control: HTMLElement): boolean {
84
+ return (
85
+ control.matches(':disabled') ||
86
+ control.getAttribute('aria-disabled') === 'true'
87
+ );
88
+ }
89
+
90
+ /** Whether `input` is a switch checkbox, which plays the tap haptic itself. */
91
+ function isSwitch(input: HTMLInputElement): boolean {
92
+ return input.type === 'checkbox' && input.hasAttribute('switch');
93
+ }
94
+
95
+ function swallow(event: Event): void {
96
+ event.stopPropagation();
97
+ }
98
+
99
+ /**
100
+ * Make every real tap on `label` play iOS Safari's light impact, by giving it
101
+ * a hidden switch to toggle. Returns a function that removes the switch.
102
+ * Harmless where switches have no haptics.
103
+ *
104
+ * - The switch never keeps the focus: where a label's activation focuses
105
+ * its control (Chromium), the focus goes on to the label's own control,
106
+ * or back where it was, so it never lands on an `aria-hidden` element.
107
+ * - A label that already has a control keeps it: the switch takes the
108
+ * label's activation (it goes first) and hands the click on to that
109
+ * control, so the tap plays and the control still toggles. The control
110
+ * keeps its name too: while the switch is attached it is labelled by the
111
+ * label through `aria-labelledby`, unless it already names itself. A control
112
+ * that is itself a switch plays on its own, and a label tied to its
113
+ * control by `for` cannot be given a second one: both are left as they
114
+ * are.
115
+ */
116
+ export function attachTapHaptic(label: HTMLLabelElement): () => void {
117
+ const existing = label.control;
118
+ if (
119
+ (existing instanceof HTMLInputElement && isSwitch(existing)) ||
120
+ (existing != null && label.htmlFor !== '')
121
+ ) {
122
+ return () => {};
123
+ }
124
+ const doc = label.ownerDocument;
125
+ const input = doc.createElement('input');
126
+ input.type = 'checkbox';
127
+ input.setAttribute('switch', '');
128
+ input.setAttribute(HAPTIC_SWITCH_ATTRIBUTE, '');
129
+ input.setAttribute('aria-hidden', 'true');
130
+ input.tabIndex = -1;
131
+ input.style.cssText = VISUALLY_HIDDEN;
132
+ // The label's activation clicks the switch, and that click would bubble
133
+ // through the control as a second click: keep it, and the switch's
134
+ // change events, inside. A label with a control of its own gets that
135
+ // click handed on, so the control still toggles.
136
+ // A disabled control gives no feedback (spec:AST-094 FR23): its label's
137
+ // activation neither toggles the switch, so nothing plays, nor reaches
138
+ // the control.
139
+ const onClick = (event: Event) => {
140
+ event.stopPropagation();
141
+ if (existing != null && isDisabledControl(existing)) {
142
+ event.preventDefault();
143
+ return;
144
+ }
145
+ existing?.click();
146
+ };
147
+ const onFocus = (event: FocusEvent) => {
148
+ const previous = event.relatedTarget;
149
+ if (
150
+ existing instanceof HTMLElement &&
151
+ existing.tabIndex >= 0 &&
152
+ !isDisabledControl(existing)
153
+ ) {
154
+ existing.focus({preventScroll: true});
155
+ }
156
+ if (doc.activeElement !== input) {
157
+ return;
158
+ }
159
+ if (previous instanceof HTMLElement && previous.isConnected) {
160
+ previous.focus({preventScroll: true});
161
+ }
162
+ if (doc.activeElement === input) {
163
+ input.blur();
164
+ }
165
+ };
166
+ input.addEventListener('click', onClick);
167
+ input.addEventListener('change', swallow);
168
+ input.addEventListener('input', swallow);
169
+ input.addEventListener('focus', onFocus);
170
+ // The switch becomes the label's control, so the control the label named
171
+ // is named through `aria-labelledby` instead (the switch is aria-hidden,
172
+ // so the label's text is the name).
173
+ let labelledControl: HTMLElement | null = null;
174
+ let addedLabelId = false;
175
+ if (existing != null) {
176
+ label.prepend(input);
177
+ if (
178
+ existing instanceof HTMLElement &&
179
+ !existing.hasAttribute('aria-labelledby') &&
180
+ !existing.hasAttribute('aria-label')
181
+ ) {
182
+ if (label.id === '') {
183
+ label.id = `astryx-haptic-label-${(nextLabelId += 1)}`;
184
+ addedLabelId = true;
185
+ }
186
+ existing.setAttribute('aria-labelledby', label.id);
187
+ labelledControl = existing;
188
+ }
189
+ } else {
190
+ label.appendChild(input);
191
+ }
192
+ return () => {
193
+ labelledControl?.removeAttribute('aria-labelledby');
194
+ if (addedLabelId) {
195
+ label.removeAttribute('id');
196
+ }
197
+ input.removeEventListener('click', onClick);
198
+ input.removeEventListener('change', swallow);
199
+ input.removeEventListener('input', swallow);
200
+ input.removeEventListener('focus', onFocus);
201
+ input.remove();
202
+ };
203
+ }
@@ -0,0 +1,277 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file nativeScroll.ts
5
+ * @input An element a touch landed on
6
+ * @output Exports createNativeScrollProbe (the NativeScrollProbe a host gives
7
+ * each touch) and parseTouchAction
8
+ * @position DOM half of the scroll coordination in packages/core/src/gestures
9
+ *
10
+ * Answers one question for a recognizer without letting it touch the DOM:
11
+ * would the browser scroll something if this finger moved that way?
12
+ *
13
+ * The browser scrolls the innermost scroller that has room, then chains
14
+ * outward, so the answer is "yes" while ANY scroller under the touch can
15
+ * still move in that direction, with three narrowings that match the
16
+ * browser's own:
17
+ *
18
+ * - `touch-action` between the touch target and a scroller must allow the
19
+ * axis, or the browser will not pan that scroller with a finger;
20
+ * - an `overscroll-behavior` other than `auto` on the axis stops the chain at
21
+ * that scroller (a sheet body with `contain` never scrolls the page);
22
+ * - a finger moving down scrolls content toward its START (scrollTop
23
+ * decreasing), and horizontally the range follows the writing direction
24
+ * (RTL `scrollLeft` runs from minus the overflow to zero).
25
+ *
26
+ * Styles are read once per touch, on the first question; scroll offsets are
27
+ * read fresh on every question, since a native scroll moves them.
28
+ */
29
+
30
+ import type {NativeScrollProbe} from './types';
31
+
32
+ export interface AllowedPans {
33
+ readonly x: boolean;
34
+ readonly y: boolean;
35
+ }
36
+
37
+ /** Which pans a `touch-action` value lets the browser perform. */
38
+ export function parseTouchAction(value: string): AllowedPans {
39
+ const tokens = value.trim().split(/\s+/);
40
+ if (tokens.includes('auto') || tokens.includes('manipulation')) {
41
+ return {x: true, y: true};
42
+ }
43
+ if (tokens.includes('none')) {
44
+ return {x: false, y: false};
45
+ }
46
+ return {
47
+ x: tokens.some(
48
+ token =>
49
+ token === 'pan-x' || token === 'pan-left' || token === 'pan-right',
50
+ ),
51
+ y: tokens.some(
52
+ token => token === 'pan-y' || token === 'pan-up' || token === 'pan-down',
53
+ ),
54
+ };
55
+ }
56
+
57
+ interface ChainEntry {
58
+ readonly element: Element;
59
+ readonly x: boolean;
60
+ readonly y: boolean;
61
+ readonly isRtl: boolean;
62
+ readonly stopsX: boolean;
63
+ readonly stopsY: boolean;
64
+ /**
65
+ * The scroller sits beyond a `position: fixed` ancestor of the touch: it
66
+ * still scrolls under the finger, but it does not carry the touched
67
+ * element, so the element is not inside a scroll view (spec:AST-094 FR11).
68
+ */
69
+ readonly isBeyondFixed: boolean;
70
+ }
71
+
72
+ const SCROLLABLE = new Set(['auto', 'scroll', 'overlay']);
73
+ const EDGE_TOLERANCE = 1;
74
+
75
+ /**
76
+ * The node's parent in the flat tree: a slotted node's slot, then out of
77
+ * shadow roots to their hosts, so a scroller inside a shadow tree around the
78
+ * slot is found.
79
+ */
80
+ export function parentAcrossShadow(node: Node): Node | null {
81
+ const slot =
82
+ node instanceof Element || node instanceof Text ? node.assignedSlot : null;
83
+ if (slot != null) {
84
+ return slot;
85
+ }
86
+ if (node.parentNode != null) {
87
+ return node.parentNode instanceof ShadowRoot
88
+ ? node.parentNode.host
89
+ : node.parentNode;
90
+ }
91
+ return null;
92
+ }
93
+
94
+ interface Chain {
95
+ readonly entries: ChainEntry[];
96
+ /** touch-action allows some pan all the way up. */
97
+ readonly allowsPan: boolean;
98
+ }
99
+
100
+ function buildChain(target: Element): Chain {
101
+ const doc = target.ownerDocument;
102
+ const view = doc.defaultView;
103
+ if (view == null) {
104
+ return {entries: [], allowsPan: false};
105
+ }
106
+ const root = doc.scrollingElement ?? doc.documentElement;
107
+ const chain: ChainEntry[] = [];
108
+ let allowX = true;
109
+ let allowY = true;
110
+ let reachedRoot = false;
111
+ let isBeyondFixed = false;
112
+ for (
113
+ let node: Node | null = target;
114
+ node != null;
115
+ node = parentAcrossShadow(node)
116
+ ) {
117
+ if (!(node instanceof Element)) {
118
+ continue;
119
+ }
120
+ const style = view.getComputedStyle(node);
121
+ const pans = parseTouchAction(style.touchAction || 'auto');
122
+ allowX &&= pans.x;
123
+ allowY &&= pans.y;
124
+ const isRoot = node === root || node === doc.documentElement;
125
+ if (isRoot) {
126
+ reachedRoot = true;
127
+ }
128
+ const overflowX = isRoot ? rootOverflow(doc, 'x') : style.overflowX;
129
+ const overflowY = isRoot ? rootOverflow(doc, 'y') : style.overflowY;
130
+ const scrollsX = SCROLLABLE.has(overflowX);
131
+ const scrollsY = SCROLLABLE.has(overflowY);
132
+ if (scrollsX || scrollsY) {
133
+ const element = isRoot ? root : node;
134
+ const [behaviorX, behaviorY] = overscrollBehavior(style);
135
+ chain.push({
136
+ element,
137
+ x: scrollsX && allowX,
138
+ y: scrollsY && allowY,
139
+ isRtl: style.direction === 'rtl',
140
+ stopsX: behaviorX !== 'auto',
141
+ stopsY: behaviorY !== 'auto',
142
+ isBeyondFixed,
143
+ });
144
+ }
145
+ if (isRoot) {
146
+ break;
147
+ }
148
+ if (style.position === 'fixed') {
149
+ isBeyondFixed = true;
150
+ }
151
+ }
152
+ if (!reachedRoot && root instanceof Element) {
153
+ chain.push({
154
+ element: root,
155
+ x: allowX && SCROLLABLE.has(rootOverflow(doc, 'x')),
156
+ y: allowY && SCROLLABLE.has(rootOverflow(doc, 'y')),
157
+ isRtl: false,
158
+ stopsX: false,
159
+ stopsY: false,
160
+ isBeyondFixed,
161
+ });
162
+ }
163
+ return {entries: chain, allowsPan: allowX || allowY};
164
+ }
165
+
166
+ /** `overscroll-behavior` per axis, from the longhands or the shorthand. */
167
+ function overscrollBehavior(style: CSSStyleDeclaration): [string, string] {
168
+ const x = style.overscrollBehaviorX;
169
+ const y = style.overscrollBehaviorY;
170
+ if (x !== '' && y !== '' && x != null && y != null) {
171
+ return [x, y];
172
+ }
173
+ const [first = 'auto', second = first] = (
174
+ style.getPropertyValue('overscroll-behavior') || 'auto'
175
+ )
176
+ .trim()
177
+ .split(/\s+/);
178
+ return [x || first, y || second];
179
+ }
180
+
181
+ /**
182
+ * The viewport scrolls unless the root or the body clips it (a scroll lock
183
+ * sets `overflow: hidden` on one of them).
184
+ */
185
+ function rootOverflow(doc: Document, axis: 'x' | 'y'): string {
186
+ const view = doc.defaultView;
187
+ if (view == null) {
188
+ return 'hidden';
189
+ }
190
+ for (const element of [doc.documentElement, doc.body]) {
191
+ if (element == null) {
192
+ continue;
193
+ }
194
+ const style = view.getComputedStyle(element);
195
+ const value = axis === 'x' ? style.overflowX : style.overflowY;
196
+ if (value === 'hidden' || value === 'clip') {
197
+ return 'hidden';
198
+ }
199
+ }
200
+ return 'auto';
201
+ }
202
+
203
+ function canMoveY(element: Element, dy: number): boolean {
204
+ if (element.scrollHeight - element.clientHeight <= EDGE_TOLERANCE) {
205
+ return false;
206
+ }
207
+ return dy > 0
208
+ ? element.scrollTop > EDGE_TOLERANCE - 0.5
209
+ : element.scrollTop <
210
+ element.scrollHeight - element.clientHeight - EDGE_TOLERANCE + 0.5;
211
+ }
212
+
213
+ function canMoveX(entry: ChainEntry, dx: number): boolean {
214
+ const {element} = entry;
215
+ const overflow = element.scrollWidth - element.clientWidth;
216
+ if (overflow <= EDGE_TOLERANCE) {
217
+ return false;
218
+ }
219
+ const min = entry.isRtl ? -overflow : 0;
220
+ const max = entry.isRtl ? 0 : overflow;
221
+ // A finger moving right drags the content right: scrollLeft decreases.
222
+ return dx > 0
223
+ ? element.scrollLeft > min + EDGE_TOLERANCE - 0.5
224
+ : element.scrollLeft < max - EDGE_TOLERANCE + 0.5;
225
+ }
226
+
227
+ /** A probe of the native scrollers under `target`. */
228
+ export function createNativeScrollProbe(target: unknown): NativeScrollProbe {
229
+ let chain: Chain | null = null;
230
+ const getChain = (): ChainEntry[] => {
231
+ chain ??=
232
+ target instanceof Element
233
+ ? buildChain(target)
234
+ : {entries: [], allowsPan: false};
235
+ return chain.entries;
236
+ };
237
+ return {
238
+ canScroll(dx: number, dy: number): boolean {
239
+ const ax = Math.abs(dx);
240
+ const ay = Math.abs(dy);
241
+ // The minor axis of a mostly-straight drag is the hand's wobble, not
242
+ // a direction: the browser locks to the major one.
243
+ const considerX = ax > 0 && ax >= ay / 2;
244
+ const considerY = ay > 0 && ay >= ax / 2;
245
+ for (const entry of getChain()) {
246
+ if (considerY && entry.y && canMoveY(entry.element, dy)) {
247
+ return true;
248
+ }
249
+ if (considerX && entry.x && canMoveX(entry, dx)) {
250
+ return true;
251
+ }
252
+ const stops =
253
+ (!considerX || entry.stopsX) && (!considerY || entry.stopsY);
254
+ if (stops && (entry.stopsX || entry.stopsY)) {
255
+ return false;
256
+ }
257
+ }
258
+ return false;
259
+ },
260
+ get allowsNativePan(): boolean {
261
+ getChain();
262
+ return chain?.allowsPan === true;
263
+ },
264
+ get isInsideScroller(): boolean {
265
+ return getChain().some(
266
+ entry =>
267
+ !entry.isBeyondFixed &&
268
+ ((entry.y &&
269
+ entry.element.scrollHeight - entry.element.clientHeight >
270
+ EDGE_TOLERANCE) ||
271
+ (entry.x &&
272
+ entry.element.scrollWidth - entry.element.clientWidth >
273
+ EDGE_TOLERANCE)),
274
+ );
275
+ },
276
+ };
277
+ }
@@ -0,0 +1,191 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file touchAction.ts
5
+ * @input The recognizers attached to one element
6
+ * @output Exports deriveTouchAction (the element's `touch-action`),
7
+ * gestureHostStyle (the inline style a host sets for its recognizers),
8
+ * intersectTouchAction (what both a derived and a caller's value allow) and
9
+ * holdSelectionStyle (the selection and callout lock for one held press)
10
+ * @position Browser negotiation of packages/core/src/gestures, pure so a
11
+ * server render can emit the same style the client would set
12
+ *
13
+ * `touch-action` is decided when the finger lands and cannot change during
14
+ * the gesture, so it must say up front what the browser may still do: the
15
+ * intersection of what every recognizer on the element leaves it.
16
+ *
17
+ * - A tap or long press only removes double-tap zoom (`manipulation`): no
18
+ * 300 ms click delay, scrolling and pinch-zoom untouched.
19
+ * - A one-axis pan or swipe gives the browser the other axis.
20
+ * - A pan that coordinates with native scrolling (`scrollEdge`) keeps both
21
+ * axes: who gets the drag is decided once, when it begins, and a drag the
22
+ * browser began scrolling stays the browser's to the lift (`spec:AST-094`
23
+ * FR14).
24
+ * - `pinch-zoom` stays in every value unless a pinch recognizer is attached,
25
+ * so the page stays zoomable (WCAG 1.4.4).
26
+ *
27
+ * WebKit pans a drag with any vertical component even under `pan-y`, so the
28
+ * value alone does not protect a horizontal pan there: the host also cancels
29
+ * `touchmove` while a recognizer owns the touch (see GestureHost.ts).
30
+ */
31
+
32
+ import type {GestureRecognizer, NativeTouchBehavior} from './GestureRecognizer';
33
+
34
+ /** The intersection of what each touch recognizer leaves the browser. */
35
+ export function combinedNativeBehavior(
36
+ recognizers: ReadonlyArray<GestureRecognizer>,
37
+ ): NativeTouchBehavior {
38
+ let panX = true;
39
+ let panY = true;
40
+ let pinchZoom = true;
41
+ let doubleTapZoom = true;
42
+ for (const recognizer of recognizers) {
43
+ if (!recognizer.acceptsTouchInput) {
44
+ continue;
45
+ }
46
+ const behavior = recognizer.nativeTouchBehavior;
47
+ panX &&= behavior.panX;
48
+ panY &&= behavior.panY;
49
+ pinchZoom &&= behavior.pinchZoom;
50
+ doubleTapZoom &&= behavior.doubleTapZoom;
51
+ }
52
+ return {panX, panY, pinchZoom, doubleTapZoom};
53
+ }
54
+
55
+ /** The `touch-action` value for a set of recognizers, or null for none needed. */
56
+ export function deriveTouchAction(
57
+ recognizers: ReadonlyArray<GestureRecognizer>,
58
+ ): string | null {
59
+ const behavior = combinedNativeBehavior(recognizers);
60
+ if (
61
+ behavior.panX &&
62
+ behavior.panY &&
63
+ behavior.pinchZoom &&
64
+ behavior.doubleTapZoom
65
+ ) {
66
+ return null;
67
+ }
68
+ if (behavior.panX && behavior.panY && behavior.pinchZoom) {
69
+ return 'manipulation';
70
+ }
71
+ const tokens: string[] = [];
72
+ if (behavior.panX) {
73
+ tokens.push('pan-x');
74
+ }
75
+ if (behavior.panY) {
76
+ tokens.push('pan-y');
77
+ }
78
+ if (behavior.pinchZoom) {
79
+ tokens.push('pinch-zoom');
80
+ }
81
+ return tokens.length === 0 ? 'none' : tokens.join(' ');
82
+ }
83
+
84
+ export interface GestureHostStyle {
85
+ touchAction?: string;
86
+ WebkitTouchCallout?: string;
87
+ WebkitUserSelect?: string;
88
+ userSelect?: string;
89
+ }
90
+
91
+ /**
92
+ * The style a host sets for these recognizers while they are attached: the
93
+ * `touch-action`. Text selection is not locked here: a mouse, a keyboard and
94
+ * assistive technology keep selecting as they do today (spec:AST-017 FR59,
95
+ * spec:AST-094 FR41).
96
+ */
97
+ export function gestureHostStyle(
98
+ recognizers: ReadonlyArray<GestureRecognizer>,
99
+ ): GestureHostStyle {
100
+ const style: GestureHostStyle = {};
101
+ const touchAction = deriveTouchAction(recognizers);
102
+ if (touchAction != null) {
103
+ style.touchAction = touchAction;
104
+ }
105
+ return style;
106
+ }
107
+
108
+ /** What a `touch-action` value lets the browser do, as tokens. */
109
+ const ALL_PANS = [
110
+ 'pan-left',
111
+ 'pan-right',
112
+ 'pan-up',
113
+ 'pan-down',
114
+ 'pinch-zoom',
115
+ ] as const;
116
+
117
+ function touchActionTokens(value: string): Set<string> {
118
+ const parts = value
119
+ .trim()
120
+ .split(/\s+/)
121
+ .filter(part => part !== '');
122
+ const tokens = new Set<string>();
123
+ if (parts.length === 0) {
124
+ parts.push('auto');
125
+ }
126
+ for (const part of parts) {
127
+ if (part === 'auto') {
128
+ ALL_PANS.forEach(token => tokens.add(token));
129
+ tokens.add('double-tap-zoom');
130
+ } else if (part === 'manipulation') {
131
+ ALL_PANS.forEach(token => tokens.add(token));
132
+ } else if (part === 'pan-x') {
133
+ tokens.add('pan-left').add('pan-right');
134
+ } else if (part === 'pan-y') {
135
+ tokens.add('pan-up').add('pan-down');
136
+ } else if ((ALL_PANS as ReadonlyArray<string>).includes(part)) {
137
+ tokens.add(part);
138
+ }
139
+ }
140
+ return tokens;
141
+ }
142
+
143
+ /**
144
+ * The `touch-action` that allows only what both values allow: the host's
145
+ * derived value never overrides a caller's narrower one.
146
+ */
147
+ export function intersectTouchAction(derived: string, caller: string): string {
148
+ const own = touchActionTokens(caller);
149
+ const both = new Set(
150
+ [...touchActionTokens(derived)].filter(token => own.has(token)),
151
+ );
152
+ if (ALL_PANS.every(token => both.has(token))) {
153
+ return both.has('double-tap-zoom') ? 'auto' : 'manipulation';
154
+ }
155
+ const parts: string[] = [];
156
+ for (const [axis, start, end] of [
157
+ ['pan-x', 'pan-left', 'pan-right'],
158
+ ['pan-y', 'pan-up', 'pan-down'],
159
+ ] as const) {
160
+ if (both.has(start) && both.has(end)) {
161
+ parts.push(axis);
162
+ } else if (both.has(start)) {
163
+ parts.push(start);
164
+ } else if (both.has(end)) {
165
+ parts.push(end);
166
+ }
167
+ }
168
+ if (both.has('pinch-zoom')) {
169
+ parts.push('pinch-zoom');
170
+ }
171
+ return parts.length === 0 ? 'none' : parts.join(' ');
172
+ }
173
+
174
+ /**
175
+ * The lock a host applies for one press only, from touch-down to the lift or
176
+ * a cancel, on an element whose hold does something (a long press): no iOS
177
+ * callout and no text-selection start under the held finger, as a
178
+ * long-pressed row in UIKit selects no text (spec:AST-094 FR41). Null where
179
+ * the hold does nothing, so a long press there selects natively.
180
+ */
181
+ export function holdSelectionStyle(
182
+ recognizers: ReadonlyArray<GestureRecognizer>,
183
+ ): GestureHostStyle | null {
184
+ const holds = recognizers.some(
185
+ recognizer =>
186
+ recognizer.kind === 'longPress' && recognizer.acceptsTouchInput,
187
+ );
188
+ return holds
189
+ ? {WebkitTouchCallout: 'none', WebkitUserSelect: 'none', userSelect: 'none'}
190
+ : null;
191
+ }