diff --git a/example/App.tsx b/example/App.tsx
index de46a28dfd..725fec0961 100644
--- a/example/App.tsx
+++ b/example/App.tsx
@@ -52,6 +52,7 @@ import HorizontalDrawer from './src/basic/horizontalDrawer';
import PagerAndDrawer from './src/basic/pagerAndDrawer';
import ForceTouch from './src/basic/forcetouch';
import Fling from './src/basic/fling';
+import WebStylesResetExample from './src/release_tests/webStylesReset';
import ReanimatedSimple from './src/new_api/reanimated';
import Camera from './src/new_api/camera';
@@ -151,6 +152,7 @@ const EXAMPLES: ExamplesSection[] = [
{ name: 'Swipeable Reanimation', component: SwipeableReanimation },
{ name: 'RectButton (borders)', component: RectButtonBorders },
{ name: 'Gesturized pressable', component: GesturizedPressable },
+ { name: 'Web styles reset', component: WebStylesResetExample },
],
},
{
diff --git a/example/src/release_tests/webStylesReset/index.tsx b/example/src/release_tests/webStylesReset/index.tsx
new file mode 100644
index 0000000000..b14bfac396
--- /dev/null
+++ b/example/src/release_tests/webStylesReset/index.tsx
@@ -0,0 +1,99 @@
+import React, { useState } from 'react';
+import { StyleSheet, Text, View } from 'react-native';
+import {
+ Gesture,
+ GestureDetector,
+ Pressable,
+} from 'react-native-gesture-handler';
+import Animated, {
+ interpolateColor,
+ useAnimatedStyle,
+ useSharedValue,
+ withTiming,
+} from 'react-native-reanimated';
+
+const Colors = {
+ enabled: '#32a852',
+ disabled: '#b02525',
+};
+
+const AnimationDuration = 250;
+
+export default function WebStylesResetExample() {
+ const [enabled, setEnabled] = useState(true);
+ const [x, setX] = useState(0);
+ const [y, setY] = useState(0);
+
+ const colorProgress = useSharedValue(0);
+
+ const animatedStyles = useAnimatedStyle(() => {
+ const backgroundColor = interpolateColor(
+ colorProgress.value,
+ [0, 1],
+ [Colors.enabled, Colors.disabled]
+ );
+
+ return { backgroundColor };
+ });
+
+ const g = Gesture.Pan()
+ .onUpdate((e) => {
+ setX(e.x);
+ setY(e.y);
+ })
+ .enabled(enabled);
+
+ return (
+
+
+
+ Lorem Ipsum
+
+
+
+ {
+ setEnabled((prev) => !prev);
+
+ colorProgress.value = withTiming(enabled ? 1 : 0, {
+ duration: AnimationDuration,
+ });
+ }}>
+ {enabled ? 'Disable' : 'Enable'}
+
+
+
+ {' '}
+ x: {x}, y: {y}{' '}
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ centered: {
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+
+ container: {
+ flex: 1,
+ backgroundColor: '#F5FCFF',
+ },
+
+ button: {
+ width: 250,
+ height: 35,
+ backgroundColor: 'plum',
+ borderRadius: 10,
+ margin: 25,
+ },
+
+ box: {
+ width: 250,
+ height: 250,
+ borderRadius: 25,
+ },
+});
diff --git a/src/web/handlers/GestureHandler.ts b/src/web/handlers/GestureHandler.ts
index 9ff9a357ef..d8c643d4ea 100644
--- a/src/web/handlers/GestureHandler.ts
+++ b/src/web/handlers/GestureHandler.ts
@@ -14,10 +14,10 @@ import EventManager from '../tools/EventManager';
import GestureHandlerOrchestrator from '../tools/GestureHandlerOrchestrator';
import InteractionManager from '../tools/InteractionManager';
import PointerTracker, { TrackerElement } from '../tools/PointerTracker';
-import { GestureHandlerDelegate } from '../tools/GestureHandlerDelegate';
import IGestureHandler from './IGestureHandler';
import { MouseButton } from '../../handlers/gestureHandlerCommon';
import { PointerType } from '../../PointerType';
+import { GestureHandlerDelegate } from '../tools/GestureHandlerDelegate';
export default abstract class GestureHandler implements IGestureHandler {
private lastSentState: State | null = null;
@@ -589,6 +589,8 @@ export default abstract class GestureHandler implements IGestureHandler {
this.config = { enabled: enabled, ...props };
this.enabled = enabled;
+ this.delegate.onEnabledChange(enabled);
+
if (this.config.shouldCancelWhenOutside !== undefined) {
this.setShouldCancelWhenOutside(this.config.shouldCancelWhenOutside);
}
diff --git a/src/web/interfaces.ts b/src/web/interfaces.ts
index b42961d933..8f4cd39a0b 100644
--- a/src/web/interfaces.ts
+++ b/src/web/interfaces.ts
@@ -36,7 +36,7 @@ type ConfigArgs =
| undefined;
export interface Config extends Record {
- enabled?: boolean;
+ enabled: boolean;
simultaneousHandlers?: Handler[] | null;
waitFor?: Handler[] | null;
blocksHandlers?: Handler[] | null;
diff --git a/src/web/tools/GestureHandlerDelegate.ts b/src/web/tools/GestureHandlerDelegate.ts
index 04e6f6f797..09dd1f1d47 100644
--- a/src/web/tools/GestureHandlerDelegate.ts
+++ b/src/web/tools/GestureHandlerDelegate.ts
@@ -20,6 +20,7 @@ export interface GestureHandlerDelegate {
onEnd(): void;
onCancel(): void;
onFail(): void;
+ onEnabledChange(enabled: boolean): void;
destroy(config: Config): void;
}
diff --git a/src/web/tools/GestureHandlerWebDelegate.ts b/src/web/tools/GestureHandlerWebDelegate.ts
index bf3a9a67e5..5929e44f73 100644
--- a/src/web/tools/GestureHandlerWebDelegate.ts
+++ b/src/web/tools/GestureHandlerWebDelegate.ts
@@ -13,12 +13,22 @@ import { Config } from '../interfaces';
import { MouseButton } from '../../handlers/gestureHandlerCommon';
import KeyboardEventManager from './KeyboardEventManager';
+interface DefaultViewStyles {
+ userSelect: string;
+ touchAction: string;
+}
+
export class GestureHandlerWebDelegate
implements GestureHandlerDelegate
{
+ private isInitialized = false;
private view!: HTMLElement;
private gestureHandler!: IGestureHandler;
private eventManagers: EventManager[] = [];
+ private defaultViewStyles: DefaultViewStyles = {
+ userSelect: '',
+ touchAction: '',
+ };
getView(): HTMLElement {
return this.view;
@@ -31,19 +41,21 @@ export class GestureHandlerWebDelegate
);
}
+ this.isInitialized = true;
+
this.gestureHandler = handler;
this.view = findNodeHandle(viewRef) as unknown as HTMLElement;
- const config = handler.getConfig();
+ this.defaultViewStyles = {
+ userSelect: this.view.style.userSelect,
+ touchAction: this.view.style.touchAction,
+ };
- this.addContextMenuListeners(config);
+ const config = handler.getConfig();
- this.view.style['userSelect'] = config.userSelect ?? 'none';
- this.view.style['webkitUserSelect'] = config.userSelect ?? 'none';
-
- this.view.style['touchAction'] = config.touchAction ?? 'none';
- // @ts-ignore This one disables default events on Safari
- this.view.style['WebkitTouchCallout'] = 'none';
+ this.setUserSelect(config.enabled);
+ this.setTouchAction(config.enabled);
+ this.setContextMenu(config.enabled);
this.eventManagers.push(new PointerEventManager(this.view));
this.eventManagers.push(new TouchEventManager(this.view));
@@ -119,6 +131,51 @@ export class GestureHandlerWebDelegate
e.stopPropagation();
}
+ private setUserSelect(isHandlerEnabled: boolean) {
+ const { userSelect } = this.gestureHandler.getConfig();
+
+ this.view.style['userSelect'] = isHandlerEnabled
+ ? userSelect ?? 'none'
+ : this.defaultViewStyles.userSelect;
+
+ this.view.style['webkitUserSelect'] = isHandlerEnabled
+ ? userSelect ?? 'none'
+ : this.defaultViewStyles.userSelect;
+ }
+
+ private setTouchAction(isHandlerEnabled: boolean) {
+ const { touchAction } = this.gestureHandler.getConfig();
+
+ this.view.style['touchAction'] = isHandlerEnabled
+ ? touchAction ?? 'none'
+ : this.defaultViewStyles.touchAction;
+
+ // @ts-ignore This one disables default events on Safari
+ this.view.style['WebkitTouchCallout'] = isHandlerEnabled
+ ? touchAction ?? 'none'
+ : this.defaultViewStyles.touchAction;
+ }
+
+ private setContextMenu(isHandlerEnabled: boolean) {
+ const config = this.gestureHandler.getConfig();
+
+ if (isHandlerEnabled) {
+ this.addContextMenuListeners(config);
+ } else {
+ this.removeContextMenuListeners(config);
+ }
+ }
+
+ onEnabledChange(enabled: boolean): void {
+ if (!this.isInitialized) {
+ return;
+ }
+
+ this.setUserSelect(enabled);
+ this.setTouchAction(enabled);
+ this.setContextMenu(enabled);
+ }
+
onBegin(): void {
// no-op for now
}