Skip to content
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

[Web] Reset web-exclusive properties when handler is disabled. #3041

Merged
merged 8 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions example/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
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';
Expand Down Expand Up @@ -151,6 +152,7 @@
{ name: 'Swipeable Reanimation', component: SwipeableReanimation },
{ name: 'RectButton (borders)', component: RectButtonBorders },
{ name: 'Gesturized pressable', component: GesturizedPressable },
{ name: 'Web styles reset', component: WebStylesResetExample },
],
},
{
Expand Down Expand Up @@ -271,7 +273,7 @@
renderSectionHeader={({ section: { sectionTitle } }) => (
<Text style={styles.sectionTitle}>{sectionTitle}</Text>
)}
ItemSeparatorComponent={() => <View style={styles.separator} />}

Check warning on line 276 in example/App.tsx

View workflow job for this annotation

GitHub Actions / check (example)

Do not define components during render. React will see a new component type on every render and destroy the entire subtree’s DOM nodes and state (https://reactjs.org/docs/reconciliation.html#elements-of-different-types). Instead, move this component definition out of the parent component “MainScreen” and pass data as props. If you want to allow component creation in props, set allowAsProps option to true
/>
</SafeAreaView>
);
Expand Down
99 changes: 99 additions & 0 deletions example/src/release_tests/webStylesReset/index.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<View style={[styles.container, styles.centered]}>
<GestureDetector gesture={g} enableContextMenu={false}>
<Animated.View style={[styles.box, styles.centered, animatedStyles]}>
<Text style={{ fontSize: 32 }}> Lorem Ipsum </Text>
</Animated.View>
</GestureDetector>

<Pressable
style={[styles.button, styles.centered]}
onPress={() => {
setEnabled((prev) => !prev);

colorProgress.value = withTiming(enabled ? 1 : 0, {
duration: AnimationDuration,
});
}}>
<Text style={{ fontSize: 16 }}>{enabled ? 'Disable' : 'Enable'}</Text>
</Pressable>

<Text style={{ fontSize: 16 }}>
{' '}
x: {x}, y: {y}{' '}
</Text>
</View>
);
}

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,
},
});
14 changes: 8 additions & 6 deletions src/web/handlers/GestureHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 { GestureHandlerWebDelegate } from '../tools/GestureHandlerWebDelegate';

export default abstract class GestureHandler implements IGestureHandler {
private lastSentState: State | null = null;
Expand All @@ -41,11 +41,9 @@ export default abstract class GestureHandler implements IGestureHandler {
protected shouldResetProgress = false;
protected pointerType: PointerType = PointerType.MOUSE;

protected delegate: GestureHandlerDelegate<unknown, IGestureHandler>;
protected delegate: GestureHandlerWebDelegate;
j-piasecki marked this conversation as resolved.
Show resolved Hide resolved

public constructor(
delegate: GestureHandlerDelegate<unknown, IGestureHandler>
) {
public constructor(delegate: GestureHandlerWebDelegate) {
this.delegate = delegate;
}

Expand Down Expand Up @@ -589,6 +587,10 @@ export default abstract class GestureHandler implements IGestureHandler {
this.config = { enabled: enabled, ...props };
this.enabled = enabled;

if (this.delegate.wasInitialized()) {
this.delegate.onEnabledChange(enabled);
}
j-piasecki marked this conversation as resolved.
Show resolved Hide resolved

if (this.config.shouldCancelWhenOutside !== undefined) {
this.setShouldCancelWhenOutside(this.config.shouldCancelWhenOutside);
}
Expand Down Expand Up @@ -761,7 +763,7 @@ export default abstract class GestureHandler implements IGestureHandler {
return this.config;
}

public getDelegate(): GestureHandlerDelegate<unknown, IGestureHandler> {
public getDelegate(): GestureHandlerWebDelegate {
return this.delegate;
}

Expand Down
59 changes: 51 additions & 8 deletions src/web/tools/GestureHandlerWebDelegate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import KeyboardEventManager from './KeyboardEventManager';
export class GestureHandlerWebDelegate
implements GestureHandlerDelegate<HTMLElement, IGestureHandler>
{
private ready = false;
private view!: HTMLElement;
private gestureHandler!: IGestureHandler;
private eventManagers: EventManager<unknown>[] = [];
Expand All @@ -31,19 +32,16 @@ export class GestureHandlerWebDelegate
);
}

this.ready = true;

m-bert marked this conversation as resolved.
Show resolved Hide resolved
this.gestureHandler = handler;
this.view = findNodeHandle(viewRef) as unknown as HTMLElement;

const config = handler.getConfig();

this.addContextMenuListeners(config);

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!); // `enabled` has default value of true if it was not provided
j-piasecki marked this conversation as resolved.
Show resolved Hide resolved
this.setTouchAction(config.enabled!);
this.setContextMenu(config.enabled!);

this.eventManagers.push(new PointerEventManager(this.view));
this.eventManagers.push(new TouchEventManager(this.view));
Expand Down Expand Up @@ -119,6 +117,47 @@ export class GestureHandlerWebDelegate
e.stopPropagation();
}

private setUserSelect(isHandlerEnabled: boolean) {
const { userSelect } = this.gestureHandler.getConfig();

this.view.style['userSelect'] = isHandlerEnabled
? userSelect ?? 'none'
: 'auto';

this.view.style['webkitUserSelect'] = isHandlerEnabled
? userSelect ?? 'none'
: 'auto';
}

private setTouchAction(isHandlerEnabled: boolean) {
const { touchAction } = this.gestureHandler.getConfig();

this.view.style['touchAction'] = isHandlerEnabled
? touchAction ?? 'none'
: 'auto';

// @ts-ignore This one disables default events on Safari
this.view.style['WebkitTouchCallout'] = isHandlerEnabled
? touchAction ?? 'none'
: 'auto';
}
j-piasecki marked this conversation as resolved.
Show resolved Hide resolved

private setContextMenu(isHandlerEnabled: boolean) {
const config = this.gestureHandler.getConfig();

if (isHandlerEnabled) {
this.addContextMenuListeners(config);
} else {
this.removeContextMenuListeners(config);
}
}

onEnabledChange(enabled: boolean): void {
this.setUserSelect(enabled);
this.setTouchAction(enabled);
this.setContextMenu(enabled);
}

onBegin(): void {
// no-op for now
}
Expand Down Expand Up @@ -153,4 +192,8 @@ export class GestureHandlerWebDelegate
manager.unregisterListeners();
});
}

public wasInitialized() {
return this.ready;
}
}
Loading