Skip to content

Commit f2e6ebc

Browse files
authored
Add relation props to Pressable and ReanimatedSwipeable (#3473)
## Description It has been reported that if `Pressable` is placed under the view with attached `GestureDetector`, it doesn't work since it gets cancelled by other gestures. This PR adds relation properties to `Pressable` and `ReanimatedSwipeable`, so we can make interactions with external handlers possible. > [!NOTE] > This change was partially added into `ReanimatedSwipeable` in #3324 ## Test plan <details> <summary>Tested on the following example:</summary> ```jsx import React from 'react'; import { StyleSheet, View } from 'react-native'; import { Gesture, Pressable, GestureDetector, GestureHandlerRootView, } from 'react-native-gesture-handler'; export default function App() { const gesture = Gesture.Pan(); return ( <GestureHandlerRootView> <GestureDetector gesture={gesture}> <View style={styles.buttonContainer} collapsable={false}> <Pressable style={styles.button} onPressIn={() => console.log('Pressable onPressIn')} onPressOut={() => console.log('Pressable onPressOut')} simultaneousWithExternalGesture={gesture} /> </View> </GestureDetector> </GestureHandlerRootView> ); } const styles = StyleSheet.create({ buttonContainer: { flex: 1, alignItems: 'center', justifyContent: 'space-around', backgroundColor: '#535353', }, button: { width: 200, height: 200, borderRadius: 30, backgroundColor: '#1db954', }, }); ``` </details>
1 parent 8a37a71 commit f2e6ebc

File tree

4 files changed

+103
-22
lines changed

4 files changed

+103
-22
lines changed

src/components/Pressable/Pressable.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ import {
2929
import { PressabilityDebugView } from '../../handlers/PressabilityDebugView';
3030
import { GestureTouchEvent } from '../../handlers/gestureHandlerCommon';
3131
import { INT32_MAX, isFabric, isTestEnv } from '../../utils';
32+
import {
33+
applyRelationProp,
34+
RelationPropName,
35+
RelationPropType,
36+
} from '../utils';
3237

3338
const DEFAULT_LONG_PRESS_DURATION = 500;
3439
const IS_TEST_ENV = isTestEnv();
@@ -57,9 +62,18 @@ const Pressable = forwardRef(
5762
android_ripple,
5863
disabled,
5964
accessible,
65+
simultaneousWithExternalGesture,
66+
requireExternalGestureToFail,
67+
blocksExternalGesture,
6068
...remainingProps
6169
} = props;
6270

71+
const relationProps = {
72+
simultaneousWithExternalGesture,
73+
requireExternalGestureToFail,
74+
blocksExternalGesture,
75+
};
76+
6377
const [pressedState, setPressedState] = useState(testOnly_pressed ?? false);
6478

6579
// Disabled when onLongPress has been called
@@ -406,6 +420,14 @@ const Pressable = forwardRef(
406420
gesture.runOnJS(true);
407421
gesture.hitSlop(appliedHitSlop);
408422
gesture.shouldCancelWhenOutside(Platform.OS === 'web' ? false : true);
423+
424+
Object.entries(relationProps).forEach(([relationName, relation]) => {
425+
applyRelationProp(
426+
gesture,
427+
relationName as RelationPropName,
428+
relation as RelationPropType
429+
);
430+
});
409431
}
410432

411433
// Uses different hitSlop, to activate on hitSlop area instead of pressRetentionOffset area

src/components/Pressable/PressableProps.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
PressableStateCallbackType as RNPressableStateCallbackType,
88
PressableAndroidRippleConfig as RNPressableAndroidRippleConfig,
99
} from 'react-native';
10+
import { RelationPropType } from '../utils';
1011

1112
export type PressableStateCallbackType = RNPressableStateCallbackType;
1213
export type PressableAndroidRippleConfig = RNPressableAndroidRippleConfig;
@@ -139,4 +140,22 @@ export interface PressableProps
139140
* Duration (in milliseconds) to wait after press down before calling onPressIn.
140141
*/
141142
unstable_pressDelay?: number;
143+
144+
/**
145+
* A gesture object or an array of gesture objects containing the configuration and callbacks to be
146+
* used with the Pressable's gesture handlers.
147+
*/
148+
simultaneousWithExternalGesture?: RelationPropType;
149+
150+
/**
151+
* A gesture object or an array of gesture objects containing the configuration and callbacks to be
152+
* used with the Pressable's gesture handlers.
153+
*/
154+
requireExternalGestureToFail?: RelationPropType;
155+
156+
/**
157+
* A gesture object or an array of gesture objects containing the configuration and callbacks to be
158+
* used with the Pressable's gesture handlers.
159+
*/
160+
blocksExternalGesture?: RelationPropType;
142161
}

