Skip to content

Implement gesture relations #3664

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 13 commits into
base: @mbert/shared-values
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ class RNGestureHandlerEvent private constructor() : Event<RNGestureHandlerEvent>
EVENT_NAME
}

override fun canCoalesce() = true
// Unfortunately getCoalescingKey is not considered when sending event to C++, therefore we have to disable coalescing in v3
override fun canCoalesce() = actionType != GestureHandler.ACTION_TYPE_NATIVE_DETECTOR

override fun getCoalescingKey() = coalescingKey

Expand Down
3 changes: 3 additions & 0 deletions packages/react-native-gesture-handler/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,5 +166,8 @@ export type { NativeDetectorProps } from './v3/NativeDetector';
export { NativeDetector } from './v3/NativeDetector';

export * from './v3/hooks/useGesture';
export * from './v3/hooks/relations/useSimultaneous';
export * from './v3/hooks/relations/useExclusive';
export * from './v3/hooks/relations/useRace';

initialize();
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { NativeGesture } from './hooks/useGesture';
import { NativeGesture } from './types';
import { Reanimated } from '../handlers/gestures/reanimatedWrapper';

import { Animated, StyleSheet } from 'react-native';
Expand Down Expand Up @@ -47,7 +47,7 @@ export function NativeDetector({ gesture, children }: NativeDetectorProps) {
gesture.gestureEvents.onGestureHandlerTouchEvent
}
moduleId={globalThis._RNGH_MODULE_ID}
handlerTags={[gesture.tag]}
handlerTags={gesture.tag}
style={styles.detector}>
{children}
</NativeDetectorComponent>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {
NativeGesture,
StateChangeEvent,
UpdateEvent,
TouchEvent,
} from '../../types';

export function useComposedGesture(...gestures: NativeGesture[]) {

Check failure on line 8 in packages/react-native-gesture-handler/src/v3/hooks/relations/useComposedGesture.ts

View workflow job for this annotation

GitHub Actions / check

If your function doesn't call any Hooks, avoid the 'use' prefix. Instead, write it as a regular function without the 'use' prefix
const tags = gestures.flatMap((gesture) => gesture.tag);

const config = {
shouldUseReanimated: gestures.some(
(gesture) => gesture.config.shouldUseReanimated
),
dispatchesAnimatedEvents: gestures.some(
(gesture) => gesture.config.dispatchesAnimatedEvents
),
};

const onGestureHandlerStateChange = (
event: StateChangeEvent<Record<string, unknown>>
) => {
for (const gesture of gestures) {
if (gesture.gestureEvents.onGestureHandlerStateChange) {
gesture.gestureEvents.onGestureHandlerStateChange(event);
}
}
};

const onGestureHandlerEvent = (
event: UpdateEvent<Record<string, unknown>>
) => {
for (const gesture of gestures) {
if (gesture.gestureEvents.onGestureHandlerEvent) {
gesture.gestureEvents.onGestureHandlerEvent(event);
}
}
};

const onGestureHandlerTouchEvent = (event: TouchEvent) => {
for (const gesture of gestures) {
if (gesture.gestureEvents.onGestureHandlerTouchEvent) {
gesture.gestureEvents.onGestureHandlerTouchEvent(event);
}
}
};

let onGestureHandlerAnimatedEvent;

for (const gesture of gestures) {
if (gesture.gestureEvents.onGestureHandlerAnimatedEvent) {
onGestureHandlerAnimatedEvent =
gesture.gestureEvents.onGestureHandlerAnimatedEvent;

break;
}
}

return {
tag: tags,
name: 'ComposedGesture',
config,
gestureEvents: {
onGestureHandlerStateChange,
onGestureHandlerEvent,
onGestureHandlerAnimatedEvent,
onGestureHandlerTouchEvent,
},
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { NativeGesture } from '../../types';
import { useComposedGesture } from './useComposedGesture';

export function useExclusive(...gestures: NativeGesture[]) {
const composedGesture = useComposedGesture(...gestures);

const tags = gestures.flatMap((gesture) => gesture.tag);

for (let i = 0; i < gestures.length; i++) {
gestures[i].config.waitFor = tags.slice(0, i);
}

composedGesture.name = 'ExclusiveGesture';

return composedGesture;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { NativeGesture } from '../../types';
import { useComposedGesture } from './useComposedGesture';

export function useRace(...gestures: NativeGesture[]) {
const composedGesture = useComposedGesture(...gestures);

composedGesture.name = 'RaceGesture';

return composedGesture;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { NativeGesture } from '../../types';
import { useComposedGesture } from './useComposedGesture';

export function useSimultaneous(...gestures: NativeGesture[]) {
const composedGesture = useComposedGesture(...gestures);

const tags = gestures.flatMap((gesture) => gesture.tag);

for (const gesture of gestures) {
const simultaneousHandlersTags = [
...tags.filter((tag) => !gesture.tag.includes(tag)),
];

gesture.config.simultaneousHandlers = simultaneousHandlersTags;
}

composedGesture.name = 'SimultaneousGesture';

return composedGesture;
}
29 changes: 2 additions & 27 deletions packages/react-native-gesture-handler/src/v3/hooks/useGesture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,7 @@ import {
SharedValue,
} from '../../handlers/gestures/reanimatedWrapper';
import { tagMessage } from '../../utils';
import { AnimatedEvent } from '../types';

type GestureType =
| 'TapGestureHandler'
| 'LongPressGestureHandler'
| 'PanGestureHandler'
| 'PinchGestureHandler'
| 'RotationGestureHandler'
| 'FlingGestureHandler'
| 'ForceTouchGestureHandler'
| 'ManualGestureHandler'
| 'NativeViewGestureHandler';

type GestureEvents = {
onGestureHandlerStateChange: (event: any) => void;
onGestureHandlerEvent: undefined | ((event: any) => void);
onGestureHandlerTouchEvent: (event: any) => void;
onGestureHandlerAnimatedEvent: undefined | AnimatedEvent;
};

export interface NativeGesture {
tag: number;
name: GestureType;
config: Record<string, unknown>;
gestureEvents: GestureEvents;
}
import { GestureType, NativeGesture } from '../types';

function hasWorkletEventHandlers(config: Record<string, unknown>) {
return Object.values(config).some(
Expand Down Expand Up @@ -181,7 +156,7 @@ export function useGesture(
}, [config, tag]);

return {
tag: tag,
tag: [tag],
name: type,
config,
gestureEvents: {
Expand Down
25 changes: 25 additions & 0 deletions packages/react-native-gesture-handler/src/v3/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,28 @@ export type CallbackHandlers = Omit<
export type AnimatedEvent = ((...args: any[]) => void) & {
_argMapping?: unknown;
};

export type GestureType =
| 'TapGestureHandler'
| 'LongPressGestureHandler'
| 'PanGestureHandler'
| 'PinchGestureHandler'
| 'RotationGestureHandler'
| 'FlingGestureHandler'
| 'ForceTouchGestureHandler'
| 'ManualGestureHandler'
| 'NativeViewGestureHandler';

export type GestureEvents = {
onGestureHandlerStateChange: (event: any) => void;
onGestureHandlerEvent: undefined | ((event: any) => void);
onGestureHandlerTouchEvent: (event: any) => void;
onGestureHandlerAnimatedEvent: undefined | AnimatedEvent;
};

export interface NativeGesture {
tag: number[];
name: GestureType;
config: Record<string, unknown>;
gestureEvents: GestureEvents;
}
Loading