@astryxdesign/core 0.7.8 → 0.7.9-canary.7400c1f

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 (203) hide show
  1. package/dist/Calendar/styles.d.ts +1 -1
  2. package/dist/ClickableCard/ClickableCard.d.ts +1 -1
  3. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  4. package/dist/ClickableCard/ClickableCard.js +5 -1
  5. package/dist/DateInput/PickerWheel.d.ts.map +1 -1
  6. package/dist/DateInput/PickerWheel.js +7 -1
  7. package/dist/DateRangeInput/DateRangeInput.js +1 -1
  8. package/dist/Dialog/Dialog.d.ts +2 -1
  9. package/dist/Dialog/Dialog.d.ts.map +1 -1
  10. package/dist/Dialog/Dialog.js +8 -1
  11. package/dist/Drawer/useDrawerDialogPresence.d.ts +2 -1
  12. package/dist/Drawer/useDrawerDialogPresence.d.ts.map +1 -1
  13. package/dist/Drawer/useDrawerDialogPresence.js +11 -2
  14. package/dist/DropdownMenu/DropdownMenu.d.ts +9 -1
  15. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  16. package/dist/DropdownMenu/DropdownMenu.js +9 -4
  17. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  18. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +3 -2
  19. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  20. package/dist/DropdownMenu/DropdownMenuItem.js +3 -2
  21. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  22. package/dist/DropdownMenu/DropdownMenuRadioItem.js +3 -2
  23. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  24. package/dist/DropdownMenu/DropdownMenuSubMenu.js +3 -2
  25. package/dist/Field/inputDisabledFocus.d.ts +4 -4
  26. package/dist/Field/inputStyles.stylex.d.ts +1 -1
  27. package/dist/FileInput/FileInput.d.ts.map +1 -1
  28. package/dist/FileInput/FileInput.js +16 -2
  29. package/dist/Icon/Icon.d.ts +1 -1
  30. package/dist/InfoTip/InfoTip.d.ts +1 -0
  31. package/dist/InfoTip/InfoTip.d.ts.map +1 -1
  32. package/dist/InfoTip/InfoTip.js +1 -0
  33. package/dist/Item/Item.d.ts +2 -1
  34. package/dist/Item/Item.d.ts.map +1 -1
  35. package/dist/Item/Item.js +29 -6
  36. package/dist/Layer/holdEntranceInput.d.ts +24 -0
  37. package/dist/Layer/holdEntranceInput.d.ts.map +1 -0
  38. package/dist/Layer/holdEntranceInput.js +65 -0
  39. package/dist/Layer/inputHold.d.ts +49 -0
  40. package/dist/Layer/inputHold.d.ts.map +1 -0
  41. package/dist/Layer/inputHold.js +636 -0
  42. package/dist/Layer/preserveAnimations.d.ts +1 -5
  43. package/dist/Layer/preserveAnimations.d.ts.map +1 -1
  44. package/dist/Layer/preserveAnimations.js +12 -214
  45. package/dist/MetadataList/MetadataList.js +1 -1
  46. package/dist/MobileNav/MobileNav.d.ts +11 -1
  47. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  48. package/dist/MobileNav/MobileNav.js +34 -1
  49. package/dist/MoreMenu/MoreMenu.d.ts +5 -3
  50. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  51. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  52. package/dist/RadioList/RadioListItem.js +4 -1
  53. package/dist/SelectableCard/SelectableCard.d.ts +1 -1
  54. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  55. package/dist/SelectableCard/SelectableCard.js +5 -1
  56. package/dist/Spinner/Spinner.d.ts.map +1 -1
  57. package/dist/Spinner/Spinner.js +12 -2
  58. package/dist/Table/plugins/sortable/useTableSortable.js +1 -1
  59. package/dist/Token/Token.d.ts.map +1 -1
  60. package/dist/Token/Token.js +9 -0
  61. package/dist/Token/TokenLink.d.ts +1 -1
  62. package/dist/Token/TokenLink.d.ts.map +1 -1
  63. package/dist/Token/TokenLink.js +4 -1
  64. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  65. package/dist/Tooltip/useTooltip.js +162 -59
  66. package/dist/TreeList/TreeListItem.d.ts +2 -1
  67. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  68. package/dist/TreeList/TreeListItem.js +27 -3
  69. package/dist/astryx.css +30 -7
  70. package/dist/gestures/GestureHost.d.ts +2 -2
  71. package/dist/gestures/GestureHost.d.ts.map +1 -1
  72. package/dist/gestures/GestureHost.js +21 -6
  73. package/dist/gestures/nativeScroll.d.ts +3 -6
  74. package/dist/gestures/nativeScroll.d.ts.map +1 -1
  75. package/dist/gestures/nativeScroll.js +7 -52
  76. package/dist/gestures/wheelSegmenter.d.ts +68 -1
  77. package/dist/gestures/wheelSegmenter.d.ts.map +1 -1
  78. package/dist/gestures/wheelSegmenter.js +199 -10
  79. package/dist/hooks/menuPressGesture.d.ts +133 -21
  80. package/dist/hooks/menuPressGesture.d.ts.map +1 -1
  81. package/dist/hooks/menuPressGesture.js +155 -30
  82. package/dist/hooks/menuPressValues.d.ts +16 -0
  83. package/dist/hooks/menuPressValues.d.ts.map +1 -0
  84. package/dist/hooks/menuPressValues.js +18 -0
  85. package/dist/hooks/useClickableContainer.d.ts +27 -5
  86. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  87. package/dist/hooks/useClickableContainer.js +41 -5
  88. package/dist/hooks/useMenuPress.d.ts +34 -1
  89. package/dist/hooks/useMenuPress.d.ts.map +1 -1
  90. package/dist/hooks/useMenuPress.js +93 -5
  91. package/dist/hooks/usePressFeedback.d.ts +6 -6
  92. package/dist/hooks/usePressFeedback.js +6 -6
  93. package/dist/motion/carryAnimations.d.ts +58 -0
  94. package/dist/motion/carryAnimations.d.ts.map +1 -0
  95. package/dist/motion/carryAnimations.js +305 -0
  96. package/dist/motion/transientCopy.d.ts +4 -2
  97. package/dist/motion/transientCopy.d.ts.map +1 -1
  98. package/dist/motion/transientCopy.js +34 -5
  99. package/dist/motion/uikit/gestures.d.ts +41 -0
  100. package/dist/motion/uikit/gestures.d.ts.map +1 -1
  101. package/dist/motion/uikit/gestures.js +25 -0
  102. package/dist/motion/uikit/index.d.ts +1 -1
  103. package/dist/motion/uikit/index.d.ts.map +1 -1
  104. package/dist/motion/uikit/index.js +1 -1
  105. package/dist/privateAttributes/menuPressLit.stylex.d.ts +52 -0
  106. package/dist/privateAttributes/menuPressLit.stylex.d.ts.map +1 -0
  107. package/dist/privateAttributes/menuPressLit.stylex.js +99 -0
  108. package/dist/privateAttributes/press.stylex.d.ts +139 -0
  109. package/dist/privateAttributes/press.stylex.d.ts.map +1 -0
  110. package/dist/privateAttributes/press.stylex.js +98 -0
  111. package/dist/theme/tokens.stylex.d.ts +2 -2
  112. package/dist/theme/tokens.stylex.js +1 -1
  113. package/dist/utils/flatTree.d.ts +6 -3
  114. package/dist/utils/flatTree.d.ts.map +1 -1
  115. package/dist/utils/flatTree.js +13 -3
  116. package/dist/utils/hitArea.d.ts.map +1 -1
  117. package/dist/utils/hitArea.js +82 -5
  118. package/dist/utils/inputStream.d.ts +14 -1
  119. package/dist/utils/inputStream.d.ts.map +1 -1
  120. package/dist/utils/inputStream.js +40 -9
  121. package/dist/utils/interactionOverlay.stylex.d.ts +2 -1
  122. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  123. package/dist/utils/interactionOverlay.stylex.js +7 -2
  124. package/dist/utils/pressFeedback.d.ts +7 -0
  125. package/dist/utils/pressFeedback.d.ts.map +1 -1
  126. package/dist/utils/pressFeedback.js +32 -7
  127. package/dist/utils/pressGesture.d.ts +6 -0
  128. package/dist/utils/pressGesture.d.ts.map +1 -1
  129. package/dist/utils/pressGesture.js +17 -0
  130. package/dist/utils/rowPressLayer.stylex.d.ts +14 -0
  131. package/dist/utils/rowPressLayer.stylex.d.ts.map +1 -0
  132. package/dist/utils/rowPressLayer.stylex.js +25 -0
  133. package/dist/utils/scrollView.d.ts +25 -0
  134. package/dist/utils/scrollView.d.ts.map +1 -0
  135. package/dist/utils/scrollView.js +108 -0
  136. package/dist/utils/selectionLock.d.ts.map +1 -1
  137. package/dist/utils/selectionLock.js +1 -10
  138. package/dist/utils/triggerFocus.d.ts +5 -0
  139. package/dist/utils/triggerFocus.d.ts.map +1 -1
  140. package/dist/utils/triggerFocus.js +9 -2
  141. package/package.json +5 -5
  142. package/src/ClickableCard/ClickableCard.tsx +8 -1
  143. package/src/DateInput/PickerWheel.tsx +7 -2
  144. package/src/DateRangeInput/DateRangeInput.tsx +1 -1
  145. package/src/Dialog/Dialog.doc.mjs +1 -1
  146. package/src/Dialog/Dialog.tsx +8 -1
  147. package/src/Drawer/Drawer.doc.mjs +1 -1
  148. package/src/Drawer/useDrawerDialogPresence.ts +9 -2
  149. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -2
  150. package/src/DropdownMenu/DropdownMenu.tsx +29 -5
  151. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +5 -1
  152. package/src/DropdownMenu/DropdownMenuItem.tsx +8 -4
  153. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +5 -1
  154. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +5 -1
  155. package/src/FileInput/FileInput.tsx +16 -2
  156. package/src/InfoTip/InfoTip.doc.mjs +26 -2
  157. package/src/InfoTip/InfoTip.tsx +1 -0
  158. package/src/Item/Item.tsx +51 -9
  159. package/src/Layer/holdEntranceInput.ts +84 -0
  160. package/src/Layer/inputHold.ts +734 -0
  161. package/src/Layer/preserveAnimations.ts +12 -275
  162. package/src/MetadataList/MetadataList.tsx +1 -1
  163. package/src/MobileNav/MobileNav.doc.mjs +3 -3
  164. package/src/MobileNav/MobileNav.tsx +34 -1
  165. package/src/MoreMenu/MoreMenu.doc.mjs +2 -4
  166. package/src/MoreMenu/MoreMenu.tsx +5 -3
  167. package/src/RadioList/RadioListItem.tsx +4 -5
  168. package/src/SelectableCard/SelectableCard.tsx +8 -1
  169. package/src/Spinner/Spinner.tsx +13 -3
  170. package/src/Table/plugins/sortable/useTableSortable.tsx +1 -1
  171. package/src/Token/Token.tsx +18 -1
  172. package/src/Token/TokenLink.tsx +7 -1
  173. package/src/Tooltip/useTooltip.tsx +175 -79
  174. package/src/TreeList/TreeListItem.tsx +36 -2
  175. package/src/gestures/GestureHost.ts +21 -5
  176. package/src/gestures/README.md +12 -4
  177. package/src/gestures/nativeScroll.ts +12 -68
  178. package/src/gestures/wheelSegmenter.ts +243 -10
  179. package/src/hooks/menuPressGesture.ts +244 -38
  180. package/src/hooks/menuPressValues.ts +18 -0
  181. package/src/hooks/useClickableContainer.doc.mjs +8 -2
  182. package/src/hooks/useClickableContainer.ts +72 -6
  183. package/src/hooks/useMenuPress.ts +118 -11
  184. package/src/hooks/usePressFeedback.doc.mjs +3 -3
  185. package/src/hooks/usePressFeedback.ts +6 -6
  186. package/src/motion/MEASUREMENTS.md +11 -0
  187. package/src/motion/carryAnimations.ts +389 -0
  188. package/src/motion/transientCopy.ts +45 -5
  189. package/src/motion/uikit/gestures.ts +22 -0
  190. package/src/motion/uikit/index.ts +1 -0
  191. package/src/privateAttributes/menuPressLit.stylex.ts +104 -0
  192. package/src/privateAttributes/press.stylex.ts +244 -0
  193. package/src/theme/tokens.stylex.ts +1 -1
  194. package/src/utils/flatTree.ts +15 -3
  195. package/src/utils/hitArea.ts +107 -5
  196. package/src/utils/inputStream.ts +47 -10
  197. package/src/utils/interactionOverlay.stylex.ts +7 -2
  198. package/src/utils/pressFeedback.ts +31 -6
  199. package/src/utils/pressGesture.ts +24 -1
  200. package/src/utils/rowPressLayer.stylex.ts +27 -0
  201. package/src/utils/scrollView.ts +135 -0
  202. package/src/utils/selectionLock.ts +5 -10
  203. package/src/utils/triggerFocus.ts +17 -4