src/components/ReanimatedSwipeable.tsx

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import React, {
99
useImperativeHandle,
1010
useMemo,
1111
} from 'react';
12-
import { GestureRef } from '../handlers/gestures/gesture';
1312
import { GestureObjects as Gesture } from '../handlers/gestures/gestureObjects';
1413
import { GestureDetector } from '../handlers/gestures/GestureDetector';
1514
import {
@@ -38,6 +37,7 @@ import {
3837
View,
3938
ViewStyle,
4039
} from 'react-native';
40+
import { applyRelationProp, RelationPropName, RelationPropType } from './utils';
4141

4242
const DRAG_TOSS = 0.05;
4343

@@ -208,9 +208,19 @@ export interface SwipeableProps
208208
* A gesture object or an array of gesture objects containing the configuration and callbacks to be
209209
* used with the swipeable's gesture handler.
210210
*/
211-
simultaneousWithExternalGesture?:
212-
| Exclude<GestureRef, number>
213-
| Exclude<GestureRef, number>[];
211+
simultaneousWithExternalGesture?: RelationPropType;
212+
213+
/**
214+
* A gesture object or an array of gesture objects containing the configuration and callbacks to be
215+
* used with the swipeable's gesture handler.
216+
*/
217+
requireExternalGestureToFail?: RelationPropType;
218+
219+
/**
220+
* A gesture object or an array of gesture objects containing the configuration and callbacks to be
221+
* used with the swipeable's gesture handler.
222+
*/
223+
blocksExternalGesture?: RelationPropType;
214224
}
215225

216226
export interface SwipeableMethods {
@@ -257,9 +267,17 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
257267
renderLeftActions,
258268
renderRightActions,
259269
simultaneousWithExternalGesture,
270+
requireExternalGestureToFail,
271+
blocksExternalGesture,
260272
...remainingProps
261273
} = props;
262274

275+
const relationProps = {
276+
simultaneousWithExternalGesture,
277+
requireExternalGestureToFail,
278+
blocksExternalGesture,
279+
};
280+
263281
const rowState = useSharedValue<number>(0);
264282

265283
const userDrag = useSharedValue<number>(0);
@@ -654,15 +672,13 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
654672
}
655673
});
656674

657-
if (!simultaneousWithExternalGesture) {
658-
return tap;
659-
}
660-
661-
if (Array.isArray(simultaneousWithExternalGesture)) {
662-
tap.simultaneousWithExternalGesture(...simultaneousWithExternalGesture);
663-
} else {
664-
tap.simultaneousWithExternalGesture(simultaneousWithExternalGesture);
665-
}
675+
Object.entries(relationProps).forEach(([relationName, relation]) => {
676+
applyRelationProp(
677+
tap,
678+
relationName as RelationPropName,
679+
relation as RelationPropType
680+
);
681+
});
666682

667683
return tap;
668684
}, [close, rowState, simultaneousWithExternalGesture]);
@@ -707,15 +723,13 @@ const Swipeable = forwardRef<SwipeableMethods, SwipeableProps>(
707723
dragStarted.value = false;
708724
});
709725

710-
if (!simultaneousWithExternalGesture) {
711-
return pan;
712-
}
713-
714-
if (Array.isArray(simultaneousWithExternalGesture)) {
715-
pan.simultaneousWithExternalGesture(...simultaneousWithExternalGesture);
716-
} else {
717-
pan.simultaneousWithExternalGesture(simultaneousWithExternalGesture);
718-
}
726+
Object.entries(relationProps).forEach(([relationName, relation]) => {
727+
applyRelationProp(
728+
pan,
729+
relationName as RelationPropName,
730+
relation as RelationPropType
731+
);
732+
});
719733

720734
return pan;
721735
}, [

src/components/utils.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { BaseGesture, GestureRef } from '../handlers/gestures/gesture';
2+
3+
export type RelationPropName =
4+
| 'simultaneousWithExternalGesture'
5+
| 'requireExternalGestureToFail'
6+
| 'blocksExternalGesture';
7+
8+
export type RelationPropType =
9+
| Exclude<GestureRef, number>
10+
| Exclude<GestureRef, number>[];
11+
12+
export function applyRelationProp(
13+
gesture: BaseGesture<any>,
14+
relationPropName: RelationPropName,
15+
relationProp: RelationPropType
16+
) {
17+
if (!relationProp) {
18+
return;
19+
}
20+
21+
if (Array.isArray(relationProp)) {
22+
gesture[relationPropName](...relationProp);
23+
} else {
24+
gesture[relationPropName](relationProp);
25+
}
26+
}

0 commit comments

Comments
 (0)