Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -216,4 +216,6 @@ export const ButtonComponent = ({
);
};

ButtonComponent.displayName = 'Button';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we want to change that to GestureHandlerButton or do we want to keep it generic?

I'm not sure whether this will behave well with other components, given the integration between native gesture and button.


export default ButtonComponent;
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import type {
} from '../../handlers/gestureHandlerCommon';
import RNGestureHandlerModule from '../../RNGestureHandlerModule.web';
import { tagMessage } from '../../utils';
import type { PropsRef } from '../../web/interfaces';
import { NATIVE_GESTURE_ROLE_ATTRIBUTE } from '../../web/constants';
import { NativeGestureRole, type PropsRef } from '../../web/interfaces';

export interface GestureHandlerDetectorProps extends PropsRef {
handlerTags: number[];
Expand Down Expand Up @@ -103,6 +104,39 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => {
});
};

useEffect(() => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Any chance to unify this with the one in virtual detector?

// @ts-ignore This exists on React.ReactNode
const displayName = children?.type?.displayName as string;

// In case of Native gesture detector will have only one child
const child = viewRef.current?.firstChild as HTMLElement | undefined;

if (!child) {
return;
}

if (displayName === 'ScrollView') {
child.setAttribute(
NATIVE_GESTURE_ROLE_ATTRIBUTE,
NativeGestureRole.ScrollView
);
} else if (displayName === 'Switch') {
Comment thread
m-bert marked this conversation as resolved.
child.setAttribute(
NATIVE_GESTURE_ROLE_ATTRIBUTE,
NativeGestureRole.Switch
);
} else if (displayName === 'Button') {
child.setAttribute(
NATIVE_GESTURE_ROLE_ATTRIBUTE,
NativeGestureRole.Button
);
}

return () => {
child.removeAttribute(NATIVE_GESTURE_ROLE_ATTRIBUTE);
};
}, [children]);

