@astryxdesign/core 0.7.7-canary.bf14580 → 0.7.7-canary.c19ca12

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 (266) hide show
  1. package/dist/AlertDialog/AlertDialog.d.ts +4 -1
  2. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  3. package/dist/AlertDialog/AlertDialog.js +36 -6
  4. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  5. package/dist/BottomSheet/BottomSheet.js +76 -6
  6. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  7. package/dist/BottomSheet/BottomSheetPanel.js +23 -4
  8. package/dist/BottomSheet/bottomSheetStack.d.ts.map +1 -1
  9. package/dist/BottomSheet/bottomSheetStack.js +5 -4
  10. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  11. package/dist/BottomSheet/useSheetGestures.js +3 -1
  12. package/dist/Button/Button.d.ts.map +1 -1
  13. package/dist/Button/Button.js +47 -3
  14. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  15. package/dist/CheckboxInput/CheckboxInput.js +13 -2
  16. package/dist/ComplexSelector/ComplexSelector.d.ts +4 -1
  17. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  18. package/dist/ComplexSelector/ComplexSelector.js +14 -4
  19. package/dist/ContextMenu/ContextMenu.js +2 -2
  20. package/dist/DateInput/MonthScroller.d.ts.map +1 -1
  21. package/dist/DateInput/MonthScroller.js +13 -27
  22. package/dist/DateInput/Wheel.d.ts.map +1 -1
  23. package/dist/DateInput/Wheel.js +22 -41
  24. package/dist/Dialog/Dialog.d.ts.map +1 -1
  25. package/dist/Dialog/Dialog.js +7 -31
  26. package/dist/Field/Field.js +2 -2
  27. package/dist/HoverCard/HoverCard.d.ts +4 -3
  28. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  29. package/dist/HoverCard/HoverCard.js +27 -2
  30. package/dist/HoverCard/useHoverCard.d.ts +5 -4
  31. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  32. package/dist/HoverCard/useHoverCard.js +17 -31
  33. package/dist/Icon/Icon.d.ts +5 -1
  34. package/dist/Icon/Icon.d.ts.map +1 -1
  35. package/dist/Icon/Icon.js +5 -2
  36. package/dist/Layer/layerAnimations.stylex.d.ts +7 -8
  37. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  38. package/dist/Layer/layerAnimations.stylex.js +25 -20
  39. package/dist/Layer/usePressPin.d.ts +17 -5
  40. package/dist/Layer/usePressPin.d.ts.map +1 -1
  41. package/dist/Layer/usePressPin.js +62 -12
  42. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  43. package/dist/Lightbox/Lightbox.js +18 -0
  44. package/dist/PowerSearch/PowerSearch.d.ts +36 -3
  45. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  46. package/dist/PowerSearch/PowerSearch.js +522 -269
  47. package/dist/PowerSearch/PowerSearchBottomSheet.d.ts +100 -0
  48. package/dist/PowerSearch/PowerSearchBottomSheet.d.ts.map +1 -0
  49. package/dist/PowerSearch/PowerSearchBottomSheet.js +1913 -0
  50. package/dist/PowerSearch/PowerSearchEditPopover.d.ts +32 -1
  51. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  52. package/dist/PowerSearch/PowerSearchEditPopover.js +17 -6
  53. package/dist/PowerSearch/PowerSearchValueEditor.d.ts +38 -3
  54. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  55. package/dist/PowerSearch/PowerSearchValueEditor.js +170 -61
  56. package/dist/PowerSearch/formatFilterValue.d.ts +7 -0
  57. package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
  58. package/dist/PowerSearch/formatFilterValue.js +11 -1
  59. package/dist/PowerSearch/formatTokenValue.d.ts +28 -0
  60. package/dist/PowerSearch/formatTokenValue.d.ts.map +1 -0
  61. package/dist/PowerSearch/formatTokenValue.js +160 -0
  62. package/dist/PowerSearch/powerSearchSheetUtils.d.ts +65 -0
  63. package/dist/PowerSearch/powerSearchSheetUtils.d.ts.map +1 -0
  64. package/dist/PowerSearch/powerSearchSheetUtils.js +168 -0
  65. package/dist/RadioList/RadioList.d.ts.map +1 -1
  66. package/dist/RadioList/RadioList.js +3 -2
  67. package/dist/RadioList/RadioListItem.d.ts +1 -1
  68. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  69. package/dist/RadioList/RadioListItem.js +5 -3
  70. package/dist/SegmentedControl/useSegmentedSelectionMotion.d.ts.map +1 -1
  71. package/dist/SegmentedControl/useSegmentedSelectionMotion.js +3 -3
  72. package/dist/Slider/sliderTouchController.js +2 -2
  73. package/dist/Spinner/Spinner.d.ts +1 -1
  74. package/dist/Spinner/Spinner.d.ts.map +1 -1
  75. package/dist/Spinner/Spinner.js +60 -58
  76. package/dist/Switch/useSwitchTouch.js +4 -4
  77. package/dist/Table/BaseTable.d.ts.map +1 -1
  78. package/dist/Table/BaseTable.js +29 -8
  79. package/dist/TextInput/TextInput.d.ts +2 -2
  80. package/dist/TextInput/TextInput.d.ts.map +1 -1
  81. package/dist/TextInput/TextInput.js +10 -7
  82. package/dist/Toast/Toast.d.ts +11 -1
  83. package/dist/Toast/Toast.d.ts.map +1 -1
  84. package/dist/Toast/Toast.js +25 -15
  85. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  86. package/dist/Toast/ToastViewport.js +94 -34
  87. package/dist/Toast/types.d.ts +9 -3
  88. package/dist/Toast/types.d.ts.map +1 -1
  89. package/dist/Token/Token.d.ts +2 -2
  90. package/dist/Token/Token.d.ts.map +1 -1
  91. package/dist/Token/Token.js +12 -4
  92. package/dist/Token/TokenNamingContext.d.ts +14 -0
  93. package/dist/Token/TokenNamingContext.d.ts.map +1 -0
  94. package/dist/Token/TokenNamingContext.js +23 -0
  95. package/dist/Tooltip/useTooltip.d.ts +19 -1
  96. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  97. package/dist/Tooltip/useTooltip.js +34 -53
  98. package/dist/Typeahead/TypeaheadItem.d.ts +2 -1
  99. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  100. package/dist/animator/createAnimator.d.ts +98 -0
  101. package/dist/animator/createAnimator.d.ts.map +1 -0
  102. package/dist/animator/createAnimator.js +259 -0
  103. package/dist/astryx.css +22 -6
  104. package/dist/gestures/GestureHost.d.ts +27 -1
  105. package/dist/gestures/GestureHost.d.ts.map +1 -1
  106. package/dist/gestures/GestureHost.js +90 -8
  107. package/dist/gestures/PressTracker.d.ts.map +1 -1
  108. package/dist/gestures/PressTracker.js +4 -3
  109. package/dist/hooks/actionGroupTracker.d.ts +4 -1
  110. package/dist/hooks/actionGroupTracker.d.ts.map +1 -1
  111. package/dist/hooks/actionGroupTracker.js +13 -3
  112. package/dist/hooks/index.d.ts +1 -0
  113. package/dist/hooks/index.d.ts.map +1 -1
  114. package/dist/hooks/index.js +1 -0
  115. package/dist/hooks/pressGroupContext.d.ts +6 -0
  116. package/dist/hooks/pressGroupContext.d.ts.map +1 -0
  117. package/dist/hooks/pressGroupContext.js +20 -0
  118. package/dist/hooks/useActionGroupTracking.d.ts +53 -0
  119. package/dist/hooks/useActionGroupTracking.d.ts.map +1 -0
  120. package/dist/hooks/useActionGroupTracking.js +174 -0
  121. package/dist/hooks/useAnimator.d.ts +41 -0
  122. package/dist/hooks/useAnimator.d.ts.map +1 -0
  123. package/dist/hooks/useAnimator.js +70 -0
  124. package/dist/hooks/useAttachment.d.ts +1 -1
  125. package/dist/hooks/useAttachment.js +1 -1
  126. package/dist/hooks/useInputStatusIcon.d.ts +1 -1
  127. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -1
  128. package/dist/hooks/useInputStatusIcon.js +2 -2
  129. package/dist/hooks/useLongPress.d.ts +6 -2
  130. package/dist/hooks/useLongPress.d.ts.map +1 -1
  131. package/dist/hooks/useLongPress.js +95 -15
  132. package/dist/hooks/useMenuPress.d.ts.map +1 -1
  133. package/dist/hooks/useMenuPress.js +4 -2
  134. package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.d.ts +8 -0
  135. package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.d.ts.map +1 -0
  136. package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.js +42 -0
  137. package/dist/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.d.ts.map +1 -1
  138. package/dist/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.js +6 -0
  139. package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.d.ts +8 -0
  140. package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.d.ts.map +1 -0
  141. package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.js +14 -0
  142. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  143. package/dist/i18n/generated-locales/en.generated.js +30 -0
  144. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  145. package/dist/i18n/generated-locales/pseudo.generated.js +30 -0
  146. package/dist/index.d.ts +1 -0
  147. package/dist/index.d.ts.map +1 -1
  148. package/dist/index.js +4 -0
  149. package/dist/internal/indicatorPress.stylex.d.ts +20 -0
  150. package/dist/internal/indicatorPress.stylex.d.ts.map +1 -0
  151. package/dist/internal/indicatorPress.stylex.js +26 -0
  152. package/dist/motion/animator.d.ts +14 -14
  153. package/dist/motion/animator.d.ts.map +1 -1
  154. package/dist/motion/animator.js +41 -18
  155. package/dist/motion/reducedMotion.d.ts +24 -0
  156. package/dist/motion/reducedMotion.d.ts.map +1 -1
  157. package/dist/motion/reducedMotion.js +78 -1
  158. package/dist/motion/uikit/alert.d.ts +41 -0
  159. package/dist/motion/uikit/alert.d.ts.map +1 -0
  160. package/dist/motion/uikit/alert.js +43 -0
  161. package/dist/privateAttributes/pressGroupItem.stylex.d.ts +15 -0
  162. package/dist/privateAttributes/pressGroupItem.stylex.d.ts.map +1 -0
  163. package/dist/privateAttributes/pressGroupItem.stylex.js +23 -0
  164. package/dist/utils/clickGuard.d.ts +8 -1
  165. package/dist/utils/clickGuard.d.ts.map +1 -1
  166. package/dist/utils/clickGuard.js +24 -1
  167. package/dist/utils/dateParser.d.ts.map +1 -1
  168. package/dist/utils/dateParser.js +12 -0
  169. package/dist/utils/focusReturn.d.ts +15 -0
  170. package/dist/utils/focusReturn.d.ts.map +1 -1
  171. package/dist/utils/focusReturn.js +28 -0
  172. package/dist/utils/interactionOverlay.stylex.d.ts +3 -2
  173. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  174. package/dist/utils/interactionOverlay.stylex.js +3 -2
  175. package/dist/utils/pressFeedback.d.ts.map +1 -1
  176. package/dist/utils/pressFeedback.js +19 -6
  177. package/dist/utils/triggerFocus.d.ts +36 -0
  178. package/dist/utils/triggerFocus.d.ts.map +1 -0
  179. package/dist/utils/triggerFocus.js +65 -0
  180. package/locales/en.json +120 -0
  181. package/locales/pseudo.json +90 -0
  182. package/package.json +9 -4
  183. package/src/AlertDialog/AlertDialog.doc.mjs +2 -2
  184. package/src/AlertDialog/AlertDialog.tsx +37 -5
  185. package/src/BottomSheet/BottomSheet.tsx +85 -5
  186. package/src/BottomSheet/BottomSheetPanel.tsx +40 -5
  187. package/src/BottomSheet/bottomSheetStack.ts +8 -4
  188. package/src/BottomSheet/useSheetGestures.ts +3 -1
  189. package/src/Button/Button.tsx +63 -5
  190. package/src/CheckboxInput/CheckboxInput.doc.mjs +18 -0
  191. package/src/CheckboxInput/CheckboxInput.tsx +36 -9
  192. package/src/ComplexSelector/ComplexSelector.tsx +20 -5
  193. package/src/ContextMenu/ContextMenu.tsx +2 -2
  194. package/src/DateInput/MonthScroller.tsx +18 -22
  195. package/src/DateInput/Wheel.tsx +30 -32
  196. package/src/Dialog/Dialog.tsx +10 -31
  197. package/src/Field/Field.tsx +2 -2
  198. package/src/HoverCard/HoverCard.doc.mjs +4 -2
  199. package/src/HoverCard/HoverCard.tsx +35 -4
  200. package/src/HoverCard/useHoverCard.doc.mjs +178 -36
  201. package/src/HoverCard/useHoverCard.tsx +46 -52
  202. package/src/Icon/Icon.doc.mjs +49 -8
  203. package/src/Icon/Icon.tsx +15 -7
  204. package/src/Layer/layerAnimations.stylex.ts +24 -16
  205. package/src/Layer/usePressPin.ts +81 -15
  206. package/src/Lightbox/Lightbox.tsx +18 -0
  207. package/src/PowerSearch/PowerSearch.doc.mjs +54 -5
  208. package/src/PowerSearch/PowerSearch.tsx +734 -288
  209. package/src/PowerSearch/PowerSearchBottomSheet.tsx +2515 -0
  210. package/src/PowerSearch/PowerSearchEditPopover.tsx +28 -6
  211. package/src/PowerSearch/PowerSearchValueEditor.tsx +203 -38
  212. package/src/PowerSearch/formatFilterValue.ts +18 -1
  213. package/src/PowerSearch/formatTokenValue.ts +215 -0
  214. package/src/PowerSearch/powerSearchSheetUtils.ts +224 -0
  215. package/src/RadioList/RadioList.tsx +3 -2
  216. package/src/RadioList/RadioListItem.tsx +5 -3
  217. package/src/SegmentedControl/useSegmentedSelectionMotion.ts +6 -3
  218. package/src/Slider/sliderTouchController.ts +2 -2
  219. package/src/Spinner/Spinner.tsx +77 -63
  220. package/src/Switch/useSwitchTouch.ts +4 -4
  221. package/src/Table/BaseTable.tsx +46 -12
  222. package/src/TextInput/TextInput.tsx +11 -10
  223. package/src/Toast/Toast.doc.mjs +4 -3
  224. package/src/Toast/Toast.tsx +52 -32
  225. package/src/Toast/ToastViewport.tsx +121 -35
  226. package/src/Toast/types.ts +9 -3
  227. package/src/Token/Token.tsx +13 -4
  228. package/src/Token/TokenNamingContext.ts +28 -0
  229. package/src/Tooltip/useTooltip.doc.mjs +24 -0
  230. package/src/Tooltip/useTooltip.tsx +61 -56
  231. package/src/Typeahead/TypeaheadItem.doc.mjs +6 -3
  232. package/src/Typeahead/TypeaheadItem.tsx +2 -1
  233. package/src/animator/createAnimator.doc.mjs +153 -0
  234. package/src/animator/createAnimator.ts +324 -0
  235. package/src/gestures/GestureHost.ts +105 -10
  236. package/src/gestures/PressTracker.ts +4 -3
  237. package/src/gestures/README.md +7 -1
  238. package/src/hooks/actionGroupTracker.ts +20 -5
  239. package/src/hooks/index.ts +2 -0
  240. package/src/hooks/pressGroupContext.ts +21 -0
  241. package/src/hooks/useActionGroupTracking.ts +224 -0
  242. package/src/hooks/useAnimator.doc.mjs +109 -0
  243. package/src/hooks/useAnimator.ts +67 -0
  244. package/src/hooks/useAttachment.ts +1 -1
  245. package/src/hooks/useInputStatusIcon.tsx +3 -2
  246. package/src/hooks/useLongPress.doc.mjs +2 -2
  247. package/src/hooks/useLongPress.ts +136 -15
  248. package/src/hooks/useMenuPress.ts +8 -2
  249. package/src/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.ts +43 -0
  250. package/src/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.ts +6 -0
  251. package/src/i18n/generated-english/PowerSearch/formatTokenValue.generated.ts +15 -0
  252. package/src/i18n/generated-locales/en.generated.ts +41 -0
  253. package/src/i18n/generated-locales/pseudo.generated.ts +42 -0
  254. package/src/index.ts +13 -0
  255. package/src/internal/indicatorPress.stylex.ts +24 -0
  256. package/src/motion/README.md +8 -5
  257. package/src/motion/animator.ts +61 -29
  258. package/src/motion/reducedMotion.ts +79 -1
  259. package/src/motion/uikit/alert.ts +43 -0
  260. package/src/privateAttributes/pressGroupItem.stylex.ts +23 -0
  261. package/src/utils/clickGuard.ts +37 -1
  262. package/src/utils/dateParser.ts +16 -0
  263. package/src/utils/focusReturn.ts +30 -0
  264. package/src/utils/interactionOverlay.stylex.ts +3 -2
  265. package/src/utils/pressFeedback.ts +19 -6
  266. package/src/utils/triggerFocus.ts +80 -0
