react-native-gtkx 0.2.0-alpha.2 → 0.3.0-alpha.1

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 (340) hide show
  1. package/README.md +5 -2
  2. package/dist/aliases/index.d.ts +84 -0
  3. package/dist/aliases/index.js +208 -0
  4. package/dist/aliases/index.js.map +1 -0
  5. package/dist/animated/index.d.ts +2 -0
  6. package/dist/animated/index.js +5 -0
  7. package/dist/animated/index.js.map +1 -1
  8. package/dist/animated/types.d.ts +1 -0
  9. package/dist/animated/types.js.map +1 -1
  10. package/dist/animated/value-animation.js +17 -4
  11. package/dist/animated/value-animation.js.map +1 -1
  12. package/dist/apis/host.gtkx.js +9 -12
  13. package/dist/apis/host.gtkx.js.map +1 -1
  14. package/dist/apis/index.d.ts +4 -2
  15. package/dist/apis/index.js +2 -0
  16. package/dist/apis/index.js.map +1 -1
  17. package/dist/apis/keyboard.d.ts +60 -0
  18. package/dist/apis/keyboard.js +63 -0
  19. package/dist/apis/keyboard.js.map +1 -0
  20. package/dist/apis/log-box.d.ts +15 -0
  21. package/dist/apis/log-box.js +46 -0
  22. package/dist/apis/log-box.js.map +1 -0
  23. package/dist/apis/platform.d.ts +18 -1
  24. package/dist/apis/platform.js.map +1 -1
  25. package/dist/common/index.d.ts +0 -1
  26. package/dist/common/index.js +24 -1
  27. package/dist/common/index.js.map +1 -1
  28. package/dist/components/animated.d.ts +70 -6
  29. package/dist/components/animated.js +559 -46
  30. package/dist/components/animated.js.map +1 -1
  31. package/dist/components/driven-size.d.ts +27 -0
  32. package/dist/components/driven-size.js +85 -0
  33. package/dist/components/driven-size.js.map +1 -0
  34. package/dist/components/find-node-handle.d.ts +11 -0
  35. package/dist/components/find-node-handle.js +53 -0
  36. package/dist/components/find-node-handle.js.map +1 -0
  37. package/dist/components/flat-list.d.ts +3 -2
  38. package/dist/components/flat-list.js.map +1 -1
  39. package/dist/components/frame-scheduler.d.ts +16 -0
  40. package/dist/components/frame-scheduler.js +37 -0
  41. package/dist/components/frame-scheduler.js.map +1 -0
  42. package/dist/components/image.d.ts +6 -1
  43. package/dist/components/image.js +15 -4
  44. package/dist/components/image.js.map +1 -1
  45. package/dist/components/index.d.ts +7 -5
  46. package/dist/components/index.js +5 -3
  47. package/dist/components/index.js.map +1 -1
  48. package/dist/components/measure.d.ts +51 -1
  49. package/dist/components/measure.js +101 -2
  50. package/dist/components/measure.js.map +1 -1
  51. package/dist/components/pressable.d.ts +61 -1
  52. package/dist/components/pressable.js +151 -4
  53. package/dist/components/pressable.js.map +1 -1
  54. package/dist/components/rect-store.d.ts +58 -0
  55. package/dist/components/rect-store.js +124 -3
  56. package/dist/components/rect-store.js.map +1 -1
  57. package/dist/components/root.js +6 -1
  58. package/dist/components/root.js.map +1 -1
  59. package/dist/components/scroll-phase.d.ts +35 -0
  60. package/dist/components/scroll-phase.js +26 -0
  61. package/dist/components/scroll-phase.js.map +1 -0
  62. package/dist/components/scroll-view.d.ts +39 -3
  63. package/dist/components/scroll-view.js +378 -43
  64. package/dist/components/scroll-view.js.map +1 -1
  65. package/dist/components/text-input.js +9 -25
  66. package/dist/components/text-input.js.map +1 -1
  67. package/dist/components/text.d.ts +6 -2
  68. package/dist/components/text.js +43 -8
  69. package/dist/components/text.js.map +1 -1
  70. package/dist/components/use-focus.d.ts +27 -0
  71. package/dist/components/use-focus.js +116 -0
  72. package/dist/components/use-focus.js.map +1 -0
  73. package/dist/components/use-layout-child.d.ts +13 -1
  74. package/dist/components/use-layout-child.js +188 -5
  75. package/dist/components/use-layout-child.js.map +1 -1
  76. package/dist/components/view.d.ts +5 -2
  77. package/dist/components/view.js +18 -13
  78. package/dist/components/view.js.map +1 -1
  79. package/dist/components/virtualized-list.d.ts +45 -5
  80. package/dist/components/virtualized-list.js +90 -24
  81. package/dist/components/virtualized-list.js.map +1 -1
  82. package/dist/components/wheel-scroll-session.d.ts +7 -0
  83. package/dist/components/wheel-scroll-session.js +53 -0
  84. package/dist/components/wheel-scroll-session.js.map +1 -0
  85. package/dist/components/widget-retention.d.ts +35 -0
  86. package/dist/components/widget-retention.js +169 -0
  87. package/dist/components/widget-retention.js.map +1 -0
  88. package/dist/contracts.d.ts +4 -0
  89. package/dist/contracts.js.map +1 -1
  90. package/dist/dnd/context.d.ts +69 -0
  91. package/dist/dnd/context.js +149 -0
  92. package/dist/dnd/context.js.map +1 -0
  93. package/dist/dnd/draggable.d.ts +59 -0
  94. package/dist/dnd/draggable.js +149 -0
  95. package/dist/dnd/draggable.js.map +1 -0
  96. package/dist/dnd/droppable.d.ts +24 -0
  97. package/dist/dnd/droppable.js +74 -0
  98. package/dist/dnd/droppable.js.map +1 -0
  99. package/dist/dnd/gtk-controllers.d.ts +39 -0
  100. package/dist/dnd/gtk-controllers.js +110 -0
  101. package/dist/dnd/gtk-controllers.js.map +1 -0
  102. package/dist/dnd/index.d.ts +6 -0
  103. package/dist/dnd/index.js +31 -0
  104. package/dist/dnd/index.js.map +1 -0
  105. package/dist/dnd/order.d.ts +7 -0
  106. package/dist/dnd/order.js +20 -0
  107. package/dist/dnd/order.js.map +1 -0
  108. package/dist/dnd/payload.d.ts +14 -0
  109. package/dist/dnd/payload.js +41 -0
  110. package/dist/dnd/payload.js.map +1 -0
  111. package/dist/dnd/sortable.d.ts +49 -0
  112. package/dist/dnd/sortable.js +307 -0
  113. package/dist/dnd/sortable.js.map +1 -0
  114. package/dist/dnd/types.d.ts +290 -0
  115. package/dist/dnd/types.js +25 -0
  116. package/dist/dnd/types.js.map +1 -0
  117. package/dist/gesture-handler-compat/builder.d.ts +251 -0
  118. package/dist/gesture-handler-compat/builder.js +513 -0
  119. package/dist/gesture-handler-compat/builder.js.map +1 -0
  120. package/dist/gesture-handler-compat/composition.d.ts +23 -0
  121. package/dist/gesture-handler-compat/composition.js +111 -0
  122. package/dist/gesture-handler-compat/composition.js.map +1 -0
  123. package/dist/gesture-handler-compat/deciders.d.ts +3 -0
  124. package/dist/gesture-handler-compat/deciders.js +47 -0
  125. package/dist/gesture-handler-compat/deciders.js.map +1 -0
  126. package/dist/gesture-handler-compat/detector-runtime.d.ts +44 -0
  127. package/dist/gesture-handler-compat/detector-runtime.js +374 -0
  128. package/dist/gesture-handler-compat/detector-runtime.js.map +1 -0
  129. package/dist/gesture-handler-compat/detector.d.ts +22 -0
  130. package/dist/gesture-handler-compat/detector.js +104 -0
  131. package/dist/gesture-handler-compat/detector.js.map +1 -0
  132. package/dist/gesture-handler-compat/fling.d.ts +24 -0
  133. package/dist/gesture-handler-compat/fling.js +123 -0
  134. package/dist/gesture-handler-compat/fling.js.map +1 -0
  135. package/dist/gesture-handler-compat/force-touch.d.ts +13 -0
  136. package/dist/gesture-handler-compat/force-touch.js +40 -0
  137. package/dist/gesture-handler-compat/force-touch.js.map +1 -0
  138. package/dist/gesture-handler-compat/hooks.d.ts +157 -0
  139. package/dist/gesture-handler-compat/hooks.js +268 -0
  140. package/dist/gesture-handler-compat/hooks.js.map +1 -0
  141. package/dist/gesture-handler-compat/hover.d.ts +2 -0
  142. package/dist/gesture-handler-compat/hover.js +22 -0
  143. package/dist/gesture-handler-compat/hover.js.map +1 -0
  144. package/dist/gesture-handler-compat/index.d.ts +98 -0
  145. package/dist/gesture-handler-compat/index.js +255 -0
  146. package/dist/gesture-handler-compat/index.js.map +1 -0
  147. package/dist/gesture-handler-compat/long-press.d.ts +6 -0
  148. package/dist/gesture-handler-compat/long-press.js +21 -0
  149. package/dist/gesture-handler-compat/long-press.js.map +1 -0
  150. package/dist/gesture-handler-compat/manual.d.ts +2 -0
  151. package/dist/gesture-handler-compat/manual.js +18 -0
  152. package/dist/gesture-handler-compat/manual.js.map +1 -0
  153. package/dist/gesture-handler-compat/native.d.ts +7 -0
  154. package/dist/gesture-handler-compat/native.js +34 -0
  155. package/dist/gesture-handler-compat/native.js.map +1 -0
  156. package/dist/gesture-handler-compat/orchestrator.d.ts +77 -0
  157. package/dist/gesture-handler-compat/orchestrator.js +284 -0
  158. package/dist/gesture-handler-compat/orchestrator.js.map +1 -0
  159. package/dist/gesture-handler-compat/pan.d.ts +23 -0
  160. package/dist/gesture-handler-compat/pan.js +116 -0
  161. package/dist/gesture-handler-compat/pan.js.map +1 -0
  162. package/dist/gesture-handler-compat/recognizer.d.ts +268 -0
  163. package/dist/gesture-handler-compat/recognizer.js +1056 -0
  164. package/dist/gesture-handler-compat/recognizer.js.map +1 -0
  165. package/dist/gesture-handler-compat/relations.d.ts +43 -0
  166. package/dist/gesture-handler-compat/relations.js +128 -0
  167. package/dist/gesture-handler-compat/relations.js.map +1 -0
  168. package/dist/gesture-handler-compat/tap.d.ts +6 -0
  169. package/dist/gesture-handler-compat/tap.js +29 -0
  170. package/dist/gesture-handler-compat/tap.js.map +1 -0
  171. package/dist/gesture-handler-compat/touchpad.d.ts +41 -0
  172. package/dist/gesture-handler-compat/touchpad.js +64 -0
  173. package/dist/gesture-handler-compat/touchpad.js.map +1 -0
  174. package/dist/gesture-handler-compat/types.d.ts +441 -0
  175. package/dist/gesture-handler-compat/types.js +167 -0
  176. package/dist/gesture-handler-compat/types.js.map +1 -0
  177. package/dist/gtk/controllers.d.ts +39 -0
  178. package/dist/gtk/controllers.js +92 -0
  179. package/dist/gtk/controllers.js.map +1 -0
  180. package/dist/gtk/index.d.ts +2 -1
  181. package/dist/gtk/index.js +7 -1
  182. package/dist/gtk/index.js.map +1 -1
  183. package/dist/gtkx/bridge/geometry.js +6 -4
  184. package/dist/gtkx/bridge/geometry.js.map +1 -1
  185. package/dist/gtkx/bridge/index.d.ts +5 -3
  186. package/dist/gtkx/bridge/index.js +6 -3
  187. package/dist/gtkx/bridge/index.js.map +1 -1
  188. package/dist/gtkx/bridge/slot-portal.js.map +1 -1
  189. package/dist/gtkx/bridge/use-signal.js +1 -1
  190. package/dist/gtkx/bridge/use-signal.js.map +1 -1
  191. package/dist/gtkx/bridge/view-box.d.ts +56 -0
  192. package/dist/gtkx/bridge/view-box.js +394 -9
  193. package/dist/gtkx/bridge/view-box.js.map +1 -1
  194. package/dist/gtkx/bridge/widget-css.d.ts +17 -0
  195. package/dist/gtkx/bridge/widget-css.js +72 -0
  196. package/dist/gtkx/bridge/widget-css.js.map +1 -0
  197. package/dist/index.d.ts +3 -3
  198. package/dist/index.js +2 -2
  199. package/dist/index.js.map +1 -1
  200. package/dist/layout/driven-size.d.ts +12 -0
  201. package/dist/layout/driven-size.js +70 -0
  202. package/dist/layout/driven-size.js.map +1 -0
  203. package/dist/layout/engine.d.ts +17 -1
  204. package/dist/layout/engine.js +3 -20
  205. package/dist/layout/engine.js.map +1 -1
  206. package/dist/layout/node.d.ts +13 -0
  207. package/dist/layout/node.js +14 -0
  208. package/dist/layout/node.js.map +1 -1
  209. package/dist/layout/yoga.d.ts +3 -2
  210. package/dist/layout/yoga.js +5 -2
  211. package/dist/layout/yoga.js.map +1 -1
  212. package/dist/mcp/data/generated.d.ts +257 -60
  213. package/dist/mcp/data/generated.js +306 -62
  214. package/dist/mcp/data/generated.js.map +1 -1
  215. package/dist/metro/index.d.ts +10 -0
  216. package/dist/metro/index.js +21 -12
  217. package/dist/metro/index.js.map +1 -1
  218. package/dist/navigation/sidebar.d.ts +16 -0
  219. package/dist/navigation/sidebar.js +65 -1
  220. package/dist/navigation/sidebar.js.map +1 -1
  221. package/dist/reanimated-compat/animated-ref.d.ts +32 -0
  222. package/dist/reanimated-compat/animated-ref.js +57 -0
  223. package/dist/reanimated-compat/animated-ref.js.map +1 -0
  224. package/dist/reanimated-compat/animation.d.ts +177 -0
  225. package/dist/reanimated-compat/animation.js +540 -0
  226. package/dist/reanimated-compat/animation.js.map +1 -0
  227. package/dist/reanimated-compat/color.d.ts +28 -0
  228. package/dist/reanimated-compat/color.js +249 -0
  229. package/dist/reanimated-compat/color.js.map +1 -0
  230. package/dist/reanimated-compat/decay.d.ts +39 -0
  231. package/dist/reanimated-compat/decay.js +96 -0
  232. package/dist/reanimated-compat/decay.js.map +1 -0
  233. package/dist/reanimated-compat/easing.d.ts +30 -0
  234. package/dist/reanimated-compat/easing.js +76 -0
  235. package/dist/reanimated-compat/easing.js.map +1 -0
  236. package/dist/reanimated-compat/hooks.d.ts +14 -0
  237. package/dist/reanimated-compat/hooks.js +217 -0
  238. package/dist/reanimated-compat/hooks.js.map +1 -0
  239. package/dist/reanimated-compat/index.d.ts +258 -0
  240. package/dist/reanimated-compat/index.js +464 -0
  241. package/dist/reanimated-compat/index.js.map +1 -0
  242. package/dist/reanimated-compat/interpolation.d.ts +18 -0
  243. package/dist/reanimated-compat/interpolation.js +122 -0
  244. package/dist/reanimated-compat/interpolation.js.map +1 -0
  245. package/dist/reanimated-compat/layout-animation-config.d.ts +43 -0
  246. package/dist/reanimated-compat/layout-animation-config.js +78 -0
  247. package/dist/reanimated-compat/layout-animation-config.js.map +1 -0
  248. package/dist/reanimated-compat/layout-animation-presets.d.ts +117 -0
  249. package/dist/reanimated-compat/layout-animation-presets.js +498 -0
  250. package/dist/reanimated-compat/layout-animation-presets.js.map +1 -0
  251. package/dist/reanimated-compat/layout-animation-runtime.d.ts +21 -0
  252. package/dist/reanimated-compat/layout-animation-runtime.js +311 -0
  253. package/dist/reanimated-compat/layout-animation-runtime.js.map +1 -0
  254. package/dist/reanimated-compat/layout-animation-view.d.ts +27 -0
  255. package/dist/reanimated-compat/layout-animation-view.js +266 -0
  256. package/dist/reanimated-compat/layout-animation-view.js.map +1 -0
  257. package/dist/reanimated-compat/layout-animation.d.ts +171 -0
  258. package/dist/reanimated-compat/layout-animation.js +469 -0
  259. package/dist/reanimated-compat/layout-animation.js.map +1 -0
  260. package/dist/reanimated-compat/layout-transitions.d.ts +83 -0
  261. package/dist/reanimated-compat/layout-transitions.js +350 -0
  262. package/dist/reanimated-compat/layout-transitions.js.map +1 -0
  263. package/dist/reanimated-compat/mutable.d.ts +29 -0
  264. package/dist/reanimated-compat/mutable.js +139 -0
  265. package/dist/reanimated-compat/mutable.js.map +1 -0
  266. package/dist/reanimated-compat/props.d.ts +18 -0
  267. package/dist/reanimated-compat/props.js +111 -0
  268. package/dist/reanimated-compat/props.js.map +1 -0
  269. package/dist/reanimated-compat/scroll-handler.d.ts +119 -0
  270. package/dist/reanimated-compat/scroll-handler.js +274 -0
  271. package/dist/reanimated-compat/scroll-handler.js.map +1 -0
  272. package/dist/reanimated-compat/scroll-offset.d.ts +12 -0
  273. package/dist/reanimated-compat/scroll-offset.js +104 -0
  274. package/dist/reanimated-compat/scroll-offset.js.map +1 -0
  275. package/dist/reanimated-compat/style.d.ts +90 -0
  276. package/dist/reanimated-compat/style.js +452 -0
  277. package/dist/reanimated-compat/style.js.map +1 -0
  278. package/dist/reanimated-compat/test-timers.d.ts +28 -0
  279. package/dist/reanimated-compat/test-timers.js +136 -0
  280. package/dist/reanimated-compat/test-timers.js.map +1 -0
  281. package/dist/reanimated-compat/threads.d.ts +13 -0
  282. package/dist/reanimated-compat/threads.js +41 -0
  283. package/dist/reanimated-compat/threads.js.map +1 -0
  284. package/dist/reanimated-compat/tracking.d.ts +41 -0
  285. package/dist/reanimated-compat/tracking.js +106 -0
  286. package/dist/reanimated-compat/tracking.js.map +1 -0
  287. package/dist/reanimated-compat/updater-animations.d.ts +36 -0
  288. package/dist/reanimated-compat/updater-animations.js +273 -0
  289. package/dist/reanimated-compat/updater-animations.js.map +1 -0
  290. package/dist/responder/system.d.ts +80 -7
  291. package/dist/responder/system.js +143 -23
  292. package/dist/responder/system.js.map +1 -1
  293. package/dist/responder/use-responder.d.ts +11 -0
  294. package/dist/responder/use-responder.js +225 -6
  295. package/dist/responder/use-responder.js.map +1 -1
  296. package/dist/runner/host-dev.js +1 -1
  297. package/dist/runner/host-dev.js.map +1 -1
  298. package/dist/runner/host.js +1 -1
  299. package/dist/runner/host.js.map +1 -1
  300. package/dist/runner/index.js +3 -3
  301. package/dist/runner/index.js.map +1 -1
  302. package/dist/sea/gtkx-config-module.js +1 -1
  303. package/dist/sea/gtkx-config-module.js.map +1 -1
  304. package/dist/style/absolute-insets.d.ts +26 -0
  305. package/dist/style/absolute-insets.js +113 -0
  306. package/dist/style/absolute-insets.js.map +1 -0
  307. package/dist/style/animated-size.d.ts +25 -0
  308. package/dist/style/animated-size.js +225 -0
  309. package/dist/style/animated-size.js.map +1 -0
  310. package/dist/style/colors.d.ts +17 -0
  311. package/dist/style/colors.js +25 -6
  312. package/dist/style/colors.js.map +1 -1
  313. package/dist/style/imperative-css.d.ts +21 -0
  314. package/dist/style/imperative-css.js +63 -0
  315. package/dist/style/imperative-css.js.map +1 -0
  316. package/dist/style/index.d.ts +2 -1
  317. package/dist/style/index.js +2 -1
  318. package/dist/style/index.js.map +1 -1
  319. package/dist/style/split-style.js +5 -0
  320. package/dist/style/split-style.js.map +1 -1
  321. package/dist/testing/index.js.map +1 -1
  322. package/dist/unsupported-export.d.ts +8 -0
  323. package/dist/unsupported-export.js +66 -0
  324. package/dist/unsupported-export.js.map +1 -0
  325. package/dist/vite/index.d.ts +21 -8
  326. package/dist/vite/index.js +127 -82
  327. package/dist/vite/index.js.map +1 -1
  328. package/dist/vitest/index.d.ts +2 -2
  329. package/dist/vitest/index.js.map +1 -1
  330. package/dist/worklets-compat/index.d.ts +4 -0
  331. package/dist/worklets-compat/index.js +28 -0
  332. package/dist/worklets-compat/index.js.map +1 -0
  333. package/dist/worklets-compat/surface.d.ts +110 -0
  334. package/dist/worklets-compat/surface.js +153 -0
  335. package/dist/worklets-compat/surface.js.map +1 -0
  336. package/package.json +25 -9
  337. package/types.d.ts +9 -0
  338. package/dist/common/list.d.ts +0 -82
  339. package/dist/common/list.js +0 -166
  340. package/dist/common/list.js.map +0 -1
