Skip to content
Merged
Show file tree
Hide file tree
Changes from 59 commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
ea8bd73
basic component
akwasniewski Jul 25, 2025
32fea07
Merge branch 'next' into @akwasniewski/native-detector-web
akwasniewski Jul 25, 2025
968112c
better useffect triggers:
akwasniewski Jul 25, 2025
6a69cd7
added todos
akwasniewski Jul 25, 2025
2b404af
moved Gesture handler detectors
akwasniewski Jul 25, 2025
9c30c1a
path fix
akwasniewski Jul 25, 2025
d7dd057
extracted host gesture detector
akwasniewski Jul 25, 2025
56b93d3
not extending view props
akwasniewski Jul 25, 2025
ad8ef64
native detector action type
akwasniewski Jul 25, 2025
0304177
moved implementation of web handler
akwasniewski Jul 28, 2025
4aaf700
memoisation
akwasniewski Jul 28, 2025
eeddd24
detaching handlers
akwasniewski Jul 28, 2025
3242ffd
forgot to asign to oldHandlerTags
akwasniewski Jul 28, 2025
7147ab3
removed unnecessary types
akwasniewski Jul 28, 2025
cc78f74
native detector animated event action type
akwasniewski Jul 28, 2025
067becb
removed console log
akwasniewski Jul 28, 2025
d782c95
handling animated events in sendEvents
akwasniewski Jul 29, 2025
86046bd
first refactor
akwasniewski Jul 29, 2025
6c34d33
using native detector animated event
akwasniewski Jul 29, 2025
cd793b3
refactor 2
akwasniewski Jul 29, 2025
580494d
use callback in native detector
akwasniewski Jul 30, 2025
eb98bf4
tagMessage
akwasniewski Jul 30, 2025
af663c8
should prevent drop
akwasniewski Jul 30, 2025
42e84b4
old api compatibility
akwasniewski Jul 30, 2025
079fdb4
not throwing strings
akwasniewski Jul 30, 2025
cb72d7e
removed unnessary check
akwasniewski Jul 30, 2025
de98017
even more refactoring
akwasniewski Jul 30, 2025
e193ff4
throwing when propsref is null
akwasniewski Jul 30, 2025
2ba053a
helper action type functions
akwasniewski Jul 30, 2025
8ab666b
separate paths on touch events
akwasniewski Jul 30, 2025
c445262
removed unsafe memoisation
akwasniewski Jul 30, 2025
46a9570
props extending
akwasniewski Jul 30, 2025
37c4070
failing on detach
akwasniewski Jul 31, 2025
20cbc9e
one more refactor
akwasniewski Aug 1, 2025
58f8d60
display contents
akwasniewski Aug 1, 2025
532f35a
Merge branch 'next' into @akwasniewski/native-detector-web
akwasniewski Aug 1, 2025
7d205dc
removed action type native detector animated event
akwasniewski Aug 1, 2025
fb7af53
handler data
akwasniewski Aug 1, 2025
cc3b639
fixed cyclic dependency
akwasniewski Aug 4, 2025
0c6f200
Update packages/react-native-gesture-handler/src/web/tools/GestureHan…
akwasniewski Aug 4, 2025
00738d6
minor fixes
akwasniewski Aug 4, 2025
9c17a96
type predicate
akwasniewski Aug 4, 2025
2353a84
readded memoisation
akwasniewski Aug 4, 2025
2443971
changed dependencies
akwasniewski Aug 5, 2025
f3198f5
moved forAnimated to config
akwasniewski Aug 5, 2025
686fadd
removed forAnimated from attachHandlers
akwasniewski Aug 5, 2025
b0d384d
type predicate using protected props ref
akwasniewski Aug 5, 2025
2ba5ae8
native gesture bugfix
akwasniewski Aug 5, 2025
38b240a
Merge branch 'next' into @akwasniewski/native-detector-web
akwasniewski Aug 5, 2025
7c00105
removed forAnimated
akwasniewski Aug 5, 2025
dceba3b
Merge branch 'next' into @akwasniewski/native-detector-web
akwasniewski Aug 5, 2025
14190fc
Merge branch '@akwasniewski/native-detector-web' into @akwasniewski/v…
akwasniewski Aug 5, 2025
b738254
removed usecallback
akwasniewski Aug 6, 2025
1f62d5c
private isInitialized
akwasniewski Aug 6, 2025
4d916f5
removed type predicate
akwasniewski Aug 6, 2025
0b16cdb
removed unnecessary type indications
akwasniewski Aug 6, 2025
8579092
typing wherever possible
akwasniewski Aug 6, 2025
089466e
Merge branch '@akwasniewski/native-detector-web' into @akwasniewski/v…
akwasniewski Aug 6, 2025
04ec35f
v2 compatibility
akwasniewski Aug 8, 2025
5837159
old state
akwasniewski Aug 8, 2025
0c96928
safe old state
akwasniewski Aug 8, 2025
4d6d1f2
merge with parent branch
akwasniewski Aug 8, 2025
6391704
better oldState handling
akwasniewski Aug 8, 2025
9e6fa97
specific transform event functions
akwasniewski Aug 11, 2025
323acf6
Merge branch 'next' into @akwasniewski/v3-web-typing
akwasniewski Aug 11, 2025
3336d48
Merge branch 'next' into @akwasniewski/v3-web-typing
akwasniewski Aug 11, 2025
4a75f5f
Merge branch 'next' into @akwasniewski/v3-web-typing
akwasniewski Aug 12, 2025
a5148ac
fixing v2?
akwasniewski Aug 14, 2025
1cd5eef
remove merge remnants
akwasniewski Aug 14, 2025
2e47b1a
removed obsolete comment
akwasniewski Aug 14, 2025
01e2ba9
Removing native synthetic event
akwasniewski Aug 18, 2025
7ee21d9
Merge branch 'next' into @akwasniewski/v3-web-typing
akwasniewski Aug 19, 2025
545cd3d
using common type for touchevent
akwasniewski Aug 19, 2025
5299992
Merge branch 'next' into @akwasniewski/v3-web-typing
akwasniewski Aug 19, 2025
dd73a7d
Merge branch 'next' into @akwasniewski/v3-web-typing
akwasniewski Aug 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/react-native-gesture-handler/src/ActionType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export const ActionType = {
NATIVE_ANIMATED_EVENT: 2,
JS_FUNCTION_OLD_API: 3,
JS_FUNCTION_NEW_API: 4,
NATIVE_DETECTOR: 5,
} as const;

