@rootnative/impulse 0.0.0-alpha.0

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 (44) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/LICENSE +21 -0
  3. package/README.md +174 -0
  4. package/dist/chunk-5BMRKYVY.js +39 -0
  5. package/dist/chunk-F4RHM4ZK.js +77 -0
  6. package/dist/chunk-FR242SUF.js +174 -0
  7. package/dist/chunk-IG5RXCYR.js +74 -0
  8. package/dist/chunk-LM645QQT.js +37 -0
  9. package/dist/chunk-PMR25UCT.js +8 -0
  10. package/dist/chunk-ZX7WNICB.js +39 -0
  11. package/dist/compose/index.d.ts +80 -0
  12. package/dist/compose/index.js +2 -0
  13. package/dist/drag/index.d.ts +277 -0
  14. package/dist/drag/index.js +4 -0
  15. package/dist/gesture-handler/index.d.ts +1 -0
  16. package/dist/gesture-handler/index.js +1 -0
  17. package/dist/index.d.ts +9 -0
  18. package/dist/index.js +8 -0
  19. package/dist/raw/index.d.ts +63 -0
  20. package/dist/raw/index.js +3 -0
  21. package/dist/tap/index.d.ts +150 -0
  22. package/dist/tap/index.js +4 -0
  23. package/dist/types-Ch2HM3aP.d.ts +142 -0
  24. package/dist/useGestureMemo-Ccv8rB0C.d.ts +38 -0
  25. package/jest-preset.cjs +60 -0
  26. package/jest-setup.cjs +56 -0
  27. package/package.json +115 -0
  28. package/src/compose/index.ts +7 -0
  29. package/src/compose/useGestures.ts +136 -0
  30. package/src/gesture-handler/index.ts +18 -0
  31. package/src/index.ts +53 -0
  32. package/src/intents/drag/index.ts +8 -0
  33. package/src/intents/tap/index.ts +2 -0
  34. package/src/intents/useDrag.ts +563 -0
  35. package/src/intents/useTap.ts +285 -0
  36. package/src/internal/useGestureMemo.ts +110 -0
  37. package/src/internal/useLatestCallback.ts +64 -0
  38. package/src/internal/useStableList.ts +62 -0
  39. package/src/internal/useStableRecord.ts +72 -0
  40. package/src/internal/warnOnce.ts +60 -0
  41. package/src/raw/index.ts +2 -0
  42. package/src/raw/useRawGesture.ts +71 -0
  43. package/src/relations/index.ts +120 -0
  44. package/src/types.ts +187 -0