@@ -0,0 +1,1056 @@
1
+ import { GESTURE_STATE, POINTER_TYPE, TOUCH_EVENT_TYPE, } from "./types";
2
+ const newRuntime = () => ({
3
+ state: GESTURE_STATE.UNDETERMINED,
4
+ oldState: GESTURE_STATE.UNDETERMINED,
5
+ pressTime: 0,
6
+ originX: 0,
7
+ originY: 0,
8
+ pressX: 0,
9
+ pressY: 0,
10
+ lastX: 0,
11
+ lastY: 0,
12
+ lastTime: 0,
13
+ velocityX: 0,
14
+ velocityY: 0,
15
+ changeFromX: 0,
16
+ changeFromY: 0,
17
+ hasEmittedUpdate: false,
18
+ hasActivated: false,
19
+ activationTimer: null,
20
+ timerElapsed: false,
21
+ pointerCount: 0,
22
+ maxPointerCount: 0,
23
+ taps: 0,
24
+ delayTimer: null,
25
+ authorized: false,
26
+ hasResponder: false,
27
+ forcedFailure: false,
28
+ scale: 1,
29
+ rotation: 0,
30
+ scaleVelocity: 0,
31
+ rotationVelocity: 0,
32
+ changeFromScale: 1,
33
+ changeFromRotation: 0,
34
+ force: 0,
35
+ changeFromForce: 0,
36
+ lastSampleTime: 0,
37
+ });
38
+ /**
39
+ * RNGH's `checkHitSlop`, restated: build the acceptable box in the view's own
40
+ * coordinates, then test the pointer's offset into the view against it.
41
+ *
42
+ * Every side is "outward is positive", so a NEGATIVE value shrinks the box —
43
+ * the capability RN's own View `hitSlop` does not have. Explicit sides
44
+ * override `horizontal`/`vertical`, and `width`/`height` then anchor the
45
+ * opposite edge to whichever side was named.
46
+ *
47
+ * The plain-number spelling is normalised into the four sides up front.
48
+ * Upstream's web path does not do this and silently ignores a number, which
49
+ * is a bug rather than a semantic worth reproducing — its own native paths
50
+ * normalise exactly this way.
51
+ */
52
+ export const hitSlopRect = (bounds, hitSlop) => {
53
+ if (hitSlop === undefined || hitSlop === null) {
54
+ return bounds;
55
+ }
56
+ const slop = typeof hitSlop === "number"
57
+ ? { left: hitSlop, right: hitSlop, top: hitSlop, bottom: hitSlop }
58
+ : hitSlop;
59
+ let left = 0;
60
+ let top = 0;
61
+ let right = bounds.width;
62
+ let bottom = bounds.height;
63
+ if (slop.horizontal !== undefined) {
64
+ left -= slop.horizontal;
65
+ right += slop.horizontal;
66
+ }
67
+ if (slop.vertical !== undefined) {
68
+ top -= slop.vertical;
69
+ bottom += slop.vertical;
70
+ }
71
+ if (slop.left !== undefined) {
72
+ left = -slop.left;
73
+ }
74
+ if (slop.right !== undefined) {
75
+ right = bounds.width + slop.right;
76
+ }
77
+ if (slop.top !== undefined) {
78
+ top = -slop.top;
79
+ }
80
+ if (slop.bottom !== undefined) {
81
+ bottom = bounds.height + slop.bottom;
82
+ }
83
+ if (slop.width !== undefined) {
84
+ if (slop.left !== undefined) {
85
+ right = left + slop.width;
86
+ }
87
+ else if (slop.right !== undefined) {
88
+ left = right - slop.width;
89
+ }
90
+ }
91
+ if (slop.height !== undefined) {
92
+ if (slop.top !== undefined) {
93
+ bottom = top + slop.height;
94
+ }
95
+ else if (slop.bottom !== undefined) {
96
+ top = bottom - slop.height;
97
+ }
98
+ }
99
+ return {
100
+ x: bounds.x + left,
101
+ y: bounds.y + top,
102
+ width: right - left,
103
+ height: bottom - top,
104
+ };
105
+ };
106
+ const contains = (rect, x, y) => x >= rect.x &&
107
+ x <= rect.x + rect.width &&
108
+ y >= rect.y &&
109
+ y <= rect.y + rect.height;
110
+ /**
111
+ * Builds the machine. `readConfig` is called on every event rather than
112
+ * captured, so a re-render that hands the detector a fresh gesture object —
113
+ * which is what both spellings produce, every render — takes effect without
114
+ * swapping the handler set mid-drag.
115
+ */
116
+ export const createRecognizer = (handlerTag, decider, readConfig, env) => {
117
+ let runtime = newRuntime();
118
+ const isEnabled = () => readConfig().enabled !== false;
119
+ /**
120
+ * What kind of device this gesture's events come from.
121
+ *
122
+ * `MOUSE` for everything except `ForceTouch`, and that is not a default with
123
+ * a hole in it: this platform has one pointer and no touch injection, so
124
+ * every other kind really is a mouse. A pressure reading can only have come
125
+ * from a tablet tool, so the one kind that reads pressure reports `STYLUS`.
126
+ */
127
+ const pointerType = decider.source === "stylus" ? POINTER_TYPE.STYLUS : POINTER_TYPE.MOUSE;
128
+ const setState = (next) => {
129
+ runtime.oldState = runtime.state;
130
+ runtime.state = next;
131
+ };
132
+ const viewOf = () => ({
133
+ translationX: runtime.lastX - runtime.originX,
134
+ translationY: runtime.lastY - runtime.originY,
135
+ distanceFromPress: Math.hypot(runtime.lastX - runtime.pressX, runtime.lastY - runtime.pressY),
136
+ velocityX: runtime.velocityX,
137
+ velocityY: runtime.velocityY,
138
+ timerElapsed: runtime.timerElapsed,
139
+ pointerCount: runtime.pointerCount,
140
+ maxPointerCount: runtime.maxPointerCount,
141
+ taps: runtime.taps,
142
+ scale: runtime.scale,
143
+ rotation: runtime.rotation,
144
+ force: runtime.force,
145
+ });
146
+ /**
147
+ * One payload, from a point rather than from an event.
148
+ *
149
+ * A timer has no event to build one from and every one of the three kinds
150
+ * has a timer that can end the gesture — `Tap`'s `maxDuration` and
151
+ * `maxDelay` both finalize with no pointer in flight. Fabricating a
152
+ * `GestureResponderEvent` to feed an event-shaped builder would have put an
153
+ * invented event into the responder system's own vocabulary; taking the
154
+ * three numbers the payload actually needs does not.
155
+ */
156
+ const payloadAt = (pageX, pageY, pointerCount, fallback) => {
157
+ const bounds = env.boundsInWindow();
158
+ const translationX = pageX - runtime.originX;
159
+ const translationY = pageY - runtime.originY;
160
+ return {
161
+ handlerTag,
162
+ numberOfPointers: pointerCount,
163
+ pointerType,
164
+ state: runtime.state,
165
+ oldState: runtime.oldState,
166
+ // RNGH's `x`/`y` are relative to the GESTURE's view. The responder
167
+ // event's own `locationX` is relative to whichever widget carried the
168
+ // event, which is the deepest one with a controller and not always this
169
+ // one — so it is recomputed rather than passed through.
170
+ x: bounds ? pageX - bounds.x : (fallback?.locationX ?? pageX),
171
+ y: bounds ? pageY - bounds.y : (fallback?.locationY ?? pageY),
172
+ absoluteX: pageX,
173
+ absoluteY: pageY,
174
+ translationX,
175
+ translationY,
176
+ velocityX: runtime.velocityX,
177
+ velocityY: runtime.velocityY,
178
+ // On the first update the change IS the translation, which is
179
+ // upstream's rule rather than an approximation of it.
180
+ changeX: runtime.hasEmittedUpdate
181
+ ? translationX - runtime.changeFromX
182
+ : translationX,
183
+ changeY: runtime.hasEmittedUpdate
184
+ ? translationY - runtime.changeFromY
185
+ : translationY,
186
+ duration: Date.now() - runtime.pressTime,
187
+ scale: runtime.scale,
188
+ // A RATIO, not a difference — scale composes by multiplication, and
189
+ // upstream's `changeEventCalculator` for `Pinch` divides where the one
190
+ // for `Rotation` subtracts. On the first update it is the scale itself,
191
+ // which is upstream's rule for both and the same rule `changeX` follows.
192
+ scaleChange: runtime.hasEmittedUpdate
193
+ ? runtime.scale / runtime.changeFromScale
194
+ : runtime.scale,
195
+ // The focal point IS the position for these two kinds: the touchpad
196
+ // channel writes the gesture's bounding-box centre into the same
197
+ // last-position fields the pointer kinds write a touch into, so `x`/`y`
198
+ // and `focalX`/`focalY` are the same number arrived at the same way.
199
+ focalX: bounds ? pageX - bounds.x : (fallback?.locationX ?? pageX),
200
+ focalY: bounds ? pageY - bounds.y : (fallback?.locationY ?? pageY),
201
+ rotation: runtime.rotation,
202
+ rotationChange: runtime.hasEmittedUpdate
203
+ ? runtime.rotation - runtime.changeFromRotation
204
+ : runtime.rotation,
205
+ anchorX: bounds ? pageX - bounds.x : (fallback?.locationX ?? pageX),
206
+ anchorY: bounds ? pageY - bounds.y : (fallback?.locationY ?? pageY),
207
+ force: runtime.force,
208
+ // A DIFFERENCE, like `rotationChange` and unlike `scaleChange`, because
209
+ // upstream's `changeEventCalculator` for `ForceTouch` subtracts. On the
210
+ // first update it is the force itself, which is upstream's rule there
211
+ // and the same rule `changeX` follows.
212
+ forceChange: runtime.hasEmittedUpdate
213
+ ? runtime.force - runtime.changeFromForce
214
+ : runtime.force,
215
+ velocity: decider.kind === "rotation"
216
+ ? runtime.rotationVelocity
217
+ : runtime.scaleVelocity,
218
+ };
219
+ };
220
+ const payloadOf = (event) => {
221
+ const { pageX, pageY, locationX, locationY } = event.nativeEvent;
222
+ return payloadAt(pageX, pageY, event.nativeEvent.touches.length, {
223
+ locationX,
224
+ locationY,
225
+ });
226
+ };
227
+ /** The payload for a decision a timer made, from the last known position. */
228
+ const payloadNow = () => payloadAt(runtime.lastX, runtime.lastY, runtime.pointerCount);
229
+ const touchEventOf = (event, eventType) => {
230
+ const bounds = env.boundsInWindow();
231
+ const toData = (touch) => ({
232
+ id: touch.identifier,
233
+ x: bounds ? touch.pageX - bounds.x : touch.pageX,
234
+ y: bounds ? touch.pageY - bounds.y : touch.pageY,
235
+ absoluteX: touch.pageX,
236
+ absoluteY: touch.pageY,
237
+ });
238
+ return {
239
+ handlerTag,
240
+ numberOfTouches: event.nativeEvent.touches.length,
241
+ eventType,
242
+ allTouches: event.nativeEvent.touches.map(toData),
243
+ changedTouches: event.nativeEvent.changedTouches.map(toData),
244
+ state: runtime.state,
245
+ pointerType,
246
+ };
247
+ };
248
+ const clearActivationTimer = () => {
249
+ if (runtime.activationTimer !== null) {
250
+ clearTimeout(runtime.activationTimer);
251
+ runtime.activationTimer = null;
252
+ }
253
+ };
254
+ const clearDelayTimer = () => {
255
+ if (runtime.delayTimer !== null) {
256
+ clearTimeout(runtime.delayTimer);
257
+ runtime.delayTimer = null;
258
+ }
259
+ };
260
+ const track = (event) => {
261
+ const { pageX, pageY, timestamp } = event.nativeEvent;
262
+ const elapsed = timestamp - runtime.lastTime;
263
+ if (elapsed > 0) {
264
+ runtime.velocityX = ((pageX - runtime.lastX) / elapsed) * 1000;
265
+ runtime.velocityY = ((pageY - runtime.lastY) / elapsed) * 1000;
266
+ }
267
+ runtime.lastX = pageX;
268
+ runtime.lastY = pageY;
269
+ runtime.lastTime = timestamp;
270
+ countPointers(event);
271
+ };
272
+ const countPointers = (event) => {
273
+ runtime.pointerCount = event.nativeEvent.touches.length;
274
+ runtime.maxPointerCount = Math.max(runtime.maxPointerCount, runtime.pointerCount);
275
+ };
276
+ /**
277
+ * Every exit from a live gesture goes through here exactly once, and this is
278
+ * where the tap sequence dies: `newRuntime()` drops `taps` along with
279
+ * everything else, so a finalized multi-tap starts counting from zero.
280
+ */
281
+ const finalize = (payloadFor, success) => {
282
+ if (runtime.state === GESTURE_STATE.UNDETERMINED) {
283
+ // Already over. The responder system dispatches `onResponderRelease` to
284
+ // whoever holds the lock at the end of the interaction, and a gesture
285
+ // can have cancelled itself while still holding it — a `LongPress` that
286
+ // wandered past `maxDistance`, or one the orchestrator cancelled. One
287
+ // exit per gesture, and this is where the second one stops.
288
+ return;
289
+ }
290
+ const config = readConfig();
291
+ clearActivationTimer();
292
+ clearDelayTimer();
293
+ // Read the flag, not the state: a cancellation sets the state to
294
+ // CANCELLED on its way in, and an ACTIVE gesture that gets cancelled must
295
+ // still be told it deactivated.
296
+ const wasActive = runtime.hasActivated;
297
+ if (success) {
298
+ setState(GESTURE_STATE.END);
299
+ }
300
+ // Built AFTER the transition, so `event.state` is the state the gesture
301
+ // ended in — which is what a consumer comparing it against `State` reads.
302
+ const payload = payloadFor();
303
+ // The deactivation callback is the ACTIVE gesture's ending. A gesture
304
+ // that never activated gets `onFinalize` and nothing else, which is what
305
+ // lets a consumer distinguish a drag that finished from one that never
306
+ // happened — `react-native-reanimated-dnd` relies on exactly that.
307
+ if (wasActive) {
308
+ config.onDeactivate?.(payload, success);
309
+ }
310
+ config.onFinalize?.(payload, success);
311
+ // Told AFTER the callbacks and BEFORE the reset. After, so a gesture this
312
+ // failure releases from `awaiting` starts where the previous one left off
313
+ // rather than in the middle of it; before, so the loop is handed the state
314
+ // the gesture actually ended in — END releases nobody and cancels the
315
+ // waiters, FAILED and CANCELLED release them.
316
+ env.orchestrator.finished(participant, runtime.state);
317
+ runtime = newRuntime();
318
+ };
319
+ const finalizeAt = (event, success) => {
320
+ finalize(() => payloadOf(event), success);
321
+ };
322
+ const fail = (event) => {
323
+ clearActivationTimer();
324
+ setState(GESTURE_STATE.FAILED);
325
+ finalize(() => payloadOf(event), false);
326
+ };
327
+ /** A timer decided this, so there is no event and the position is the last one. */
328
+ const failNow = () => {
329
+ clearActivationTimer();
330
+ setState(GESTURE_STATE.FAILED);
331
+ finalize(payloadNow, false);
332
+ };
333
+ /** Whether this kind takes the interaction when it activates. */
334
+ const claimsResponder = decider.claimsResponder !== false;
335
+ /**
336
+ * Everything becoming ACTIVE means, from a position rather than an event.
337
+ *
338
+ * Three ways in now, and the third is the whole of slice 3: `onResponderGrant`
339
+ * for a recognizer that took the lock, `authorize()` for one that never will
340
+ * (`claimsResponder: false`), and `authorize()` again for one that WOULD have
341
+ * but was told the interaction is already held by a gesture it is
342
+ * simultaneous with. Everything after the transition is identical, because
343
+ * being ACTIVE is a fact about the gesture and not about the lock.
344
+ */
345
+ const enterActive = (pageX, pageY) => {
346
+ setState(GESTURE_STATE.ACTIVE);
347
+ runtime.authorized = false;
348
+ clearActivationTimer();
349
+ clearDelayTimer();
350
+ // Translation is measured from where the gesture BECAME ACTIVE, not
351
+ // from the press: a pan with `activeOffsetY([-10, 10])` that reported
352
+ // 10px of travel the moment it started would jump the content by the
353
+ // threshold on every drag. With `activateAfterLongPress` the pointer
354
+ // has not moved when the timer grants, so the two origins coincide —
355
+ // which is only true because the grant no longer waits for a move.
356
+ runtime.originX = pageX;
357
+ runtime.originY = pageY;
358
+ runtime.lastX = pageX;
359
+ runtime.lastY = pageY;
360
+ runtime.changeFromX = 0;
361
+ runtime.changeFromY = 0;
362
+ runtime.hasEmittedUpdate = false;
363
+ runtime.hasActivated = true;
364
+ };
365
+ /**
366
+ * `Fling`'s ending, which is the same instant as its activation.
367
+ *
368
+ * Called at the tail of every path into ACTIVE, so the one-shot kind behaves
369
+ * identically however it got there — a grant, an authorization without one,
370
+ * or a controller channel. Upstream reaches this by overriding `activate()`
371
+ * to call `end()`; a flag on the decider is the same statement without a
372
+ * subclass to put it on.
373
+ */
374
+ const endIfOneShot = () => {
375
+ if (decider.endsOnActivate === true) {
376
+ finalize(payloadNow, true);
377
+ }
378
+ };
379
+ /** Becoming ACTIVE without a grant to carry the position or the payload. */
380
+ const activateHere = () => {
381
+ enterActive(runtime.lastX, runtime.lastY);
382
+ const payload = payloadNow();
383
+ // Mutual exclusion is enforced here rather than at the moment permission
384
+ // was given, because permission is not always the same instant: a gesture
385
+ // that has to take the responder becomes ACTIVE only if the negotiation
386
+ // grants it, and an ancestor can still win.
387
+ env.orchestrator.activated(participant);
388
+ readConfig().onActivate?.(payload);
389
+ endIfOneShot();
390
+ };
391
+ /**
392
+ * The orchestrator's answer, and the only place the two locks touch.
393
+ *
394
+ * `needsResponder` false means another gesture already holds the interaction
395
+ * and this one was cleared to run alongside it. It does NOT ask for the
396
+ * responder: the lock is single-holder by design, GTK has already been told
397
+ * everything it can be told, and winning the lock would take the interaction
398
+ * away from the gesture this one was written to accompany.
399
+ *
400
+ * A recognizer that never claims takes the same path for its own reason —
401
+ * it is not asking for the interaction, it is reporting that the widget
402
+ * underneath it has taken one.
403
+ */
404
+ const authorize = (needsResponder) => {
405
+ if (runtime.state !== GESTURE_STATE.BEGAN) {
406
+ return;
407
+ }
408
+ runtime.authorized = true;
409
+ if (!claimsResponder || !needsResponder) {
410
+ activateHere();
411
+ return;
412
+ }
413
+ // The out-of-event channel, asked from inside a touch event as well as
414
+ // from a timer, and that uniformity is not laziness. Slice 1 could leave
415
+ // an in-event activation to the negotiation the responder system runs
416
+ // after the touch props, because there was only ever one gesture asking.
417
+ // With two, both would defer into the same negotiation, exactly one would
418
+ // win it, and the loser would sit authorized and never activate — so
419
+ // `Simultaneous` would silently be a race. Asking here settles the lock
420
+ // before the second gesture is even consulted, which is what lets it be
421
+ // told the interaction is already taken.
422
+ //
423
+ // It is the same negotiation either way: `requestResponder` reuses
424
+ // `negotiateAndTransfer` unchanged, so capture still beats bubble and an
425
+ // ancestor can still win.
426
+ env.requestResponder();
427
+ };
428
+ /**
429
+ * The one place a recognizer asks to become active, and therefore the seam
430
+ * the orchestrator sits in.
431
+ *
432
+ * Every path that used to take the responder now goes through here first:
433
+ * the gesture's own criteria being met is a request, not a decision. What
434
+ * comes back is `authorize()` (with or without the lock), a parking in
435
+ * `awaiting` that leaves the gesture BEGAN holding nothing, or a
436
+ * cancellation. `Native` goes through it too — a gesture that never takes
437
+ * the lock still takes part in arbitration, and it is the one kind that can
438
+ * cancel an already-active gesture.
439
+ */
440
+ const decide = () => {
441
+ if (runtime.authorized || runtime.state !== GESTURE_STATE.BEGAN) {
442
+ return;
443
+ }
444
+ env.orchestrator.tryActivate(participant);
445
+ };
446
+ /** Cancelled by the loop: another gesture won, or the one it waited for ended. */
447
+ const cancelFromOrchestrator = () => {
448
+ if (runtime.state !== GESTURE_STATE.BEGAN &&
449
+ runtime.state !== GESTURE_STATE.ACTIVE) {
450
+ return;
451
+ }
452
+ setState(GESTURE_STATE.CANCELLED);
453
+ finalize(payloadNow, false);
454
+ };
455
+ const participant = {
456
+ tag: handlerTag,
457
+ kind: decider.kind,
458
+ authorize,
459
+ holdsResponder: () => runtime.hasResponder,
460
+ cancel: cancelFromOrchestrator,
461
+ };
462
+ /**
463
+ * The state, read through a call.
464
+ *
465
+ * Not decoration: `decide()` can change the state before it returns — the
466
+ * orchestrator may activate the gesture, or cancel it, synchronously — and a
467
+ * narrowed `runtime.state` read straight afterwards is a fact the compiler
468
+ * believes and the runtime does not.
469
+ */
470
+ const stateNow = () => runtime.state;
471
+ /**
472
+ * The four transitions an app may drive by hand — upstream's
473
+ * `GestureStateManager`, and the whole of what `Gesture.Manual()` is.
474
+ *
475
+ * These used to be two real transitions and two deferrals: `fail()` and
476
+ * `end()` both set a flag that the next `onTouchMove` noticed, which was
477
+ * enough while the only caller was a `Pan` narrowing its own criteria, and
478
+ * is not enough for a gesture that has NO criteria. `Manual` is driven
479
+ * entirely from here, so each of the four has to mean what upstream's means,
480
+ * at the moment it is called:
481
+ *
482
+ * begin UNDETERMINED -> BEGAN
483
+ * activate BEGAN -> ACTIVE, through the orchestrator like every other
484
+ * activation — a manual gesture takes part in arbitration
485
+ * rather than bypassing it, which is the point of having it
486
+ * end BEGAN or ACTIVE -> END, successfully
487
+ * fail BEGAN or ACTIVE -> FAILED
488
+ *
489
+ * `activate()` is FORCED past `manualActivation`, exactly as upstream's web
490
+ * state manager calls `handler.activate(true)`: that flag exists to stop the
491
+ * gesture's own predicates activating it, and this is not them.
492
+ */
493
+ const stateManager = {
494
+ begin: () => {
495
+ if (runtime.state === GESTURE_STATE.UNDETERMINED) {
496
+ setState(GESTURE_STATE.BEGAN);
497
+ readConfig().onBegin?.(payloadNow());
498
+ }
499
+ },
500
+ activate: () => {
501
+ // Upstream forces the BEGAN step first when asked to activate a gesture
502
+ // that never began, "to preserve the correct state transition flow" —
503
+ // and here it also matters that the orchestrator has been told about the
504
+ // gesture, which `record()` on the press is what does.
505
+ if (runtime.state === GESTURE_STATE.UNDETERMINED) {
506
+ setState(GESTURE_STATE.BEGAN);
507
+ env.orchestrator.record(participant);
508
+ readConfig().onBegin?.(payloadNow());
509
+ }
510
+ if (runtime.state === GESTURE_STATE.BEGAN) {
511
+ decide();
512
+ }
513
+ },
514
+ fail: () => {
515
+ if (runtime.state === GESTURE_STATE.BEGAN ||
516
+ runtime.state === GESTURE_STATE.ACTIVE) {
517
+ // Kept as well as acted on: a gesture failed from inside
518
+ // `onTouchesDown` has not reached the move that would have consulted
519
+ // its predicates, and the flag is what stops a later move reviving it.
520
+ runtime.forcedFailure = true;
521
+ failNow();
522
+ }
523
+ },
524
+ end: () => {
525
+ if (runtime.state === GESTURE_STATE.BEGAN ||
526
+ runtime.state === GESTURE_STATE.ACTIVE) {
527
+ runtime.forcedFailure = true;
528
+ finalize(payloadNow, true);
529
+ }
530
+ },
531
+ };
532
+ const outsideBounds = (event) => {
533
+ const bounds = env.boundsInWindow();
534
+ if (bounds === null) {
535
+ // Before the first layout there is no truth to test against, and
536
+ // refusing every press would be worse than accepting them.
537
+ return false;
538
+ }
539
+ const { pageX, pageY } = event.nativeEvent;
540
+ return !contains(hitSlopRect(bounds, readConfig().hitSlop), pageX, pageY);
541
+ };
542
+ const pointerCountAllowed = (event) => {
543
+ const config = readConfig();
544
+ const count = event.nativeEvent.touches.length;
545
+ return (count >= (config.minPointers ?? 1) && count <= (config.maxPointers ?? 10));
546
+ };
547
+ /**
548
+ * One ACTIVE gesture's reaction to the pointer moving, from whichever
549
+ * channel reached it: `onResponderMove` for the lock's holder,
550
+ * `onTouchMove` for a recognizer that never takes it.
551
+ *
552
+ * Returns nothing; the gesture may have finalized itself on the way out.
553
+ */
554
+ const advance = (event) => {
555
+ track(event);
556
+ const config = readConfig();
557
+ if ((config.shouldCancelWhenOutside === true && outsideBounds(event)) ||
558
+ // `LongPress` is the one kind that can be cancelled by movement it has
559
+ // already accepted: `maxDistance` keeps applying once the press has
560
+ // matured, and travelling past it while ACTIVE is a cancellation
561
+ // rather than a failure.
562
+ decider.shouldCancelWhileActive?.(viewOf(), config) === true) {
563
+ setState(GESTURE_STATE.CANCELLED);
564
+ finalizeAt(event, false);
565
+ return;
566
+ }
567
+ const payload = payloadOf(event);
568
+ // The event that GRANTED the responder reaches here too — the system
569
+ // dispatches the move to whoever holds the lock after the handoff, in
570
+ // the same event. Emitting it would be an update of zero travel that
571
+ // `onActivate` has just reported the position of, and it would make
572
+ // upstream's rule that the FIRST change equals the translation vacuous:
573
+ // that first delta would always be zero. So the activation event is not
574
+ // an update, and the first real move is.
575
+ if (!runtime.hasEmittedUpdate &&
576
+ payload.translationX === 0 &&
577
+ payload.translationY === 0) {
578
+ return;
579
+ }
580
+ config.onUpdate?.(payload);
581
+ config.onChange?.(payload);
582
+ runtime.changeFromX = payload.translationX;
583
+ runtime.changeFromY = payload.translationY;
584
+ runtime.hasEmittedUpdate = true;
585
+ };
586
+ const handlers = {
587
+ // Always false. A pan that takes the interaction on press cannot honour
588
+ // an `activeOffset`, and letting a sibling or an ancestor win instead is
589
+ // the behaviour every offset knob exists to produce.
590
+ onStartShouldSetResponder: () => false,
591
+ onTouchStart: (event) => {
592
+ const config = readConfig();
593
+ if (!isEnabled() || outsideBounds(event)) {
594
+ return;
595
+ }
596
+ // A tap sequence in progress carries its count across the press. The
597
+ // delay timer's existence IS the sequence — while it is armed the
598
+ // gesture is between taps, still BEGAN, and this press is the next one
599
+ // rather than a fresh gesture.
600
+ const continuing = runtime.delayTimer !== null && runtime.state === GESTURE_STATE.BEGAN;
601
+ const carriedTaps = continuing ? runtime.taps : 0;
602
+ clearDelayTimer();
603
+ runtime = newRuntime();
604
+ runtime.taps = carriedTaps;
605
+ const { pageX, pageY, timestamp } = event.nativeEvent;
606
+ runtime.pressTime = Date.now();
607
+ runtime.originX = pageX;
608
+ runtime.originY = pageY;
609
+ runtime.pressX = pageX;
610
+ runtime.pressY = pageY;
611
+ runtime.lastX = pageX;
612
+ runtime.lastY = pageY;
613
+ runtime.lastTime = timestamp;
614
+ countPointers(event);
615
+ setState(GESTURE_STATE.BEGAN);
616
+ // Recorded on the press, not on mount. That is the whole islands answer
617
+ // in one line: a gesture takes part in an interaction only when the
618
+ // interaction's pointer reaches it, so a relation naming a gesture in
619
+ // another `Root` — or one the pointer never went near — never has an
620
+ // occasion to apply and can never park anything for ever. Recording on
621
+ // mount would have made `requireExternalGestureToFail` across two
622
+ // islands a permanent deadlock. See docs/api.md.
623
+ env.orchestrator.record(participant);
624
+ // Upstream begins once per SEQUENCE, not once per tap: its `begin()` is
625
+ // reached only from the UNDETERMINED branch, so the second press of a
626
+ // double tap gets no second `onBegin`.
627
+ if (!continuing) {
628
+ config.onBegin?.(payloadOf(event));
629
+ }
630
+ config.onTouchesDown?.(touchEventOf(event, TOUCH_EVENT_TYPE.TOUCHES_DOWN), stateManager);
631
+ const timer = decider.timer?.(config) ?? null;
632
+ if (timer !== null) {
633
+ runtime.activationTimer = setTimeout(() => {
634
+ runtime.activationTimer = null;
635
+ if (runtime.state !== GESTURE_STATE.BEGAN) {
636
+ return;
637
+ }
638
+ runtime.timerElapsed = true;
639
+ if (timer.elapsed === "fail") {
640
+ // `Tap`'s `maxDuration`: the pointer is still down and the tap is
641
+ // already too slow. There is no event to fail with.
642
+ failNow();
643
+ return;
644
+ }
645
+ if (readConfig().manualActivation === true) {
646
+ return;
647
+ }
648
+ // Asked rather than assumed: the timer maturing is one criterion
649
+ // among the kind's own, and `LongPress` also wants the right number
650
+ // of pointers down before it will take the gesture.
651
+ if (!decider.shouldActivate(viewOf(), readConfig())) {
652
+ return;
653
+ }
654
+ // THE OUT-OF-EVENT GRANT. Without it the gesture could not take the
655
+ // interaction until the pointer next moved — one frame late for a
656
+ // drag, and never for a press-and-hold that stays still. This is
657
+ // the single extension slice 1 makes to the responder model;
658
+ // docs/research/gestures.md records it with its reason.
659
+ decide();
660
+ }, timer.delay);
661
+ }
662
+ // A recognizer that never claims may activate on the press itself, and
663
+ // `Gesture.Native().shouldActivateOnStart(true)` is the shape that
664
+ // wants to: a native BUTTON takes the press at once rather than waiting
665
+ // to see whether the pointer travels.
666
+ //
667
+ // Deliberately not asked for the claiming kinds, and that is the same
668
+ // rule `onStartShouldSetResponder` states by always answering false — a
669
+ // gesture that grabbed the interaction on press cannot honour an
670
+ // `activeOffset`, and deciding late is what every offset knob is for.
671
+ // Nothing is being decided late here, because nothing is being taken.
672
+ if (!claimsResponder &&
673
+ config.manualActivation !== true &&
674
+ decider.shouldActivate(viewOf(), config)) {
675
+ decide();
676
+ }
677
+ },
678
+ onTouchMove: (event) => {
679
+ if (runtime.state === GESTURE_STATE.ACTIVE) {
680
+ // A gesture that does not hold the lock has no `onResponderMove`
681
+ // coming, so this is where its updates live. One that does is driven
682
+ // from there instead, and emitting here as well would double every
683
+ // update it reports.
684
+ //
685
+ // Two kinds of gesture end up here and the flag covers both: one that
686
+ // never takes the lock (`claimsResponder: false`), and one that would
687
+ // have but is ACTIVE alongside a gesture it is `Simultaneous` with.
688
+ // Without the second, `Simultaneous` would be a state with no events
689
+ // attached to it.
690
+ if (!runtime.hasResponder) {
691
+ advance(event);
692
+ }
693
+ return;
694
+ }
695
+ if (runtime.state !== GESTURE_STATE.BEGAN) {
696
+ // A finished gesture is not tracked at all.
697
+ return;
698
+ }
699
+ track(event);
700
+ const config = readConfig();
701
+ config.onTouchesMove?.(touchEventOf(event, TOUCH_EVENT_TYPE.TOUCHES_MOVE), stateManager);
702
+ const view = viewOf();
703
+ // Failure is tested before activation, and the comparisons differ —
704
+ // failure is strict, activation is not — so a translation sitting
705
+ // exactly on a bound activates rather than fails. That asymmetry is
706
+ // upstream's and is reproduced rather than tidied.
707
+ if (runtime.forcedFailure ||
708
+ !pointerCountAllowed(event) ||
709
+ // Upstream fails a BEGAN handler that leaves the view, not just an
710
+ // ACTIVE one — `onPointerLeave` splits exactly that way. It is the
711
+ // default for `Tap` and `LongPress`, which is why it surfaces here;
712
+ // `Pan` leaves the flag off unless an app sets it.
713
+ (config.shouldCancelWhenOutside === true && outsideBounds(event)) ||
714
+ decider.shouldFail(view, config)) {
715
+ fail(event);
716
+ return;
717
+ }
718
+ if (config.manualActivation === true) {
719
+ return;
720
+ }
721
+ if (decider.shouldActivate(view, config)) {
722
+ decide();
723
+ }
724
+ },
725
+ // The instant of decision. Everything above ran holding nothing.
726
+ onMoveShouldSetResponder: () => {
727
+ // `Native` never answers yes: winning here is what makes the platform
728
+ // claim the GTK sequence and suspend the scroller it is reporting on.
729
+ if (!claimsResponder) {
730
+ return false;
731
+ }
732
+ if (runtime.state !== GESTURE_STATE.BEGAN || !isEnabled()) {
733
+ return false;
734
+ }
735
+ // The orchestrator's answer and nothing else. Slice 1 asked the decider
736
+ // a second time from here, on the reasoning that a move arriving while
737
+ // another view held the responder had not reached the decision above —
738
+ // which was never true (system.ts dispatches the touch props BEFORE it
739
+ // negotiates, in both entry points) and is now actively wrong: a gesture
740
+ // parked behind `requireExternalGestureToFail` still meets its own
741
+ // criteria, and answering them here would let it take the lock it was
742
+ // told to wait for.
743
+ return runtime.authorized;
744
+ },
745
+ onResponderGrant: (event) => {
746
+ // The host holds the responder whoever asked for it — the recognizer's
747
+ // props are merged into the child's, so a child with responder props of
748
+ // its own can be the reason this fired.
749
+ runtime.hasResponder = true;
750
+ if (!runtime.authorized) {
751
+ // Not this gesture's grant. Becoming ACTIVE is the orchestrator's to
752
+ // authorize, and it has not.
753
+ return;
754
+ }
755
+ enterActive(event.nativeEvent.pageX, event.nativeEvent.pageY);
756
+ const payload = payloadOf(event);
757
+ env.orchestrator.activated(participant);
758
+ readConfig().onActivate?.(payload);
759
+ endIfOneShot();
760
+ },
761
+ onResponderMove: (event) => {
762
+ if (runtime.state !== GESTURE_STATE.ACTIVE) {
763
+ return;
764
+ }
765
+ advance(event);
766
+ },
767
+ onResponderRelease: (event) => {
768
+ runtime.hasResponder = false;
769
+ finalizeAt(event, true);
770
+ },
771
+ onResponderTerminate: (event) => {
772
+ runtime.hasResponder = false;
773
+ // The interaction went somewhere this registry cannot follow — an
774
+ // ancestor took the responder, the window lost focus, a scroller moved
775
+ // under it. Anything running alongside this gesture was riding on a lock
776
+ // that is gone, and there is no callback of its own coming to tell it.
777
+ env.orchestrator.interactionLost(participant);
778
+ if (runtime.state === GESTURE_STATE.UNDETERMINED) {
779
+ // Already over: this gesture cancelled itself while still holding the
780
+ // lock, and the system is telling the HOST the interaction ended.
781
+ return;
782
+ }
783
+ setState(GESTURE_STATE.CANCELLED);
784
+ finalizeAt(event, false);
785
+ },
786
+ onTouchEnd: (event) => {
787
+ readConfig().onTouchesUp?.(touchEventOf(event, TOUCH_EVENT_TYPE.TOUCHES_UP), stateManager);
788
+ // The responder holder is finalized by `onResponderRelease`, which the
789
+ // system dispatches AFTER the touch props. A gesture that is ACTIVE
790
+ // without holding the lock — a `Native`, or a `Simultaneous` partner
791
+ // that lost the race for it — has no responder callback coming, and
792
+ // this is where its successful ending is.
793
+ if (runtime.state === GESTURE_STATE.ACTIVE) {
794
+ if (!runtime.hasResponder) {
795
+ finalizeAt(event, true);
796
+ }
797
+ return;
798
+ }
799
+ if (runtime.state !== GESTURE_STATE.BEGAN) {
800
+ return;
801
+ }
802
+ const config = readConfig();
803
+ runtime.taps += 1;
804
+ const outcome = decider.onRelease?.(viewOf(), config) ?? { kind: "fail" };
805
+ if (outcome.kind === "hold") {
806
+ // `Manual`: the pointer lifting decides nothing, because nothing about
807
+ // this gesture is decided by the pointer. It stays BEGAN, holding no
808
+ // lock and running no timer, until the app says otherwise — and the
809
+ // `onTouchesUp` above has just given it the state manager to say it
810
+ // with. The next press resets it, as it resets any BEGAN gesture.
811
+ return;
812
+ }
813
+ if (outcome.kind === "await") {
814
+ // Not over. The gesture stays BEGAN with its count intact, holding no
815
+ // lock, and the next press continues it — or this timer fails it.
816
+ clearActivationTimer();
817
+ runtime.delayTimer = setTimeout(() => {
818
+ runtime.delayTimer = null;
819
+ if (runtime.state === GESTURE_STATE.BEGAN) {
820
+ failNow();
821
+ }
822
+ }, outcome.delay);
823
+ return;
824
+ }
825
+ if (outcome.kind === "fail") {
826
+ // `fail()` rather than a bare `finalizeAt(..., false)`, because the
827
+ // payload has to carry the state the gesture ENDED in and this path
828
+ // was reporting BEGAN. Upstream's `fail()` moves to FAILED first, and
829
+ // a consumer comparing `event.state` against `State.FAILED` — which is
830
+ // exactly what two of the four target libraries do with `State` — was
831
+ // being told the gesture was still deciding. `Fling` made it visible
832
+ // because failing on the lift is its ordinary ending rather than an
833
+ // edge case, but `Tap` and `Pan` were reporting it too.
834
+ fail(event);
835
+ return;
836
+ }
837
+ // `Tap` activating: this is the one kind whose activation criterion is
838
+ // the pointer coming back UP, so the grant is asked for here rather than
839
+ // from a move. The responder system dispatches the touch props before it
840
+ // clears the session, so the out-of-event channel still has an
841
+ // interaction to negotiate over — and the `onResponderRelease` that
842
+ // follows in the same event is what ends the gesture with success.
843
+ clearActivationTimer();
844
+ decide();
845
+ if (stateNow() === GESTURE_STATE.ACTIVE) {
846
+ if (!runtime.hasResponder) {
847
+ // It activated alongside a gesture that holds the lock, so no
848
+ // `onResponderRelease` is coming for it either.
849
+ finalizeAt(event, true);
850
+ }
851
+ return;
852
+ }
853
+ // Nobody granted it — an ancestor holds the interaction, the gesture is
854
+ // not on its path, or the orchestrator parked it behind another. There
855
+ // is no release callback coming, and a discrete gesture whose pointer
856
+ // has lifted cannot be woken later: the interaction it would have taken
857
+ // is over.
858
+ finalizeAt(event, false);
859
+ },
860
+ onTouchCancel: (event) => {
861
+ readConfig().onTouchesCancel?.(touchEventOf(event, TOUCH_EVENT_TYPE.TOUCHES_CANCEL), stateManager);
862
+ // THIS is where a stolen sequence lands, and why the responder system
863
+ // learned to tell `->DENIED` from an ordinary `drag-end`
864
+ // (`responder/use-responder.ts`). A native ancestor claiming mid-drag
865
+ // used to arrive here as `onTouchEnd`, which for an ACTIVE `Native`
866
+ // gesture would have reported a clean, successful ending of a drag that
867
+ // was actually taken away.
868
+ //
869
+ // An ACTIVE gesture that DID hold the lock is cancelled by
870
+ // `onResponderTerminate`, which the system dispatches after these touch
871
+ // props — finalizing here too would end it twice.
872
+ if (runtime.state === GESTURE_STATE.BEGAN ||
873
+ (!runtime.hasResponder && runtime.state === GESTURE_STATE.ACTIVE)) {
874
+ setState(GESTURE_STATE.CANCELLED);
875
+ finalizeAt(event, false);
876
+ }
877
+ },
878
+ };
879
+ /**
880
+ * The second entry surface: a GTK controller, for the kinds the pointer
881
+ * stream cannot carry.
882
+ *
883
+ * THREE sources arrive here and the machine cannot tell them apart, which is
884
+ * the point — `GtkGestureZoom`/`GtkGestureRotate` for `Pinch`/`Rotation`,
885
+ * `GtkEventControllerMotion` for `Hover`, `GtkGestureStylus` for
886
+ * `ForceTouch`. What they have in common is everything that matters: no
887
+ * button goes down, so there is no GTK sequence, no responder session and
888
+ * nothing to claim; and the numbers they carry are already in the shape the
889
+ * payload wants.
890
+ *
891
+ * Everything below the entry point is the machine above, unchanged. `begin`
892
+ * does what `onTouchStart` does (record with the orchestrator, BEGAN,
893
+ * `onBegin`), `update` does what `onTouchMove` and `advance` do between them
894
+ * (test the kind's predicate, `decide()`, emit), and `end` does what
895
+ * `onTouchEnd` does. `decide()` is the same call into the same
896
+ * `tryActivate`, and `finalize` is the same one exit.
897
+ *
898
+ * ONE DIFFERENCE FROM `Pan`, and it is upstream's: `scale`, `rotation` and
899
+ * `force` are NOT re-based when the gesture activates. Translation is,
900
+ * because a pan that reported its `activeOffset` as travel would jump the
901
+ * content by the threshold on every drag. Upstream's `scale` is not —
902
+ * `resetProgress()` resets it only while the handler is not yet ACTIVE, so
903
+ * the value that crossed the activation threshold is the value the first
904
+ * `onUpdate` reports. GTK measures from its own recognition point for the
905
+ * same reason, so the two origins already agree and nothing is re-derived.
906
+ */
907
+ const controller = {
908
+ begin: (sample) => {
909
+ if (!isEnabled() || runtime.state !== GESTURE_STATE.UNDETERMINED) {
910
+ return;
911
+ }
912
+ const bounds = env.boundsInWindow();
913
+ const pageX = (bounds?.x ?? 0) + sample.x;
914
+ const pageY = (bounds?.y ?? 0) + sample.y;
915
+ if (bounds !== null &&
916
+ !contains(hitSlopRect(bounds, readConfig().hitSlop), pageX, pageY)) {
917
+ return;
918
+ }
919
+ runtime = newRuntime();
920
+ runtime.pressTime = Date.now();
921
+ runtime.lastSampleTime = runtime.pressTime;
922
+ runtime.originX = pageX;
923
+ runtime.originY = pageY;
924
+ runtime.pressX = pageX;
925
+ runtime.pressY = pageY;
926
+ runtime.lastX = pageX;
927
+ runtime.lastY = pageY;
928
+ runtime.scale = sample.scale ?? 1;
929
+ runtime.rotation = sample.rotation ?? 0;
930
+ runtime.force = sample.force ?? 0;
931
+ runtime.pointerCount = sample.pointers;
932
+ runtime.maxPointerCount = sample.pointers;
933
+ setState(GESTURE_STATE.BEGAN);
934
+ // On the gesture, not on mount — the same rule and the same line as
935
+ // `onTouchStart`, and the same islands answer follows from it.
936
+ env.orchestrator.record(participant);
937
+ readConfig().onBegin?.(payloadNow());
938
+ // `Hover` activates on the crossing itself: upstream's
939
+ // `onPointerMoveOver` calls `begin()` and `activate()` one after the
940
+ // other, because a pointer that is over the view IS the gesture and
941
+ // there is nothing further to wait for. Asked through the decider rather
942
+ // than flagged, so it is the same question `onTouchStart` asks of a
943
+ // `Native` that activates on the press.
944
+ if (readConfig().manualActivation !== true &&
945
+ decider.shouldActivate(viewOf(), readConfig())) {
946
+ decide();
947
+ }
948
+ },
949
+ update: (sample) => {
950
+ if (runtime.state !== GESTURE_STATE.BEGAN &&
951
+ runtime.state !== GESTURE_STATE.ACTIVE) {
952
+ return;
953
+ }
954
+ const scale = sample.scale ?? 1;
955
+ const rotation = sample.rotation ?? 0;
956
+ const now = Date.now();
957
+ const elapsed = now - runtime.lastSampleTime;
958
+ if (elapsed > 0) {
959
+ // Per SECOND, which upstream documents and neither of its web paths
960
+ // computes. See `GestureEventPayload.velocity`.
961
+ runtime.scaleVelocity = ((scale - runtime.scale) / elapsed) * 1000;
962
+ runtime.rotationVelocity =
963
+ ((rotation - runtime.rotation) / elapsed) * 1000;
964
+ runtime.lastSampleTime = now;
965
+ }
966
+ runtime.scale = scale;
967
+ runtime.rotation = rotation;
968
+ runtime.force = sample.force ?? 0;
969
+ runtime.pointerCount = sample.pointers;
970
+ runtime.maxPointerCount = Math.max(runtime.maxPointerCount, sample.pointers);
971
+ const bounds = env.boundsInWindow();
972
+ runtime.lastX = (bounds?.x ?? 0) + sample.x;
973
+ runtime.lastY = (bounds?.y ?? 0) + sample.y;
974
+ const config = readConfig();
975
+ // A kind that can be cancelled by its own numbers while ACTIVE gets the
976
+ // same treatment `advance` gives `LongPress` — `ForceTouch` is the one
977
+ // that wants it, because `maxForce` keeps applying after activation.
978
+ if (runtime.state === GESTURE_STATE.ACTIVE &&
979
+ decider.shouldCancelWhileActive?.(viewOf(), config) === true) {
980
+ setState(GESTURE_STATE.CANCELLED);
981
+ finalize(payloadNow, false);
982
+ return;
983
+ }
984
+ if (runtime.state === GESTURE_STATE.BEGAN) {
985
+ if (decider.shouldFail(viewOf(), config)) {
986
+ failNow();
987
+ return;
988
+ }
989
+ if (config.manualActivation === true) {
990
+ return;
991
+ }
992
+ if (!decider.shouldActivate(viewOf(), config)) {
993
+ return;
994
+ }
995
+ // The same request into the same loop every other kind makes. It can
996
+ // come back as an activation, as a parking behind
997
+ // `requireExternalGestureToFail`, or as a cancellation.
998
+ decide();
999
+ if (stateNow() !== GESTURE_STATE.ACTIVE) {
1000
+ return;
1001
+ }
1002
+ // `onActivate` has just reported this sample; emitting it again as an
1003
+ // update would double it, exactly as `advance` refuses the event that
1004
+ // granted the responder.
1005
+ runtime.changeFromScale = runtime.scale;
1006
+ runtime.changeFromRotation = runtime.rotation;
1007
+ runtime.changeFromForce = runtime.force;
1008
+ return;
1009
+ }
1010
+ const payload = payloadNow();
1011
+ config.onUpdate?.(payload);
1012
+ config.onChange?.(payload);
1013
+ runtime.changeFromX = payload.translationX;
1014
+ runtime.changeFromY = payload.translationY;
1015
+ runtime.changeFromScale = runtime.scale;
1016
+ runtime.changeFromRotation = runtime.rotation;
1017
+ runtime.changeFromForce = runtime.force;
1018
+ runtime.hasEmittedUpdate = true;
1019
+ },
1020
+ /** GTK's `end`: a success if it activated, a failure if it never did. */
1021
+ end: () => {
1022
+ if (runtime.state === GESTURE_STATE.ACTIVE) {
1023
+ finalize(payloadNow, true);
1024
+ return;
1025
+ }
1026
+ if (runtime.state === GESTURE_STATE.BEGAN) {
1027
+ setState(GESTURE_STATE.FAILED);
1028
+ finalize(payloadNow, false);
1029
+ }
1030
+ },
1031
+ /** GTK's `cancel`: the sequence was taken away. */
1032
+ cancel: () => {
1033
+ if (runtime.state === GESTURE_STATE.BEGAN ||
1034
+ runtime.state === GESTURE_STATE.ACTIVE) {
1035
+ setState(GESTURE_STATE.CANCELLED);
1036
+ finalize(payloadNow, false);
1037
+ }
1038
+ },
1039
+ };
1040
+ const drivenByController = decider.source !== undefined && decider.source !== "pointer";
1041
+ return {
1042
+ // Exactly one surface is live. A controller kind that also answered the
1043
+ // touch props would begin on a mouse press — `onBegin` on every click, on
1044
+ // a gesture that cannot happen — and a pointer kind with a live controller
1045
+ // channel would pan on a pinch.
1046
+ handlers: drivenByController ? {} : handlers,
1047
+ controller: drivenByController ? controller : null,
1048
+ participant,
1049
+ dispose: () => {
1050
+ clearActivationTimer();
1051
+ clearDelayTimer();
1052
+ env.orchestrator.forget(participant);
1053
+ },
1054
+ };
1055
+ };
1056
+ //# sourceMappingURL=recognizer.js.map