react-native-gesture-handler 2.4.1 → 2.4.2
Sign up to get free protection for your applications and to get access to all the features.
- package/lib/commonjs/components/DrawerLayout.js +38 -11
- package/lib/commonjs/components/DrawerLayout.js.map +1 -1
- package/lib/commonjs/handlers/ForceTouchGestureHandler.js +2 -1
- package/lib/commonjs/handlers/ForceTouchGestureHandler.js.map +1 -1
- package/lib/commonjs/handlers/createHandler.js +19 -8
- package/lib/commonjs/handlers/createHandler.js.map +1 -1
- package/lib/commonjs/handlers/gestureHandlerCommon.js.map +1 -1
- package/lib/commonjs/handlers/gestures/GestureDetector.js +83 -63
- package/lib/commonjs/handlers/gestures/GestureDetector.js.map +1 -1
- package/lib/commonjs/handlers/gestures/gesture.js +13 -2
- package/lib/commonjs/handlers/gestures/gesture.js.map +1 -1
- package/lib/commonjs/web/utils.js.map +1 -1
- package/lib/module/components/DrawerLayout.js +38 -11
- package/lib/module/components/DrawerLayout.js.map +1 -1
- package/lib/module/handlers/ForceTouchGestureHandler.js +1 -1
- package/lib/module/handlers/ForceTouchGestureHandler.js.map +1 -1
- package/lib/module/handlers/createHandler.js +20 -8
- package/lib/module/handlers/createHandler.js.map +1 -1
- package/lib/module/handlers/gestureHandlerCommon.js.map +1 -1
- package/lib/module/handlers/gestures/GestureDetector.js +83 -63
- package/lib/module/handlers/gestures/GestureDetector.js.map +1 -1
- package/lib/module/handlers/gestures/gesture.js +13 -2
- package/lib/module/handlers/gestures/gesture.js.map +1 -1
- package/lib/module/web/utils.js.map +1 -1
- package/lib/typescript/components/DrawerLayout.d.ts +3 -0
- package/lib/typescript/handlers/ForceTouchGestureHandler.d.ts +2 -2
- package/lib/typescript/handlers/gestureHandlerCommon.d.ts +1 -0
- package/lib/typescript/handlers/gestures/GestureDetector.d.ts +2 -1
- package/lib/typescript/handlers/gestures/gesture.d.ts +3 -0
- package/package.json +1 -1
- package/src/components/DrawerLayout.tsx +34 -10
- package/src/handlers/ForceTouchGestureHandler.ts +3 -2
- package/src/handlers/createHandler.ts +25 -11
- package/src/handlers/gestureHandlerCommon.ts +2 -0
- package/src/handlers/gestures/GestureDetector.tsx +107 -81
- package/src/handlers/gestures/gesture.ts +16 -0
- package/src/web/utils.ts +1 -1
@@ -148,6 +148,11 @@ export interface DrawerLayoutProps {
|
|
148
148
|
onDrawerSlide?: (position: number) => void;
|
149
149
|
|
150
150
|
onGestureRef?: (ref: PanGestureHandler) => void;
|
151
|
+
|
152
|
+
// implicit `children` prop has been removed in @types/react^18.0.0
|
153
|
+
children?:
|
154
|
+
| React.ReactNode
|
155
|
+
| ((openValue?: Animated.AnimatedInterpolation) => React.ReactNode);
|
151
156
|
}
|
152
157
|
|
153
158
|
export type DrawerLayoutState = {
|
@@ -155,6 +160,8 @@ export type DrawerLayoutState = {
|
|
155
160
|
touchX: Animated.Value;
|
156
161
|
drawerTranslation: Animated.Value;
|
157
162
|
containerWidth: number;
|
163
|
+
drawerState: DrawerState;
|
164
|
+
drawerOpened: boolean;
|
158
165
|
};
|
159
166
|
|
160
167
|
export type DrawerMovementOption = {
|
@@ -189,6 +196,8 @@ export default class DrawerLayout extends Component<
|
|
189
196
|
touchX,
|
190
197
|
drawerTranslation,
|
191
198
|
containerWidth: 0,
|
199
|
+
drawerState: IDLE,
|
200
|
+
drawerOpened: false,
|
192
201
|
};
|
193
202
|
|
194
203
|
this.updateAnimatedEvent(props, this.state);
|
@@ -349,6 +358,7 @@ export default class DrawerLayout extends Component<
|
|
349
358
|
this.handleRelease({ nativeEvent });
|
350
359
|
} else if (nativeEvent.state === State.ACTIVE) {
|
351
360
|
this.emitStateChanged(DRAGGING, false);
|
361
|
+
this.setState({ drawerState: DRAGGING });
|
352
362
|
if (this.props.keyboardDismissMode === 'on-drag') {
|
353
363
|
Keyboard.dismiss();
|
354
364
|
}
|
@@ -464,6 +474,7 @@ export default class DrawerLayout extends Component<
|
|
464
474
|
const willShow = toValue !== 0;
|
465
475
|
this.updateShowing(willShow);
|
466
476
|
this.emitStateChanged(SETTLING, willShow);
|
477
|
+
this.setState({ drawerState: SETTLING });
|
467
478
|
if (this.props.hideStatusBar) {
|
468
479
|
StatusBar.setHidden(willShow, this.props.statusBarAnimation || 'slide');
|
469
480
|
}
|
@@ -476,6 +487,12 @@ export default class DrawerLayout extends Component<
|
|
476
487
|
}).start(({ finished }) => {
|
477
488
|
if (finished) {
|
478
489
|
this.emitStateChanged(IDLE, willShow);
|
490
|
+
this.setState({ drawerOpened: willShow });
|
491
|
+
if (this.state.drawerState !== DRAGGING) {
|
492
|
+
// it's possilbe that user started drag while the drawer
|
493
|
+
// was settling, don't override state in this case
|
494
|
+
this.setState({ drawerState: IDLE });
|
495
|
+
}
|
479
496
|
if (willShow) {
|
480
497
|
this.props.onDrawerOpen?.();
|
481
498
|
} else {
|
@@ -516,11 +533,14 @@ export default class DrawerLayout extends Component<
|
|
516
533
|
private renderOverlay = () => {
|
517
534
|
/* Overlay styles */
|
518
535
|
invariant(this.openValue, 'should be set');
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
}
|
536
|
+
let overlayOpacity;
|
537
|
+
|
538
|
+
if (this.state.drawerState !== IDLE) {
|
539
|
+
overlayOpacity = this.openValue;
|
540
|
+
} else {
|
541
|
+
overlayOpacity = this.state.drawerOpened ? 1 : 0;
|
542
|
+
}
|
543
|
+
|
524
544
|
const dynamicOverlayStyles = {
|
525
545
|
opacity: overlayOpacity,
|
526
546
|
backgroundColor: this.props.overlayColor,
|
@@ -579,11 +599,15 @@ export default class DrawerLayout extends Component<
|
|
579
599
|
let drawerTranslateX: number | Animated.AnimatedInterpolation = 0;
|
580
600
|
if (drawerSlide) {
|
581
601
|
const closedDrawerOffset = fromLeft ? -drawerWidth! : drawerWidth!;
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
602
|
+
if (this.state.drawerState !== IDLE) {
|
603
|
+
drawerTranslateX = openValue.interpolate({
|
604
|
+
inputRange: [0, 1],
|
605
|
+
outputRange: [closedDrawerOffset, 0],
|
606
|
+
extrapolate: 'clamp',
|
607
|
+
});
|
608
|
+
} else {
|
609
|
+
drawerTranslateX = this.state.drawerOpened ? 0 : closedDrawerOffset;
|
610
|
+
}
|
587
611
|
}
|
588
612
|
const drawerStyles: {
|
589
613
|
transform: { translateX: number | Animated.AnimatedInterpolation }[];
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import React from 'react';
|
1
|
+
import React, { PropsWithChildren } from 'react';
|
2
2
|
import { tagMessage } from '../utils';
|
3
3
|
import PlatformConstants from '../PlatformConstants';
|
4
4
|
import createHandler from './createHandler';
|
@@ -13,7 +13,8 @@ export const forceTouchGestureHandlerProps = [
|
|
13
13
|
'feedbackOnActivation',
|
14
14
|
] as const;
|
15
15
|
|
16
|
-
|
16
|
+
// implicit `children` prop has been removed in @types/react^18.0.0
|
17
|
+
class ForceTouchFallback extends React.Component<PropsWithChildren<unknown>> {
|
17
18
|
static forceTouchAvailable = false;
|
18
19
|
componentDidMount() {
|
19
20
|
console.warn(
|
@@ -148,6 +148,8 @@ type InternalEventHandlers = {
|
|
148
148
|
onGestureHandlerStateChange?: (event: any) => void;
|
149
149
|
};
|
150
150
|
|
151
|
+
const UNRESOLVED_REFS_RETRY_LIMIT = 1;
|
152
|
+
|
151
153
|
// TODO(TS) - make sure that BaseGestureHandlerProps doesn't need other generic parameter to work with custom properties.
|
152
154
|
export default function createHandler<
|
153
155
|
T extends BaseGestureHandlerProps<U>,
|
@@ -198,7 +200,7 @@ export default function createHandler<
|
|
198
200
|
'toggleElementInspector',
|
199
201
|
() => {
|
200
202
|
this.setState((_) => ({ allowTouches }));
|
201
|
-
this.update();
|
203
|
+
this.update(UNRESOLVED_REFS_RETRY_LIMIT);
|
202
204
|
}
|
203
205
|
);
|
204
206
|
}
|
@@ -211,7 +213,7 @@ export default function createHandler<
|
|
211
213
|
// be resolved by then.
|
212
214
|
this.updateEnqueued = setImmediate(() => {
|
213
215
|
this.updateEnqueued = null;
|
214
|
-
this.update();
|
216
|
+
this.update(UNRESOLVED_REFS_RETRY_LIMIT);
|
215
217
|
});
|
216
218
|
}
|
217
219
|
|
@@ -231,7 +233,7 @@ export default function createHandler<
|
|
231
233
|
if (this.viewTag !== viewTag) {
|
232
234
|
this.attachGestureHandler(viewTag as number); // TODO(TS) - check interaction between _viewTag & findNodeHandle
|
233
235
|
}
|
234
|
-
this.update();
|
236
|
+
this.update(UNRESOLVED_REFS_RETRY_LIMIT);
|
235
237
|
}
|
236
238
|
|
237
239
|
componentWillUnmount() {
|
@@ -361,14 +363,26 @@ export default function createHandler<
|
|
361
363
|
scheduleFlushOperations();
|
362
364
|
};
|
363
365
|
|
364
|
-
private update() {
|
365
|
-
const
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
if (
|
371
|
-
this.
|
366
|
+
private update(remainingTries: number) {
|
367
|
+
const props: HandlerProps<U> = this.props;
|
368
|
+
|
369
|
+
// When ref is set via a function i.e. `ref={(r) => refObject.current = r}` instead of
|
370
|
+
// `ref={refObject}` it's possible that it won't be resolved in time. Seems like trying
|
371
|
+
// again is easy enough fix.
|
372
|
+
if (hasUnresolvedRefs(props) && remainingTries > 0) {
|
373
|
+
this.updateEnqueued = setImmediate(() => {
|
374
|
+
this.updateEnqueued = null;
|
375
|
+
this.update(remainingTries - 1);
|
376
|
+
});
|
377
|
+
} else {
|
378
|
+
const newConfig = filterConfig(
|
379
|
+
transformProps ? transformProps(this.props) : this.props,
|
380
|
+
[...allowedProps, ...customNativeProps],
|
381
|
+
config
|
382
|
+
);
|
383
|
+
if (!deepEqual(this.config, newConfig)) {
|
384
|
+
this.updateGestureHandler(newConfig);
|
385
|
+
}
|
372
386
|
}
|
373
387
|
}
|
374
388
|
|
@@ -126,6 +126,8 @@ export type BaseGestureHandlerProps<
|
|
126
126
|
onHandlerStateChange?: (
|
127
127
|
event: HandlerStateChangeEvent<ExtraEventPayloadT>
|
128
128
|
) => void;
|
129
|
+
// implicit `children` prop has been removed in @types/react^18.0.0
|
130
|
+
children?: React.ReactNode;
|
129
131
|
};
|
130
132
|
|
131
133
|
function isConfigParam(param: unknown, name: string) {
|
@@ -268,11 +268,34 @@ function updateHandlers(
|
|
268
268
|
}
|
269
269
|
|
270
270
|
if (preparedGesture.animatedHandlers) {
|
271
|
-
|
271
|
+
const previousHandlersValue =
|
272
|
+
preparedGesture.animatedHandlers.value ?? [];
|
273
|
+
const newHandlersValue = (preparedGesture.config
|
272
274
|
.filter((g) => g.shouldUseReanimated) // ignore gestures that shouldn't run on UI
|
273
275
|
.map((g) => g.handlers) as unknown) as HandlerCallbacks<
|
274
276
|
Record<string, unknown>
|
275
277
|
>[];
|
278
|
+
|
279
|
+
// if amount of gesture configs changes, we need to update the callbacks in shared value
|
280
|
+
let shouldUpdateSharedValue =
|
281
|
+
previousHandlersValue.length !== newHandlersValue.length;
|
282
|
+
|
283
|
+
if (!shouldUpdateSharedValue) {
|
284
|
+
// if the amount is the same, we need to check if any of the configs inside has changed
|
285
|
+
for (let i = 0; i < newHandlersValue.length; i++) {
|
286
|
+
if (
|
287
|
+
// we can use the `gestureId` prop as it's unique for every config instance
|
288
|
+
newHandlersValue[i].gestureId !== previousHandlersValue[i].gestureId
|
289
|
+
) {
|
290
|
+
shouldUpdateSharedValue = true;
|
291
|
+
break;
|
292
|
+
}
|
293
|
+
}
|
294
|
+
}
|
295
|
+
|
296
|
+
if (shouldUpdateSharedValue) {
|
297
|
+
preparedGesture.animatedHandlers.value = newHandlersValue;
|
298
|
+
}
|
276
299
|
}
|
277
300
|
|
278
301
|
scheduleFlushOperations();
|
@@ -299,90 +322,90 @@ function needsToReattach(
|
|
299
322
|
return false;
|
300
323
|
}
|
301
324
|
|
302
|
-
function
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
325
|
+
function isStateChangeEvent(
|
326
|
+
event: GestureUpdateEvent | GestureStateChangeEvent | GestureTouchEvent
|
327
|
+
): event is GestureStateChangeEvent {
|
328
|
+
'worklet';
|
329
|
+
// @ts-ignore Yes, the oldState prop is missing on GestureTouchEvent, that's the point
|
330
|
+
return event.oldState != null;
|
331
|
+
}
|
309
332
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
}
|
333
|
+
function isTouchEvent(
|
334
|
+
event: GestureUpdateEvent | GestureStateChangeEvent | GestureTouchEvent
|
335
|
+
): event is GestureTouchEvent {
|
336
|
+
'worklet';
|
337
|
+
return event.eventType != null;
|
338
|
+
}
|
317
339
|
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
340
|
+
function getHandler(
|
341
|
+
type: CALLBACK_TYPE,
|
342
|
+
gesture: HandlerCallbacks<Record<string, unknown>>
|
343
|
+
) {
|
344
|
+
'worklet';
|
345
|
+
switch (type) {
|
346
|
+
case CALLBACK_TYPE.BEGAN:
|
347
|
+
return gesture.onBegin;
|
348
|
+
case CALLBACK_TYPE.START:
|
349
|
+
return gesture.onStart;
|
350
|
+
case CALLBACK_TYPE.UPDATE:
|
351
|
+
return gesture.onUpdate;
|
352
|
+
case CALLBACK_TYPE.CHANGE:
|
353
|
+
return gesture.onChange;
|
354
|
+
case CALLBACK_TYPE.END:
|
355
|
+
return gesture.onEnd;
|
356
|
+
case CALLBACK_TYPE.FINALIZE:
|
357
|
+
return gesture.onFinalize;
|
358
|
+
case CALLBACK_TYPE.TOUCHES_DOWN:
|
359
|
+
return gesture.onTouchesDown;
|
360
|
+
case CALLBACK_TYPE.TOUCHES_MOVE:
|
361
|
+
return gesture.onTouchesMove;
|
362
|
+
case CALLBACK_TYPE.TOUCHES_UP:
|
363
|
+
return gesture.onTouchesUp;
|
364
|
+
case CALLBACK_TYPE.TOUCHES_CANCELLED:
|
365
|
+
return gesture.onTouchesCancelled;
|
323
366
|
}
|
367
|
+
}
|
324
368
|
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
return gesture.onChange;
|
339
|
-
case CALLBACK_TYPE.END:
|
340
|
-
return gesture.onEnd;
|
341
|
-
case CALLBACK_TYPE.FINALIZE:
|
342
|
-
return gesture.onFinalize;
|
343
|
-
case CALLBACK_TYPE.TOUCHES_DOWN:
|
344
|
-
return gesture.onTouchesDown;
|
345
|
-
case CALLBACK_TYPE.TOUCHES_MOVE:
|
346
|
-
return gesture.onTouchesMove;
|
347
|
-
case CALLBACK_TYPE.TOUCHES_UP:
|
348
|
-
return gesture.onTouchesUp;
|
349
|
-
case CALLBACK_TYPE.TOUCHES_CANCELLED:
|
350
|
-
return gesture.onTouchesCancelled;
|
351
|
-
}
|
369
|
+
function touchEventTypeToCallbackType(
|
370
|
+
eventType: TouchEventType
|
371
|
+
): CALLBACK_TYPE {
|
372
|
+
'worklet';
|
373
|
+
switch (eventType) {
|
374
|
+
case TouchEventType.TOUCHES_DOWN:
|
375
|
+
return CALLBACK_TYPE.TOUCHES_DOWN;
|
376
|
+
case TouchEventType.TOUCHES_MOVE:
|
377
|
+
return CALLBACK_TYPE.TOUCHES_MOVE;
|
378
|
+
case TouchEventType.TOUCHES_UP:
|
379
|
+
return CALLBACK_TYPE.TOUCHES_UP;
|
380
|
+
case TouchEventType.TOUCHES_CANCELLED:
|
381
|
+
return CALLBACK_TYPE.TOUCHES_CANCELLED;
|
352
382
|
}
|
383
|
+
return CALLBACK_TYPE.UNDEFINED;
|
384
|
+
}
|
353
385
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
return CALLBACK_TYPE.UNDEFINED;
|
386
|
+
function runWorklet(
|
387
|
+
type: CALLBACK_TYPE,
|
388
|
+
gesture: HandlerCallbacks<Record<string, unknown>>,
|
389
|
+
event: GestureStateChangeEvent | GestureUpdateEvent | GestureTouchEvent,
|
390
|
+
...args: any[]
|
391
|
+
) {
|
392
|
+
'worklet';
|
393
|
+
const handler = getHandler(type, gesture);
|
394
|
+
if (gesture.isWorklet[type]) {
|
395
|
+
// @ts-ignore Logic below makes sure the correct event is send to the
|
396
|
+
// correct handler.
|
397
|
+
handler?.(event, ...args);
|
398
|
+
} else if (handler) {
|
399
|
+
console.warn(tagMessage('Animated gesture callback must be a worklet'));
|
369
400
|
}
|
401
|
+
}
|
370
402
|
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
'worklet';
|
378
|
-
const handler = getHandler(type, gesture);
|
379
|
-
if (gesture.isWorklet[type]) {
|
380
|
-
// @ts-ignore Logic below makes sure the correct event is send to the
|
381
|
-
// correct handler.
|
382
|
-
handler?.(event, ...args);
|
383
|
-
} else if (handler) {
|
384
|
-
console.warn(tagMessage('Animated gesture callback must be a worklet'));
|
385
|
-
}
|
403
|
+
function useAnimatedGesture(
|
404
|
+
preparedGesture: GestureConfigReference,
|
405
|
+
needsRebuild: boolean
|
406
|
+
) {
|
407
|
+
if (!Reanimated) {
|
408
|
+
return;
|
386
409
|
}
|
387
410
|
|
388
411
|
// Hooks are called conditionally, but the condition is whether the
|
@@ -490,10 +513,9 @@ function useAnimatedGesture(
|
|
490
513
|
|
491
514
|
interface GestureDetectorProps {
|
492
515
|
gesture?: ComposedGesture | GestureType;
|
516
|
+
children?: React.ReactNode;
|
493
517
|
}
|
494
|
-
export const GestureDetector:
|
495
|
-
props
|
496
|
-
) => {
|
518
|
+
export const GestureDetector = (props: GestureDetectorProps) => {
|
497
519
|
const gestureConfig = props.gesture;
|
498
520
|
const gesture = gestureConfig?.toGestureArray?.() ?? [];
|
499
521
|
const useReanimatedHook = gesture.some((g) => g.shouldUseReanimated);
|
@@ -605,7 +627,11 @@ export const GestureDetector: React.FunctionComponent<GestureDetectorProps> = (
|
|
605
627
|
}
|
606
628
|
};
|
607
629
|
|
608
|
-
class Wrap extends React.Component<{
|
630
|
+
class Wrap extends React.Component<{
|
631
|
+
onGestureHandlerEvent?: unknown;
|
632
|
+
// implicit `children` prop has been removed in @types/react^18.0.0
|
633
|
+
children?: React.ReactNode;
|
634
|
+
}> {
|
609
635
|
render() {
|
610
636
|
// I don't think that fighting with types over such a simple function is worth it
|
611
637
|
// The only thing it does is add 'collapsable: false' to the child component
|
@@ -53,6 +53,7 @@ type TouchEventHandlerType = (
|
|
53
53
|
) => void;
|
54
54
|
|
55
55
|
export type HandlerCallbacks<EventPayloadT extends Record<string, unknown>> = {
|
56
|
+
gestureId: number;
|
56
57
|
handlerTag: number;
|
57
58
|
onBegin?: (event: GestureStateChangeEvent<EventPayloadT>) => void;
|
58
59
|
onStart?: (event: GestureStateChangeEvent<EventPayloadT>) => void;
|
@@ -115,17 +116,32 @@ export abstract class Gesture {
|
|
115
116
|
abstract prepare(): void;
|
116
117
|
}
|
117
118
|
|
119
|
+
let nextGestureId = 0;
|
118
120
|
export abstract class BaseGesture<
|
119
121
|
EventPayloadT extends Record<string, unknown>
|
120
122
|
> extends Gesture {
|
123
|
+
private gestureId = -1;
|
121
124
|
public handlerTag = -1;
|
122
125
|
public handlerName = '';
|
123
126
|
public config: BaseGestureConfig = {};
|
124
127
|
public handlers: HandlerCallbacks<EventPayloadT> = {
|
128
|
+
gestureId: -1,
|
125
129
|
handlerTag: -1,
|
126
130
|
isWorklet: [],
|
127
131
|
};
|
128
132
|
|
133
|
+
constructor() {
|
134
|
+
super();
|
135
|
+
|
136
|
+
// Used to check whether the gesture config has been updated when wrapping it
|
137
|
+
// with `useMemo`. Since every config will have a unique id, when the dependencies
|
138
|
+
// don't change, the config won't be recreated and the id will stay the same.
|
139
|
+
// If the id is different, it means that the config has changed and the gesture
|
140
|
+
// needs to be updated.
|
141
|
+
this.gestureId = nextGestureId++;
|
142
|
+
this.handlers.gestureId = this.gestureId;
|
143
|
+
}
|
144
|
+
|
129
145
|
private addDependency(
|
130
146
|
key: 'simultaneousWith' | 'requireToFail',
|
131
147
|
gesture: Exclude<GestureRef, number>
|
package/src/web/utils.ts
CHANGED