@@ -0,0 +1,39 @@
1
+ import { useRef } from 'react';
2
+
3
+ // src/internal/warnOnce.ts
4
+ var warned = /* @__PURE__ */ new Set();
5
+ function isDevBuild() {
6
+ return typeof __DEV__ !== "undefined" && __DEV__;
7
+ }
8
+ function warnOnce(key, message) {
9
+ if (!isDevBuild() || warned.has(key)) {
10
+ return;
11
+ }
12
+ warned.add(key);
13
+ console.warn(`[impulse] ${message}`);
14
+ }
15
+ var EMPTY = [];
16
+ function sameContents(a, b) {
17
+ if (a === b) {
18
+ return true;
19
+ }
20
+ if (a.length !== b.length) {
21
+ return false;
22
+ }
23
+ for (let index = 0; index < a.length; index += 1) {
24
+ if (!Object.is(a[index], b[index])) {
25
+ return false;
26
+ }
27
+ }
28
+ return true;
29
+ }
30
+ function useStableList(value) {
31
+ const next = value === void 0 ? EMPTY : Array.isArray(value) ? value : [value];
32
+ const held = useRef(EMPTY);
33
+ if (!sameContents(held.current, next)) {
34
+ held.current = next;
35
+ }
36
+ return held.current;
37
+ }
38
+
39
+ export { isDevBuild, useStableList, warnOnce };
@@ -0,0 +1,80 @@
1
+ import { ComposedGesture } from 'react-native-gesture-handler';
2
+ import { A as AttachableGesture, a as ComposeMode } from '../types-Ch2HM3aP.js';
3
+ import 'react';
4
+ import 'react-native-reanimated';
5
+
6
+ /**
7
+ * Anything carrying a gesture — every Impulse hook result, including the one
8
+ * `useGestures` itself returns.
9
+ */
10
+ interface GestureCarrier {
11
+ readonly gesture: AttachableGesture;
12
+ }
13
+ /**
14
+ * A member of a composition: a hook result, or a bare gesture from
15
+ * `@rootnative/impulse/gesture-handler`.
16
+ */
17
+ type GestureMember = GestureCarrier | AttachableGesture;
18
+ /** What `useGestures` returns. It is itself a valid composition member. */
19
+ interface GesturesResult {
20
+ /** The composed gesture. Hand it to `<GestureDetector>`. */
21
+ readonly gesture: ComposedGesture;
22
+ }
23
+ /** Options for {@link useGestures}. */
24
+ interface UseGesturesOptions {
25
+ /** How the members relate to one another. */
26
+ mode: ComposeMode;
27
+ }
28
+ /**
29
+ * Compose gestures under one relation.
30
+ *
31
+ * ```tsx
32
+ * // tap and double-tap race; the winner runs alongside the drag
33
+ * const { gesture } = useGestures(
34
+ * [useGestures([tap, double], { mode: 'race' }), drag],
35
+ * { mode: 'simultaneous' },
36
+ * )
37
+ *
38
+ * return (
39
+ * <GestureDetector gesture={gesture}>
40
+ * <View />
41
+ * </GestureDetector>
42
+ * )
43
+ * ```
44
+ *
45
+ * Composition is **data, not nesting**. RNGH expresses the same thing as
46
+ * `Gesture.Simultaneous(Gesture.Race(tap, doubleTap), drag)`, which has to be
47
+ * read backwards to learn what wins and gains a level of nesting per
48
+ * relation. Here each call is one flat list plus the relation that holds over
49
+ * it, and the result is itself a member, so precedence reads left to right
50
+ * and depth is a choice rather than a consequence.
51
+ *
52
+ * The modes:
53
+ *
54
+ * - `race` — the first member to activate wins and cancels the rest. This is
55
+ * what you want for mutually exclusive readings of one touch.
56
+ * - `simultaneous` — every member recognizes independently. A pinch and a
57
+ * rotate on one image.
58
+ * - `exclusive` — members are tried in order, and a later one activates only
59
+ * after every earlier one has failed. A tap and a double-tap, where the tap
60
+ * must wait to learn whether a second one is coming.
61
+ *
62
+ * **Coexistence options are not accepted here, and that is a limitation
63
+ * rather than a choice.** RNGH's three external-gesture relations are methods
64
+ * on a single gesture; a composed gesture does not have them. Put
65
+ * `alongside` / `blocks` / `deferTo` on the member hooks instead — Impulse
66
+ * cannot apply them for you without mutating gestures another hook owns and
67
+ * memoised.
68
+ *
69
+ * **Accessibility.** A composition is as reachable as its members, which is
70
+ * to say a screen reader and a keyboard see none of it. Each member hook
71
+ * documents its own fallback; a composition needs the union of them.
72
+ *
73
+ * @param members - The gestures to compose, in precedence order. The order
74
+ * matters for `exclusive` and is ignored by the other two modes.
75
+ * @param options - The relation to compose under.
76
+ * @returns The composed gesture, stable while the members and the mode are.
77
+ */
78
+ declare function useGestures(members: readonly GestureMember[], options: UseGesturesOptions): GesturesResult;
79
+
80
+ export { type GestureCarrier, type GestureMember, type GesturesResult, type UseGesturesOptions, useGestures };
@@ -0,0 +1,2 @@
1
+ export { useGestures } from '../chunk-LM645QQT.js';
2
+ import '../chunk-ZX7WNICB.js';
@@ -0,0 +1,277 @@
1
+ import { PanGesture } from 'react-native-gesture-handler';
2
+ import { SharedValue } from 'react-native-reanimated';
3
+ import { G as GestureMemoOptions } from '../useGestureMemo-Ccv8rB0C.js';
4
+ import { P as Point, H as HitSlop, I as IntentResult } from '../types-Ch2HM3aP.js';
5
+ import 'react';
6
+
7
+ /** Which way the drag is allowed to move. */
8
+ type DragAxis = 'x' | 'y' | 'both';
9
+ /**
10
+ * How far the drag may travel, in the same coordinates as `x` and `y`.
11
+ *
12
+ * Every edge is optional, and an omitted edge is unbounded. The names are the
13
+ * edges of the travel, not of the view: `left` is the smallest `x`, `bottom`
14
+ * is the largest `y`.
15
+ */
16
+ interface DragBounds {
17
+ /** Smallest `x`. */
18
+ left?: number;
19
+ /** Largest `x`. */
20
+ right?: number;
21
+ /** Smallest `y`. */
22
+ top?: number;
23
+ /** Largest `y`. */
24
+ bottom?: number;
25
+ }
26
+ /** The intent-shaped payload a {@link useDrag} callback receives. */
27
+ interface DragEvent {
28
+ /**
29
+ * Where the drag is now — the same numbers as `x` and `y`, after `bounds`
30
+ * and `elastic` were applied.
31
+ *
32
+ * This accumulates across gestures. A second drag continues from where the
33
+ * first one stopped, which is why `bounds` can be written against a layout
34
+ * rather than against one gesture's travel.
35
+ */
36
+ readonly position: Point;
37
+ /**
38
+ * How far the finger moved since this gesture activated, raw.
39
+ *
40
+ * Untouched by `bounds` and `elastic`, and reset to zero at the start of
41
+ * every gesture. Read `position` for where the thing being dragged actually
42
+ * sits.
43
+ */
44
+ readonly translation: Point;
45
+ /** Finger speed, in points per second. The input a release spring needs. */
46
+ readonly velocity: Point;
47
+ /** The touch point relative to the window. */
48
+ readonly absolute: Point;
49
+ /**
50
+ * The nearest point inside `bounds`. Equal to `position` whenever the drag
51
+ * is in bounds, which with the default `elastic` of `0` is always.
52
+ *
53
+ * With `elastic` set, the finger can pull the value past an edge and
54
+ * Impulse leaves it there on release — moving it back is an animation, and
55
+ * Impulse owns no animation vocabulary. This field is the destination that
56
+ * animation needs, so the consumer does not have to re-derive the clamp
57
+ * from bounds it already handed over.
58
+ */
59
+ readonly settled: Point;
60
+ /** How many fingers are down. */
61
+ readonly pointers: number;
62
+ }
63
+ /** Options for {@link useDrag}. */
64
+ interface UseDragOptions extends GestureMemoOptions {
65
+ /**
66
+ * Which way the drag may move. Default `'both'`.
67
+ *
68
+ * The locked axis's shared value never changes — `axis: 'x'` leaves `y` at
69
+ * its initial value for the life of the hook. The axis also decides the
70
+ * activation criterion, which is the part that matters inside a scroll
71
+ * view: see `threshold`.
72
+ */
73
+ axis?: DragAxis;
74
+ /**
75
+ * How far the finger must travel before the drag activates, in points.
76
+ * Default `10`.
77
+ *
78
+ * On a single axis this is a directional threshold, so a drag with
79
+ * `axis: 'x'` ignores vertical movement entirely and a vertical scroll view
80
+ * under it keeps working. On `'both'` it is a radial distance.
81
+ *
82
+ * Lower it for a drag that must feel immediate and owns its view. Raise it
83
+ * when the drag shares the view with something that should usually win.
84
+ */
85
+ threshold?: number;
86
+ /**
87
+ * Movement across the axis that makes the drag fail, in points. Unset by
88
+ * default, which leaves RNGH's own behaviour in place.
89
+ *
90
+ * `threshold` decides when the drag wins; this decides when it gives up.
91
+ * Set it when the cross-axis gesture must win a diagonal — a horizontal
92
+ * row action inside a vertical list, where a mostly-vertical drag should
93
+ * scroll rather than half-open the row. Ignored when `axis` is `'both'`,
94
+ * which has no cross axis.
95
+ */
96
+ failOffset?: number;
97
+ /**
98
+ * How far the drag may travel. Unbounded by default.
99
+ *
100
+ * Written inline as an object is fine — the gesture is not rebuilt when the
101
+ * contents are unchanged.
102
+ */
103
+ bounds?: DragBounds;
104
+ /**
105
+ * How much of the finger's movement survives past a bound, `0` to `1`.
106
+ * Default `0`.
107
+ *
108
+ * `0` clamps hard at the edge. `1` ignores the bound while the finger is
109
+ * down. `0.3` or so gives the rubber-band pull an over-scroll has.
110
+ *
111
+ * **Impulse does not put the value back on release.** That is an animation,
112
+ * and Principle 5 keeps animation out of this library. `onDragEnd` carries
113
+ * `settled` — the point to animate to — and `@rootnative/inertia` or a
114
+ * plain `withSpring` does the moving.
115
+ */
116
+ elastic?: number;
117
+ /**
118
+ * Where `x` and `y` start. Default `{ x: 0, y: 0 }`.
119
+ *
120
+ * Read once, when the hook mounts. Changing it later does nothing, because
121
+ * the shared values are the drag's state from then on — write
122
+ * `drag.x.value` to move it instead.
123
+ */
124
+ initial?: Point;
125
+ /**
126
+ * How many fingers must be on the view. Unset by default, which leaves
127
+ * RNGH's range of one to ten in place.
128
+ *
129
+ * Setting it fixes the count exactly, so `pointers: 2` is a two-finger drag
130
+ * that neither starts with one finger nor survives a third.
131
+ */
132
+ pointers?: number;
133
+ /**
134
+ * Extra touchable area around the view, in points.
135
+ *
136
+ * Written inline as an object is fine — the gesture is not rebuilt when the
137
+ * contents are unchanged.
138
+ */
139
+ hitSlop?: HitSlop;
140
+ /**
141
+ * Whether the gesture is recognized at all. Default `true`.
142
+ *
143
+ * Prefer this over unmounting the `<GestureDetector>`: a disabled gesture
144
+ * keeps its identity and its relations, so re-enabling it does not
145
+ * re-attach anything.
146
+ */
147
+ enabled?: boolean;
148
+ /**
149
+ * The drag passed `threshold` and now owns the touch. **Runs on the JS
150
+ * thread** — Impulse owns the `runOnJS` boundary, so this is an ordinary
151
+ * function and may touch React state.
152
+ *
153
+ * This is the first moment the drag has definitely won. `onBegin` fires
154
+ * earlier and promises nothing.
155
+ */
156
+ onDragStart?: (event: DragEvent) => void;
157
+ /**
158
+ * The finger lifted and the drag is over. **Runs on the JS thread.**
159
+ *
160
+ * Fires only for a drag that activated and then released. A drag the system
161
+ * took away — a competing gesture won, or the app went to the background —
162
+ * reaches `onFinalize` with `success: false` and never gets here, because
163
+ * there was no release and so no velocity worth seeding a spring with.
164
+ *
165
+ * Read `velocity` to seed that release animation, and `settled` for where
166
+ * an elastic overshoot should return to.
167
+ */
168
+ onDragEnd?: (event: DragEvent) => void;
169
+ /**
170
+ * The finger went down and the gesture is now a candidate. **This is a
171
+ * worklet** — mark it with the `'worklet'` directive, and do not touch
172
+ * React state from it.
173
+ *
174
+ * Being a candidate is not the same as winning: the drag has not passed
175
+ * `threshold` yet and may never. Use it to show a grabbed state, and undo
176
+ * that state in `onFinalize`.
177
+ */
178
+ onBegin?: (event: DragEvent) => void;
179
+ /**
180
+ * The drag moved. **This is a worklet**, and it runs on every frame the
181
+ * finger moves.
182
+ *
183
+ * There is no JS-thread counterpart on purpose. A per-frame `runOnJS` is a
184
+ * scheduling cost paid sixty times a second for a value that is already on
185
+ * the thread that needs it — read `x` and `y` from a `useAnimatedStyle`
186
+ * instead, and let this callback handle what the style cannot.
187
+ */
188
+ onUpdate?: (event: DragEvent) => void;
189
+ /**
190
+ * The gesture is over, whether it activated or not. **This is a worklet.**
191
+ *
192
+ * `success` is `true` when the drag activated and ended normally. This is
193
+ * the right place to clear anything `onBegin` set, because it runs on both
194
+ * paths.
195
+ */
196
+ onFinalize?: (event: DragEvent, success: boolean) => void;
197
+ }
198
+ /** What {@link useDrag} returns. */
199
+ interface UseDragResult extends IntentResult<PanGesture> {
200
+ /**
201
+ * Where the drag is along the x axis, after `bounds` and `elastic`.
202
+ *
203
+ * Writable: assigning `drag.x.value` moves the drag, and the next gesture
204
+ * continues from the new number rather than snapping back. That is how a
205
+ * release animation hands control back — animate this value, and the drag
206
+ * picks up wherever the animation left it.
207
+ *
208
+ * Frozen at its initial value when `axis` is `'y'`.
209
+ */
210
+ readonly x: SharedValue<number>;
211
+ /**
212
+ * Where the drag is along the y axis, after `bounds` and `elastic`.
213
+ *
214
+ * Writable, on the same terms as `x`. Frozen at its initial value when
215
+ * `axis` is `'x'`.
216
+ */
217
+ readonly y: SharedValue<number>;
218
+ }
219
+ /**
220
+ * Recognize a drag, and stream where it is.
221
+ *
222
+ * ```tsx
223
+ * const drag = useDrag({ axis: 'x', bounds: { left: -120, right: 0 } })
224
+ * const style = useAnimatedStyle(() => ({
225
+ * transform: [{ translateX: drag.x.value }],
226
+ * }))
227
+ *
228
+ * return (
229
+ * <GestureDetector gesture={drag.gesture}>
230
+ * <Animated.View style={style} />
231
+ * </GestureDetector>
232
+ * )
233
+ * ```
234
+ *
235
+ * `x` and `y` are shared values, so the view follows the finger on the UI
236
+ * thread with no re-render. They accumulate across gestures: a second drag
237
+ * continues from where the first stopped.
238
+ *
239
+ * **No style, and no animation.** This hook returns numbers and stops there.
240
+ * Building the `transform` is the consumer's call, and so is any spring back.
241
+ * `@rootnative/inertia-gestures` has a `useDrag` that does both, and needs
242
+ * `@rootnative/inertia` to do it — reach for that one when a `Motion.View`
243
+ * should follow a finger and spring home, and for this one when the values
244
+ * are what you want.
245
+ *
246
+ * **Activation criteria.** `threshold` defaults to 10 points. With
247
+ * `axis: 'x'` or `'y'` it is directional, so a horizontal drag inside a
248
+ * vertical `ScrollView` leaves the scroll alone until the finger commits
249
+ * sideways; with `'both'` it is a radial distance. Set `failOffset` as well
250
+ * when a mostly-diagonal move should go to the other gesture rather than to
251
+ * this one. The default has not been measured on hardware yet.
252
+ *
253
+ * **Coexistence.** A threshold decides who moves first; it does not decide
254
+ * who wins a contested touch. For a drag inside a scroll view, say which one
255
+ * the touch belongs to as well — `deferTo: scrollRef` for a drag that is the
256
+ * fallback, `blocks: listRef` for one that is the foreground affordance.
257
+ *
258
+ * **Web.** RNGH recognizes pan from pointer events, so a mouse drag behaves
259
+ * the same as a touch drag and `velocity` is reported in the same units. A
260
+ * trackpad's momentum scroll is not a pan and never reaches this hook.
261
+ * `pointers` above 1 is unreliable on web.
262
+ *
263
+ * **Accessibility.** A drag is invisible to a screen reader and unreachable
264
+ * from a keyboard, and this hook does not fix that. Whatever the drag
265
+ * adjusts must be reachable another way: a pair of buttons for a slider, a
266
+ * visible action for a swipeable row, or `accessibilityActions` with
267
+ * `onAccessibilityAction` — `increment` and `decrement` for a value,
268
+ * `magicTap` or a named action for a dismissal. A drag-only affordance is a
269
+ * bug, not a trade-off.
270
+ *
271
+ * @param options - Activation criteria, bounds, callbacks, and the
272
+ * `alongside` / `blocks` / `deferTo` coexistence options every Impulse hook
273
+ * accepts.
274
+ */
275
+ declare function useDrag(options?: UseDragOptions): UseDragResult;
276
+
277
+ export { type DragAxis, type DragBounds, type DragEvent, type UseDragOptions, type UseDragResult, useDrag };
@@ -0,0 +1,4 @@
1
+ export { useDrag } from '../chunk-FR242SUF.js';
2
+ import '../chunk-5BMRKYVY.js';
3
+ import '../chunk-F4RHM4ZK.js';
4
+ import '../chunk-ZX7WNICB.js';
@@ -0,0 +1 @@
1
+ export * from 'react-native-gesture-handler';
@@ -0,0 +1 @@
1
+ export * from 'react-native-gesture-handler';
@@ -0,0 +1,9 @@
1
+ export { GestureDetector } from 'react-native-gesture-handler';
2
+ export { GestureCarrier, GestureMember, GesturesResult, UseGesturesOptions, useGestures } from './compose/index.js';
3
+ export { RawGestureResult, UseRawGestureOptions, useRawGesture } from './raw/index.js';
4
+ export { TapEvent, UseTapOptions, UseTapResult, useTap } from './tap/index.js';
5
+ export { DragAxis, DragBounds, DragEvent, UseDragOptions, UseDragResult, useDrag } from './drag/index.js';
6
+ export { A as AttachableGesture, C as CoexistenceOptions, a as ComposeMode, G as GestureReference, b as GestureReferences, H as HitSlop, I as IntentResult, P as Point } from './types-Ch2HM3aP.js';
7
+ import 'react';
8
+ import './useGestureMemo-Ccv8rB0C.js';
9
+ import 'react-native-reanimated';
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { useGestures } from './chunk-LM645QQT.js';
2
+ export { useRawGesture } from './chunk-PMR25UCT.js';
3
+ export { useTap } from './chunk-IG5RXCYR.js';
4
+ export { useDrag } from './chunk-FR242SUF.js';
5
+ import './chunk-5BMRKYVY.js';
6
+ import './chunk-F4RHM4ZK.js';
7
+ import './chunk-ZX7WNICB.js';
8
+ export { GestureDetector } from 'react-native-gesture-handler';
@@ -0,0 +1,63 @@
1
+ import { DependencyList } from 'react';
2
+ import { GestureType } from 'react-native-gesture-handler';
3
+ import { B as BuiltGesture, G as GestureMemoOptions } from '../useGestureMemo-Ccv8rB0C.js';
4
+ import '../types-Ch2HM3aP.js';
5
+ import 'react-native-reanimated';
6
+
7
+ /** Options for {@link useRawGesture}. */
8
+ type UseRawGestureOptions = GestureMemoOptions;
9
+ /** What {@link useRawGesture} returns. */
10
+ type RawGestureResult<G extends GestureType> = BuiltGesture<G>;
11
+ /**
12
+ * Build an RNGH gesture by hand, with Impulse's memoisation and coexistence
13
+ * handling applied to it.
14
+ *
15
+ * The mechanism-level escape hatch. Impulse's intent hooks cover the common
16
+ * cases; this covers a recognizer they do not model, or a configuration they
17
+ * do not expose, without giving up the two things that are tedious to get
18
+ * right by hand:
19
+ *
20
+ * ```tsx
21
+ * const fling = useRawGesture(
22
+ * () => Gesture.Fling().direction(Directions.RIGHT).onEnd(onFling),
23
+ * [onFling],
24
+ * { deferTo: scrollRef },
25
+ * )
26
+ *
27
+ * <GestureDetector gesture={fling.gesture}>…</GestureDetector>
28
+ * ```
29
+ *
30
+ * What you still own here, because you are building the gesture yourself:
31
+ *
32
+ * - **The dependency list.** `build` runs only when `deps` change, and a
33
+ * stale capture is a stale gesture. A worklet callback belongs in `deps`,
34
+ * because a worklet is captured as written. A JS-thread callback does not —
35
+ * put it through `useLatestCallback` and depend on the stable result.
36
+ * - **The thread.** RNGH decides per callback, by whether it carries the
37
+ * `'worklet'` directive, and warns in development when a gesture mixes the
38
+ * two. Impulse does not insert a `runOnJS` boundary for you here; that is
39
+ * something the intent hooks do because they know what each callback means.
40
+ * - **The payload.** You get RNGH's flat event, not an intent-shaped one.
41
+ *
42
+ * What Impulse still owns:
43
+ *
44
+ * - Gesture identity across renders, so an inline option cannot re-attach a
45
+ * gesture mid-drag.
46
+ * - `alongside` / `blocks` / `deferTo` resolution, so the three RNGH
47
+ * relations are chosen by outcome rather than by method name.
48
+ * - A `ref` other hooks can name in their own coexistence options.
49
+ *
50
+ * **Accessibility.** Nothing here is reachable by a screen reader or a
51
+ * keyboard, and Impulse cannot name a fallback for a gesture it did not
52
+ * design. Whatever this gesture does must also be doable another way — an
53
+ * `accessibilityActions` entry, or a visible control.
54
+ *
55
+ * @param build - Constructs the gesture. Do not call the relation methods
56
+ * here; pass `alongside` / `blocks` / `deferTo` in `options` instead, so
57
+ * the three-way choice stays in one place and is applied exactly once.
58
+ * @param deps - What the gesture depends on, in `useMemo` terms.
59
+ * @param options - Coexistence options and `testId`.
60
+ */
61
+ declare function useRawGesture<G extends GestureType>(build: () => G, deps: DependencyList, options?: UseRawGestureOptions): RawGestureResult<G>;
62
+
63
+ export { type RawGestureResult, type UseRawGestureOptions, useRawGesture };
@@ -0,0 +1,3 @@
1
+ export { useRawGesture } from '../chunk-PMR25UCT.js';
2
+ import '../chunk-F4RHM4ZK.js';
3
+ import '../chunk-ZX7WNICB.js';
@@ -0,0 +1,150 @@
1
+ import { TapGesture } from 'react-native-gesture-handler';
2
+ import { G as GestureMemoOptions } from '../useGestureMemo-Ccv8rB0C.js';
3
+ import { P as Point, H as HitSlop, I as IntentResult } from '../types-Ch2HM3aP.js';
4
+ import 'react';
5
+ import 'react-native-reanimated';
6
+
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
+ /** Options for {@link useTap}. */
24
+ interface UseTapOptions extends GestureMemoOptions {
25
+ /**
26
+ * How many fingers must be down. Default `1`.
27
+ *
28
+ * A two-finger tap is a common "undo" or "zoom out" affordance, and it is
29
+ * the same intent with a different pointer count rather than a separate
30
+ * one.
31
+ */
32
+ pointers?: number;
33
+ /**
34
+ * How long the finger may stay down, in milliseconds. Default `500`.
35
+ *
36
+ * Past this the gesture fails rather than firing, which is what leaves the
37
+ * touch available to a `useLongPress` racing against it.
38
+ */
39
+ maxDuration?: number;
40
+ /**
41
+ * How far the finger may travel, in points. Default `10`.
42
+ *
43
+ * Raising it makes the tap more forgiving and makes it harder for a drag in
44
+ * the same view to win the touch.
45
+ */
46
+ maxDistance?: number;
47
+ /**
48
+ * Extra touchable area around the view, in points.
49
+ *
50
+ * Written inline as an object is fine — the gesture is not rebuilt when the
51
+ * contents are unchanged.
52
+ */
53
+ hitSlop?: HitSlop;
54
+ /**
55
+ * Whether the gesture is recognized at all. Default `true`.
56
+ *
57
+ * Prefer this over unmounting the `<GestureDetector>`: a disabled gesture
58
+ * keeps its identity and its relations, so re-enabling it does not
59
+ * re-attach anything.
60
+ */
61
+ enabled?: boolean;
62
+ /**
63
+ * The tap happened. **Runs on the JS thread** — Impulse owns the
64
+ * `runOnJS` boundary, so this is an ordinary function and may touch React
65
+ * state.
66
+ *
67
+ * It fires only for a successful tap. A touch that moved too far or stayed
68
+ * down too long reaches `onFinalize` with `success: false` instead.
69
+ */
70
+ onTap?: (event: TapEvent) => void;
71
+ /**
72
+ * The finger went down and the gesture is now a candidate. **This is a
73
+ * worklet** — mark it with the `'worklet'` directive, and do not touch
74
+ * React state from it.
75
+ *
76
+ * Being a candidate is not the same as winning: in a race with a
77
+ * long press or a drag, this fires and the gesture may still fail. Use it
78
+ * to show a pressed state, and undo that state in `onFinalize`.
79
+ */
80
+ onBegin?: (event: TapEvent) => void;
81
+ /**
82
+ * The gesture is over, whether it was recognized or not. **This is a
83
+ * worklet.**
84
+ *
85
+ * `success` is `true` when the tap was recognized. This is the right place
86
+ * to clear anything `onBegin` set, because it runs on both paths.
87
+ */
88
+ onFinalize?: (event: TapEvent, success: boolean) => void;
89
+ }
90
+ /**
91
+ * What {@link useTap} returns.
92
+ *
93
+ * An alias rather than an extending interface, because a tap produces no
94
+ * continuous value of its own: the gesture, the ref, and `isActive` are the
95
+ * whole result. An intent that does produce one — a drag's `x`, a pinch's
96
+ * `scale` — extends {@link IntentResult} instead.
97
+ */
98
+ type UseTapResult = IntentResult<TapGesture>;
99
+ /**
100
+ * Recognize a single tap.
101
+ *
102
+ * ```tsx
103
+ * const tap = useTap({ onTap: () => select(item.id) })
104
+ *
105
+ * return (
106
+ * <GestureDetector gesture={tap.gesture}>
107
+ * <View />
108
+ * </GestureDetector>
109
+ * )
110
+ * ```
111
+ *
112
+ * `onTap` runs on the JS thread and may set React state directly. `onBegin`
113
+ * and `onFinalize` are worklets and run on the UI thread — the name states
114
+ * the thread, so there is nothing to configure and no `runOnJS` to write.
115
+ *
116
+ * `isActive` is a shared value that is `true` while the finger is down. Drive
117
+ * a pressed state from it without a re-render:
118
+ *
119
+ * ```tsx
120
+ * const tap = useTap({ onTap: select })
121
+ * const style = useAnimatedStyle(() => ({ opacity: tap.isActive.value ? 0.6 : 1 }))
122
+ * ```
123
+ *
124
+ * **Activation criteria.** `maxDuration` defaults to 500ms and `maxDistance`
125
+ * to 10 points. The distance default is Impulse's, not RNGH's: RNGH defers to
126
+ * the platform there, so the same tap is accepted on one operating system and
127
+ * rejected on the other. Neither default has been measured on hardware yet.
128
+ *
129
+ * **Racing a double tap.** A single tap and a double tap on one view is a
130
+ * composition, not an option — `useGestures([tap, double], { mode: 'race' })`.
131
+ * Do not reach for `maxDelay` to build it by hand.
132
+ *
133
+ * **Web.** RNGH's web implementation recognizes tap from pointer events, and
134
+ * `pointers` above 1 is unreliable there because a mouse reports one pointer
135
+ * and touch emulation varies by browser. A single-finger tap behaves the same
136
+ * as on native.
137
+ *
138
+ * **Accessibility.** A tap gesture is invisible to a screen reader and
139
+ * unreachable from a keyboard. This hook does not fix that, and it cannot.
140
+ * Whatever the tap does must also be reachable another way: put the same
141
+ * action on a `<Pressable>`, or declare it with `accessibilityActions` and
142
+ * `onAccessibilityAction` on the view the gesture is attached to. A tap-only
143
+ * affordance is a bug, not a trade-off.
144
+ *
145
+ * @param options - Activation criteria, callbacks, and the `alongside` /
146
+ * `blocks` / `deferTo` coexistence options every Impulse hook accepts.
147
+ */
148
+ declare function useTap(options?: UseTapOptions): UseTapResult;
149
+
150
+ export { type TapEvent, type UseTapOptions, type UseTapResult, useTap };
@@ -0,0 +1,4 @@
1
+ export { useTap } from '../chunk-IG5RXCYR.js';
2
+ import '../chunk-5BMRKYVY.js';
3
+ import '../chunk-F4RHM4ZK.js';
4
+ import '../chunk-ZX7WNICB.js';