useEffect(() => {
const shouldUpdateDOMProps =
propsRef.current.userSelect !== props.userSelect ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { findNodeHandle, Platform } from 'react-native';

import { Wrap } from '../../../handlers/gestures/GestureDetector/Wrap';
import { tagMessage } from '../../../utils';
import { NATIVE_GESTURE_ROLE_ATTRIBUTE } from '../../../web/constants';
import { NativeGestureRole } from '../../../web/interfaces';
import { isComposedGesture } from '../../hooks/utils/relationUtils';
import type { DetectorCallbacks, VirtualChild } from '../../types';
import type { VirtualDetectorProps } from '../common';
Expand Down Expand Up @@ -36,7 +38,7 @@ export function VirtualDetector<
const { register, unregister, setMode } =
useRequiredInterceptingDetectorContext();

const viewRef = useRef(null);
const viewRef = useRef<HTMLElement | null>(null);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This can be part of the new hook as well

const [viewTag, setViewTag] = useState<number>(-1);

const handleRef = useCallback(
Expand All @@ -54,6 +56,34 @@ export function VirtualDetector<
[props.children]
);

useEffect(() => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can this be a custom hook in its own file, which will no-op on native and contain the logic for web (.ts and .web.ts)?

if (Platform.OS !== 'web' || !viewRef.current) {
return;
}

// @ts-ignore This exists on React.ReactNode
const displayName = props.children?.type?.displayName as string;
// On web we need to use `firstChild` as `current` will refer to `Wrap`
const element = viewRef.current.firstChild as HTMLElement;

Comment thread
m-bert marked this conversation as resolved.
if (displayName === 'ScrollView') {
element.setAttribute(
NATIVE_GESTURE_ROLE_ATTRIBUTE,
NativeGestureRole.ScrollView
);
} else if (displayName === 'Switch') {
element.setAttribute(
NATIVE_GESTURE_ROLE_ATTRIBUTE,
NativeGestureRole.Switch
);
} else if (displayName === 'Button') {
element.setAttribute(
NATIVE_GESTURE_ROLE_ATTRIBUTE,
NativeGestureRole.Button
);
}
Comment thread
m-bert marked this conversation as resolved.
}, [props.children, viewTag]);

useEffect(() => {
if (viewTag === -1) {
return;
Expand Down
1 change: 1 addition & 0 deletions packages/react-native-gesture-handler/src/web/constants.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export const DEFAULT_TOUCH_SLOP = 15;
export const MINIMAL_RECOGNIZABLE_MAGNITUDE = 0.1;
export const NATIVE_GESTURE_ROLE_ATTRIBUTE = 'rngh-role';
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default abstract class GestureHandler implements IGestureHandler {

private viewRef: number | null = null;
private propsRef: React.RefObject<PropsRef> | null = null;
private actionType: ActionType | null = null;
protected actionType: ActionType | null = null;
private forAnimated: boolean = false;
private forReanimated: boolean = false;
private _handlerTag!: number;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { Platform } from 'react-native';

import type { ActionType } from '../../ActionType';
import { type ActionType, usesNativeOrVirtualDetector } from '../../ActionType';
import { State } from '../../State';
import { deepEqual } from '../../utils';
import type { NativeHandlerData } from '../../v3/hooks/gestures/native/NativeTypes';
import type { HandlerData } from '../../v3/types';
import { SingleGestureName } from '../../v3/types';
import { DEFAULT_TOUCH_SLOP } from '../constants';
import {
DEFAULT_TOUCH_SLOP,
NATIVE_GESTURE_ROLE_ATTRIBUTE,
} from '../constants';
import type { AdaptedEvent, Config, PropsRef } from '../interfaces';
import { NativeGestureRole } from '../interfaces';
import type { GestureHandlerDelegate } from '../tools/GestureHandlerDelegate';
import {
dispatchGestureLifecycleEvent,
Expand All @@ -17,8 +21,7 @@ import GestureHandler from './GestureHandler';
import type IGestureHandler from './IGestureHandler';

export default class NativeViewGestureHandler extends GestureHandler {
private buttonRole!: boolean;
private switchRole!: boolean;
private role: NativeGestureRole | null = null;

// TODO: Implement logic for activation on start properly
private shouldActivateOnStart = false;
Expand Down Expand Up @@ -53,9 +56,19 @@ export default class NativeViewGestureHandler extends GestureHandler {
const view = this.delegate.view as HTMLElement;

this.restoreViewStyles(view);
this.buttonRole = view.getAttribute('role') === 'button';
this.switchRole =
view.querySelector(':scope > input[role="switch"]') !== null;

if (usesNativeOrVirtualDetector(this.actionType)) {
this.role =
(view.getAttribute(
NATIVE_GESTURE_ROLE_ATTRIBUTE
) as NativeGestureRole) ?? null;
} else {
if (view.getAttribute('role') === 'button') {
this.role = NativeGestureRole.Button;
} else if (view.querySelector(':scope > input[role="switch"]') !== null) {
this.role = NativeGestureRole.Switch;
}
Comment thread
m-bert marked this conversation as resolved.
}
Comment thread
m-bert marked this conversation as resolved.
}

public override updateGestureConfig(config: Config): void {
Expand Down Expand Up @@ -114,8 +127,8 @@ export default class NativeViewGestureHandler extends GestureHandler {
const isRNGHText = view.hasAttribute('rnghtext');

if (
(this.buttonRole && this.shouldActivateOnStart) ||
this.switchRole ||
(this.role === NativeGestureRole.Button && this.shouldActivateOnStart) ||
this.role === NativeGestureRole.Switch ||
isRNGHText
) {
this.activate();
Expand All @@ -130,7 +143,10 @@ export default class NativeViewGestureHandler extends GestureHandler {
const dy = this.startY - lastCoords.y;
const distSq = dx * dx + dy * dy;

if (this.switchRole || this.buttonRole) {
if (
this.role === NativeGestureRole.Switch ||
this.role === NativeGestureRole.Button
) {
return;
}

Expand Down Expand Up @@ -159,7 +175,10 @@ export default class NativeViewGestureHandler extends GestureHandler {
this.tracker.removeFromTracker(event.pointerId);

if (this.tracker.trackedPointersCount === 0) {
if (this.buttonRole && this.state === State.BEGAN) {
if (
this.role === NativeGestureRole.Button &&
this.state === State.BEGAN
) {
this.activate();
}

Expand Down Expand Up @@ -214,7 +233,7 @@ export default class NativeViewGestureHandler extends GestureHandler {
}

public isButton(): boolean {
return this.buttonRole;
return this.role === NativeGestureRole.Button;
}

public override shouldBeginWithRecordedHandlers(
Expand Down Expand Up @@ -266,5 +285,6 @@ export default class NativeViewGestureHandler extends GestureHandler {
public override reset(): void {
super.reset();
this.lastActiveHandlerData = null;
this.role = null;
}
}
6 changes: 6 additions & 0 deletions packages/react-native-gesture-handler/src/web/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,9 @@ export type GestureHandlerRef = {
export type SVGRef = {
elementRef: { current: SVGElement };
};

export enum NativeGestureRole {
Button = 'Button',
Switch = 'Switch',
ScrollView = 'ScrollView',
}
Loading