@@ -3,7 +3,8 @@
3
3
  /**
4
4
  * @file GestureHost.ts
5
5
  * @input DOM touch, pointer, click, contextmenu, scroll and focus events of
6
- * one document; elements with recognizers and consumers attached
6
+ * one document; elements with recognizers and consumers attached; the
7
+ * input hold's held layers (Layer/inputHold `holdsTouch`)
7
8
  * @output Exports GestureHost (the DOM side of the gesture system) and
8
9
  * getGestureHost (one host per document)
9
10
  * @position Browser adapter of packages/core/src/gestures: turns real events
@@ -130,6 +131,7 @@ import { canElementScroll, createNativeScrollProbe, parentAcrossShadow } from ".
130
131
  import { containsAlongFlatTree } from "../utils/flatTree.js";
131
132
  import { isIosWebKit } from "../utils/touchQueries.js";
132
133
  import { WheelSegmenter } from "./wheelSegmenter.js";
134
+ import { holdsTouch } from "../Layer/inputHold.js";
133
135
  import { gestureHostStyle, intersectTouchAction } from "./touchAction.js";
134
136
  import { markTouchAction, TOUCH_ACTION_ATTRIBUTE, unmarkTouchAction } from "./touchActionRule.js";
135
137
  import { internals } from "./recognizerInternals.js";
@@ -1680,7 +1682,10 @@ export class GestureHost {
1680
1682
  const recognizers = [];
1681
1683
  let consumer = null;
1682
1684
  let consumerElement = null;
1683
- if (target instanceof Node && [...this.touchGates].some(gate => gate(target))) {
1685
+ // A layer that the input hold holds takes no touch while it presents.
1686
+ // The host asks the hold, so the hold needs nothing from gestures/. A
1687
+ // root-entry component can then use the hold with no gesture code.
1688
+ if (target instanceof Node && (holdsTouch(target) || [...this.touchGates].some(gate => gate(target)))) {
1684
1689
  return {
1685
1690
  recognizers,
1686
1691
  consumer,
@@ -1813,7 +1818,7 @@ export class GestureHost {
1813
1818
  };
1814
1819
  this.wheel = gesture;
1815
1820
  }
1816
- const {
1821
+ let {
1817
1822
  dx,
1818
1823
  dy
1819
1824
  } = wheelPixels(wheel, this.doc.defaultView);
@@ -1823,7 +1828,9 @@ export class GestureHost {
1823
1828
  // last wheel.
1824
1829
  let seg = null;
1825
1830
  if (kind === 'pan') {
1826
- seg = this.segmenter.push(time, horizontal ? dx : dy, horizontal ? WHEEL_FOLLOW_END_MS : WHEEL_FOLLOW_END_V_MS);
1831
+ // Only a trackpad's wheel can join its glide late; a mouse wheel's
1832
+ // notches keep the splitter's other rules.
1833
+ seg = this.segmenter.push(time, horizontal ? dx : dy, horizontal ? WHEEL_FOLLOW_END_MS : WHEEL_FOLLOW_END_V_MS, scrollTypeOf(wheel) === 'continuous');
1827
1834
  } else {
1828
1835
  this.segmenter.end();
1829
1836
  }
@@ -1834,6 +1841,14 @@ export class GestureHost {
1834
1841
  gesture = null;
1835
1842
  }
1836
1843
  }
1844
+ // A late wheel the glide took was the fingers' first when this one grows.
1845
+ if (seg != null && seg.carried !== 0) {
1846
+ if (horizontal) {
1847
+ dx += seg.carried;
1848
+ } else {
1849
+ dy += seg.carried;
1850
+ }
1851
+ }
1837
1852
  if (gesture == null) {
1838
1853
  gesture = this.beginWheel(wheel, kind, dx, dy, time);
1839
1854
  this.wheel = gesture;
@@ -2355,8 +2370,8 @@ export class GestureHost {
2355
2370
 
2356
2371
  /**
2357
2372
  * Hold touches away from every participant while `gate(target)` answers
2358
- * true (a presentation running in a layer: its content takes no touch
2359
- * until it settles). Returns the release.
2373
+ * true. Returns the release. The input hold's held layers need no gate,
2374
+ * because the host asks `holdsTouch` for every touch.
2360
2375
  */