@@ -0,0 +1,24 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file indicatorPress.stylex.ts
5
+ * @input Uses StyleX
6
+ * @output Exports `indicatorPressScope`, the marker of a first-party
7
+ * indicator that paints its own press
8
+ * @position Internal entry for Astryx's own replacement indicators;
9
+ * CheckboxInput reads it
10
+ */
11
+
12
+ import * as stylex from '@stylexjs/stylex';
13
+
14
+ /**
15
+ * Marks a first-party replacement indicator that paints its own press, for
16
+ * a shape that CheckboxInput's rounded-square press layer would tint past,
17
+ * such as a circle. CheckboxInput leaves that layer off over an indicator
18
+ * with this marker.
19
+ *
20
+ * @internal Not public API: the touch press reaches the indicator through a
21
+ * private property.
22
+ */
23
+ export const indicatorPressScope: ReturnType<typeof stylex.defineMarker> =
24
+ stylex.defineMarker();
@@ -19,6 +19,7 @@ roles through `resolveMotion`.
19
19
  | `uikit/scroll.ts` | UIScrollView: deceleration rates, rubber band, bounce-back spring, `delaysContentTouches`, scroll to top. |
20
20
  | `uikit/timing.ts` | Non-spring clocks and curves: press highlight, Core Animation's named curves, keyboard, Reduced Motion cross-fade. |