// eslint-disable-next-line @typescript-eslint/no-redeclare -- backward compatibility; it can be used as a type and as a value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import type { ActionType } from './ActionType';
import { Gestures } from './web/Gestures';
import type { Config } from './web/interfaces';
import type { Config, PropsRef } from './web/interfaces';
import InteractionManager from './web/tools/InteractionManager';
import NodeManager from './web/tools/NodeManager';
import { GestureHandlerWebDelegate } from './web/tools/GestureHandlerWebDelegate';
Expand Down Expand Up @@ -47,8 +47,8 @@
handlerTag: number,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
newView: any,
_actionType: ActionType,
propsRef: React.RefObject<unknown>
actionType: ActionType,
propsRef: React.RefObject<PropsRef>
) {
if (!(newView instanceof Element || newView instanceof React.Component)) {
shouldPreventDrop = true;
Expand All @@ -63,7 +63,15 @@
}

// @ts-ignore Types should be HTMLElement or React.Component
NodeManager.getHandler(handlerTag).init(newView, propsRef);
NodeManager.getHandler(handlerTag).init(newView, propsRef, actionType);

Check warning on line 66 in packages/react-native-gesture-handler/src/RNGestureHandlerModule.web.ts

View workflow job for this annotation

GitHub Actions / check

Unsafe call of an `any` typed value
},
detachGestureHandler(handlerTag: number) {
if (shouldPreventDrop) {
shouldPreventDrop = false;
return;
}

NodeManager.getHandler(handlerTag).detach();
},
updateGestureHandler(handlerTag: number, newConfig: Config) {
NodeManager.getHandler(handlerTag).updateGestureConfig(newConfig);
Expand All @@ -78,6 +86,7 @@
},
dropGestureHandler(handlerTag: number) {
if (shouldPreventDrop) {
shouldPreventDrop = false;
return;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { StylusData } from '../web/interfaces';
import { StylusData } from './gestureHandlerCommon';

export type FlingGestureHandlerEventPayload = {
x: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,14 @@ export type GestureTouchEvent = {
pointerType: PointerType;
};

export interface StylusData {
tiltX: number;
tiltY: number;
azimuthAngle: number;
altitudeAngle: number;
pressure: number;
Comment on lines +168 to +173
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure whether this is the right place for it, but I had to put it somewhere else than web/interfaces , and this looked like the most promising spot.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without moving StylusData there is a circular dependency. Moving this interface seemed the most sensible option to resolve it. gestureHandlerCommon contains common interfaces for all platforms.

}

export type GestureUpdateEvent<GestureEventPayloadT = Record<string, unknown>> =
GestureEventPayload & GestureEventPayloadT;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@ import { ActionType } from '../../../ActionType';
import { Platform } from 'react-native';
import type RNGestureHandlerModuleWeb from '../../../RNGestureHandlerModule.web';
import { ghQueueMicrotask } from '../../../ghQueueMicrotask';
import { AttachedGestureState, WebEventHandler } from './types';
import { AttachedGestureState } from './types';
import {
extractGestureRelations,
checkGestureCallbacksForWorklets,
ALLOWED_PROPS,
} from './utils';
import { MountRegistry } from '../../../mountRegistry';
import { PropsRef } from '../../../web/interfaces';

interface AttachHandlersConfig {
preparedGesture: AttachedGestureState;
gestureConfig: ComposedGesture | GestureType;
gesturesToAttach: GestureType[];
viewTag: number;
webEventHandlersRef: React.RefObject<WebEventHandler>;
webEventHandlersRef: React.RefObject<PropsRef>;
}

export function attachHandlers({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { GestureType, HandlerCallbacks } from '../gesture';
import { SharedValue } from '../reanimatedWrapper';
import { HandlerStateChangeEvent } from '../../gestureHandlerCommon';

export interface AttachedGestureState {
// Array of gestures that should be attached to the view under that gesture detector
Expand All @@ -23,10 +22,3 @@ export interface GestureDetectorState {
previousViewTag: number;
forceRebuildReanimatedEvent: boolean;
}

export interface WebEventHandler {
onGestureHandlerEvent: (event: HandlerStateChangeEvent<unknown>) => void;
onGestureHandlerStateChange?: (
event: HandlerStateChangeEvent<unknown>
) => void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,14 @@ import React, { useCallback } from 'react';
import { GestureType } from '../gesture';
import { ComposedGesture } from '../gestureComposition';

import {
AttachedGestureState,
GestureDetectorState,
WebEventHandler,
} from './types';
import { AttachedGestureState, GestureDetectorState } from './types';
import { attachHandlers } from './attachHandlers';
import { updateHandlers } from './updateHandlers';
import { needsToReattach } from './needsToReattach';
import { dropHandlers } from './dropHandlers';
import { useForceRender, validateDetectorChildren } from './utils';
import findNodeHandle from '../../../findNodeHandle';
import { PropsRef } from '../../../web/interfaces';

// Returns a function that's responsible for updating the attached gestures
// If the view has changed, it will reattach the handlers to the new view
Expand All @@ -22,7 +19,7 @@ export function useDetectorUpdater(
preparedGesture: AttachedGestureState,
gesturesToAttach: GestureType[],
gestureConfig: ComposedGesture | GestureType,
webEventHandlersRef: React.RefObject<WebEventHandler>
webEventHandlersRef: React.RefObject<PropsRef>
) {
const forceRender = useForceRender();
const updateAttachedGestures = useCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { RNRenderer } from '../../../RNRenderer';
import { useCallback, useRef, useState } from 'react';
import { Reanimated } from '../reanimatedWrapper';
import { onGestureHandlerEvent } from '../eventReceiver';
import { WebEventHandler } from './types';
import { PropsRef } from '../../../web/interfaces';

export const ALLOWED_PROPS = [
...baseGestureHandlerWithDetectorProps,
Expand Down Expand Up @@ -167,7 +167,7 @@ export function useForceRender() {
}

export function useWebEventHandlers() {
return useRef<WebEventHandler>({
return useRef<PropsRef>({
onGestureHandlerEvent: (e: HandlerStateChangeEvent<unknown>) => {
onGestureHandlerEvent(e.nativeEvent);
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import RNGestureHandlerDetectorNativeComponent from '../specs/RNGestureHandlerDetectorNativeComponent';
const HostGestureDetector = RNGestureHandlerDetectorNativeComponent;
export default HostGestureDetector;
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { useEffect, useRef } from 'react';
import { View } from 'react-native';
import RNGestureHandlerModule from '../RNGestureHandlerModule.web';
import { ActionType } from '../ActionType';
import { PropsRef } from '../web/interfaces';

export interface GestureHandlerDetectorProps extends PropsRef {
handlerTags: number[];
moduleId: number;
children?: React.ReactNode;
}

const HostGestureDetector = (props: GestureHandlerDetectorProps) => {
const { handlerTags, children } = props;

const viewRef = useRef(null);
const propsRef = useRef<PropsRef>(props);
const attachedHandlerTags = useRef<Set<number>>(new Set<number>());

const detachHandlers = (oldHandlerTags: Set<number>) => {
oldHandlerTags.forEach((tag) => {
RNGestureHandlerModule.detachGestureHandler(tag);
});
};

const attachHandlers = (currentHandlerTags: Set<number>) => {
const oldHandlerTags =
attachedHandlerTags.current.difference(currentHandlerTags);
const newHandlerTags = currentHandlerTags.difference(
attachedHandlerTags.current
);

detachHandlers(oldHandlerTags);

newHandlerTags.forEach((tag) => {
RNGestureHandlerModule.attachGestureHandler(
tag,
viewRef.current,
ActionType.NATIVE_DETECTOR,
propsRef
);
});
attachedHandlerTags.current = currentHandlerTags;
};

useEffect(() => {
attachHandlers(new Set(handlerTags));
}, [handlerTags]);

useEffect(() => {
return () => {
detachHandlers(attachedHandlerTags.current);
};
}, []);

return (
<View style={{ display: 'contents' }} ref={viewRef}>
{children}
</View>
);
};

export default HostGestureDetector;
14 changes: 6 additions & 8 deletions packages/react-native-gesture-handler/src/v3/NativeDetector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,27 @@ import { NativeGesture } from './hooks/useGesture';
import { Reanimated } from '../handlers/gestures/reanimatedWrapper';

import { Animated, StyleSheet } from 'react-native';
import RNGestureHandlerDetectorNativeComponent from '../specs/RNGestureHandlerDetectorNativeComponent';
import HostGestureDetector from './HostGestureDetector';
import { tagMessage } from '../utils';

export interface NativeDetectorProps {
children?: React.ReactNode;
gesture: NativeGesture;
}

const AnimatedNativeDetector = Animated.createAnimatedComponent(
RNGestureHandlerDetectorNativeComponent
);
const AnimatedNativeDetector =
Animated.createAnimatedComponent(HostGestureDetector);

const ReanimatedNativeDetector = Reanimated?.default.createAnimatedComponent(
RNGestureHandlerDetectorNativeComponent
);
const ReanimatedNativeDetector =
Reanimated?.default.createAnimatedComponent(HostGestureDetector);

export function NativeDetector({ gesture, children }: NativeDetectorProps) {
const NativeDetectorComponent = gesture.config.dispatchesAnimatedEvents
? AnimatedNativeDetector
: // TODO: Remove this cast when we properly type config
(gesture.config.shouldUseReanimated as boolean)
? ReanimatedNativeDetector
: RNGestureHandlerDetectorNativeComponent;
: HostGestureDetector;

// It might happen only with ReanimatedNativeDetector
if (!NativeDetectorComponent) {
Expand Down
Loading
Loading