Skip to content

Commit

Permalink
Add touchAction prop. (#2787)
Browse files Browse the repository at this point in the history
## Description

This PR adds new prop to gesture handlers - `touchAction`. 

While working on [fix scroll interactions PR](#2631) we've found it difficult to remove `touch-action: none;` and ensure correct behavior of handlers with scroll, especially with swipeable components. What made it even harder were differences between platforms. 

We decided to kill two birds with one stone - give our users possibility to manipulate `touchAction`, which will also make it easier for us to properly handle scroll.

## Test plan

Tested on example app.
  • Loading branch information
m-bert authored Mar 5, 2024
1 parent a341f88 commit 49021d8
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 5 deletions.
2 changes: 2 additions & 0 deletions src/components/Swipeable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@ export default class Swipeable extends Component<
return (
<PanGestureHandler
activeOffsetX={[-dragOffsetFromRightEdge, dragOffsetFromLeftEdge]}
touchAction="pan-y"
{...this.props}
onGestureEvent={this.onGestureEvent}
onHandlerStateChange={this.onHandlerStateChange}>
Expand All @@ -551,6 +552,7 @@ export default class Swipeable extends Component<
{right}
<TapGestureHandler
enabled={rowState !== 0}
touchAction="pan-y"
onHandlerStateChange={this.onTapHandlerStateChange}>
<Animated.View
pointerEvents={rowState === 0 ? 'auto' : 'box-only'}
Expand Down
19 changes: 19 additions & 0 deletions src/handlers/gestureHandlerCommon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const commonProps = [
'activeCursor',
'mouseButton',
'enableContextMenu',
'touchAction',
] as const;

const componentInteractionProps = [
Expand Down Expand Up @@ -113,6 +114,23 @@ export type ActiveCursor =
| 'zoom-in'
| 'zoom-out';

export type TouchAction =
| 'auto'
| 'none'
| 'pan-x'
| 'pan-left'
| 'pan-right'
| 'pan-y'
| 'pan-up'
| 'pan-down'
| 'pinch-zoom'
| 'manipulation'
| 'inherit'
| 'initial'
| 'revert'
| 'revert-layer'
| 'unset';

//TODO(TS) events in handlers

export interface GestureEvent<ExtraEventPayloadT = Record<string, unknown>> {
Expand Down Expand Up @@ -156,6 +174,7 @@ export type CommonGestureConfig = {
activeCursor?: ActiveCursor;
mouseButton?: MouseButton;
enableContextMenu?: boolean;
touchAction?: TouchAction;
};

// Events payloads are types instead of interfaces due to TS limitation.
Expand Down
15 changes: 15 additions & 0 deletions src/handlers/gestures/GestureDetector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
HandlerStateChangeEvent,
scheduleFlushOperations,
UserSelect,
TouchAction,
} from '../gestureHandlerCommon';
import {
GestureStateManager,
Expand Down Expand Up @@ -614,11 +615,21 @@ const applyEnableContextMenuProp = (
}
};

const applyTouchActionProp = (
touchAction: TouchAction,
gesture: ComposedGesture | GestureType
): void => {
for (const g of gesture.toGestureArray()) {
g.config.touchAction = touchAction;
}
};

interface GestureDetectorProps {
gesture: ComposedGesture | GestureType;
children?: React.ReactNode;
userSelect?: UserSelect;
enableContextMenu?: boolean;
touchAction?: TouchAction;
}
interface GestureDetectorState {
firstRender: boolean;
Expand All @@ -644,6 +655,10 @@ export const GestureDetector = (props: GestureDetectorProps) => {
applyEnableContextMenuProp(props.enableContextMenu, gestureConfig);
}

if (props.touchAction !== undefined) {
applyTouchActionProp(props.touchAction, gestureConfig);
}

const gesture = gestureConfig.toGestureArray();
const useReanimatedHook = gesture.some((g) => g.shouldUseReanimated);

Expand Down
8 changes: 7 additions & 1 deletion src/web/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { UserSelect, ActiveCursor } from '../handlers/gestureHandlerCommon';
import {
UserSelect,
ActiveCursor,
TouchAction,
} from '../handlers/gestureHandlerCommon';
import { Directions } from '../Directions';
import { State } from '../State';
import { PointerType } from '../PointerType';
Expand All @@ -23,6 +27,7 @@ type ConfigArgs =
| boolean
| HitSlop
| UserSelect
| TouchAction
| ActiveCursor
| Directions
| Handler[]
Expand All @@ -40,6 +45,7 @@ export interface Config extends Record<string, ConfigArgs> {
activeCursor?: ActiveCursor;
mouseButton?: MouseButton;
enableContextMenu?: boolean;
touchAction?: TouchAction;

activateAfterLongPress?: number;
failOffsetXStart?: number;
Expand Down
8 changes: 4 additions & 4 deletions src/web/tools/GestureHandlerWebDelegate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ export class GestureHandlerWebDelegate
this.gestureHandler = handler;
this.view = findNodeHandle(viewRef) as unknown as HTMLElement;

this.view.style['touchAction'] = 'none';
//@ts-ignore This one disables default events on Safari
this.view.style['WebkitTouchCallout'] = 'none';

const config = handler.getConfig();

this.addContextMenuListeners(config);
Expand All @@ -48,6 +44,10 @@ export class GestureHandlerWebDelegate
this.view.style['userSelect'] = config.userSelect;
}

this.view.style['touchAction'] = config.touchAction ?? 'none';
//@ts-ignore This one disables default events on Safari
this.view.style['WebkitTouchCallout'] = 'none';

this.eventManagers.push(new PointerEventManager(this.view));
this.eventManagers.push(new TouchEventManager(this.view));

Expand Down

0 comments on commit 49021d8

Please sign in to comment.