21
21
  | `uikit/paging.ts` | A paging UIScrollView: the integrator that settles a release on its page, the page rule, the catch, `setContentOffset(animated:)`. |
22
+ | `uikit/alert.ts` | UIAlertController: how a finger is tracked across the actions (highlight onset and bounds). |
22
23
  | `uikit/navigation.ts` | UINavigationController: push velocity, parallax, dimming, transition clock, interactive pop finish rule. |
23
24
  | `uikit/progressBar.ts` | UIProgressView's fill: the whole-track duration and curve, its minimum length and empty alpha; the reduced-motion pulse (three roles). |
24
25
  | `uikit/gestures.ts` | Gesture recognizer thresholds, the velocity filter and haptic stand-ins, as `{value, unit}` entries (sources in JSDoc). |
@@ -64,7 +65,7 @@ the timings that decide who gets a touch are not roles.
64
65
  ## Animator
65
66
 
66
67
  ```
67
- import {createAnimator} from '../motion/animator';
68
+ import {createMotionAnimator} from '../motion/animator';
68
69
  import {coast} from '../motion/coast';
69
70
  import {solveSpringTowardRamp, toSpring} from '../motion/spring';
70
71
  import {resolveMotion} from '../motion/resolveMotion';
@@ -73,7 +74,7 @@ import {registerSpringsTable, springs} from '../motion/uikit';
73
74
  // Once, so the springs are registered as their roles:
74
75
  registerSpringsTable();
75
76
 
76
- const animator = createAnimator(sheet);
77
+ const animator = createMotionAnimator(sheet);
77
78
  // The animator plays a motion role (spec:AST-094 FR4), its table value, as the
78
79
  // per-role tables stay internal (DEC-15); math on a spring reads the role with
79
80
  // resolveMotion.
@@ -138,8 +139,10 @@ await handle.finished; // true if it ran to the end, false if interrupted
138
139
  presented call's second frame) runs on over two frames, the start moves to
139
140
  the call's first read after it, with every call of that task. Everything
140
141
  the call starts shares that start
141
- (`handle.started`); a retarget keeps an explicit one, and `{at}` puts
142
- several animators on one clock. `{start: 'commit'}` lets the browser date
142
+ (`handle.started`); a retarget of a curve that still moves keeps an
143
+ explicit one, while one on a tail that already shows as at rest (within two
144
+ sample tolerances of its target, and slower than one a frame) counts as a
145
+ call from rest. `{at}` puts several animators on one clock. `{start: 'commit'}` lets the browser date
143
146
  a retarget too; `{start: 'presented'}` waits two frames, until the
144
147
  element at rest has been presented. `handle.curve(property)` is the curve
145
148
  a call plays, for a companion to follow. `{additive: true}` retargets the
@@ -168,7 +171,7 @@ await handle.finished; // true if it ran to the end, false if interrupted
168
171
  - `animatorFor(element)` returns the one animator for an element, so code
169
172
  that animates it from several places (a swipe on x, a removal on y)
170
173
  composes in one `translate` track.
171
- - `createAnimator(dialog, {pseudoElement: '::backdrop'})` drives a
174
+ - `createMotionAnimator(dialog, {pseudoElement: '::backdrop'})` drives a
172
175
  pseudo-element; with no inline style to hold it, a static filling animation
173
176
  holds its end value until the next animation replaces it, while the
174
177
  pseudo-element exists. A dialog's `::backdrop` is made again each time the
@@ -4,11 +4,12 @@
4
4
  * @file animator.ts
5
5
  * @input An element, target values, and a spring, momentum curves (coast.ts)
6
6
  * or any closed-form curve
7
- * @output Exports `createAnimator`: a compositor animator for translate,
8
- * rotate, scale and opacity, with presentation values and velocities read
9
- * from the analytic curves, velocity-preserving retargeting, freeze-in-place
10
- * `stop`, completion promises, a shared clock, and UIKit's Reduced Motion
11
- * substitution; plus its option, handle and transition types
7
+ * @output Exports `createMotionAnimator`, a compositor animator for
8
+ * translate, rotate, scale and opacity, and its option, handle and
9
+ * transition types. It reads presentation values and velocities from the
10
+ * analytic curves. It retargets with the velocity kept, freezes in place on
11
+ * `stop`, and gives completion promises, a shared clock, and UIKit's
12
+ * Reduced Motion substitution.
12
13
  * @position Motion foundation (DOM). Internal to core: components use it for
13
14
  * every touch-presentation animation; not exported from the package entry
14
15
  *
@@ -244,18 +245,18 @@ export interface AnimateOptions {
244
245
  crossfadeAs?: 'entrance' | 'exit';
245
246
  /**
246
247
  * When the call's animations start. By default a call from rest starts
247
- * when the browser commits it, and a call that continues motion on screen
248
- * (a retarget) starts now, from the value on screen. Ignored with `at`.
248
+ * when the browser commits it. A call that continues motion on screen (a
249
+ * retarget) starts now, from the value on screen. A curve whose tail
250
+ * already shows as at rest counts as rest. Ignored with `at`.
249
251
  * - `'commit'`: a call that continues motion also starts when the browser
250
- * commits it, dated by the browser rather than by the main thread, as a
251
- * call from rest is.
252
- * - `'presented'`: a call from rest leaves its element at rest and creates
253
- * its animations two frames later, once the frame that carries the
254
- * element at rest has been presented; they then start at their commit. A
255
- * busy UI process delays the motion instead of skipping into it, for two
256
- * frames more before an idle start. Any other change to the animator
257
- * before then creates the animations first, in call order. A call that
258
- * continues motion on screen (a retarget) starts now, as by default.
252
+ * commits it. The browser dates it, as it dates a call from rest.
253
+ * - `'presented'`: a call from rest leaves its element at rest. Its
254
+ * animations are created two frames later, once the frame with the
255
+ * element at rest has been presented. They then start at their commit,
256
+ * so a busy UI process delays the motion instead of skipping into it.
257
+ * Any other change to the animator before then creates the animations
258
+ * first, in call order. A call that continues motion on screen starts
259
+ * now, as by default.
259
260
  */
260
261
  start?: 'commit' | 'presented';
261
262
  /**
@@ -297,7 +298,7 @@ export interface MotionCurve {
297
298
  }
298
299
 
299
300
  /**
300
- * Options of `createAnimator`.
301
+ * Options of `createMotionAnimator`.
301
302
  */
302
303
  /** How `set` puts values in place. */
303
304
  export interface SetOptions {
@@ -652,6 +653,18 @@ const SAMPLE_TOLERANCE: Record<MotionProperty, number> = {
652
653
  /** Keyframes start on this grid (ms) and are split where the curve needs it. */
653
654
  const SAMPLE_GRID_MS = 1000 / 30;
654
655
 
656
+ /**
657
+ * A curve within this many sample tolerances of its target can show as at
658
+ * rest. A call on it then starts as one from rest does, at the browser's start.
659
+ */
660
+ const REST_DISTANCE_TOLERANCES = 2;
661
+
662
+ /**
663
+ * A curve shows as at rest only while it moves at most one `SAMPLE_TOLERANCE`
664
+ * a frame at this rate.
665
+ */
666
+ const REST_FRAMES_PER_SECOND = 60;
667
+
655
668
  /** A curve that has not settled by then (an undamped spring) snaps. */
656
669
  const MAX_DURATION_MS = 30_000;
657
670
 
@@ -803,7 +816,7 @@ export function animatorFor(
803
816
  const key = options.pseudoElement ?? '';
804
817
  let animator = byPseudo.get(key);
805
818
  if (animator == null) {
806
- animator = createAnimator(element, options);
819
+ animator = createMotionAnimator(element, options);
807
820
  byPseudo.set(key, animator);
808
821
  }
809
822
  return animator;
@@ -920,7 +933,7 @@ const REDUCED_TRANSITION: SpringTransition = {
920
933
  *
921
934
  * @example
922
935
  * ```
