@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
@@ -457,6 +457,12 @@ export class GestureHost {
457
457
  * lift click (`spec:AST-094` FR28: nothing acts).
458
458
  */
459
459
  cancelledFingers = new Set();
460
+ /**
461
+ * The pens and pointer-read fingers that the app's lost focus cancelled,
462
+ * by pointer id. Each is kept until its lift, which must not click either,
463
+ * or until a new press.
464
+ */
465
+ cancelledPointers = new Map();
460
466
  /** Touch.identifier → the element the finger landed on. */
461
467
  touchTargets = new Map();
462
468
  /**
@@ -478,6 +484,8 @@ export class GestureHost {
478
484
  processed = new WeakSet();
479
485
  /** pointerId → arena id, for live mouse and pen contacts. */
480
486
  pointerIds = new Map();
487
+ /** Each contact's kind: a blur refuses a pen's or a pointer-read finger's lift, never a mouse's. */
488
+ pointerKinds = new Map();
481
489
  /**
482
490
  * The boxes the host watches for screen-edge pans (syncLayoutWatch): each
483
491
  * element with an enabled one, and the root for one whose band moves with
@@ -1420,13 +1428,32 @@ export class GestureHost {
1420
1428
  }
1421
1429
  }
1422
1430
 
1423
- /** After a contact ends: an armed press that is over stops listening. */
1431
+ /**
1432
+ * A new press is a new gesture, as the click guard reads one, so the lifts
1433
+ * the blur cancelled owe nothing after it. A second finger keeps the other
1434
+ * fingers' refusals, since those fingers may still be down.
1435
+ */
1436
+ dropCancelledLifts(event) {
1437
+ const isSecond = event.pointerType === 'touch' && event.isPrimary === false;
1438
+ for (const [pointerId, kind] of [...this.cancelledPointers]) {
1439
+ if (pointerId === event.pointerId || !(isSecond && kind === 'touch')) {
1440
+ this.cancelledPointers.delete(pointerId);
1441
+ }
1442
+ }
1443
+ }
1444
+
1445
+ /**
1446
+ * After a contact ends: an armed press that is over stops listening. A
1447
+ * contact the lost focus cancelled still owes its lift, so the press is
1448
+ * not over until that lift comes. A lift the page never hears ends it at
1449
+ * the next press, or at a cancel.
1450
+ */
1424
1451
  releaseArmedPress() {
1425
1452
  if (!this.isArmedPress) {
1426
1453
  return;
1427
1454
  }
1428
1455
  queueMicrotask(() => {
1429
- if (this.isArmedPress && this.arena.touches.length === 0) {
1456
+ if (this.isArmedPress && this.arena.touches.length === 0 && this.cancelledPointers.size === 0) {
1430
1457
  this.isArmedPress = false;
1431
1458
  this.syncKeptPresses();
1432
1459
  this.syncListeners();
@@ -1457,6 +1484,7 @@ export class GestureHost {
1457
1484
  return;
1458
1485
  }
1459
1486
  this.cancelledFingers.clear();
1487
+ this.cancelledPointers.clear();
1460
1488
  this.clearClaimedLifts();
1461
1489
  this.cancelCallerRead?.();
1462
1490
  this.cancelCallerRead = null;
@@ -2415,8 +2443,9 @@ export class GestureHost {
2415
2443
  consumerElement
2416
2444
  });
2417
2445
  }
2418
- // The finger's events keep going to what the engine hit.
2419
- this.watchTarget(touch.identifier, native, recognizers.some(claimsWhileMoving) || consumer?.keepsTouch != null, consumer != null || recognizers.some(recognizer => recognizer.claimsTouches));
2446
+ // The finger's events keep going to what the engine hit. A disabled
2447
+ // participant can never claim, so a scroll never waits on it.
2448
+ this.watchTarget(touch.identifier, native, recognizers.some(recognizer => recognizer.isEnabled && claimsWhileMoving(recognizer)) || consumer?.keepsTouch != null && consumer.isEnabled?.() !== false, consumer != null && consumer.isEnabled?.() !== false || recognizers.some(recognizer => recognizer.isEnabled && recognizer.claimsTouches));
2420
2449
  this.lastFed.set(id, {
2421
2450
  x: touch.clientX,
2422
2451
  y: touch.clientY,
@@ -2460,6 +2489,11 @@ export class GestureHost {
2460
2489
  for (const input of untracked) {
2461
2490
  this.arena.untrackedBegan(input);
2462
2491
  }
2492
+ // Again once the new fingers are delivered: a pinch's second finger may
2493
+ // have landed on another one-finger pan, the next row.
2494
+ if (this.fingerTargets.length > 1 && !this.isFingerClaimed()) {
2495
+ this.failUnclaimedLoneFingerPans();
2496
+ }
2463
2497
  // Stamped after the consumers judged the touch: the brake asks whether
2464
2498
  // an EARLIER finger is still driving a scroll.
2465
2499
  const landed = landedOn(event);
@@ -2985,6 +3019,7 @@ export class GestureHost {
2985
3019
  if (!isPointerKind(event.pointerType)) {
2986
3020
  return;
2987
3021
  }
3022
+ this.dropCancelledLifts(event);
2988
3023
  if (event.pointerType === 'touch' && this.usesTouchEvents) {
2989
3024
  this.pairPointer(event.pointerId, {
2990
3025
  x: event.clientX,
@@ -2993,6 +3028,13 @@ export class GestureHost {
2993
3028
  });
2994
3029
  return;
2995
3030
  }
3031
+ if (event.pointerType === 'touch' && event.isPrimary === false) {
3032
+ // Fingers from pointer events: a second one, wherever either lands,
3033
+ // leaves the two to the page's pinch, as onTouchStart does. A finger
3034
+ // that is not the primary pointer is a second finger, even where the
3035
+ // host never heard the first.
3036
+ this.failUnclaimedLoneFingerPans();
3037
+ }
2996
3038
  // A right click, a pen's barrel button, or a ctrl-click on macOS (its
2997
3039
  // context click) starts no press (FR43).
2998
3040
  if (event.pointerType !== 'touch' && !isPrimaryClick(event)) {
@@ -3010,6 +3052,7 @@ export class GestureHost {
3010
3052
  }
3011
3053
  const id = this.nextId++;
3012
3054
  this.pointerIds.set(event.pointerId, id);
3055
+ this.pointerKinds.set(event.pointerId, event.pointerType);
3013
3056
  if (target !== landed && target != null) {
3014
3057
  this.retargeted.set(id, {
3015
3058
  x: event.clientX,
@@ -3033,6 +3076,11 @@ export class GestureHost {
3033
3076
  recognizers,
3034
3077
  consumer
3035
3078
  }]);
3079
+ if (event.pointerType === 'touch' && event.isPrimary === false && !this.isFingerClaimed()) {
3080
+ // Again once this finger is delivered: it may have landed on another
3081
+ // one-finger pan.
3082
+ this.failUnclaimedLoneFingerPans();
3083
+ }
3036
3084
  if (locksSelectionFor(event.pointerType)) {
3037
3085
  // The presented target: what the pen is on, where the engine's own
3038
3086
  // hit test may still see the motion's start.
@@ -3079,9 +3127,14 @@ export class GestureHost {
3079
3127
  }
3080
3128
  const id = this.pointerIds.get(event.pointerId);
3081
3129
  if (id == null) {
3130
+ // A contact the lost focus cancelled: its lift clicks nothing.
3131
+ if (this.cancelledPointers.delete(event.pointerId)) {
3132
+ this.refuseCancelledLift(event);
3133
+ }
3082
3134
  return;
3083
3135
  }
3084
3136
  this.pointerIds.delete(event.pointerId);
3137
+ this.pointerKinds.delete(event.pointerId);
3085
3138
  this.unlockSelection(id);
3086
3139
  const dispositions = this.arena.ended([{
3087
3140
  id,
@@ -3176,9 +3229,12 @@ export class GestureHost {
3176
3229
  }
3177
3230
  return;
3178
3231
  }
3232
+ // A contact that ends in a cancel owes no refused lift.
3233
+ this.cancelledPointers.delete(event.pointerId);
3179
3234
  const id = this.pointerIds.get(event.pointerId);
3180
3235
  if (id != null) {
3181
3236
  this.pointerIds.delete(event.pointerId);
3237
+ this.pointerKinds.delete(event.pointerId);
3182
3238
  this.unlockSelection(id, true);
3183
3239
  this.retargeted.delete(id);
3184
3240
  this.arena.cancelled([id], eventTime(event));
@@ -3348,18 +3404,22 @@ export class GestureHost {
3348
3404
  }
3349
3405
  }
3350
3406
  }
3407
+ /** Whether a finger's drag is claimed: then two fingers make no pinch. */
3408
+ isFingerClaimed() {
3409
+ return this.arena.touches.some(touch => touch.pointerType === 'touch' && this.arena.isClaimed(touch.id));
3410
+ }
3351
3411
  onVisibilityChange = () => {
3352
3412
  if (this.doc.visibilityState === 'hidden') {
3353
3413
  this.cancelEverything(this.arena.now());
3354
- // As at a blur: the press sends no further pointer event, so an armed
3355
- // press's page listeners go now (FR24).
3414
+ // As at a blur: an armed press's page listeners go once nothing is left
3415
+ // to hear.
3356
3416
  this.releaseArmedPress();
3357
3417
  }
3358
3418
  };
3359
3419
  onBlur = () => {
3360
3420
  this.cancelEverything(this.arena.now());
3361
- // The press a blur ends sends no further pointer event: an armed press's
3362
- // page listeners go now, as at its lift (FR24).
3421
+ // An armed press's page listeners go once nothing is left to hear. A pen
3422
+ // or a pointer-read finger the blur cancelled still owes its lift.
3363
3423
  this.releaseArmedPress();
3364
3424
  };
3365
3425
 
@@ -3379,8 +3439,29 @@ export class GestureHost {
3379
3439
  return this.inputMode === 'pointer-events' || view != null && !('ontouchstart' in view);
3380
3440
  }
3381
3441
 
3442
+ /**
3443
+ * The lift of a contact the lost focus cancelled: its click is refused,
3444
+ * and a finger's touchend after it is cancelled.
3445
+ */
3446
+ refuseCancelledLift(event) {
3447
+ armClickGuard(this.doc, {
3448
+ takesLateMouse: event.pointerType === 'touch'
3449
+ });
3450
+ if (event.pointerType === 'touch') {
3451
+ // Noted as a claimed lift is: capped, and gone after its time.
3452
+ this.noteClaimedLift(event);
3453
+ }
3454
+ }
3455
+
3382
3456
  /** The app lost focus: UIKit cancels every touch in flight. */
3383
3457
  cancelEverything(time) {
3458
+ // The browser may keep a pen or a pointer-read finger down: its lift must
3459
+ // not click. A mouse button held through the blur keeps its click.
3460
+ for (const [pointerId, kind] of this.pointerKinds) {
3461
+ if (kind !== 'mouse') {
3462
+ this.cancelledPointers.set(pointerId, kind);
3463
+ }
3464
+ }
3384
3465
  const ids = [...this.pointerIds.values()];
3385
3466
  for (const id of ids) {
3386
3467
  this.unlockSelection(id, true);
@@ -3394,6 +3475,7 @@ export class GestureHost {
3394
3475
  }
3395
3476
  }
3396
3477
  this.pointerIds.clear();
3478
+ this.pointerKinds.clear();
3397
3479
  this.retargeted.clear();
3398
3480
  this.fingerPointers.clear();
3399
3481
  this.pendingPointers.clear();
@@ -1 +1 @@
1
- {"version":3,"file":"PressTracker.d.ts","sourceRoot":"","sources":["../../src/gestures/PressTracker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PressTracker.d.ts","sourceRoot":"","sources":["../../src/gestures/PressTracker.ts"],"names":[],"mappings":"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"}
@@ -47,7 +47,8 @@
47
47
  */
48
48
 
49
49
  import { UIKIT_CONTROL, UIKIT_PAN, UIKIT_PRESS } from "../motion/uikit/gestures.js";
50
- import { timing } from "../motion/uikit/timing.js";
50
+ // Importing the whole table bundles its curves into each press-tracking control.
51
+ import { press as pressClocks } from "../motion/uikit/timing.js";
51
52
  import { travel } from "./geometry.js";
52
53
  const PRESS = UIKIT_PRESS;
53
54
 
@@ -63,7 +64,7 @@ export const PRESS_PROFILES = {
63
64
  // The shared press styles release every surface on the deselect clock
64
65
  // until a control runs its own release (UIButton's 250 ms), so a
65
66
  // painter's `fading` never outlasts the fade it shows.
66
- releaseMs: timing.press.highlightOutMs
67
+ releaseMs: pressClocks.highlightOutMs
67
68
  },
68
69
  /**
69
70
  * A table or collection cell: inside a scroll view it highlights after the
@@ -74,7 +75,7 @@ export const PRESS_PROFILES = {
74
75
  highlightDelay: 'inScrollers',
75
76
  cancelTravel: UIKIT_PAN.hysteresis.value,
76
77
  quickTapFlash: 0,
77
- releaseMs: timing.press.highlightOutMs
78
+ releaseMs: pressClocks.highlightOutMs
78
79
  }
79
80
  };
80
81
  /**
@@ -31,7 +31,8 @@ export interface ActionGroupTrackerOptions {
31
31
  * target past the group's ends commits the lift there wherever the finger
32
32
  * lifts.
33
33
  * @default the target whose rect, grown by `highlightExpansion`, holds the
34
- * point; none between or outside them
34
+ * point, the one the press landed on where several do; none between or
35
+ * outside them
35
36
  */
36
37
  resolveTarget?: (x: number, y: number, targets: ReadonlyArray<ActionGroupTarget>) => HTMLElement | null;
37
38
  /** Every finger position it is given (the begin and each move), first. */
@@ -52,9 +53,11 @@ export interface ActionGroupTracker {
52
53
  /**
53
54
  * A press began at viewport (x, y): read the actions' rects once and
54
55
  * highlight the one under it. An inert press is taken but tracks nothing.
56
+ * `landed` is the node the press landed on.
55
57
  */
56
58
  begin(x: number, y: number, options?: {
57
59
  inert?: boolean;
60
+ landed?: Node | null;
58
61
  }): void;
59
62
  /** The finger moved to viewport (x, y). */
60
63
  move(x: number, y: number): void;
@@ -1 +1 @@
1
- {"version":3,"file":"actionGroupTracker.d.ts","sourceRoot":"","sources":["../../src/hooks/actionGroupTracker.ts"],"names":[],"mappings":"AAsBA,wEAAwE;AACxE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,yBAAyB;IACxC,wEAAwE;IACxE,KAAK,EAAE,MAAM,QAAQ,CAAC,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IAClD,4CAA4C;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,kBAAkB,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,CAClB,IAAI,EAAE,WAAW,GAAG,IAAI,EACxB,QAAQ,EAAE,WAAW,GAAG,IAAI,KACzB,IAAI,CAAC;IACV,2EAA2E;IAC3E,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IACzC;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,CACd,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,OAAO,EAAE,aAAa,CAAC,iBAAiB,CAAC,KACtC,WAAW,GAAG,IAAI,CAAC;IACxB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9D;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC,GAAG,IAAI,CAAC;IAC/D,2CAA2C;IAC3C,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC;;;;;OAKG;IACH,GAAG,IAAI,WAAW,GAAG,IAAI,CAAC;IAC1B,+EAA+E;IAC/E,MAAM,IAAI,IAAI,CAAC;IACf;;;OAGG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC;;;;OAIG;IACH,SAAS,IAAI,IAAI,CAAC;IAClB,kCAAkC;IAClC,QAAQ,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IACzC,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IACrC,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B;;;OAGG;IACH,OAAO,IAAI,IAAI,CAAC;CACjB;AAUD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,yBAAyB,GAC1C,kBAAkB,CAgOpB"}
1
+ {"version":3,"file":"actionGroupTracker.d.ts","sourceRoot":"","sources":["../../src/hooks/actionGroupTracker.ts"],"names":[],"mappings":"AAsBA,wEAAwE;AACxE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,yBAAyB;IACxC,wEAAwE;IACxE,KAAK,EAAE,MAAM,QAAQ,CAAC,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IAClD,4CAA4C;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,kBAAkB,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,CAClB,IAAI,EAAE,WAAW,GAAG,IAAI,EACxB,QAAQ,EAAE,WAAW,GAAG,IAAI,KACzB,IAAI,CAAC;IACV,2EAA2E;IAC3E,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IACzC;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,CACd,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,OAAO,EAAE,aAAa,CAAC,iBAAiB,CAAC,KACtC,WAAW,GAAG,IAAI,CAAC;IACxB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9D;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,KAAK,CACH,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAA;KAAC,GAChD,IAAI,CAAC;IACR,2CAA2C;IAC3C,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC;;;;;OAKG;IACH,GAAG,IAAI,WAAW,GAAG,IAAI,CAAC;IAC1B,+EAA+E;IAC/E,MAAM,IAAI,IAAI,CAAC;IACf;;;OAGG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC;;;;OAIG;IACH,SAAS,IAAI,IAAI,CAAC;IAClB,kCAAkC;IAClC,QAAQ,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IACzC,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IACrC,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B;;;OAGG;IACH,OAAO,IAAI,IAAI,CAAC;CACjB;AAUD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,yBAAyB,GAC1C,kBAAkB,CAyOpB"}
@@ -42,6 +42,7 @@ export function createActionGroupTracker(getOptions) {
42
42
  let actions = [];
43
43
  let highlighted = null;
44
44
  let beganOn = null;
45
+ let landedOn = null;
45
46
  let isTracking = false;
46
47
  let isInert = false;
47
48
  // The finger's last position: `remeasure` resolves it again.
@@ -131,23 +132,31 @@ export function createActionGroupTracker(getOptions) {
131
132
  if (resolveTarget != null) {
132
133
  return actions.length === 0 ? null : resolveTarget(x, y, actions);
133
134
  }
135
+ // Rects that share an edge, or that have no layout (a test renderer), can
136
+ // both hold the point: the one the press landed on wins.
137
+ let first = null;
134
138
  for (const action of actions) {
135
139
  if (x >= action.left - margin && x <= action.right + margin && y >= action.top - margin && y <= action.bottom + margin) {
136
- return action.element;
140
+ if (landedOn != null && action.element.contains(landedOn)) {
141
+ return action.element;
142
+ }
143
+ first ??= action.element;
137
144
  }
138
145
  }
139
- return null;
146
+ return first;
140
147
  };
141
148
  const reset = () => {
142
149
  actions = [];
143
150
  highlighted = null;
144
151
  beganOn = null;
152
+ landedOn = null;
145
153
  isTracking = false;
146
154
  isInert = false;
147
155
  };
148
156
  return {
149
157
  begin(x, y, {
150
- inert = false
158
+ inert = false,
159
+ landed = null
151
160
  } = {}) {
152
161
  // A press can begin while another is tracked, when a second recognizer
153
162
  // takes over the finger (a trigger's held press, then the menu's): the
@@ -161,6 +170,7 @@ export function createActionGroupTracker(getOptions) {
161
170
  highlighted = null;
162
171
  isTracking = true;
163
172
  isInert = inert;
173
+ landedOn = landed;
164
174
  lastX = x;
165
175
  lastY = y;
166
176
  getOptions().onMove?.(x, y);
@@ -53,6 +53,7 @@ export { useInputStatusIcon } from './useInputStatusIcon.js';
53
53
  export type { UseInputStatusIconOptions, UseInputStatusIconReturn, } from './useInputStatusIcon.js';
54
54
  export { useInteractiveRole } from './useInteractiveRole.js';
55
55
  export type { InteractiveRole, UseInteractiveRoleOptions, } from './useInteractiveRole.js';
56
+ export { useAnimator } from './useAnimator.js';
56
57
  export { useLongPress } from './useLongPress.js';
57
58
  export type { UseLongPressOptions, UseLongPressHandlers } from './useLongPress.js';
58
59
  export { usePressFeedback } from './usePressFeedback.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"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"}
@@ -35,6 +35,7 @@ export { useClickableContainer, INTERACTIVE_SELECTORS, hasInteractiveAncestor, h
35
35
  export { useInputContainer } from "./useInputContainer.js";
36
36
  export { useInputStatusIcon } from "./useInputStatusIcon.js";
37
37
  export { useInteractiveRole } from "./useInteractiveRole.js";
38
+ export { useAnimator } from "./useAnimator.js";
38
39
  export { useLongPress } from "./useLongPress.js";
39
40
  export { usePressFeedback } from "./usePressFeedback.js";
40
41
  export { useDevWarning } from "./useDevWarning.js";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * True inside a group whose tracker paints a pointer's press on its actions.
3
+ * Those actions take no `:active` paint, so one press never lights two.
4
+ */
5
+ export declare const PressGroupContext: import("react").Context<boolean>;
6
+ //# sourceMappingURL=pressGroupContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pressGroupContext.d.ts","sourceRoot":"","sources":["../../src/hooks/pressGroupContext.ts"],"names":[],"mappings":"AAeA;;;GAGG;AACH,eAAO,MAAM,iBAAiB,kCAAuB,CAAC"}
@@ -0,0 +1,20 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file pressGroupContext.ts
7
+ * @input Nothing
8
+ * @output PressGroupContext: true inside a group whose own tracker paints
9
+ * its actions' press
10
+ * @position Internal; not exported from the hooks entry. AlertDialog
11
+ * provides it around its tracked actions, and Button reads it.
12
+ */
13
+ import { createContext } from 'react';
14
+
15
+ /**
16
+ * True inside a group whose tracker paints a pointer's press on its actions.
17
+ * Those actions take no `:active` paint, so one press never lights two.
18
+ */
19
+ export const PressGroupContext = /*#__PURE__*/createContext(false);
20
+ PressGroupContext.displayName = 'PressGroupContext';
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @file useActionGroupTracking.ts
3
+ * @input A ref to a group of sibling actions, the actions' selector, and the
4
+ * component's tracking constants
5
+ * @output Exports useActionGroupTracking and ActionGroupTrackingOptions
6
+ * @position Internal hook, not exported from the hooks entry. A long press
7
+ * drives the shared action-group tracker (hooks/actionGroupTracker.ts) for
8
+ * a group that a held pointer slides across. AlertDialog consumes it.
9
+ *
10
+ * SYNC: When modified, update useActionGroupTracking.test.tsx
11
+ */
12
+ import { type RefObject } from 'react';
13
+ import { LongPressGestureRecognizer } from '../gestures/LongPressGestureRecognizer.js';
14
+ import type { GestureTouch, PointerKind } from '../gestures/types.js';
15
+ export interface ActionGroupTrackingOptions {
16
+ /** Selects the actions inside the tracked element. */
17
+ itemSelector: string;
18
+ /** How long after touch-down the touched action highlights, ms. */
19
+ highlightOnsetMs: number;
20
+ /**
21
+ * How far outside an action a finger keeps it highlighted, px. A mouse or a
22
+ * pen hit-tests the actions' exact rects.
23
+ */
24
+ highlightExpansion: number;
25
+ /** Pointers that are tracked; others keep the browser's click. @default every pointer */
26
+ pointerTypes?: ReadonlyArray<PointerKind>;
27
+ /** A press whose target this rejects is not tracked. */
28
+ shouldTrack?: (target: unknown) => boolean;
29
+ /** Off, nothing is attached and nothing is tracked. @default true */
30
+ isEnabled?: boolean;
31
+ /**
32
+ * Let the gesture host write the group's `touch-action` and refuse a text
33
+ * selection while a finger or a pen holds it.
34
+ * @default true
35
+ */
36
+ manageStyle?: boolean;
37
+ /** Runs the action under the finger at the lift. @default its own click */
38
+ onActivate?: (item: HTMLElement) => void;
39
+ }
40
+ /**
41
+ * The group's press: a long press that a second finger on the group ends with
42
+ * nothing acting. A plain long press ignores a second finger.
43
+ */
44
+ declare class ActionGroupPress extends LongPressGestureRecognizer {
45
+ protected acceptsTouch(touch: GestureTouch): boolean;
46
+ }
47
+ /**
48
+ * Track a held pointer on the group behind `ref` across the actions inside
49
+ * it, through the shared action-group tracker.
50
+ */
51
+ export declare function useActionGroupTracking(ref: RefObject<HTMLElement | null>, options: ActionGroupTrackingOptions): ActionGroupPress;
52
+ export {};
53
+ //# sourceMappingURL=useActionGroupTracking.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useActionGroupTracking.d.ts","sourceRoot":"","sources":["../../src/hooks/useActionGroupTracking.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,EAA8B,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAElE,OAAO,EAAC,0BAA0B,EAAC,MAAM,wCAAwC,CAAC;AAElF,OAAO,KAAK,EAAC,YAAY,EAAE,WAAW,EAAC,MAAM,mBAAmB,CAAC;AAOjE,MAAM,WAAW,0BAA0B;IACzC,sDAAsD;IACtD,YAAY,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B,yFAAyF;IACzF,YAAY,CAAC,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAC1C,wDAAwD;IACxD,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;IAC3C,qEAAqE;IACrE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;CAC1C;AASD;;;GAGG;AACH,cAAM,gBAAiB,SAAQ,0BAA0B;cACpC,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO;CAO9D;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EAClC,OAAO,EAAE,0BAA0B,GAClC,gBAAgB,CAoHlB"}
@@ -0,0 +1,174 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useActionGroupTracking.ts
7
+ * @input A ref to a group of sibling actions, the actions' selector, and the
8
+ * component's tracking constants
9
+ * @output Exports useActionGroupTracking and ActionGroupTrackingOptions
10
+ * @position Internal hook, not exported from the hooks entry. A long press
11
+ * drives the shared action-group tracker (hooks/actionGroupTracker.ts) for
12
+ * a group that a held pointer slides across. AlertDialog consumes it.
13
+ *
14
+ * SYNC: When modified, update useActionGroupTracking.test.tsx
15
+ */
16
+ import { useEffect, useRef, useState } from 'react';
17
+ import { isActive } from "../gestures/GestureRecognizer.js";
18
+ import { LongPressGestureRecognizer } from "../gestures/LongPressGestureRecognizer.js";
19
+ import { lockSelection } from "../utils/selectionLock.js";
20
+ import { useGestureRecognizer } from "../gestures/useGestureRecognizer.js";
21
+ import { UIKIT_PRESS } from "../motion/uikit/gestures.js";
22
+ import { isIosWebKit } from "../utils/touchQueries.js";
23
+ import { createActionGroupTracker } from "./actionGroupTracker.js";
24
+ import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect.js";
25
+ const PRESS = UIKIT_PRESS;
26
+
27
+ /** The ref handed to the recognizer while tracking is off: no element. */
28
+ const DETACHED = {
29
+ current: null
30
+ };
31
+ const EVERY_POINTER = ['touch', 'pen', 'mouse'];
32
+
33
+ /**
34
+ * The group's press: a long press that a second finger on the group ends with
35
+ * nothing acting. A plain long press ignores a second finger.
36
+ */
37
+ class ActionGroupPress extends LongPressGestureRecognizer {
38
+ acceptsTouch(touch) {
39
+ if (isActive(this.state)) {
40
+ this.setState('cancelled');
41
+ return false;
42
+ }
43
+ return super.acceptsTouch(touch);
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Track a held pointer on the group behind `ref` across the actions inside
49
+ * it, through the shared action-group tracker.
50
+ */
51
+ export function useActionGroupTracking(ref, options) {
52
+ const optionsRef = useRef(options);
53
+ // Keeps the recognizer's delegate and the tracker, which run outside
54
+ // render, reading the latest options.
55
+ useIsomorphicLayoutEffect(() => {
56
+ optionsRef.current = options;
57
+ });
58
+ // The pointer that started the current press. Only a finger gets the hit
59
+ // expansion; a mouse or a pen hit-tests the actions' exact rects.
60
+ const pointerRef = useRef('touch');
61
+ const landedRef = useRef(null);
62
+ const [tracker] = useState(() => createActionGroupTracker(() => ({
63
+ roots: () => [ref.current],
64
+ itemSelector: optionsRef.current.itemSelector,
65
+ highlightExpansion: pointerRef.current === 'touch' ? optionsRef.current.highlightExpansion : 0,
66
+ onActivate: optionsRef.current.onActivate,
67
+ // The browser's own click for a press the group ran is taken, so the
68
+ // action runs once. The gesture host swallows it for a press it claims.
69
+ // This covers one it does not claim, a mouse under a coarse pointer.
70
+ swallowsNativeClick: true
71
+ })));
72
+ const isEnabled = options.isEnabled ?? true;
73
+ // Synchronizes the tracker with tracking on and mounted. Turning tracking
74
+ // off, or unmounting, drops a tracked press and ends its release fades.
75
+ // The host reports no cancellation to a disabled recognizer, and the lift
76
+ // that runs an action often closes the owner before its fade ends.
77
+ useEffect(() => () => {
78
+ tracker.cancel();
79
+ tracker.dispose();
80
+ }, [tracker, isEnabled]);
81
+ return useGestureRecognizer(isEnabled ? ref : DETACHED, () => new ActionGroupPress({
82
+ name: 'ActionGroupTracking',
83
+ minimumPressDuration: options.highlightOnsetMs,
84
+ allowableMovement: Number.POSITIVE_INFINITY,
85
+ allowedPointerTypes: options.pointerTypes ?? EVERY_POINTER,
86
+ delaysTouchesBegan: true,
87
+ // The hold picks an action, so a held finger or pen starts no text
88
+ // selection and opens no callout while down. A long press brings this
89
+ // lock by default; this hold has no minimum duration, so it names it.
90
+ selectionLock: lockSelection,
91
+ shouldReceiveTouch: (recognizer, touch) => {
92
+ if (recognizer.numberOfTouches === 0) {
93
+ pointerRef.current = touch.pointerType;
94
+ const target = touch.target;
95
+ landedRef.current = typeof target?.nodeType === 'number' ? target : null;
96
+ }
97
+ return receiveTouch(recognizer, touch, optionsRef.current);
98
+ }
99
+ }), recognizer => {
100
+ switch (recognizer.state) {
101
+ case 'began':
102
+ {
103
+ const {
104
+ x,
105
+ y
106
+ } = recognizer.location();
107
+ tracker.begin(x, y, {
108
+ landed: landedRef.current
109
+ });
110
+ break;
111
+ }
112
+ case 'changed':
113
+ {
114
+ const {
115
+ x,
116
+ y
117
+ } = recognizer.location();
118
+ tracker.move(x, y);
119
+ break;
120
+ }
121
+ case 'ended':
122
+ {
123
+ const doc = ref.current?.ownerDocument;
124
+ const focusedBefore = doc?.activeElement ?? null;
125
+ const beganOn = tracker.beganOn;
126
+ // Whether a click on the lifted action would focus it. A mouse's or
127
+ // a pen's pointerdown already gave the engine's answer, on the action
128
+ // it began on. A tap focuses on a later mousedown, which the claimed
129
+ // touch cancels, so the platform answers for a finger.
130
+ // iOS WebKit focuses no tapped button; Chromium and Firefox do.
131
+ const clickFocuses = pointerRef.current === 'touch' ? !isIosWebKit(doc?.defaultView) : beganOn != null && focusedBefore === beganOn;
132
+ const lifted = tracker.end();
133
+ // The lift runs the action's own click(), which moves no focus. So
134
+ // focus moves to the lifted action, unless its click moved focus.
135
+ if (clickFocuses && lifted != null && lifted.isConnected && doc?.activeElement === focusedBefore && focusedBefore !== lifted) {
136
+ lifted.focus({
137
+ preventScroll: true,
138
+ focusVisible: false
139
+ });
140
+ }
141
+ break;
142
+ }
143
+ case 'cancelled':
144
+ tracker.cancel();
145
+ break;
146
+ case 'possible':
147
+ case 'failed':
148
+ // Listeners are not called for these.
149
+ break;
150
+ }
151
+ }, {
152
+ isEnabled,
153
+ manageStyle: options.manageStyle ?? true,
154
+ actionGroup: true
155
+ });
156
+ }
157
+
158
+ /**
159
+ * The recognizer's should-receive delegate. It also picks the press's clock.
160
+ * Inside a scroller the press waits the content-touch delay and fails past
161
+ * the slop, so the scroller takes a flick. Elsewhere it begins at once, with
162
+ * no movement limit.
163
+ */
164
+ function receiveTouch(recognizer, touch, options) {
165
+ if (options.shouldTrack?.(touch.target) === false) {
166
+ return false;
167
+ }
168
+ if (recognizer.numberOfTouches === 0) {
169
+ const isInsideScroller = touch.scroll?.isInsideScroller === true;
170
+ recognizer.minimumPressDuration = isInsideScroller ? PRESS.contentTouchDelay.value : options.highlightOnsetMs;
171
+ recognizer.allowableMovement = isInsideScroller ? PRESS.slop.value : Number.POSITIVE_INFINITY;
172
+ }
173
+ return true;
174
+ }