@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
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { RotationGesture } from 'react-native-gesture-handler';
|
|
2
|
+
import { SharedValue } from 'react-native-reanimated';
|
|
3
|
+
import { G as GestureMemoOptions } from '../useGestureMemo-BRW1EKcJ.js';
|
|
4
|
+
import { P as Point, H as HitSlop, I as IntentEndInfo, c as IntentResult } from '../types-ChGKY28a.js';
|
|
5
|
+
import 'react';
|
|
6
|
+
|
|
7
|
+
/** The intent-shaped payload a {@link useRotate} callback receives. */
|
|
8
|
+
interface RotateEvent {
|
|
9
|
+
/**
|
|
10
|
+
* How far the thing is turned now, in **degrees**, after `min`, `max` and
|
|
11
|
+
* `elastic` were applied.
|
|
12
|
+
*
|
|
13
|
+
* This accumulates across gestures. A second rotation continues from where
|
|
14
|
+
* the first one stopped, which is why `min` and `max` can be written as the
|
|
15
|
+
* travel of the whole control rather than of one gesture.
|
|
16
|
+
*
|
|
17
|
+
* Positive is clockwise, which is what React Native's `rotate` transform
|
|
18
|
+
* also treats as positive.
|
|
19
|
+
*/
|
|
20
|
+
readonly angle: number;
|
|
21
|
+
/**
|
|
22
|
+
* How far the fingers turned during **this gesture alone**, in degrees.
|
|
23
|
+
*
|
|
24
|
+
* Untouched by `min`, `max` and `elastic`, and reset to zero at the start of
|
|
25
|
+
* every gesture. This is RNGH's own `rotation`, converted. Read `angle` for
|
|
26
|
+
* how far the thing being turned actually sits.
|
|
27
|
+
*/
|
|
28
|
+
readonly gestureAngle: number;
|
|
29
|
+
/**
|
|
30
|
+
* The point the rotation turns about — RNGH's anchor, the centre between
|
|
31
|
+
* the fingers, relative to the view.
|
|
32
|
+
*
|
|
33
|
+
* A rotation applied about the view's own centre turns the content under
|
|
34
|
+
* the fingers rather than with them. This is the point the transform has to
|
|
35
|
+
* pivot on, and it is the rotation counterpart of `usePinch`'s `focal`.
|
|
36
|
+
*/
|
|
37
|
+
readonly anchor: Point;
|
|
38
|
+
/**
|
|
39
|
+
* How fast the angle is changing, in degrees per second.
|
|
40
|
+
*
|
|
41
|
+
* RNGH's own documentation calls this "point units per second" for the
|
|
42
|
+
* rotation handler, which is a copy of the pan handler's wording rather
|
|
43
|
+
* than a description of this value. It is an angular speed.
|
|
44
|
+
*/
|
|
45
|
+
readonly velocity: number;
|
|
46
|
+
/**
|
|
47
|
+
* The nearest angle inside `min` and `max`. Equal to `angle` whenever the
|
|
48
|
+
* rotation is in range, which with the default `elastic` of `0` is always.
|
|
49
|
+
*
|
|
50
|
+
* With `elastic` set, the fingers can turn past an end and Impulse leaves
|
|
51
|
+
* the angle there on release — moving it back is an animation, and Impulse
|
|
52
|
+
* owns no animation vocabulary. This field is the destination that
|
|
53
|
+
* animation needs, so the consumer does not have to re-derive the clamp
|
|
54
|
+
* from a range it already handed over.
|
|
55
|
+
*/
|
|
56
|
+
readonly settled: number;
|
|
57
|
+
/** How many fingers are down. */
|
|
58
|
+
readonly pointers: number;
|
|
59
|
+
}
|
|
60
|
+
/** Options for {@link useRotate}. */
|
|
61
|
+
interface UseRotateOptions extends GestureMemoOptions {
|
|
62
|
+
/**
|
|
63
|
+
* The angle before any rotation, in degrees. Default `0`.
|
|
64
|
+
*
|
|
65
|
+
* Read once, at mount. `useSharedValue` keeps its first argument and
|
|
66
|
+
* ignores every later one, and that is the behaviour this option
|
|
67
|
+
* documents: after mount `angle` is the rotation's state, and the consumer
|
|
68
|
+
* moves it by writing it.
|
|
69
|
+
*/
|
|
70
|
+
initial?: number;
|
|
71
|
+
/**
|
|
72
|
+
* The smallest angle, in degrees. Unset by default, which lets the fingers
|
|
73
|
+
* turn the thing without limit.
|
|
74
|
+
*
|
|
75
|
+
* Impulse does not wrap the angle at a full turn. Without `min` and `max`
|
|
76
|
+
* a second turn reports 360 more degrees than the first, which is what a
|
|
77
|
+
* dial counting revolutions needs and what a photo editor does not.
|
|
78
|
+
*/
|
|
79
|
+
min?: number;
|
|
80
|
+
/**
|
|
81
|
+
* The largest angle, in degrees. Unset by default, which lets the fingers
|
|
82
|
+
* turn the thing without limit.
|
|
83
|
+
*/
|
|
84
|
+
max?: number;
|
|
85
|
+
/**
|
|
86
|
+
* How much of the travel past `min` or `max` reaches `angle`, from `0` to
|
|
87
|
+
* `1`. Default `0`.
|
|
88
|
+
*
|
|
89
|
+
* `0` stops dead at the end. `1` ignores the end while the fingers are
|
|
90
|
+
* down. Anything between is resistance — the rotation keeps turning and
|
|
91
|
+
* turns less than the fingers do.
|
|
92
|
+
*
|
|
93
|
+
* Impulse does not bring the angle back. The end callback carries
|
|
94
|
+
* `settled` — the angle to animate to — and `@rootnative/inertia` or a
|
|
95
|
+
* `withSpring` of your own does the rest.
|
|
96
|
+
*/
|
|
97
|
+
elastic?: number;
|
|
98
|
+
/**
|
|
99
|
+
* Extra touchable area around the view, in points.
|
|
100
|
+
*
|
|
101
|
+
* Written inline as an object is fine — the gesture is not rebuilt when the
|
|
102
|
+
* contents are unchanged.
|
|
103
|
+
*/
|
|
104
|
+
hitSlop?: HitSlop;
|
|
105
|
+
/**
|
|
106
|
+
* Whether the gesture is recognized at all. Default `true`.
|
|
107
|
+
*
|
|
108
|
+
* Prefer this over unmounting the `<GestureDetector>`: a disabled gesture
|
|
109
|
+
* keeps its identity and its relations, so re-enabling it does not
|
|
110
|
+
* re-attach anything.
|
|
111
|
+
*/
|
|
112
|
+
enabled?: boolean;
|
|
113
|
+
/**
|
|
114
|
+
* The rotation now owns the touch. **Runs on the JS thread** — Impulse owns
|
|
115
|
+
* the `scheduleOnRN` boundary, so this is an ordinary function and may
|
|
116
|
+
* touch React state.
|
|
117
|
+
*
|
|
118
|
+
* This is the first moment the rotation has definitely won. `onBegin` fires
|
|
119
|
+
* earlier and promises nothing.
|
|
120
|
+
*/
|
|
121
|
+
onRotateStart?: (event: RotateEvent) => void;
|
|
122
|
+
/**
|
|
123
|
+
* The rotation is over. **Runs on the JS thread.**
|
|
124
|
+
*
|
|
125
|
+
* Fires only for a rotation that activated, so a touch that never became
|
|
126
|
+
* one never reaches here on either path.
|
|
127
|
+
*
|
|
128
|
+
* It fires for both endings, and `cancelled` says which. `false` is the
|
|
129
|
+
* fingers lifting, and `velocity` then describes the release. `true` is the
|
|
130
|
+
* system taking the rotation away — a competing gesture won, or the app
|
|
131
|
+
* went to the background. **There was no release on that path, so the
|
|
132
|
+
* velocity describes the last movement rather than a throw.**
|
|
133
|
+
*
|
|
134
|
+
* Read `settled` on both paths for where an elastic overshoot belongs.
|
|
135
|
+
*/
|
|
136
|
+
onRotateEnd?: (event: RotateEvent, info: IntentEndInfo) => void;
|
|
137
|
+
/**
|
|
138
|
+
* A finger went down and the gesture is now a candidate. **This is a
|
|
139
|
+
* worklet** — mark it with the `'worklet'` directive, and do not touch
|
|
140
|
+
* React state from it.
|
|
141
|
+
*
|
|
142
|
+
* Being a candidate is not the same as winning: one finger cannot turn
|
|
143
|
+
* anything, and a second one may never arrive. Undo whatever it sets in
|
|
144
|
+
* `onFinalize`.
|
|
145
|
+
*/
|
|
146
|
+
onBegin?: (event: RotateEvent) => void;
|
|
147
|
+
/**
|
|
148
|
+
* The angle changed. **This is a worklet**, and it runs on every frame the
|
|
149
|
+
* fingers turn.
|
|
150
|
+
*
|
|
151
|
+
* `angle` is already written by the time this runs, so a `useAnimatedStyle`
|
|
152
|
+
* reading it needs nothing from here. Use this for the work that turning
|
|
153
|
+
* alone does not do — snapping a readout to whole degrees, say.
|
|
154
|
+
*
|
|
155
|
+
* There is no JS-thread counterpart on purpose. A per-frame `scheduleOnRN`
|
|
156
|
+
* is a scheduling cost paid sixty times a second for a value that is
|
|
157
|
+
* already on the thread that needs it.
|
|
158
|
+
*/
|
|
159
|
+
onUpdate?: (event: RotateEvent) => void;
|
|
160
|
+
/**
|
|
161
|
+
* The gesture is over, whether it activated or not. **This is a worklet.**
|
|
162
|
+
*
|
|
163
|
+
* `success` is `true` when the rotation activated and ended normally. This
|
|
164
|
+
* is the right place to clear anything `onBegin` set, because it runs on
|
|
165
|
+
* both paths.
|
|
166
|
+
*/
|
|
167
|
+
onFinalize?: (event: RotateEvent, success: boolean) => void;
|
|
168
|
+
}
|
|
169
|
+
/** What {@link useRotate} returns. */
|
|
170
|
+
interface UseRotateResult extends IntentResult<RotationGesture> {
|
|
171
|
+
/**
|
|
172
|
+
* How far the thing is turned, in **degrees**, after `min`, `max` and
|
|
173
|
+
* `elastic`.
|
|
174
|
+
*
|
|
175
|
+
* **This is a position, not a movement.** It accumulates across gestures,
|
|
176
|
+
* so a second rotation continues from where the first stopped. `usePan` is
|
|
177
|
+
* the hook whose value zeroes at every gesture.
|
|
178
|
+
*
|
|
179
|
+
* Writing it is allowed and is how a release animation, or a reset button,
|
|
180
|
+
* hands control back. The next rotation continues from whatever it holds.
|
|
181
|
+
*/
|
|
182
|
+
readonly angle: SharedValue<number>;
|
|
183
|
+
/**
|
|
184
|
+
* The point the rotation turns about, relative to the view.
|
|
185
|
+
*
|
|
186
|
+
* It keeps the last gesture's anchor after the fingers lift, so a release
|
|
187
|
+
* animation turns about the same point the gesture did rather than
|
|
188
|
+
* snapping to the view's centre.
|
|
189
|
+
*/
|
|
190
|
+
readonly anchor: SharedValue<Point>;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Recognize a two-finger rotation, and own the angle it produces.
|
|
194
|
+
*
|
|
195
|
+
* ```tsx
|
|
196
|
+
* const rotate = useRotate({ min: -45, max: 45 })
|
|
197
|
+
*
|
|
198
|
+
* const style = useAnimatedStyle(() => ({
|
|
199
|
+
* transform: [{ rotate: `${rotate.angle.value}deg` }],
|
|
200
|
+
* }))
|
|
201
|
+
*
|
|
202
|
+
* return (
|
|
203
|
+
* <GestureDetector gesture={rotate.gesture}>
|
|
204
|
+
* <Animated.Image style={style} source={source} />
|
|
205
|
+
* </GestureDetector>
|
|
206
|
+
* )
|
|
207
|
+
* ```
|
|
208
|
+
*
|
|
209
|
+
* **The angle is in degrees, and RNGH's is in radians.** This is the one
|
|
210
|
+
* place Impulse changes a unit rather than passing one through. A range
|
|
211
|
+
* written as `min: -45` is a range a person wrote; `-Math.PI / 4` is one they
|
|
212
|
+
* derived, and deriving it per project is the work this library exists to
|
|
213
|
+
* remove. A React Native style takes either unit, so the choice costs the
|
|
214
|
+
* consumer nothing. `gestureAngle` is the same conversion applied to RNGH's
|
|
215
|
+
* per-gesture value.
|
|
216
|
+
*
|
|
217
|
+
* **The angle accumulates; RNGH's does not.** A bare `Gesture.Rotation()`
|
|
218
|
+
* reports a value that restarts at zero on every gesture, so a control built
|
|
219
|
+
* on it springs back to upright the moment the fingers lift again. Carrying
|
|
220
|
+
* the angle across gestures is the stored start that every consumer otherwise
|
|
221
|
+
* writes, and it is why `min` and `max` can be the travel of the control
|
|
222
|
+
* rather than of one gesture.
|
|
223
|
+
*
|
|
224
|
+
* **It does not wrap at a full turn.** Without `min` and `max`, a second
|
|
225
|
+
* revolution reports 360 degrees more than the first. That is what a dial
|
|
226
|
+
* counting turns needs. A control that should read `10` rather than `370`
|
|
227
|
+
* either sets a range or takes the remainder itself — Impulse does not guess
|
|
228
|
+
* which, because both are correct for something.
|
|
229
|
+
*
|
|
230
|
+
* **`anchor` is the point the turn happens about.** Rotating about the view's
|
|
231
|
+
* own centre turns the content under the fingers rather than with them. It is
|
|
232
|
+
* the rotation counterpart of `usePinch`'s `focal`, and it is read at
|
|
233
|
+
* `onStart` as well as at every update so the first callback does not report
|
|
234
|
+
* the previous gesture's point.
|
|
235
|
+
*
|
|
236
|
+
* **Activation criteria.** There are none to set. RNGH's rotation takes the
|
|
237
|
+
* touch as soon as two fingers turn, and it exposes no threshold, so Impulse
|
|
238
|
+
* has none to pass on. `usePinch` is the other intent in this position.
|
|
239
|
+
*
|
|
240
|
+
* **Coexistence.** A rotation almost always shares its view with a pinch, and
|
|
241
|
+
* neither has a threshold to separate them with. Say it: `useGestures` with
|
|
242
|
+
* `mode: 'simultaneous'` for gestures this screen owns, `alongside` for one
|
|
243
|
+
* it does not.
|
|
244
|
+
*
|
|
245
|
+
* **Web.** RNGH recognizes rotation from pointer events, so it needs two
|
|
246
|
+
* pointers — a touchscreen, or a device that reports them. A trackpad's
|
|
247
|
+
* rotation is not a pointer pair and never reaches this hook, so a desktop
|
|
248
|
+
* browser with a trackpad alone cannot turn anything. Give it a control that
|
|
249
|
+
* writes `angle` directly, which the accessibility fallback needs anyway.
|
|
250
|
+
*
|
|
251
|
+
* **Accessibility.** A rotation is invisible to a screen reader and
|
|
252
|
+
* unreachable from a keyboard, and this hook does not fix that. Whatever the
|
|
253
|
+
* rotation turns must be reachable another way: buttons that step the angle
|
|
254
|
+
* by a documented amount, a control that resets it to zero, or
|
|
255
|
+
* `accessibilityActions` with `onAccessibilityAction`. A rotate-only
|
|
256
|
+
* affordance is a bug, not a trade-off.
|
|
257
|
+
*
|
|
258
|
+
* @param options - The angle range, callbacks, and the `alongside` /
|
|
259
|
+
* `blocks` / `deferTo` coexistence options every Impulse hook accepts.
|
|
260
|
+
*/
|
|
261
|
+
declare function useRotate(options?: UseRotateOptions): UseRotateResult;
|
|
262
|
+
|
|
263
|
+
export { type RotateEvent, type UseRotateOptions, type UseRotateResult, useRotate };
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { PanGesture } from 'react-native-gesture-handler';
|
|
2
|
+
import { SharedValue } from 'react-native-reanimated';
|
|
3
|
+
import { G as GestureMemoOptions } from '../useGestureMemo-BRW1EKcJ.js';
|
|
4
|
+
import { P as Point, H as HitSlop, I as IntentEndInfo, c as IntentResult } from '../types-ChGKY28a.js';
|
|
5
|
+
import 'react';
|
|
6
|
+
|
|
7
|
+
/** Which way a swipe went. The same vocabulary as the `directions` option. */
|
|
8
|
+
type SwipeDirection = 'left' | 'right' | 'up' | 'down';
|
|
9
|
+
/** The intent-shaped payload a {@link useSwipe} callback receives. */
|
|
10
|
+
interface SwipeEvent {
|
|
11
|
+
/**
|
|
12
|
+
* Which way the swipe went, or `null` when the release did not commit.
|
|
13
|
+
*
|
|
14
|
+
* `onSwipe` receives a payload whose direction is never `null` — that is
|
|
15
|
+
* the whole meaning of that callback. `onSwipeEnd` fires on every release
|
|
16
|
+
* of a swipe that activated, so it is the one that has to read this.
|
|
17
|
+
*/
|
|
18
|
+
readonly direction: SwipeDirection | null;
|
|
19
|
+
/**
|
|
20
|
+
* How far the finger travelled along the dominant axis, in points, always
|
|
21
|
+
* positive.
|
|
22
|
+
*
|
|
23
|
+
* Compare it against `commitDistance` to see how close a release that did
|
|
24
|
+
* not commit came.
|
|
25
|
+
*/
|
|
26
|
+
readonly distance: number;
|
|
27
|
+
/**
|
|
28
|
+
* How fast the finger was moving along the dominant axis, in points per
|
|
29
|
+
* second, always positive. The scalar counterpart of `velocity`, and what
|
|
30
|
+
* `commitSpeed` is compared against.
|
|
31
|
+
*/
|
|
32
|
+
readonly speed: number;
|
|
33
|
+
/**
|
|
34
|
+
* How far the finger moved since the swipe activated, signed and per axis.
|
|
35
|
+
*
|
|
36
|
+
* Measured from the activation point, so it does not include the
|
|
37
|
+
* `threshold` travel the finger spent before the swipe existed.
|
|
38
|
+
*/
|
|
39
|
+
readonly translation: Point;
|
|
40
|
+
/** Finger speed, in points per second, signed and per axis. */
|
|
41
|
+
readonly velocity: Point;
|
|
42
|
+
/** The touch point relative to the window. */
|
|
43
|
+
readonly absolute: Point;
|
|
44
|
+
/** How many fingers are down. */
|
|
45
|
+
readonly pointers: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A {@link SwipeEvent} that committed, so its direction is known.
|
|
49
|
+
*
|
|
50
|
+
* `onSwipe` takes this rather than `SwipeEvent`, which is what saves every
|
|
51
|
+
* consumer of that callback a null check for a case it cannot be in.
|
|
52
|
+
*/
|
|
53
|
+
type CommittedSwipeEvent = SwipeEvent & {
|
|
54
|
+
readonly direction: SwipeDirection;
|
|
55
|
+
};
|
|
56
|
+
/** Options for {@link useSwipe}. */
|
|
57
|
+
interface UseSwipeOptions extends GestureMemoOptions {
|
|
58
|
+
/**
|
|
59
|
+
* Which directions may commit. Every direction by default.
|
|
60
|
+
*
|
|
61
|
+
* **This also sets the activation criterion**, which is the reason to
|
|
62
|
+
* narrow it even when the extra directions would never fire. A list that is
|
|
63
|
+
* entirely horizontal gives the gesture a directional threshold on the x
|
|
64
|
+
* axis, so a vertical scroll view under it keeps working; an entirely
|
|
65
|
+
* vertical list does the same on y. A mixed list has no axis to lock, so
|
|
66
|
+
* the threshold is radial and the swipe competes with a scroller for every
|
|
67
|
+
* touch.
|
|
68
|
+
*
|
|
69
|
+
* Written inline as an array is fine — the gesture is not rebuilt when the
|
|
70
|
+
* contents are unchanged.
|
|
71
|
+
*/
|
|
72
|
+
directions?: readonly SwipeDirection[];
|
|
73
|
+
/**
|
|
74
|
+
* How far the finger must travel before the swipe activates and starts
|
|
75
|
+
* tracking, in points. Default `10`.
|
|
76
|
+
*
|
|
77
|
+
* This is not the commit test. It is the point at which the swipe takes the
|
|
78
|
+
* touch and `x` and `y` start reporting — see `commitDistance` for what
|
|
79
|
+
* decides that a release counts.
|
|
80
|
+
*/
|
|
81
|
+
threshold?: number;
|
|
82
|
+
/**
|
|
83
|
+
* How far the finger must travel for a release to commit, in points.
|
|
84
|
+
* Default `80`.
|
|
85
|
+
*
|
|
86
|
+
* Measured along the dominant axis, from the activation point.
|
|
87
|
+
*/
|
|
88
|
+
commitDistance?: number;
|
|
89
|
+
/**
|
|
90
|
+
* How fast the finger must be moving for a release to commit, in points per
|
|
91
|
+
* second. Default `800`.
|
|
92
|
+
*
|
|
93
|
+
* The flick: a release this fast commits even when it never travelled
|
|
94
|
+
* `commitDistance`. Either test is enough on its own.
|
|
95
|
+
*/
|
|
96
|
+
commitSpeed?: number;
|
|
97
|
+
/**
|
|
98
|
+
* Movement across the axis that makes the swipe fail, in points. Unset by
|
|
99
|
+
* default, which leaves RNGH's own behaviour in place.
|
|
100
|
+
*
|
|
101
|
+
* Ignored when `directions` has no single axis, which has no cross axis.
|
|
102
|
+
*/
|
|
103
|
+
failOffset?: number;
|
|
104
|
+
/**
|
|
105
|
+
* How many fingers must be on the view. Unset by default, which leaves
|
|
106
|
+
* RNGH's range of one to ten in place.
|
|
107
|
+
*/
|
|
108
|
+
pointers?: number;
|
|
109
|
+
/**
|
|
110
|
+
* Extra touchable area around the view, in points.
|
|
111
|
+
*
|
|
112
|
+
* Written inline as an object is fine — the gesture is not rebuilt when the
|
|
113
|
+
* contents are unchanged.
|
|
114
|
+
*/
|
|
115
|
+
hitSlop?: HitSlop;
|
|
116
|
+
/**
|
|
117
|
+
* Whether the gesture is recognized at all. Default `true`.
|
|
118
|
+
*
|
|
119
|
+
* Prefer this over unmounting the `<GestureDetector>`: a disabled gesture
|
|
120
|
+
* keeps its identity and its relations, so re-enabling it does not
|
|
121
|
+
* re-attach anything.
|
|
122
|
+
*/
|
|
123
|
+
enabled?: boolean;
|
|
124
|
+
/**
|
|
125
|
+
* The swipe committed. **Runs on the JS thread** — Impulse owns the
|
|
126
|
+
* `scheduleOnRN` boundary, so this is an ordinary function and may touch
|
|
127
|
+
* React state.
|
|
128
|
+
*
|
|
129
|
+
* This is the callback almost every consumer wants, and it fires only for
|
|
130
|
+
* the thing it is named after: a release that passed `commitDistance` or
|
|
131
|
+
* `commitSpeed` in a direction `directions` allows. It never fires for a
|
|
132
|
+
* cancelled gesture, because a swipe the system took away did not happen.
|
|
133
|
+
*
|
|
134
|
+
* `event.direction` is never `null` here.
|
|
135
|
+
*/
|
|
136
|
+
onSwipe?: (event: CommittedSwipeEvent) => void;
|
|
137
|
+
/**
|
|
138
|
+
* The swipe ended, committed or not. **Runs on the JS thread.**
|
|
139
|
+
*
|
|
140
|
+
* Fires on every release of a swipe that activated, which is what a view
|
|
141
|
+
* that followed the finger needs: `onSwipe` says to act, and this says to
|
|
142
|
+
* put the view back. Read `event.direction` for whether it committed —
|
|
143
|
+
* `null` is the release that did not.
|
|
144
|
+
*
|
|
145
|
+
* It fires for both endings, and `cancelled` says which. A cancelled
|
|
146
|
+
* gesture never commits, so its direction is always `null`.
|
|
147
|
+
*/
|
|
148
|
+
onSwipeEnd?: (event: SwipeEvent, info: IntentEndInfo) => void;
|
|
149
|
+
/**
|
|
150
|
+
* The finger went down and the gesture is now a candidate. **This is a
|
|
151
|
+
* worklet** — mark it with the `'worklet'` directive, and do not touch
|
|
152
|
+
* React state from it.
|
|
153
|
+
*
|
|
154
|
+
* The swipe has not passed `threshold` yet and may never. Undo whatever it
|
|
155
|
+
* sets in `onFinalize`.
|
|
156
|
+
*/
|
|
157
|
+
onBegin?: (event: SwipeEvent) => void;
|
|
158
|
+
/**
|
|
159
|
+
* The finger moved. **This is a worklet**, and it runs on every frame.
|
|
160
|
+
*
|
|
161
|
+
* `direction` is `null` on every one of these: a swipe is decided at
|
|
162
|
+
* release, not while the finger is down. Read `translation` to show the
|
|
163
|
+
* view following, and `distance` to show how close a commit is.
|
|
164
|
+
*/
|
|
165
|
+
onUpdate?: (event: SwipeEvent) => void;
|
|
166
|
+
/**
|
|
167
|
+
* The gesture is over, whether it activated or not. **This is a worklet.**
|
|
168
|
+
*
|
|
169
|
+
* `success` is `true` when the swipe activated and ended normally — which
|
|
170
|
+
* is not the same as committing. A release that stopped short is a
|
|
171
|
+
* successful gesture with no direction.
|
|
172
|
+
*/
|
|
173
|
+
onFinalize?: (event: SwipeEvent, success: boolean) => void;
|
|
174
|
+
}
|
|
175
|
+
/** What {@link useSwipe} returns. */
|
|
176
|
+
interface UseSwipeResult extends IntentResult<PanGesture> {
|
|
177
|
+
/**
|
|
178
|
+
* How far the finger has moved along the x axis since the swipe activated.
|
|
179
|
+
*
|
|
180
|
+
* **This is movement, not a position**, on the same terms as `usePan`: it
|
|
181
|
+
* is zeroed at the start of every gesture and keeps its final number after
|
|
182
|
+
* the gesture ends, so a release animation has something to animate from.
|
|
183
|
+
*
|
|
184
|
+
* Impulse does not put it back. That is an animation, and Principle 5 keeps
|
|
185
|
+
* animation out of this library — `onSwipeEnd` is where a consumer springs
|
|
186
|
+
* it home or off the screen.
|
|
187
|
+
*/
|
|
188
|
+
readonly x: SharedValue<number>;
|
|
189
|
+
/**
|
|
190
|
+
* How far the finger has moved along the y axis since the swipe activated.
|
|
191
|
+
* Same terms as `x`.
|
|
192
|
+
*/
|
|
193
|
+
readonly y: SharedValue<number>;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Recognize a directional swipe.
|
|
197
|
+
*
|
|
198
|
+
* ```tsx
|
|
199
|
+
* const swipe = useSwipe({
|
|
200
|
+
* directions: ['left'],
|
|
201
|
+
* onSwipe: () => archive(item.id),
|
|
202
|
+
* onSwipeEnd: () => {
|
|
203
|
+
* swipe.x.value = withSpring(0)
|
|
204
|
+
* },
|
|
205
|
+
* })
|
|
206
|
+
*
|
|
207
|
+
* return (
|
|
208
|
+
* <GestureDetector gesture={swipe.gesture}>
|
|
209
|
+
* <Animated.View style={style} />
|
|
210
|
+
* </GestureDetector>
|
|
211
|
+
* )
|
|
212
|
+
* ```
|
|
213
|
+
*
|
|
214
|
+
* A swipe is a pan that is judged at release. The finger moves, `x` and `y`
|
|
215
|
+
* report it so the view can follow, and on release the travel and the speed
|
|
216
|
+
* along the dominant axis decide whether it counted. `onSwipe` fires only for
|
|
217
|
+
* a release that counted, in a direction `directions` allows.
|
|
218
|
+
*
|
|
219
|
+
* **Narrowing `directions` is the coexistence setting, not only a filter.**
|
|
220
|
+
* An all-horizontal list gives the gesture a directional threshold on x, so a
|
|
221
|
+
* vertical scroll view under it keeps working without a relation. A mixed
|
|
222
|
+
* list has no axis to lock, so the threshold is radial and the swipe competes
|
|
223
|
+
* for every touch — declare `deferTo` or `blocks` there.
|
|
224
|
+
*
|
|
225
|
+
* **Two thresholds, and they mean different things.** `threshold` is when the
|
|
226
|
+
* swipe takes the touch and starts reporting. `commitDistance` and
|
|
227
|
+
* `commitSpeed` are what a release is measured against. A swipe that
|
|
228
|
+
* activates and stops short reaches `onSwipeEnd` with a `null` direction, and
|
|
229
|
+
* never reaches `onSwipe`.
|
|
230
|
+
*
|
|
231
|
+
* **No style, and no animation.** This hook returns numbers and stops there.
|
|
232
|
+
* Whether a committed row leaves the screen and an uncommitted one springs
|
|
233
|
+
* back is the consumer's decision, taken in `onSwipeEnd`.
|
|
234
|
+
* `@rootnative/inertia-gestures` has a `useSwipe` that owns both, and needs
|
|
235
|
+
* `@rootnative/inertia` to do it.
|
|
236
|
+
*
|
|
237
|
+
* **Web.** RNGH recognizes pan from pointer events, so a mouse drag commits
|
|
238
|
+
* the same way a touch does, and `speed` is reported in the same units. A
|
|
239
|
+
* trackpad's two-finger swipe is a scroll, not a pan, and never reaches this
|
|
240
|
+
* hook.
|
|
241
|
+
*
|
|
242
|
+
* **Accessibility.** A swipe is invisible to a screen reader and unreachable
|
|
243
|
+
* from a keyboard. Whatever it commits must be reachable another way: a
|
|
244
|
+
* visible button for a row action, a paging control for a carousel, or
|
|
245
|
+
* `accessibilityActions` with `onAccessibilityAction`. A swipe-only action is
|
|
246
|
+
* a bug, not a trade-off.
|
|
247
|
+
*
|
|
248
|
+
* @param options - Directions, activation and commit criteria, callbacks, and
|
|
249
|
+
* the `alongside` / `blocks` / `deferTo` coexistence options every Impulse
|
|
250
|
+
* hook accepts.
|
|
251
|
+
*/
|
|
252
|
+
declare function useSwipe(options?: UseSwipeOptions): UseSwipeResult;
|
|
253
|
+
|
|
254
|
+
export { type CommittedSwipeEvent, type SwipeDirection, type SwipeEvent, type UseSwipeOptions, type UseSwipeResult, useSwipe };
|
package/dist/tap/index.d.ts
CHANGED
|
@@ -1,25 +1,10 @@
|
|
|
1
1
|
import { TapGesture } from 'react-native-gesture-handler';
|
|
2
|
-
import { G as GestureMemoOptions } from '../useGestureMemo-
|
|
3
|
-
import {
|
|
2
|
+
import { G as GestureMemoOptions } from '../useGestureMemo-BRW1EKcJ.js';
|
|
3
|
+
import { T as TapEvent } from '../tapEvent-KSSojt_l.js';
|
|
4
|
+
import { H as HitSlop, I as IntentEndInfo, c as IntentResult } from '../types-ChGKY28a.js';
|
|
4
5
|
import 'react';
|
|
5
6
|
import 'react-native-reanimated';
|
|
6
7
|
|
|
7
|
-
/** The intent-shaped payload a {@link useTap} callback receives. */
|
|
8
|
-
interface TapEvent {
|
|
9
|
-
/** X of the tap, in points, relative to the view the gesture is attached to. */
|
|
10
|
-
readonly x: number;
|
|
11
|
-
/** Y of the tap, in points, relative to the view the gesture is attached to. */
|
|
12
|
-
readonly y: number;
|
|
13
|
-
/**
|
|
14
|
-
* The same point relative to the window.
|
|
15
|
-
*
|
|
16
|
-
* Prefer it over `x` / `y` when the view itself is being transformed by the
|
|
17
|
-
* gesture — a tap on a view that is mid-animation reports a moving `x`.
|
|
18
|
-
*/
|
|
19
|
-
readonly absolute: Point;
|
|
20
|
-
/** How many fingers were down when the tap was recognized. */
|
|
21
|
-
readonly pointers: number;
|
|
22
|
-
}
|
|
23
8
|
/** Options for {@link useTap}. */
|
|
24
9
|
interface UseTapOptions extends GestureMemoOptions {
|
|
25
10
|
/**
|
|
@@ -60,14 +45,23 @@ interface UseTapOptions extends GestureMemoOptions {
|
|
|
60
45
|
*/
|
|
61
46
|
enabled?: boolean;
|
|
62
47
|
/**
|
|
63
|
-
* The tap
|
|
64
|
-
* `
|
|
48
|
+
* The tap ended. **Runs on the JS thread** — Impulse owns the
|
|
49
|
+
* `scheduleOnRN` boundary, so this is an ordinary function and may touch React
|
|
65
50
|
* state.
|
|
66
51
|
*
|
|
67
|
-
* It fires only for a
|
|
68
|
-
*
|
|
52
|
+
* It fires only for a tap the recognizer accepted, and `cancelled` says
|
|
53
|
+
* what happened after that. `false` is the ordinary tap. `true` means the
|
|
54
|
+
* system took the recognized tap away before it could be acted on — a
|
|
55
|
+
* competing gesture in a relation won it, or the app went to the
|
|
56
|
+
* background.
|
|
57
|
+
*
|
|
58
|
+
* **Check `cancelled` before you act on the tap.** A handler that navigates
|
|
59
|
+
* or submits should do nothing when it is `true`. The path is rare: a touch
|
|
60
|
+
* that moved past `maxDistance` or stayed down past `maxDuration` was never
|
|
61
|
+
* a tap at all, so it reaches `onFinalize` with `success: false` and never
|
|
62
|
+
* gets here.
|
|
69
63
|
*/
|
|
70
|
-
onTap?: (event: TapEvent) => void;
|
|
64
|
+
onTap?: (event: TapEvent, info: IntentEndInfo) => void;
|
|
71
65
|
/**
|
|
72
66
|
* The finger went down and the gesture is now a candidate. **This is a
|
|
73
67
|
* worklet** — mark it with the `'worklet'` directive, and do not touch
|
|
@@ -111,7 +105,7 @@ type UseTapResult = IntentResult<TapGesture>;
|
|
|
111
105
|
*
|
|
112
106
|
* `onTap` runs on the JS thread and may set React state directly. `onBegin`
|
|
113
107
|
* and `onFinalize` are worklets and run on the UI thread — the name states
|
|
114
|
-
* the thread, so there is nothing to configure and no `
|
|
108
|
+
* the thread, so there is nothing to configure and no `scheduleOnRN` to write.
|
|
115
109
|
*
|
|
116
110
|
* `isActive` is a shared value that is `true` while the finger is down. Drive
|
|
117
111
|
* a pressed state from it without a re-render:
|
|
@@ -124,11 +118,22 @@ type UseTapResult = IntentResult<TapGesture>;
|
|
|
124
118
|
* **Activation criteria.** `maxDuration` defaults to 500ms and `maxDistance`
|
|
125
119
|
* to 10 points. The distance default is Impulse's, not RNGH's: RNGH defers to
|
|
126
120
|
* the platform there, so the same tap is accepted on one operating system and
|
|
127
|
-
* rejected on the other.
|
|
121
|
+
* rejected on the other. The device sweep did not test either default.
|
|
122
|
+
*
|
|
123
|
+
* **Pairing with a double tap.** A single tap and a double tap on one view is
|
|
124
|
+
* a composition, not an option — and the mode is `exclusive`, with the double
|
|
125
|
+
* tap named first:
|
|
126
|
+
*
|
|
127
|
+
* ```tsx
|
|
128
|
+
* useGestures([double, tap], { mode: 'exclusive' })
|
|
129
|
+
* ```
|
|
128
130
|
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
131
|
+
* `race` is the wrong mode here and fails quietly. A single tap recognizes on
|
|
132
|
+
* the first release, so it wins the race every time and the double tap never
|
|
133
|
+
* fires. `exclusive` is what makes the single tap wait to learn whether a
|
|
134
|
+
* second tap is coming — at the cost of `useDoubleTap`'s `maxDelay` in
|
|
135
|
+
* latency on every single tap. Do not reach for `maxDelay` to build the pair
|
|
136
|
+
* by hand.
|
|
132
137
|
*
|
|
133
138
|
* **Web.** RNGH's web implementation recognizes tap from pointer events, and
|
|
134
139
|
* `pointers` above 1 is unreliable there because a mouse reports one pointer
|
|
@@ -147,4 +152,4 @@ type UseTapResult = IntentResult<TapGesture>;
|
|
|
147
152
|
*/
|
|
148
153
|
declare function useTap(options?: UseTapOptions): UseTapResult;
|
|
149
154
|
|
|
150
|
-
export {
|
|
155
|
+
export { TapEvent, type UseTapOptions, type UseTapResult, useTap };
|
package/dist/tap/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export { useTap } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
3
|
-
import '../chunk-
|
|
1
|
+
export { useTap } from '../chunk-IH7SQ5X6.js';
|
|
2
|
+
import '../chunk-VEPUHGPN.js';
|
|
3
|
+
import '../chunk-2UZTAWUQ.js';
|
|
4
|
+
import '../chunk-NYDDZD4G.js';
|
|
4
5
|
import '../chunk-ZX7WNICB.js';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { P as Point } from './types-ChGKY28a.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The intent-shaped payload a tap callback receives.
|
|
5
|
+
*
|
|
6
|
+
* Shared by `useTap` and `useDoubleTap`, because the two recognize the same
|
|
7
|
+
* touch and differ only in how many times it happens. One payload rather than
|
|
8
|
+
* two identical ones means a consumer who already knows `useTap`'s event
|
|
9
|
+
* knows this one, and a field added later cannot reach one hook and miss the
|
|
10
|
+
* other.
|
|
11
|
+
*/
|
|
12
|
+
interface TapEvent {
|
|
13
|
+
/** X of the tap, in points, relative to the view the gesture is attached to. */
|
|
14
|
+
readonly x: number;
|
|
15
|
+
/** Y of the tap, in points, relative to the view the gesture is attached to. */
|
|
16
|
+
readonly y: number;
|
|
17
|
+
/**
|
|
18
|
+
* The same point relative to the window.
|
|
19
|
+
*
|
|
20
|
+
* Prefer it over `x` / `y` when the view itself is being transformed by the
|
|
21
|
+
* gesture — a tap on a view that is mid-animation reports a moving `x`.
|
|
22
|
+
*/
|
|
23
|
+
readonly absolute: Point;
|
|
24
|
+
/** How many fingers were down when the tap was recognized. */
|
|
25
|
+
readonly pointers: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type { TapEvent as T };
|