923
- * const animator = createAnimator(sheet);
936
+ * const animator = createMotionAnimator(sheet);
924
937
  * // finger lifts at 1200 px/s heading for the detent at 0 (a component
925
938
  * // passes the spring of its motion role):
926
939
  * animator.animate({y: 0}, {spring: {response: 0.45, dampingRatio: 0.9}, velocity: {y: 1200}});
@@ -929,7 +942,7 @@ const REDUCED_TRANSITION: SpringTransition = {
929
942
  * animator.stop(['y']);
930
943
  * ```
931
944
  */
932
- export function createAnimator(
945
+ export function createMotionAnimator(
933
946
  element: HTMLElement | null,
934
947
  options: AnimatorOptions = {},
935
948
  ): Animator {
@@ -1165,6 +1178,22 @@ export function createAnimator(
1165
1178
  return curveAt(property, time);
1166
1179
  };
1167
1180
 
1181
+ /**
1182
+ * Whether a curve's tail already shows as at rest at `time`. Under a
1183
+ * starved main thread the browser commits a call late. A call that starts
1184
+ * at its own time then skips that far into its curve. One that starts at
1185
+ * the commit holds an element that hardly moves.
1186
+ */
1187
+ const showsAtRest = (
1188
+ property: MotionProperty,
1189
+ segment: Segment,
1190
+ time: number,
1191
+ ): boolean =>
1192
+ Math.abs(valueAt(property, time) - segment.target) <=
1193
+ REST_DISTANCE_TOLERANCES * SAMPLE_TOLERANCE[property] &&
1194
+ Math.abs(velocityAt(property, time)) <=
1195
+ SAMPLE_TOLERANCE[property] * REST_FRAMES_PER_SECOND;
1196
+
1168
1197
  const velocityAt = (property: MotionProperty, time: number): number => {
1169
1198
  const held = segments.get(property);
1170
1199
  if (held != null && isHeld(held)) {
@@ -2033,10 +2062,10 @@ export function createAnimator(
2033
2062
  // -- starting from rest ---------------------------------------------------
2034
2063
 
2035
2064
  /**
2036
- * A call starts from rest when nothing on the channels it drives is moving
2037
- * (a curve still waiting for its first frame counts as rest) and nothing
2038
- * pins its clock (`at`). Returns its start group, or null for an explicit
2039
- * start.
2065
+ * A call starts from rest when nothing on the channels it drives moves on
2066
+ * screen and nothing pins its clock (`at`). A curve still waiting for its
2067
+ * first frame counts as rest, as does one whose tail already shows as at
2068
+ * rest. Returns its start group, or null for an explicit start.
2040
2069
  */
2041
2070
  const beginGroup = (
2042
2071
  time: number,
@@ -2070,10 +2099,12 @@ export function createAnimator(
2070
2099
  }
2071
2100
  return null;
2072
2101
  }
2073
- // A call that continues motion starts now unless it asks for the
2074
- // browser's start (`commit`). `presented` asks only for a call from rest:
2075
- // an element still moving is not at rest, so a retarget starts now, from
2076
- // the value and velocity on screen (spec:AST-094 FR6).
2102
+ // A call that continues motion starts now, unless it asks for the
2103
+ // browser's start (`commit`). `presented` asks only for a call from rest.
2104
+ // A curve that still moves is not at rest, so a call on it starts now,
2105
+ // from the value and velocity on screen. A tail that shows as at rest
2106
+ // counts as rest, so a call on it waits, under `presented` for its two
2107
+ // frames.
2077
2108
  for (const channel of start === 'commit' ? [] : channels) {
2078
2109
  for (const property of CHANNELS[channel]) {
2079
2110
  const segment = segments.get(property);
@@ -2084,7 +2115,8 @@ export function createAnimator(
2084
2115
  time < endOf(property, segment) &&
2085
2116
  (segment.group == null ||
2086
2117
  segment.group.resolved ||
2087
- shownSince(property, segment) != null)
2118
+ shownSince(property, segment) != null) &&
2119
+ !showsAtRest(property, segment, time)
2088
2120
  ) {
2089
2121
  return null;
2090
2122
  }
@@ -4,7 +4,7 @@
4
4
  * @file reducedMotion.ts
5
5
  * @input The Reduced Motion preference, the primary pointer, the motion tables
6
6
  * @output reducedMotionFade (the fade the animator would play, for motion it
7
- * does not drive) and the two media queries a StyleX transition spells
7
+ * does not drive), playReducedMotionDip, and the queries a transition spells
8
8
  * @position The one Reduced Motion policy (design:reduced-motion) for code
9
9
  * beside the animator: a raw Web Animations fade, a CSS transition
10
10
  *
@@ -102,3 +102,81 @@ export function reducedMotionFade(
102
102
  easing: `cubic-bezier(${curve.join(', ')})`,
103
103
  };
104
104
  }
105
+
106
+ /** A running Reduced Motion dip. */
107
+ export interface ReducedMotionDip {
108
+ /** Stops the dip. A change that has not landed yet never lands. */
109
+ cancel(): void;
110
+ }
111
+
112
+ /**
113
+ * Plays `fade` on `element` as a dip, and runs `land` at its middle: the
114
+ * fade-out, the change, then the fade-in. The fade-out holds at 0 until `land`
115
+ * has run, and the fade-in starts only after it. So the change never shows at
116
+ * full opacity, however late the main thread runs. A timer at the middle can
117
+ * fire after the fade-in has begun.
118
+ *
119
+ * `land` must apply the change before it returns: the fade-in starts right
120
+ * after it. `from` is the opacity the fade-out starts at: a leaving element
121
+ * fades out from where it is.
122
+ *
123
+ * @example
124
+ * ```
125
+ * const dip = playReducedMotionDip(scroller, fade, () => scroller.scrollTo({top}));
126
+ * // A newer change replaces it:
127
+ * dip.cancel();
128
+ * ```
129
+ */
130
+ export function playReducedMotionDip(
131
+ element: Element,
132
+ fade: ReducedMotionFade,
133
+ land: () => void,
134
+ from = 1,
135
+ ): ReducedMotionDip {
136
+ const half = fade.duration / 2;
137
+ let isCancelled = false;
138
+ let fadeIn: Animation | null = null;
139
+ const fadeOut = element.animate(
140
+ [{opacity: from, easing: fade.easing}, {opacity: 0}],
141
+ {duration: half, fill: 'forwards'},
142
+ );
143
+ fadeOut.finished.then(
144
+ () => {
145
+ if (isCancelled) {
146
+ return;
147
+ }
148
+ land();
149
+ // The fade-in holds 0 until it starts: after a long stall, its start can
150
+ // come after the frame that runs this.
151
+ fadeIn = element.animate(
152
+ [{opacity: 0, easing: fade.easing}, {opacity: 1}],
153
+ {duration: half, fill: 'backwards'},
154
+ );
155
+ // The fade-in keeps the dip's own schedule: it starts where the fade-out
156
+ // ended, so the dip keeps its length. The clock is read after the
157
+ // landing, because a frame's timestamp does not count the landing's own
158
+ // work. After a long stall, half of the fade-in still plays.
159
+ const fadeOutEnd = endTime(fadeOut, half);
160
+ if (fadeOutEnd != null) {
161
+ fadeIn.startTime = Math.max(fadeOutEnd, performance.now() - half / 2);
162
+ }
163
+ // The fade-in now holds the opacity, so the held 0 can go.
164
+ fadeOut.cancel();
165
+ },
166
+ // A cancelled fade-out rejects `finished`: nothing lands.
167
+ () => {},
168
+ );
169
+ return {
170
+ cancel() {
171
+ isCancelled = true;
172
+ fadeOut.cancel();
173
+ fadeIn?.cancel();
174
+ },
175
+ };
176
+ }
177
+
178
+ /** When `animation` was due to end, on its timeline (ms), if it has started. */
179
+ function endTime(animation: Animation, duration: number): number | null {
180
+ const start = animation.startTime;
181
+ return typeof start === 'number' ? start + duration : null;
182
+ }
@@ -0,0 +1,43 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file alert.ts
5
+ * @input Nothing; data only
6
+ * @output Exports `alertController`: how UIAlertController (`.alert`) tracks
7
+ * a finger across its actions
8
+ * @position The alert domain of the UIKit constants table (motion/uikit);
9
+ * its springs are `springs.alertPresent` and `springs.alertDismiss`
10
+ *
11
+ * Every value was read from UIKit on the iOS 26.2 simulator. A host app
12
+ * presented `UIAlertController(.alert)` with two and with three actions.
13
+ * Real HID touches drove it, and the action views' highlight was sampled
14
+ * every frame. Lengths are in pt (CSS px), durations in ms.
15
+ *
16
+ * Every leaf carries one or more `@source`; GUESS values also carry the
17
+ * `@measure` task that will confirm or replace them. uikit.test.ts enforces
18
+ * both.
19
+ *
20
+ * SYNC: When modified, update /packages/core/src/motion/MEASUREMENTS.md
21
+ */
22
+
23
+ export const alertController = {
24
+ actions: {
25
+ /**
26
+ * How long after touch-down the touched action highlights: none, it is
27
+ * highlighted in the touch-down frame (a tap and a 300 ms hold alike).
28
+ * @source measured: UIKit lab (injected host app), iOS 26.2 simulator,
29
+ * iPhone 17 Pro, per-frame highlight of the action views under real HID
30
+ * touches, Oct 23 2026
31
+ */
32
+ highlightOnsetMs: 0,
33
+ /**
34
+ * How far outside an action the finger may go and keep it highlighted:
35
+ * none. The highlight cleared 1 pt below the action, and a lift there ran
36
+ * nothing. UIButton keeps 70 pt; an alert action does not.
37
+ * @source measured: UIKit lab (injected host app), iOS 26.2 simulator,
38
+ * iPhone 17 Pro, per-frame highlight of the action views under real HID
39
+ * touches, Oct 23 2026
40
+ */
41
+ highlightExpansion: 0,
42
+ },
43
+ } as const;
@@ -0,0 +1,23 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ /**
3
+ * @file privateAttributes/pressGroupItem.stylex.ts
4
+ * @input Nothing: the private press-group-item attribute
5
+ * @output pressGroupItemProps, which marks an action a group's tracker
6
+ * follows, and isTrackedByGroup, which reads the mark
7
+ * @position Owner module of the private press-group-item attribute: the only
8
+ * file that writes its name. AlertDialog marks its actions, and the press
9
+ * controller (utils/pressFeedback.ts) leaves their press to the group's
10
+ * tracker. No package entry exports it.
11
+ */
12
+
13
+ const PRESS_GROUP_ITEM = 'data-astryx-press-group-item';
14
+
15
+ /** Spread on each action that a group's tracker follows. */
16
+ export const pressGroupItemProps: Readonly<Record<string, string>> = {
17
+ [PRESS_GROUP_ITEM]: '',
18
+ };
19
+
20
+ /** Whether a group's tracker paints the press on `element` or an ancestor. */
21
+ export function isTrackedByGroup(element: Element): boolean {
22
+ return element.closest(`[${PRESS_GROUP_ITEM}]`) != null;
23
+ }
@@ -26,6 +26,11 @@
26
26
  // `touchend`, aimed at whatever is under the finger by then. They go with
27
27
  // the click, the next `pointerdown` or the window, as the guard does.
28
28
  //
29
+ // The menu press model's arm at a finger's release also cancels that touch's
30
+ // `touchend`, so the browser creates no click and no compatibility mouse
31
+ // events. A busy main thread can otherwise hold that click past the window,
32
+ // when a drill-in has put another row under the finger.
33
+ //
29
34
  // Some clicks always pass:
30
35
  // - a click with `detail` 0: a keyboard's, a screen reader's, or one that the
31
36
  // page dispatches;
@@ -56,6 +61,13 @@ export interface ClickGuardOptions {
56
61
  * @default false
57
62
  */
58
63
  readonly takesLateMouse?: boolean;
64
+ /**
65
+ * The menu press model's arm at a finger's release: the guard also cancels
66
+ * that touch's `touchend`, so the browser creates no click for it. A
67
+ * cancelled `touchend` disarms the guard.
68
+ * @default false
69
+ */
70
+ readonly cancelsTouchEnd?: boolean;
59
71
  }
60
72
 
61
73
  interface Guard {
@@ -89,7 +101,11 @@ function listenForClicks(view: Window): () => void {
89
101
  /** Swallow the browser's next click on `doc` (see the file header). */
90
102
  export function armClickGuard(
91
103
  doc: Document,
92
- {staysInstalled = false, takesLateMouse = false}: ClickGuardOptions = {},
104
+ {
105
+ staysInstalled = false,
106
+ takesLateMouse = false,
107
+ cancelsTouchEnd = false,
108
+ }: ClickGuardOptions = {},
93
109
  ): void {
94
110
  const view = doc.defaultView;
95
111
  if (view == null) {
@@ -116,6 +132,26 @@ export function armClickGuard(
116
132
  );
117
133
  }
118
134
  }
135
+ if (cancelsTouchEnd) {
136
+ // A touch sends its pointerup before its touchend, so the next touchend is
137
+ // that touch's own. The next pointerdown disarms the guard first.
138
+ const stopTouchEnd = subscribeInput(
139
+ view,
140
+ 'touchend',
141
+ event => {
142
+ stopTouchEnd();
143
+ // A touchend that the browser took for a scroll is not cancelable.
144
+ // The guard stays armed for a click that may follow it.
145
+ if (event.cancelable) {
146
+ event.preventDefault();
147
+ // No click follows a cancelled touchend.
148
+ disarmClickGuard(doc);
149
+ }
150
+ },
151
+ {cancels: true},
152
+ );
153
+ guard.unsubscribers.push(stopTouchEnd);
154
+ }
119
155
  guard.timer = setTimeout(() => disarmClickGuard(doc), CLICK_GUARD_WINDOW_MS);
120
156
  }
121
157
 
@@ -32,6 +32,11 @@ export function isLocaleDayFirst(locale: Locale = 'en'): boolean {
32
32
  return dayIndex < monthIndex;
33
33
  }
34
34
 
35
+ // Built at run time: a Unicode property escape in a literal fails a source
36
+ // build whose Babel lacks the Unicode data.
37
+ const LETTER_FREE = new RegExp('^[^\\p{L}]+$', 'u');
38
+ const YEAR_FIRST = new RegExp('^\\d{4}[^\\p{L}\\d]', 'u');
39
+
35
40
  /**
36
41
  * Parses user input into a PlainDate.
37
42
  *
@@ -138,6 +143,17 @@ export function parseDateInput(
138
143
  if (/^\d+$/.test(trimmed)) {
139
144
  return null;
140
145
  }
146
+ // In a day-first locale, letter-free text that no form above matched is not
147
+ // a date. Native `Date` reads `a/b/c` month first. It also takes a year of
148
+ // one to three digits. So "12/01/2" gives a wrong date. A four-digit year
149
+ // written first is not ambiguous.
150
+ if (
151
+ isLocaleDayFirst(locale) &&
152
+ LETTER_FREE.test(trimmed) &&
153
+ !YEAR_FIRST.test(trimmed)
154
+ ) {
155
+ return null;
156
+ }
141
157
 
142
158
  const parsed = new Date(trimmed);
143
159
  if (!isNaN(parsed.getTime())) {
@@ -151,3 +151,33 @@ export function focusForLastInput(
151
151
  }
152
152
  element.focus({...options, focusVisible: false});
153
153
  }
154
+
155
+ /**
156
+ * Prepares the focus return for an open surface that may be removed while
157
+ * open (rendered only while open, or removed with its view). Removing a
158
+ * `<dialog>` runs no close and no browser focus return. Call this while the
159
+ * surface is mounted: it reads the frame that hosts the surface now, because a
160
+ * removed frame's document has no window. Call the returned function from the
161
+ * effect cleanup with the opener.
162
+ *
163
+ * Once the commit has finished, focus moves to the opener only when the
164
+ * surface really left the document (StrictMode's simulated unmount keeps it),
165
+ * the opener is still on the page, and the removal lost focus, both in the
166
+ * surface's document and in the document that hosts its frame. A control the
167
+ * user pressed outside the surface, or outside its frame, keeps its focus.
168
+ */
169
+ export function prepareFocusReturnOnRemoval(
170
+ surface: Element,
171
+ ): (opener: Element | null) => void {
172
+ const host = surface.ownerDocument.defaultView?.frameElement ?? null;
173
+ return opener => {
174
+ queueMicrotask(() => {
175
+ if (surface.isConnected || opener == null || !opener.isConnected) {
176
+ return;
177
+ }
178
+ if (isFocusLost(surface) && (host == null || isFocusLost(host))) {
179
+ focusForLastInput(opener as HTMLElement);
180
+ }
181
+ });
182
+ };
183
+ }
@@ -15,8 +15,9 @@
15
15
  * SYNC: When modified, update pressFeedback.ts, pressGesture.ts (the fade
16
16
  * clock), hooks/usePressFeedback.doc.mjs, the components that paint the touch
17
17
  * press off an ancestor scope or a pseudo-element (Switch, Tab,
18
- * CheckboxInput, RadioListItem, ClickableCard, SelectableCard, Thumbnail)
19
- * and scripts/build-css.test.mjs.
18
+ * CheckboxInput, RadioListItem, ClickableCard, SelectableCard, Thumbnail),
19
+ * Button (a tracked action sizes its `:active` image to zero) and
20
+ * scripts/build-css.test.mjs.
20
21
  */
21
22
 
22
23
  // Every surface that paints a press uses one of these styles, so a change to
@@ -2,8 +2,8 @@
2
2
 
3
3
  /**
4
4
  * @file pressFeedback.ts
5
- * @input The press state machine (pressGesture.ts), the menu-press marker
6
- * (hooks/menuPressMarker.ts) and the document's pointer, keyboard, scroll and
5
+ * @input The press state machine (pressGesture.ts), the menu-press and
6
+ * press-group markers and the document's pointer, keyboard, scroll and
7
7
  * visibility events
8
8
  * @output PRESSABLE_ATTRIBUTE, PRESSED_ATTRIBUTE, pressableProps and
9
9
  * installPressFeedback
@@ -46,6 +46,7 @@ import {isCoarsePointer, watchCoarsePointer} from './coarsePointer';
46
46
  import {subscribeInput} from './inputStream';
47
47
  import {TOUCH_QUERY} from './touchPresentation';
48
48
  import {holdsPressThroughDrag} from '../privateAttributes/pressHoldsDrag.stylex';
49
+ import {isTrackedByGroup} from '../privateAttributes/pressGroupItem.stylex';
49
50
 
50
51
  /**
51
52
  * The marker the controller finds with `closest()`. Every Astryx component
@@ -325,8 +326,9 @@ function stylesheetDroppedActive(): boolean {
325
326
 
326
327
  /**
327
328
  * The surface a mouse button's or the Space key's press paints, if any: the
328
- * innermost marked surface, outside a menu. A key's press paints only a
329
- * focused element that is itself the surface, as `:active` does.
329
+ * innermost marked surface, outside a menu and off a tracked action. A key's
330
+ * press paints only a focused element that is itself the surface, as
331
+ * `:active` does.
330
332
  */
331
333
  function buttonSurfaceAt(
332
334
  target: EventTarget | null,
@@ -336,6 +338,10 @@ function buttonSurfaceAt(
336
338
  if (element == null || element.closest(MENU_PRESS_ROOT) != null) {
337
339
  return null;
338
340
  }
341
+ // A group's tracker follows pointers only, so a key's press stays here.
342
+ if (pointerId != null && isTrackedByGroup(element)) {
343
+ return null;
344
+ }
339
345
  if (pointerId == null && element !== target) {
340
346
  return null;
341
347
  }
@@ -414,7 +420,9 @@ function onPointerDown(event: PointerEvent): void {
414
420
  const braking = element != null && scrollerIsBraking(element);
415
421
  lastTouchAt = Date.now();
416
422
  lastTouchTarget = event.target instanceof Node ? event.target : null;
417
- if (element == null || braking) {
423
+ // A group's tracker paints a tracked action, so two writers would erase each
424
+ // other's paint. The touch still counts for the brake above.
425
+ if (element == null || braking || isTrackedByGroup(element)) {
418
426
  return;
419
427
  }
420
428
  takeOver(button, element);
@@ -560,6 +568,11 @@ function activatesOnSpace(target: EventTarget | null): boolean {
560
568
  );
561
569
  }
562
570
 
571
+ /** A tracked action takes no `:active` paint, so a key's press paints here. */
572
+ function isTrackedAction(target: EventTarget | null): boolean {
573
+ return target instanceof Element && isTrackedByGroup(target);
574
+ }
575
+
563
576
  /**
564
577
  * Space went down on a focused button that is itself a pressable surface.
565
578
  * Heard in the BUBBLE phase, after the page's own handlers, so a press the
@@ -573,7 +586,7 @@ function onKeyDown(event: KeyboardEvent): void {
573
586
  event.isComposing ||
574
587
  event.defaultPrevented ||
575
588
  !activatesOnSpace(event.target) ||
576
- !stylesheetDroppedActive()
589
+ !(stylesheetDroppedActive() || isTrackedAction(event.target))
577
590
  ) {
578
591
  return;
579
592
  }