@rootnative/impulse 0.0.0-alpha.0 → 0.0.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.
- package/CHANGELOG.md +51 -5
- package/README.md +206 -11
- package/dist/{chunk-5BMRKYVY.js → chunk-2UZTAWUQ.js} +20 -2
- package/dist/chunk-BNSFDNLA.js +75 -0
- package/dist/chunk-DXXGWG4Q.js +136 -0
- package/dist/{chunk-PMR25UCT.js → chunk-HGBCIL6X.js} +1 -1
- package/dist/chunk-HI5PHDJY.js +94 -0
- package/dist/{chunk-IG5RXCYR.js → chunk-IH7SQ5X6.js} +11 -15
- package/dist/chunk-MWCIEVTA.js +199 -0
- package/dist/{chunk-F4RHM4ZK.js → chunk-NYDDZD4G.js} +58 -1
- package/dist/chunk-PGOQSKEJ.js +144 -0
- package/dist/{chunk-FR242SUF.js → chunk-TGOGIZDH.js} +13 -7
- package/dist/chunk-VEPUHGPN.js +12 -0
- package/dist/chunk-YZHAQ4XK.js +136 -0
- package/dist/compose/index.d.ts +1 -1
- package/dist/double-tap/index.d.ts +184 -0
- package/dist/double-tap/index.js +5 -0
- package/dist/drag/index.d.ts +18 -13
- package/dist/drag/index.js +3 -3
- package/dist/index.d.ts +10 -3
- package/dist/index.js +12 -5
- package/dist/long-press/index.d.ts +219 -0
- package/dist/long-press/index.js +4 -0
- package/dist/pan/index.d.ts +221 -0
- package/dist/pan/index.js +4 -0
- package/dist/pinch/index.d.ts +239 -0
- package/dist/pinch/index.js +4 -0
- package/dist/raw/index.d.ts +3 -3
- package/dist/raw/index.js +2 -2
- package/dist/rotate/index.d.ts +263 -0
- package/dist/rotate/index.js +4 -0
- package/dist/swipe/index.d.ts +254 -0
- package/dist/swipe/index.js +4 -0
- package/dist/tap/index.d.ts +34 -29
- package/dist/tap/index.js +4 -3
- package/dist/tapEvent-KSSojt_l.d.ts +28 -0
- package/dist/{types-Ch2HM3aP.d.ts → types-ChGKY28a.d.ts} +27 -1
- package/dist/{useGestureMemo-Ccv8rB0C.d.ts → useGestureMemo-BRW1EKcJ.d.ts} +1 -1
- package/jest-setup.cjs +32 -1
- package/llms.txt +155 -0
- package/package.json +35 -2
- package/src/index.ts +50 -4
- package/src/intents/double-tap/index.ts +6 -0
- package/src/intents/long-press/index.ts +6 -0
- package/src/intents/pan/index.ts +2 -0
- package/src/intents/pinch/index.ts +2 -0
- package/src/intents/rotate/index.ts +6 -0
- package/src/intents/swipe/index.ts +8 -0
- package/src/intents/tapEvent.ts +50 -0
- package/src/intents/useDoubleTap.ts +321 -0
- package/src/intents/useDrag.ts +49 -28
- package/src/intents/useLongPress.ts +391 -0
- package/src/intents/usePan.ts +444 -0
- package/src/intents/usePinch.ts +483 -0
- package/src/intents/useRotate.ts +507 -0
- package/src/intents/useSwipe.ts +585 -0
- package/src/intents/useTap.ts +51 -60
- package/src/internal/intentResult.ts +67 -0
- package/src/internal/phaseCallbacks.ts +51 -0
- package/src/internal/useGestureMemo.ts +32 -4
- package/src/internal/useLatestCallback.ts +2 -2
- package/src/raw/useRawGesture.ts +1 -1
- package/src/relations/index.ts +94 -0
- package/src/types.ts +27 -0
|
@@ -138,5 +138,31 @@ interface IntentResult<G extends GestureType> {
|
|
|
138
138
|
*/
|
|
139
139
|
readonly isActive: SharedValue<boolean>;
|
|
140
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* How a gesture ended, handed to every intent's end callback as its second
|
|
143
|
+
* argument.
|
|
144
|
+
*
|
|
145
|
+
* The end callbacks — `onTap`, `onDoubleTap`, `onLongPressEnd`, `onDragEnd` —
|
|
146
|
+
* fire on both paths: a gesture the user completed, and one the system took
|
|
147
|
+
* away. This says which. Without it, a cancel is reported only by
|
|
148
|
+
* `onFinalize`, which is a worklet, so a consumer holding phase in React
|
|
149
|
+
* state has to write `'worklet'` plus `scheduleOnRN` by hand.
|
|
150
|
+
*
|
|
151
|
+
* It is an object rather than a bare boolean so a later field — a reason for
|
|
152
|
+
* the cancel, say — does not break the signature a second time.
|
|
153
|
+
*/
|
|
154
|
+
interface IntentEndInfo {
|
|
155
|
+
/**
|
|
156
|
+
* `true` when the system took the gesture away instead of the user
|
|
157
|
+
* completing it: a competing gesture won, the app went to the background,
|
|
158
|
+
* or a relation handed the touch to another recognizer.
|
|
159
|
+
*
|
|
160
|
+
* A gesture that never activated at all does not reach an end callback on
|
|
161
|
+
* either path, so this is never `true` for a touch that was never the
|
|
162
|
+
* intent. It separates "this ended, but not by the user" from "the user
|
|
163
|
+
* did it".
|
|
164
|
+
*/
|
|
165
|
+
readonly cancelled: boolean;
|
|
166
|
+
}
|
|
141
167
|
|
|
142
|
-
export type { AttachableGesture as A, CoexistenceOptions as C, GestureReference as G, HitSlop as H,
|
|
168
|
+
export type { AttachableGesture as A, CoexistenceOptions as C, GestureReference as G, HitSlop as H, IntentEndInfo as I, Point as P, ComposeMode as a, GestureReferences as b, IntentResult as c };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RefObject } from 'react';
|
|
2
2
|
import { GestureType } from 'react-native-gesture-handler';
|
|
3
|
-
import { C as CoexistenceOptions } from './types-
|
|
3
|
+
import { C as CoexistenceOptions } from './types-ChGKY28a.js';
|
|
4
4
|
|
|
5
5
|
/** What every hook built on this helper accepts on top of its own options. */
|
|
6
6
|
interface GestureMemoOptions extends CoexistenceOptions {
|
package/jest-setup.cjs
CHANGED
|
@@ -32,7 +32,8 @@ require('react-native-gesture-handler/jestSetup')
|
|
|
32
32
|
//
|
|
33
33
|
// What this means for tests:
|
|
34
34
|
// ✅ assert a shared value's `.value` after driving a gesture
|
|
35
|
-
// ✅ assert that a JS-thread callback fired, because `
|
|
35
|
+
// ✅ assert that a JS-thread callback fired, because `scheduleOnRN` calls
|
|
36
|
+
// it synchronously
|
|
36
37
|
// ❌ frame-level timing is not observable — nothing schedules or animates
|
|
37
38
|
jest.mock('react-native-reanimated', () => {
|
|
38
39
|
const React = require('react')
|
|
@@ -54,3 +55,33 @@ jest.mock('react-native-reanimated', () => {
|
|
|
54
55
|
setGestureState: () => {},
|
|
55
56
|
}
|
|
56
57
|
})
|
|
58
|
+
|
|
59
|
+
// Worklets mock. Impulse crosses to the JS thread with `scheduleOnRN` from
|
|
60
|
+
// `react-native-worklets`, not with Reanimated's deprecated `runOnJS`, so the
|
|
61
|
+
// mock above no longer covers that call — source importing from this package
|
|
62
|
+
// would load the real module, which needs its native module and cannot run
|
|
63
|
+
// under Jest.
|
|
64
|
+
//
|
|
65
|
+
// `scheduleOnRN` calls the function synchronously here. The real one schedules
|
|
66
|
+
// it on the JS thread, so a test asserts that a callback ran, never when it
|
|
67
|
+
// ran relative to a frame. That limit is the same one the Reanimated mock
|
|
68
|
+
// already carried.
|
|
69
|
+
//
|
|
70
|
+
// Only what Impulse imports is mocked. `runOnJS` is included because the
|
|
71
|
+
// undeprecated curried form lives in this package too, and a consumer's own
|
|
72
|
+
// code may use it under this setup file.
|
|
73
|
+
//
|
|
74
|
+
// `isWorkletFunction` reports every function as a worklet. The real one reads
|
|
75
|
+
// a hash that only the Worklets Babel plugin writes, and a Jest run without
|
|
76
|
+
// the plugin would then flag every phase callback. Here nothing runs on a UI
|
|
77
|
+
// thread, so a plain function is correct and the phase-callback warning must
|
|
78
|
+
// stay silent.
|
|
79
|
+
jest.mock('react-native-worklets', () => ({
|
|
80
|
+
__esModule: true,
|
|
81
|
+
scheduleOnRN: (fn, ...args) => fn(...args),
|
|
82
|
+
isWorkletFunction: (value) => typeof value === 'function',
|
|
83
|
+
runOnJS:
|
|
84
|
+
(fn) =>
|
|
85
|
+
(...args) =>
|
|
86
|
+
fn(...args),
|
|
87
|
+
}))
|
package/llms.txt
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# @rootnative/impulse
|
|
2
|
+
|
|
3
|
+
> Declarative gesture primitives for React Native, built on
|
|
4
|
+
> react-native-gesture-handler. One hook per intent. Hooks return shared values
|
|
5
|
+
> and a gesture; Impulse starts no animation and ships no components.
|
|
6
|
+
|
|
7
|
+
Status: 0.0.0-alpha.1. Eight intents ship. A device sweep on an Android
|
|
8
|
+
emulator and an iOS simulator drove every intent. It proves mechanics, not
|
|
9
|
+
feel, so every default below is still a design intention.
|
|
10
|
+
|
|
11
|
+
Docs: https://rootnative.github.io/impulse/
|
|
12
|
+
Full: https://rootnative.github.io/impulse/llms-full.txt
|
|
13
|
+
Source: https://github.com/rootnative/impulse
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
npm install @rootnative/impulse
|
|
18
|
+
|
|
19
|
+
Peers: react, react-native, react-native-gesture-handler,
|
|
20
|
+
react-native-reanimated, react-native-worklets. Render a
|
|
21
|
+
<GestureHandlerRootView> above any component using a gesture — if it is
|
|
22
|
+
missing, gestures never fire and nothing warns.
|
|
23
|
+
|
|
24
|
+
## What ships
|
|
25
|
+
|
|
26
|
+
useTap, useDoubleTap, useLongPress, useDrag, usePan, useSwipe, usePinch,
|
|
27
|
+
useRotate — intent hooks. useGestures — composition. useRawGesture — the
|
|
28
|
+
escape hatch. GestureDetector is re-exported from the package entry.
|
|
29
|
+
|
|
30
|
+
NOT IMPLEMENTED, do not import: useHover, useEdgeSwipe.
|
|
31
|
+
They are designed only.
|
|
32
|
+
|
|
33
|
+
usePan and useDrag are not synonyms. useDrag owns a position: x and y
|
|
34
|
+
accumulate across gestures and bounds clamps them. usePan owns nothing: it
|
|
35
|
+
reports translation, velocity, and a per-frame change the consumer adds up,
|
|
36
|
+
and it zeroes at every gesture.
|
|
37
|
+
|
|
38
|
+
useSwipe is a pan judged at release. directions is also the activation
|
|
39
|
+
criterion — an all-horizontal list gives a directional threshold, so the
|
|
40
|
+
swipe shares a view with a vertical scroller with no relation declared.
|
|
41
|
+
|
|
42
|
+
usePinch owns a scale that accumulates across gestures, so min and max are
|
|
43
|
+
the viewer's zoom range, not one gesture's. focal is the midpoint between the
|
|
44
|
+
fingers and the point the zoom must happen about — scaling about the view's
|
|
45
|
+
centre slides the content out from under them. It has NO activation criteria:
|
|
46
|
+
RNGH's pinch exposes no threshold, so a pinch and a pan are separated by a
|
|
47
|
+
relation alone — useGestures([pinch, pan], { mode: 'simultaneous' }).
|
|
48
|
+
|
|
49
|
+
useRotate reports DEGREES, not the radians RNGH reports. This is the one
|
|
50
|
+
place Impulse changes a unit: min: -45, not -Math.PI / 4. It applies to
|
|
51
|
+
angle, gestureAngle, velocity and the min/max range. The angle accumulates
|
|
52
|
+
and does NOT wrap — a second full turn reports 720. anchor is the point the
|
|
53
|
+
turn happens about, the counterpart of pinch's focal. It has NO activation
|
|
54
|
+
criteria either, so rotate and pinch are composed simultaneous.
|
|
55
|
+
|
|
56
|
+
Subpaths: @rootnative/impulse/{tap,double-tap,long-press,drag,pan,swipe,pinch,rotate,compose,raw,gesture-handler}
|
|
57
|
+
|
|
58
|
+
## The shape of every hook
|
|
59
|
+
|
|
60
|
+
const tap = useTap({ onTap: select })
|
|
61
|
+
tap.gesture // hand to <GestureDetector>
|
|
62
|
+
tap.ref // name it in another hook's alongside / blocks / deferTo
|
|
63
|
+
tap.isActive // SharedValue<boolean>
|
|
64
|
+
|
|
65
|
+
useDrag adds x and y, both SharedValue<number>, both writable, both
|
|
66
|
+
accumulating across gestures.
|
|
67
|
+
|
|
68
|
+
## Threads — the callback name states the thread
|
|
69
|
+
|
|
70
|
+
JS thread: onTap, onDoubleTap, onLongPress, onLongPressEnd, onDragStart,
|
|
71
|
+
onDragEnd, onPanStart, onPanEnd, onSwipe, onSwipeEnd, onPinchStart,
|
|
72
|
+
onPinchEnd, onRotateStart, onRotateEnd. Ordinary functions; Impulse owns the
|
|
73
|
+
scheduleOnRN boundary.
|
|
74
|
+
|
|
75
|
+
Every callback that reports an ending takes a second argument, { cancelled }:
|
|
76
|
+
onTap, onDoubleTap, onLongPressEnd, onDragEnd, onPanEnd, onSwipeEnd,
|
|
77
|
+
onPinchEnd, onRotateEnd. It fires on both endings. cancelled is true when the
|
|
78
|
+
system took the gesture away — a competing gesture won, the app went to the
|
|
79
|
+
background, or a relation handed the touch on. onSwipe fires for a commit
|
|
80
|
+
only and takes no second argument.
|
|
81
|
+
|
|
82
|
+
Worklets: onBegin, onUpdate, onFinalize. Mark with 'worklet'. A plain
|
|
83
|
+
function there makes Impulse warn once in development, and the warning names
|
|
84
|
+
the hook and the callback. Without the directive, the first touch that
|
|
85
|
+
reaches the phase throws "Tried to synchronously call a Remote Function".
|
|
86
|
+
Worklets are direct gesture dependencies, so keep their identity stable — an
|
|
87
|
+
inline worklet rebuilds the gesture every render. An inline JS-thread
|
|
88
|
+
callback does not.
|
|
89
|
+
|
|
90
|
+
## Composition
|
|
91
|
+
|
|
92
|
+
useGestures(members, { mode }) where mode is race | simultaneous | exclusive.
|
|
93
|
+
A composed result is itself a member. Precedence reads left to right.
|
|
94
|
+
|
|
95
|
+
A tap and a double tap are exclusive with the double tap FIRST, never race — a
|
|
96
|
+
single tap recognizes on first release and would win every race. The composed
|
|
97
|
+
single tap then pays maxDelay in latency.
|
|
98
|
+
|
|
99
|
+
useGestures does not accept coexistence options. Put them on the members.
|
|
100
|
+
|
|
101
|
+
## Coexistence
|
|
102
|
+
|
|
103
|
+
alongside -> both recognize (simultaneousWithExternalGesture)
|
|
104
|
+
blocks -> this gesture wins (blocksExternalGesture)
|
|
105
|
+
deferTo -> the named gesture wins (requireExternalGestureToFail)
|
|
106
|
+
|
|
107
|
+
Name another hook's .ref, not its .gesture.
|
|
108
|
+
|
|
109
|
+
TRAP: a relation target must carry a handlerTag. React Native's ScrollView has
|
|
110
|
+
none, so the relation is SILENTLY dropped on every platform. Use
|
|
111
|
+
gesture-handler's ScrollView / FlatList, from @rootnative/impulse/gesture-handler.
|
|
112
|
+
|
|
113
|
+
TRAP: a component ref needs `as unknown as GestureReference`. RNGH types the
|
|
114
|
+
target as a ref to a component type, which is not what ref={} produces.
|
|
115
|
+
|
|
116
|
+
## Defaults
|
|
117
|
+
|
|
118
|
+
useTap: pointers 1, maxDuration 500ms, maxDistance 10pt
|
|
119
|
+
useDoubleTap: same, plus maxDelay 500ms (probably too generous; 250-300 is
|
|
120
|
+
closer to what platforms use)
|
|
121
|
+
useLongPress: minDuration 500ms, maxDistance 10pt
|
|
122
|
+
useDrag: axis both, threshold 10pt, elastic 0, unbounded
|
|
123
|
+
usePan: axis both, threshold 10pt
|
|
124
|
+
useSwipe: directions all four, threshold 10pt, commitDistance 80pt,
|
|
125
|
+
commitSpeed 800pt/s
|
|
126
|
+
usePinch: initial 1, unbounded, no activation criteria
|
|
127
|
+
useRotate: initial 0, unbounded, no activation criteria
|
|
128
|
+
|
|
129
|
+
No default has a feel result. The device sweep proved that the drag
|
|
130
|
+
threshold, commitDistance, and maxDelay at 500ms and 250ms work. commitSpeed
|
|
131
|
+
is not tested, because neither harness can make a swipe that fast.
|
|
132
|
+
|
|
133
|
+
## isActive does not mean the same thing in every hook
|
|
134
|
+
|
|
135
|
+
useTap and useDoubleTap set it at touch-down. For useTap that is a real
|
|
136
|
+
pressed state. For useDoubleTap it is true for every ordinary single tap on
|
|
137
|
+
the view too, so do not drive a pressed state from it.
|
|
138
|
+
useLongPress, useDrag, usePan, useSwipe, usePinch and useRotate set it at
|
|
139
|
+
recognition.
|
|
140
|
+
|
|
141
|
+
## Known gaps a consumer can hit
|
|
142
|
+
|
|
143
|
+
- A cancel is reported, but its velocity is not a throw. The finger never
|
|
144
|
+
lifted on a cancelled gesture, so springing on velocity when cancelled is
|
|
145
|
+
true flings a view the user never released.
|
|
146
|
+
- useLongPress maxDistance cancels an ACTIVE press on web, against RNGH's own
|
|
147
|
+
documented contract. Hold-then-drag does not work there at the default.
|
|
148
|
+
- A missing <GestureHandlerRootView> fails silently and Impulse cannot warn.
|
|
149
|
+
- Web is surveyed by hand; no jsdom test pins any of it.
|
|
150
|
+
|
|
151
|
+
## Out of scope, by decision
|
|
152
|
+
|
|
153
|
+
No components. No animation vocabulary, springs or easing. No haptics policy.
|
|
154
|
+
useDrag returns values and no style — @rootnative/inertia-gestures has a
|
|
155
|
+
useDrag that returns an animatedStyle and requires @rootnative/inertia.
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rootnative/impulse",
|
|
3
|
-
"version": "0.0.0-alpha.
|
|
3
|
+
"version": "0.0.0-alpha.1",
|
|
4
4
|
"description": "Declarative gesture primitives for React Native, built on react-native-gesture-handler.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "RootNative",
|
|
7
|
-
"homepage": "https://github.
|
|
7
|
+
"homepage": "https://rootnative.github.io/impulse/",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "git+https://github.com/rootnative/impulse.git",
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
"drag",
|
|
22
22
|
"pinch",
|
|
23
23
|
"tap",
|
|
24
|
+
"double-tap",
|
|
25
|
+
"long-press",
|
|
24
26
|
"expo",
|
|
25
27
|
"declarative"
|
|
26
28
|
],
|
|
@@ -53,11 +55,41 @@
|
|
|
53
55
|
"react-native": "./dist/tap/index.js",
|
|
54
56
|
"default": "./dist/tap/index.js"
|
|
55
57
|
},
|
|
58
|
+
"./double-tap": {
|
|
59
|
+
"types": "./dist/double-tap/index.d.ts",
|
|
60
|
+
"react-native": "./dist/double-tap/index.js",
|
|
61
|
+
"default": "./dist/double-tap/index.js"
|
|
62
|
+
},
|
|
63
|
+
"./long-press": {
|
|
64
|
+
"types": "./dist/long-press/index.d.ts",
|
|
65
|
+
"react-native": "./dist/long-press/index.js",
|
|
66
|
+
"default": "./dist/long-press/index.js"
|
|
67
|
+
},
|
|
56
68
|
"./drag": {
|
|
57
69
|
"types": "./dist/drag/index.d.ts",
|
|
58
70
|
"react-native": "./dist/drag/index.js",
|
|
59
71
|
"default": "./dist/drag/index.js"
|
|
60
72
|
},
|
|
73
|
+
"./pan": {
|
|
74
|
+
"types": "./dist/pan/index.d.ts",
|
|
75
|
+
"react-native": "./dist/pan/index.js",
|
|
76
|
+
"default": "./dist/pan/index.js"
|
|
77
|
+
},
|
|
78
|
+
"./swipe": {
|
|
79
|
+
"types": "./dist/swipe/index.d.ts",
|
|
80
|
+
"react-native": "./dist/swipe/index.js",
|
|
81
|
+
"default": "./dist/swipe/index.js"
|
|
82
|
+
},
|
|
83
|
+
"./pinch": {
|
|
84
|
+
"types": "./dist/pinch/index.d.ts",
|
|
85
|
+
"react-native": "./dist/pinch/index.js",
|
|
86
|
+
"default": "./dist/pinch/index.js"
|
|
87
|
+
},
|
|
88
|
+
"./rotate": {
|
|
89
|
+
"types": "./dist/rotate/index.d.ts",
|
|
90
|
+
"react-native": "./dist/rotate/index.js",
|
|
91
|
+
"default": "./dist/rotate/index.js"
|
|
92
|
+
},
|
|
61
93
|
"./gesture-handler": {
|
|
62
94
|
"types": "./dist/gesture-handler/index.d.ts",
|
|
63
95
|
"react-native": "./dist/gesture-handler/index.js",
|
|
@@ -71,6 +103,7 @@
|
|
|
71
103
|
"src",
|
|
72
104
|
"jest-preset.cjs",
|
|
73
105
|
"jest-setup.cjs",
|
|
106
|
+
"llms.txt",
|
|
74
107
|
"README.md",
|
|
75
108
|
"LICENSE",
|
|
76
109
|
"CHANGELOG.md",
|
package/src/index.ts
CHANGED
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
* RNGH models and Impulse does not, and the `alongside` / `blocks` /
|
|
7
7
|
* `deferTo` options both accept.
|
|
8
8
|
*
|
|
9
|
-
* `useTap`
|
|
10
|
-
*
|
|
9
|
+
* `useTap`, `useDoubleTap`, `useLongPress`, `useDrag`, `usePan`, `useSwipe`,
|
|
10
|
+
* `usePinch`, and `useRotate` are the intent hooks, and they share one
|
|
11
|
+
* pattern: an intent-shaped payload, JS-thread callbacks named for what
|
|
11
12
|
* happened, worklet phase callbacks named for when, and documented activation
|
|
12
|
-
* criteria. The remaining intents — `
|
|
13
|
-
*
|
|
13
|
+
* criteria. The remaining intents — `useHover`, `useEdgeSwipe` — are designed
|
|
14
|
+
* but not implemented; see the repository README for the roadmap.
|
|
14
15
|
*/
|
|
15
16
|
|
|
16
17
|
// Re-exported because every hook's result has to be handed to it, and
|
|
@@ -32,6 +33,19 @@ export type { RawGestureResult, UseRawGestureOptions } from './raw'
|
|
|
32
33
|
export { useTap } from './intents/tap'
|
|
33
34
|
export type { TapEvent, UseTapOptions, UseTapResult } from './intents/tap'
|
|
34
35
|
|
|
36
|
+
export { useDoubleTap } from './intents/double-tap'
|
|
37
|
+
export type {
|
|
38
|
+
UseDoubleTapOptions,
|
|
39
|
+
UseDoubleTapResult,
|
|
40
|
+
} from './intents/double-tap'
|
|
41
|
+
|
|
42
|
+
export { useLongPress } from './intents/long-press'
|
|
43
|
+
export type {
|
|
44
|
+
LongPressEvent,
|
|
45
|
+
UseLongPressOptions,
|
|
46
|
+
UseLongPressResult,
|
|
47
|
+
} from './intents/long-press'
|
|
48
|
+
|
|
35
49
|
export { useDrag } from './intents/drag'
|
|
36
50
|
export type {
|
|
37
51
|
DragAxis,
|
|
@@ -41,6 +55,37 @@ export type {
|
|
|
41
55
|
UseDragResult,
|
|
42
56
|
} from './intents/drag'
|
|
43
57
|
|
|
58
|
+
export { usePan } from './intents/pan'
|
|
59
|
+
export type {
|
|
60
|
+
PanAxis,
|
|
61
|
+
PanEvent,
|
|
62
|
+
UsePanOptions,
|
|
63
|
+
UsePanResult,
|
|
64
|
+
} from './intents/pan'
|
|
65
|
+
|
|
66
|
+
export { useSwipe } from './intents/swipe'
|
|
67
|
+
export type {
|
|
68
|
+
CommittedSwipeEvent,
|
|
69
|
+
SwipeDirection,
|
|
70
|
+
SwipeEvent,
|
|
71
|
+
UseSwipeOptions,
|
|
72
|
+
UseSwipeResult,
|
|
73
|
+
} from './intents/swipe'
|
|
74
|
+
|
|
75
|
+
export { usePinch } from './intents/pinch'
|
|
76
|
+
export type {
|
|
77
|
+
PinchEvent,
|
|
78
|
+
UsePinchOptions,
|
|
79
|
+
UsePinchResult,
|
|
80
|
+
} from './intents/pinch'
|
|
81
|
+
|
|
82
|
+
export { useRotate } from './intents/rotate'
|
|
83
|
+
export type {
|
|
84
|
+
RotateEvent,
|
|
85
|
+
UseRotateOptions,
|
|
86
|
+
UseRotateResult,
|
|
87
|
+
} from './intents/rotate'
|
|
88
|
+
|
|
44
89
|
export type {
|
|
45
90
|
AttachableGesture,
|
|
46
91
|
ComposeMode,
|
|
@@ -48,6 +93,7 @@ export type {
|
|
|
48
93
|
GestureReference,
|
|
49
94
|
GestureReferences,
|
|
50
95
|
HitSlop,
|
|
96
|
+
IntentEndInfo,
|
|
51
97
|
IntentResult,
|
|
52
98
|
Point,
|
|
53
99
|
} from './types'
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type GestureStateChangeEvent,
|
|
3
|
+
type TapGestureHandlerEventPayload,
|
|
4
|
+
} from 'react-native-gesture-handler'
|
|
5
|
+
import { type Point } from '../types'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The intent-shaped payload a tap callback receives.
|
|
9
|
+
*
|
|
10
|
+
* Shared by `useTap` and `useDoubleTap`, because the two recognize the same
|
|
11
|
+
* touch and differ only in how many times it happens. One payload rather than
|
|
12
|
+
* two identical ones means a consumer who already knows `useTap`'s event
|
|
13
|
+
* knows this one, and a field added later cannot reach one hook and miss the
|
|
14
|
+
* other.
|
|
15
|
+
*/
|
|
16
|
+
export interface TapEvent {
|
|
17
|
+
/** X of the tap, in points, relative to the view the gesture is attached to. */
|
|
18
|
+
readonly x: number
|
|
19
|
+
/** Y of the tap, in points, relative to the view the gesture is attached to. */
|
|
20
|
+
readonly y: number
|
|
21
|
+
/**
|
|
22
|
+
* The same point relative to the window.
|
|
23
|
+
*
|
|
24
|
+
* Prefer it over `x` / `y` when the view itself is being transformed by the
|
|
25
|
+
* gesture — a tap on a view that is mid-animation reports a moving `x`.
|
|
26
|
+
*/
|
|
27
|
+
readonly absolute: Point
|
|
28
|
+
/** How many fingers were down when the tap was recognized. */
|
|
29
|
+
readonly pointers: number
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Shape RNGH's flat state-change event into the tap payload.
|
|
34
|
+
*
|
|
35
|
+
* A worklet, because every caller is one. Keeping the normalizer in one
|
|
36
|
+
* module means the call sites across both tap hooks cannot disagree about
|
|
37
|
+
* which RNGH field means what — which is the defect the intent payload exists
|
|
38
|
+
* to remove.
|
|
39
|
+
*/
|
|
40
|
+
export function toTapEvent(
|
|
41
|
+
event: GestureStateChangeEvent<TapGestureHandlerEventPayload>,
|
|
42
|
+
): TapEvent {
|
|
43
|
+
'worklet'
|
|
44
|
+
return {
|
|
45
|
+
x: event.x,
|
|
46
|
+
y: event.y,
|
|
47
|
+
absolute: { x: event.absoluteX, y: event.absoluteY },
|
|
48
|
+
pointers: event.numberOfPointers,
|
|
49
|
+
}
|
|
50
|
+
}
|