2361
2376
  addTouchGate(gate) {
2362
2377
  this.touchGates.add(gate);
@@ -1,12 +1,9 @@
1
1
  import type { NativeScrollProbe } from './types.js';
2
2
  import { parentAcrossShadow } from '../utils/flatTree.js';
3
+ import { parseTouchAction, type AllowedPans } from '../utils/scrollView.js';
3
4
  export { parentAcrossShadow };
4
- export interface AllowedPans {
5
- readonly x: boolean;
6
- readonly y: boolean;
7
- }
8
- /** Which pans a `touch-action` value lets the browser perform. */
9
- export declare function parseTouchAction(value: string): AllowedPans;
5
+ export { parseTouchAction };
6
+ export type { AllowedPans };
10
7
  /**
11
8
  * Whether `element` itself scrolls natively now for a finger moving by
12
9
  * (dx, dy). It uses the probe's own test of each scroller: overflow that
@@ -1 +1 @@
1
- {"version":3,"file":"nativeScroll.d.ts","sourceRoot":"","sources":["../../src/gestures/nativeScroll.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAC,kBAAkB,EAAC,MAAM,mBAAmB,CAAC;AAErD,OAAO,EAAC,kBAAkB,EAAC,CAAC;AAE5B,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAiB3D;AA+JD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,GACT,OAAO,CAcT;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,OAAO,EACf,EAAC,gBAAuB,EAAC,GAAE;IAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAM,GAC3D,iBAAiB,CAiDnB"}
1
+ {"version":3,"file":"nativeScroll.d.ts","sourceRoot":"","sources":["../../src/gestures/nativeScroll.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAC,kBAAkB,EAAC,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAEL,gBAAgB,EAGhB,KAAK,WAAW,EACjB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAC,kBAAkB,EAAC,CAAC;AAE5B,OAAO,EAAC,gBAAgB,EAAC,CAAC;AAC1B,YAAY,EAAC,WAAW,EAAC,CAAC;AA4H1B;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,GACT,OAAO,CAcT;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,OAAO,EACf,EAAC,gBAAuB,EAAC,GAAE;IAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAM,GAC3D,iBAAiB,CA2CnB"}
@@ -30,28 +30,9 @@
30
30
 
31
31
  import { isRtlElement } from "../hooks/isRtlElement.js";
32
32
  import { parentAcrossShadow } from "../utils/flatTree.js";
33
+ import { isInsideScrollView, parseTouchAction, rootOverflow, SCROLLABLE_OVERFLOW as SCROLLABLE } from "../utils/scrollView.js";
33
34
  export { parentAcrossShadow };
34
- /** Which pans a `touch-action` value lets the browser perform. */
35
- export function parseTouchAction(value) {
36
- const tokens = value.trim().split(/\s+/);
37
- if (tokens.includes('auto') || tokens.includes('manipulation')) {
38
- return {
39
- x: true,
40
- y: true
41
- };
42
- }
43
- if (tokens.includes('none')) {
44
- return {
45
- x: false,
46
- y: false
47
- };
48
- }
49
- return {
50
- x: tokens.some(token => token === 'pan-x' || token === 'pan-left' || token === 'pan-right'),
51
- y: tokens.some(token => token === 'pan-y' || token === 'pan-up' || token === 'pan-down')
52
- };
53
- }
54
- const SCROLLABLE = new Set(['auto', 'scroll', 'overlay']);
35
+ export { parseTouchAction };
55
36
  const EDGE_TOLERANCE = 1;
56
37
  function buildChain(target, readsTouchAction) {
57
38
  const doc = target.ownerDocument;
@@ -67,7 +48,6 @@ function buildChain(target, readsTouchAction) {
67
48
  let allowX = true;
68
49
  let allowY = true;
69
50
  let reachedRoot = false;
70
- let isBeyondFixed = false;
71
51
  for (let node = target; node != null; node = parentAcrossShadow(node)) {
72
52
  if (!(node instanceof Element)) {
73
53
  continue;
@@ -96,16 +76,12 @@ function buildChain(target, readsTouchAction) {
96
76
  // The writing direction through core's one helper.
97
77
  isRtl: isRtlElement(element),
98
78
  stopsX: behaviorX !== 'auto',
99
- stopsY: behaviorY !== 'auto',
100
- isBeyondFixed
79
+ stopsY: behaviorY !== 'auto'
101
80
  });
102
81
  }
103
82
  if (isRoot) {
104
83
  break;
105
84
  }
106
- if (style.position === 'fixed') {
107
- isBeyondFixed = true;
108
- }
109
85
  }
110
86
  if (!reachedRoot && root instanceof Element) {
111
87
  chain.push({
@@ -114,8 +90,7 @@ function buildChain(target, readsTouchAction) {
114
90
  y: allowY && SCROLLABLE.has(rootOverflow(doc, 'y')),
115
91
  isRtl: false,
116
92
  stopsX: false,
117
- stopsY: false,
118
- isBeyondFixed
93
+ stopsY: false
119
94
  });
120
95
  }
121
96
  return {
@@ -134,28 +109,6 @@ function overscrollBehavior(style) {
134
109
  const [first = 'auto', second = first] = (style.getPropertyValue('overscroll-behavior') || 'auto').trim().split(/\s+/);
135
110
  return [x || first, y || second];
136
111
  }
137
-
138
- /**
139
- * The viewport scrolls unless the root or the body clips it (a scroll lock
140
- * sets `overflow: hidden` on one of them).
141
- */
142
- function rootOverflow(doc, axis) {
143
- const view = doc.defaultView;
144
- if (view == null) {
145
- return 'hidden';
146
- }
147
- for (const element of [doc.documentElement, doc.body]) {
148
- if (element == null) {
149
- continue;
150
- }
151
- const style = view.getComputedStyle(element);
152
- const value = axis === 'x' ? style.overflowX : style.overflowY;
153
- if (value === 'hidden' || value === 'clip') {
154
- return 'hidden';
155
- }
156
- }
157
- return 'auto';
158
- }
159
112
  function canMoveY(element, dy) {
160
113
  if (element.scrollHeight - element.clientHeight <= EDGE_TOLERANCE) {
161
114
  return false;
@@ -241,7 +194,9 @@ export function createNativeScrollProbe(target, {
241
194
  return chain?.allowsPan === true;
242
195
  },
243
196
  get isInsideScroller() {
244
- return getChain().some(entry => !entry.isBeyondFixed && (entry.y && entry.element.scrollHeight - entry.element.clientHeight > EDGE_TOLERANCE || entry.x && entry.element.scrollWidth - entry.element.clientWidth > EDGE_TOLERANCE));
197
+ return target instanceof Element && isInsideScrollView(target, {
198
+ readsTouchAction
199
+ });
245
200
  }
246
201
  };
247
202
  }
@@ -3,6 +3,7 @@
3
3
  * @input A wheel stream's deltas along one axis, with their event times
4
4
  * @output Exports WheelSegmenter and isBunched. WheelSegmenter splits the
5
5
  * stream into gestures and reads a trackpad's lift (the onset of momentum).
6
+ * It keeps a trackpad's late or merged glide wheels in their flick.
6
7
  * isBunched tells one stall's merge from a frame.
7
8
  * @position Read by GestureHost.ts's wheel input: the web reports no
8
9
  * trackpad contact or phase, so both are inferred.
@@ -21,6 +22,12 @@ export interface WheelSegment {
21
22
  readonly decaying: boolean;
22
23
  /** This event came so soon after the one before that it is a merge. */
23
24
  readonly bunched: boolean;
25
+ /**
26
+ * The signed travel of the late wheel before this one, with any wheel
27
+ * merged with it. It is handed over when that wheel joined the glide and
28
+ * this one grows into a new gesture; else 0.
29
+ */
30
+ readonly carried: number;
24
31
  }
25
32
  /**
26
33
  * Whether a wheel `intervalMs` after the one before is one stall's merge, not
@@ -39,6 +46,16 @@ export declare function isBunched(intervalMs: number): boolean;
39
46
  * decay, or a pause of 50 ms starts a new gesture. A bunched wheel is never
40
47
  * a jump. The decay reads it by its last step. Momentum never pauses,
41
48
  * reverses or grows.
49
+ *
50
+ * A stalled page receives a trackpad's glide late, and the engine merges the
51
+ * wheels it held. In momentum, a trackpad wheel that the glide's decay
52
+ * explains stays in the flick. It may come after a pause, or rise after more
53
+ * than a frame. It is at or below the next step, or near the sum of the next
54
+ * steps its stall spans. After such a wheel, a trackpad wheel that grows
55
+ * starts a new gesture and takes the late wheel's travel. The decay also
56
+ * reads a trackpad merge in momentum, by its last step. It reads one in a
57
+ * shrinking run too, where it explains the merge better than steady steps
58
+ * do. Other wheels keep the rules above.
42
59
  */
43
60
  export declare class WheelSegmenter {
44
61
  gesture: number;
@@ -49,7 +66,57 @@ export declare class WheelSegmenter {
49
66
  private count;
50
67
  private run;
51
68
  private runStart;
52
- push(time: number, delta: number, idleMs: number): WheelSegment;
69
+ /** The decay's step-to-step ratio, from the last two one-step wheels. */
70
+ private ratio;
71
+ /** The gesture's last on-pace intervals, ms: their median is its frame. */
72
+ private paces;
73
+ /** The intervals in a row that came slower than the frame allows, ms. */
74
+ private slower;
75
+ /** When the gesture's last wheel within two frames of the one before came. */
76
+ private onPace;
77
+ /** The gesture's last wheel that was not bunched, px. */
78
+ private paced;
79
+ /** A wheel joined the glide late: a later one that grows is the fingers'. */
80
+ private resumed;
81
+ /**
82
+ * The signed travel of the last wheel that joined the glide late, with any
83
+ * wheel merged with it.
84
+ */
85
+ private joined;
86
+ /**
87
+ * Reads one wheel. `continuous` is whether it carries a trackpad's
88
+ * signature (`scrollTypeOf`). Only those wheels can join a glide late.
89
+ */
90
+ push(time: number, delta: number, idleMs: number, continuous?: boolean): WheelSegment;
91
+ /**
92
+ * Keep an interval for the frame. The gesture's first FIRST_PACES set it.
93
+ * After them, only an interval within half a frame and one and a half
94
+ * frames counts: a stall's squeezed wheels come faster. FIRST_PACES slower
95
+ * intervals in a row set the frame again, so a squeezed start cannot hold
96
+ * it down.
97
+ */
98
+ private notePace;
99
+ /**
100
+ * The gesture's frame interval: the median of its last on-pace intervals,
101
+ * or FRAME_MS before it shows one. A trackpad reports at 60 Hz or faster,
102
+ * so a longer median is a stall's.
103
+ */
104
+ private frameMs;
105
+ /** The sum of the decay's next `count` steps from `previous`. */
106
+ private glideSum;
107
+ /**
108
+ * How far a merge of `size` lies from whole steps of the run's steady
109
+ * size, or of its last step, whichever lies nearer.
110
+ */
111
+ private steadyOff;
112
+ /**
113
+ * How many of the decay's next steps a trackpad wheel of `size` carries,
114
+ * at most `most`. A wheel at or below the next step carries one. A larger
115
+ * one carries the fewest steps whose sum lies within FIT_PX or `share` of
116
+ * it. The decay goes on from `previous` at `ratio` a step. Null when
117
+ * no sum lies that near.
118
+ */
119
+ private glideSteps;
53
120
  /** The gesture ended on its own (its timer): the next event starts one. */
54
121
  end(): void;
55
122
  }
@@ -1 +1 @@
1
- {"version":3,"file":"wheelSegmenter.d.ts","sourceRoot":"","sources":["../../src/gestures/wheelSegmenter.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,KAAK,CAAC;AAC3E,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAcD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;;;;;;GAUG;AACH,qBAAa,cAAc;IACzB,OAAO,SAAK;IACZ,KAAK,EAAE,UAAU,CAAY;IAC7B,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,QAAQ,CAAK;IAErB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY;IAkE/D,2EAA2E;IAC3E,GAAG,IAAI,IAAI;CAGZ"}
1
+ {"version":3,"file":"wheelSegmenter.d.ts","sourceRoot":"","sources":["../../src/gestures/wheelSegmenter.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,KAAK,CAAC;AAC3E,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAiCD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,cAAc;IACzB,OAAO,SAAK;IACZ,KAAK,EAAE,UAAU,CAAY;IAC7B,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,QAAQ,CAAK;IACrB,yEAAyE;IACzE,OAAO,CAAC,KAAK,CAAK;IAClB,2EAA2E;IAC3E,OAAO,CAAC,KAAK,CAAgB;IAC7B,yEAAyE;IACzE,OAAO,CAAC,MAAM,CAAgB;IAC9B,8EAA8E;IAC9E,OAAO,CAAC,MAAM,CAAa;IAC3B,yDAAyD;IACzD,OAAO,CAAC,KAAK,CAAK;IAClB,6EAA6E;IAC7E,OAAO,CAAC,OAAO,CAAS;IACxB;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAK;IAEnB;;;OAGG;IACH,IAAI,CACF,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,UAAU,UAAQ,GACjB,YAAY;IA+If;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ;IAchB;;;;OAIG;IACH,OAAO,CAAC,OAAO;IAaf,iEAAiE;IACjE,OAAO,CAAC,QAAQ;IAUhB;;;OAGG;IACH,OAAO,CAAC,SAAS;IAUjB;;;;;;OAMG;IACH,OAAO,CAAC,UAAU;IAuBlB,2EAA2E;IAC3E,GAAG,IAAI,IAAI;CAGZ"}
@@ -5,6 +5,7 @@
5
5
  * @input A wheel stream's deltas along one axis, with their event times
6
6
  * @output Exports WheelSegmenter and isBunched. WheelSegmenter splits the
7
7
  * stream into gestures and reads a trackpad's lift (the onset of momentum).
8
+ * It keeps a trackpad's late or merged glide wheels in their flick.
8
9
  * isBunched tells one stall's merge from a frame.
9
10
  * @position Read by GestureHost.ts's wheel input: the web reports no
10
11
  * trackpad contact or phase, so both are inferred.
@@ -21,6 +22,25 @@ const BUNCHED_MS = 1.5;
21
22
  const LIFT_RUN = 4;
22
23
  /** And how much of its size the run has lost by then. */
23
24
  const LIFT_DECAY = 0.8;
25
+ /** How near a wheel must lie to a sum of the glide's next steps, px. */
26
+ const FIT_PX = 1;
27
+ /** Or as a share of that sum, when the share is larger. */
28
+ const FIT_SHARE = 0.25;
29
+ /** The most glide steps one wheel is read as carrying. */
30
+ const MAX_STEPS = 64;
31
+ /** How near a late or rising wheel must lie to the glide's sum, as a share. */
32
+ const LATE_FIT_SHARE = 0.15;
33
+ /**
34
+ * How far a wheel may grow past the last unbunched one and still be the
35
+ * glide, px.
36
+ */
37
+ const RISE_PX = 0.5;
38
+ /** A stream's frame interval until it shows its own, ms. */
39
+ const FRAME_MS = 16;
40
+ /** The longest frame interval a trackpad reports at: 60 Hz, ms. */
41
+ const SLOWEST_FRAME_MS = 1000 / 60;
42
+ /** The intervals that set a gesture's frame before its window applies. */
43
+ const FIRST_PACES = 3;
24
44
 
25
45
  /**
26
46
  * Whether a wheel `intervalMs` after the one before is one stall's merge, not
@@ -42,6 +62,16 @@ export function isBunched(intervalMs) {
42
62
  * decay, or a pause of 50 ms starts a new gesture. A bunched wheel is never
43
63
  * a jump. The decay reads it by its last step. Momentum never pauses,
44
64
  * reverses or grows.
65
+ *
66
+ * A stalled page receives a trackpad's glide late, and the engine merges the
67
+ * wheels it held. In momentum, a trackpad wheel that the glide's decay
68
+ * explains stays in the flick. It may come after a pause, or rise after more
69
+ * than a frame. It is at or below the next step, or near the sum of the next
70
+ * steps its stall spans. After such a wheel, a trackpad wheel that grows
71
+ * starts a new gesture and takes the late wheel's travel. The decay also
72
+ * reads a trackpad merge in momentum, by its last step. It reads one in a
73
+ * shrinking run too, where it explains the merge better than steady steps
74
+ * do. Other wheels keep the rules above.
45
75
  */
46
76
  export class WheelSegmenter {
47
77
  gesture = 0;
@@ -52,40 +82,108 @@ export class WheelSegmenter {
52
82
  count = 0;
53
83
  run = 0;
54
84
  runStart = 0;
55
- push(time, delta, idleMs) {
85
+ /** The decay's step-to-step ratio, from the last two one-step wheels. */
86
+ ratio = 1;
87
+ /** The gesture's last on-pace intervals, ms: their median is its frame. */
88
+ paces = [];
89
+ /** The intervals in a row that came slower than the frame allows, ms. */
90
+ slower = [];
91
+ /** When the gesture's last wheel within two frames of the one before came. */
92
+ onPace = -Infinity;
93
+ /** The gesture's last wheel that was not bunched, px. */
94
+ paced = 0;
95
+ /** A wheel joined the glide late: a later one that grows is the fingers'. */
96
+ resumed = false;
97
+ /**
98
+ * The signed travel of the last wheel that joined the glide late, with any
99
+ * wheel merged with it.
100
+ */
101
+ joined = 0;
102
+
103
+ /**
104
+ * Reads one wheel. `continuous` is whether it carries a trackpad's
105
+ * signature (`scrollTypeOf`). Only those wheels can join a glide late.
106
+ */
107
+ push(time, delta, idleMs, continuous = false) {
56
108
  const size = Math.abs(delta);
57
109
  const sign = Math.sign(delta);
58
110
  const gap = time - this.last;
59
111
  const bunched = this.count > 0 && isBunched(gap);
112
+ const frame = this.frameMs();
60
113
  let boundary = null;
114
+ let carriedSteps = null;
61
115
  if (this.count === 0) {
62
116
  boundary = this.gesture === 0 ? 'first' : 'idle';
63
117
  } else if (this.phase === 'finger' && gap >= idleMs) {
64
118
  boundary = 'idle';
65
119
  } else if (this.phase === 'momentum') {
120
+ // A late wheel carries the steps of its whole stall, which began at the
121
+ // last wheel that came on pace.
122
+ const frames = Math.round((time - this.onPace) / frame);
123
+ const late = gap >= MOMENTUM_GAP_MS;
124
+ const rises = !bunched && size >= 2 && size > this.previous * 1.3 + 0.5;
66
125
  if (sign !== 0 && this.sign !== 0 && sign !== this.sign) {
67
126
  boundary = 'reversal';
68
- } else if (gap >= MOMENTUM_GAP_MS) {
69
- boundary = 'gap';
70
- } else if (!bunched && size >= 2 && size > this.previous * 1.3 + 0.5) {
127
+ } else if (late || rises) {
128
+ // A wheel that rises on pace is the fingers'. One that rises after a
129
+ // skipped frame may carry the frames its stall held.
130
+ if (continuous && sign === this.sign && (late || gap >= 1.5 * frame)) {
131
+ carriedSteps = this.glideSteps(size, Math.max(1, frames), LATE_FIT_SHARE);
132
+ }
133
+ if (carriedSteps == null) {
134
+ boundary = late ? 'gap' : 'jump';
135
+ }
136
+ } else if (continuous && this.resumed && !bunched && size > FIT_PX && size > this.paced + RISE_PX) {
137
+ // A glide only falls, so after a late wheel one that grows is the
138
+ // fingers'. A whole-pixel glide repeats a step as it slows.
71
139
  boundary = 'jump';
72
140
  }
73
141
  }
142
+ if (carriedSteps == null && boundary == null && bunched && continuous) {
143
+ if (this.phase === 'momentum') {
144
+ carriedSteps = this.glideSteps(size, MAX_STEPS);
145
+ } else if (this.run > 0) {
146
+ // In a shrinking run, the decay reads a merge only where it explains
147
+ // the merge better than steady steps do.
148
+ const decay = this.glideSteps(size, MAX_STEPS);
149
+ if (decay != null && decay > 1 && Math.abs(size - this.glideSum(decay)) < this.steadyOff(size)) {
150
+ carriedSteps = decay;
151
+ }
152
+ }
153
+ }
74
154
  // A bunched wheel carries the steps of the frames it merged. The decay
75
155
  // goes on from its last step, so the steps after it read as they are.
76
- const steps = boundary == null && bunched && this.previous > 0 ? Math.max(1, Math.round(size / this.previous)) : 1;
156
+ const steps = carriedSteps ?? (boundary == null && bunched && this.previous > 0 ? Math.max(1, Math.round(size / this.previous)) : 1);
157
+ // On the decay, a wheel of several steps ends at the decay's own step.
158
+ const onDecay = carriedSteps != null && carriedSteps > 1;
159
+ // A bunched wheel is read by its last step, as the decay goes on from it.
160
+ const step = onDecay ? this.previous * this.ratio ** steps : size / steps;
77
161
  if (boundary != null) {
78
162
  this.gesture += 1;
79
163
  this.phase = 'finger';
80
164
  this.count = 0;
81
165
  this.run = 0;
166
+ this.ratio = 1;
167
+ this.resumed = false;
168
+ this.paces = [];
169
+ this.slower = [];
170
+ }
171
+ // The late wheel was the fingers' when the next one grows: the new
172
+ // gesture takes its travel.
173
+ const carried = boundary === 'jump' ? this.joined : 0;
174
+ const joinedLate = boundary == null && carriedSteps != null && !bunched;
175
+ if (joinedLate) {
176
+ this.resumed = true;
177
+ }
178
+ // A wheel merged with the late one is the same dispatch: it adds to it.
179
+ this.joined = joinedLate ? delta : bunched && boundary == null && this.joined !== 0 ? this.joined + delta : 0;
180
+ if (!bunched) {
181
+ this.paced = size;
82
182
  }
83
183
  let liftNow = false;
84
184
  let decaying = false;
85
185
  if (this.phase === 'finger' && this.count >= 3 && size > 0) {
86
- // A bunched wheel is read by its last step, as the decay goes on from it.
87
- const step = size / steps;
88
- decaying = step <= this.previous * 0.995 && step >= this.previous * 0.5;
186
+ decaying = onDecay ? this.ratio <= 0.995 : step <= this.previous * 0.995 && step >= this.previous * 0.5;
89
187
  if (decaying) {
90
188
  if (this.run === 0) {
91
189
  this.runStart = this.previous;
@@ -99,8 +197,17 @@ export class WheelSegmenter {
99
197
  liftNow = true;
100
198
  }
101
199
  }
200
+ if (steps === 1 && this.count > 0 && this.previous > 0 && step > 0) {
201
+ this.ratio = Math.min(1, Math.max(0.5, step / this.previous));
202
+ }
203
+ if (!bunched && boundary == null && gap < MOMENTUM_GAP_MS) {
204
+ this.notePace(gap, frame);
205
+ }
206
+ if (boundary != null || !bunched && gap < 2 * frame) {
207
+ this.onPace = time;
208
+ }
102
209
  this.count += 1;
103
- this.previous = size / steps;
210
+ this.previous = step;
104
211
  if (sign !== 0) {
105
212
  this.sign = sign;
106
213
  }
@@ -111,10 +218,92 @@ export class WheelSegmenter {
111
218
  gesture: this.gesture,
112
219
  liftNow,
113
220
  decaying: decaying && !liftNow,
114
- bunched: boundary == null && bunched
221
+ bunched: boundary == null && bunched,
222
+ carried
115
223
  };
116
224
  }
117
225
 
226
+ /**
227
+ * Keep an interval for the frame. The gesture's first FIRST_PACES set it.
228
+ * After them, only an interval within half a frame and one and a half
229
+ * frames counts: a stall's squeezed wheels come faster. FIRST_PACES slower
230
+ * intervals in a row set the frame again, so a squeezed start cannot hold
231
+ * it down.
232
+ */
233
+ notePace(gap, frame) {
234
+ const inWindow = gap >= 0.5 * frame && gap <= 1.5 * frame;
235
+ if (this.paces.length < FIRST_PACES || inWindow) {
236
+ this.paces = [...this.paces.slice(-7), gap];
237
+ this.slower = [];
238
+ return;
239
+ }
240
+ this.slower = gap > 1.5 * frame ? [...this.slower, gap] : [];
241
+ if (this.slower.length >= FIRST_PACES) {
242
+ this.paces = this.slower;
243
+ this.slower = [];
244
+ }
245
+ }
246
+
247
+ /**
248
+ * The gesture's frame interval: the median of its last on-pace intervals,
249
+ * or FRAME_MS before it shows one. A trackpad reports at 60 Hz or faster,
250
+ * so a longer median is a stall's.
251
+ */
252
+ frameMs() {
253
+ if (this.paces.length === 0) {
254
+ return FRAME_MS;
255
+ }
256
+ const sorted = [...this.paces].sort((a, b) => a - b);
257
+ const middle = sorted.length >> 1;
258
+ const median = sorted.length % 2 === 1 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2;
259
+ return Math.min(median, SLOWEST_FRAME_MS);
260
+ }
261
+
262
+ /** The sum of the decay's next `count` steps from `previous`. */
263
+ glideSum(count) {
264
+ let next = this.previous * this.ratio;
265
+ let sum = 0;
266
+ for (let i = 0; i < count; i++) {
267
+ sum += next;
268
+ next *= this.ratio;
269
+ }
270
+ return sum;
271
+ }
272
+
273
+ /**
274
+ * How far a merge of `size` lies from whole steps of the run's steady
275
+ * size, or of its last step, whichever lies nearer.
276
+ */
277
+ steadyOff(size) {
278
+ return Math.min(...[this.runStart, this.previous].filter(step => step > 0).map(step => Math.abs(size - Math.max(1, Math.round(size / step)) * step)));
279
+ }
280
+
281
+ /**
282
+ * How many of the decay's next steps a trackpad wheel of `size` carries,
283
+ * at most `most`. A wheel at or below the next step carries one. A larger
284
+ * one carries the fewest steps whose sum lies within FIT_PX or `share` of
285
+ * it. The decay goes on from `previous` at `ratio` a step. Null when
286
+ * no sum lies that near.
287
+ */
288
+ glideSteps(size, most, share = FIT_SHARE) {
289
+ if (this.previous <= 0 || size <= 0) {
290
+ return null;
291
+ }
292
+ let next = this.previous * this.ratio;
293
+ if (size <= next + FIT_PX) {
294
+ return 1;
295
+ }
296
+ let sum = next;
297
+ for (let count = 2; count <= Math.min(most, MAX_STEPS); count++) {
298
+ next *= this.ratio;
299
+ sum += next;
300
+ if (Math.abs(size - sum) <= Math.max(FIT_PX, sum * share)) {
301
+ return count;
302
+ }
303
+ }
304
+ return null;
305
+ }
306
+
118
307
  /** The gesture ended on its own (its timer): the next event starts one. */
119
308
  end() {
120
309
  this.count = 0;