Skip to content

Commit 4ce89e3

Browse files
authored
Create gesturized pressable component (#2942)
This PR adds our own `Pressable` component Closes #1221
1 parent 2bf6b29 commit 4ce89e3

File tree

15 files changed

+1134
-0
lines changed

15 files changed

+1134
-0
lines changed

example/App.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import AsyncStorage from '@react-native-async-storage/async-storage';
2323
import OverflowParent from './src/release_tests/overflowParent';
2424
import DoublePinchRotate from './src/release_tests/doubleScalePinchAndRotate';
2525
import DoubleDraggable from './src/release_tests/doubleDraggable';
26+
import GesturizedPressable from './src/release_tests/gesturizedPressable';
2627
import { ComboWithGHScroll } from './src/release_tests/combo';
2728
import {
2829
TouchablesIndex,
@@ -66,6 +67,7 @@ import Hover from './src/new_api/hover';
6667
import HoverableIcons from './src/new_api/hoverable_icons';
6768
import VelocityTest from './src/new_api/velocityTest';
6869
import Swipeable from 'src/new_api/swipeable';
70+
import Pressable from 'src/new_api/pressable';
6971

7072
import EmptyExample from './src/empty/EmptyExample';
7173
import RectButtonBorders from './src/release_tests/rectButton';
@@ -143,6 +145,7 @@ const EXAMPLES: ExamplesSection[] = [
143145
{ name: 'PointerType', component: PointerType },
144146
{ name: 'Swipeable Reanimation', component: SwipeableReanimation },
145147
{ name: 'RectButton (borders)', component: RectButtonBorders },
148+
{ name: 'Gesturized pressable', component: GesturizedPressable },
146149
],
147150
},
148151
{
@@ -164,6 +167,7 @@ const EXAMPLES: ExamplesSection[] = [
164167
{ name: 'Chat Heads', component: ChatHeadsNewApi },
165168
{ name: 'Drag and drop', component: DragNDrop },
166169
{ name: 'Swipeable', component: Swipeable },
170+
{ name: 'Pressable', component: Pressable },
167171
{
168172
name: 'Horizontal Drawer (Reanimated 2 & RNGH 2)',
169173
component: BetterHorizontalDrawer,
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import React from 'react';
2+
import { StyleSheet, Text, View } from 'react-native';
3+
import { Pressable } from 'react-native-gesture-handler';
4+
5+
export default function PressableExample() {
6+
const pressIn = () => {
7+
console.log('Pressable pressed in');
8+
};
9+
10+
const pressOut = () => {
11+
console.log('Pressable pressed out');
12+
};
13+
14+
const press = () => {
15+
console.log('Pressable pressed');
16+
};
17+
18+
const hoverIn = () => {
19+
console.log('Hovered in');
20+
};
21+
22+
const hoverOut = () => {
23+
console.log('Hovered out');
24+
};
25+
26+
const longPress = () => {
27+
console.log('Long pressed');
28+
};
29+
return (
30+
<View style={styles.pressRectContainer}>
31+
<View style={styles.hitRectContainer}>
32+
<Pressable
33+
style={styles.pressable}
34+
onPressIn={pressIn}
35+
onPressOut={pressOut}
36+
onPress={press}
37+
onHoverIn={hoverIn}
38+
onHoverOut={hoverOut}
39+
onLongPress={longPress}
40+
hitSlop={20}
41+
pressRetentionOffset={20}>
42+
<View style={styles.textWrapper}>
43+
<Text style={styles.text}>Pressable!</Text>
44+
</View>
45+
</Pressable>
46+
<Text style={styles.rectText}>Hit Rect</Text>
47+
</View>
48+
<Text style={styles.rectText}>Press Rect</Text>
49+
</View>
50+
);
51+
}
52+
53+
const BACKGROUND_COLOR = '#F5FCFF';
54+
55+
const styles = StyleSheet.create({
56+
pressRectContainer: {
57+
backgroundColor: '#FFD6E0',
58+
padding: 20,
59+
width: 200,
60+
height: 200,
61+
margin: 'auto',
62+
},
63+
hitRectContainer: {
64+
backgroundColor: '#F29DC3',
65+
padding: 20,
66+
width: 160,
67+
height: 160,
68+
margin: 'auto',
69+
},
70+
rectText: {
71+
color: BACKGROUND_COLOR,
72+
fontWeight: '700',
73+
position: 'absolute',
74+
right: 5,
75+
bottom: 2,
76+
},
77+
pressable: {
78+
width: 120,
79+
height: 120,
80+
backgroundColor: 'mediumpurple',
81+
},
82+
textWrapper: {
83+
flex: 1,
84+
justifyContent: 'center',
85+
alignItems: 'center',
86+
},
87+
text: {
88+
color: BACKGROUND_COLOR,
89+
},
90+
});
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from 'react';
2+
import { Platform, StyleSheet, View } from 'react-native';
3+
import TestingBase from './testingBase';
4+
5+
export function RippleExample() {
6+
const buttonOpacity =
7+
Platform.OS === 'android' ? { opacity: 1 } : { opacity: 0.6 };
8+
9+
return (
10+
<View style={styles.container}>
11+
<TestingBase
12+
style={[styles.pressable, buttonOpacity]}
13+
android_ripple={{
14+
color: 'green',
15+
borderless: false,
16+
foreground: false,
17+
}}
18+
/>
19+
</View>
20+
);
21+
}
22+
23+
const styles = StyleSheet.create({
24+
container: {
25+
flex: 1,
26+
flexDirection: 'row',
27+
justifyContent: 'center',
28+
alignItems: 'center',
29+
gap: 40,
30+
padding: 20,
31+
},
32+
pressable: {
33+
width: 100,
34+
height: 100,
35+
borderWidth: StyleSheet.hairlineWidth,
36+
backgroundColor: 'mediumpurple',
37+
},
38+
});
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import React from 'react';
2+
import { StyleSheet, View } from 'react-native';
3+
import TestingBase from './testingBase';
4+
import Animated, {
5+
useAnimatedStyle,
6+
useSharedValue,
7+
withSequence,
8+
withSpring,
9+
} from 'react-native-reanimated';
10+
11+
const signalerConfig = {
12+
duration: 200,
13+
dampingRatio: 1,
14+
stiffness: 500,
15+
overshootClamping: true,
16+
restDisplacementThreshold: 0.01,
17+
restSpeedThreshold: 2,
18+
};
19+
20+
export function DelayedPressExample() {
21+
const startColor = '#fff';
22+
const pressColor = '#ff0';
23+
const longPressColor = '#f0f';
24+
const animatedColor = useSharedValue(startColor);
25+
26+
const pressDelay = 1000;
27+
const longPressDelay = 1000;
28+
29+
const onPressIn = () => {
30+
console.log('Pressed with delay');
31+
animatedColor.value = withSequence(
32+
withSpring(pressColor, signalerConfig),
33+
withSpring(startColor, signalerConfig)
34+
);
35+
};
36+
37+
const onLongPress = () => {
38+
console.log('Long pressed with delay');
39+
animatedColor.value = withSequence(
40+
withSpring(longPressColor, signalerConfig),
41+
withSpring(startColor, signalerConfig)
42+
);
43+
};
44+
45+
const signalerStyle = useAnimatedStyle(() => ({
46+
backgroundColor: animatedColor.value,
47+
}));
48+
49+
return (
50+
<>
51+
<Animated.View style={[signalerStyle, styles.signaler]} />
52+
<View style={styles.container}>
53+
<TestingBase
54+
style={styles.pressable}
55+
delayLongPress={longPressDelay}
56+
unstable_pressDelay={pressDelay}
57+
onPressIn={onPressIn}
58+
onLongPress={onLongPress}
59+
/>
60+
</View>
61+
</>
62+
);
63+
}
64+
65+
const styles = StyleSheet.create({
66+
container: {
67+
flex: 1,
68+
flexDirection: 'row',
69+
justifyContent: 'center',
70+
alignItems: 'center',
71+
gap: 40,
72+
padding: 20,
73+
},
74+
pressable: {
75+
width: 100,
76+
height: 100,
77+
backgroundColor: 'mediumpurple',
78+
},
79+
signaler: {
80+
width: 50,
81+
height: 50,
82+
borderRadius: 25,
83+
marginTop: 15,
84+
borderWidth: StyleSheet.hairlineWidth,
85+
},
86+
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import React from 'react';
2+
import {
3+
PressableStateCallbackType,
4+
StyleProp,
5+
StyleSheet,
6+
View,
7+
ViewStyle,
8+
} from 'react-native';
9+
import TestingBase from './testingBase';
10+
11+
export function FunctionalStyleExample() {
12+
const functionalStyle = (
13+
state: PressableStateCallbackType
14+
): StyleProp<ViewStyle> => {
15+
if (state.pressed) {
16+
return {
17+
width: 100,
18+
height: 100,
19+
backgroundColor: 'red',
20+
};
21+
} else {
22+
return {
23+
width: 100,
24+
height: 100,
25+
backgroundColor: 'mediumpurple',
26+
};
27+
}
28+
};
29+
return (
30+
<View style={styles.container}>
31+
<TestingBase style={functionalStyle} />
32+
</View>
33+
);
34+
}
35+
36+
const styles = StyleSheet.create({
37+
container: {
38+
flex: 1,
39+
flexDirection: 'row',
40+
justifyContent: 'center',
41+
alignItems: 'center',
42+
gap: 40,
43+
padding: 20,
44+
},
45+
});
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import React from 'react';
2+
import { StyleSheet, Text, View } from 'react-native';
3+
import TestingBase from './testingBase';
4+
5+
const HIT_SLOP = 40;
6+
const PRESS_RETENTION_OFFSET = HIT_SLOP;
7+
8+
export function HitSlopExample() {
9+
const pressIn = () => {
10+
console.log('Pressable pressed in');
11+
};
12+
13+
const pressOut = () => {
14+
console.log('Pressable pressed out');
15+
};
16+
17+
const press = () => {
18+
console.log('Pressable pressed');
19+
};
20+
21+
const hoverIn = () => {
22+
console.log('Hovered in');
23+
};
24+
25+
const hoverOut = () => {
26+
console.log('Hovered out');
27+
};
28+
29+
const longPress = () => {
30+
console.log('Long pressed');
31+
};
32+
33+
return (
34+
<View style={styles.retentionIndicator}>
35+
<View style={styles.slopIndicator}>
36+
<View style={styles.container}>
37+
<TestingBase
38+
style={styles.pressable}
39+
hitSlop={HIT_SLOP}
40+
pressRetentionOffset={PRESS_RETENTION_OFFSET}
41+
onPressIn={() => pressIn()}
42+
onPressOut={() => pressOut()}
43+
onPress={() => press()}
44+
onHoverIn={() => hoverIn()}
45+
onHoverOut={() => hoverOut()}
46+
onLongPress={() => longPress()}
47+
/>
48+
</View>
49+
<Text style={styles.text}>Hit Slop</Text>
50+
</View>
51+
<Text style={styles.text}>Retention Offset</Text>
52+
</View>
53+
);
54+
}
55+
56+
const styles = StyleSheet.create({
57+
container: {
58+
flex: 1,
59+
justifyContent: 'flex-start',
60+
alignItems: 'center',
61+
gap: 40,
62+
},
63+
pressable: {
64+
backgroundColor: 'mediumpurple',
65+
width: 100,
66+
height: 100,
67+
},
68+
textWrapper: {
69+
flex: 1,
70+
justifyContent: 'center',
71+
alignItems: 'center',
72+
},
73+
text: {
74+
alignSelf: 'flex-end',
75+
marginBottom: 4,
76+
marginRight: 6,
77+
marginTop: 12,
78+
},
79+
slopIndicator: {
80+
display: 'flex',
81+
alignItems: 'center',
82+
width: 100 + HIT_SLOP * 2,
83+
borderRightWidth: StyleSheet.hairlineWidth,
84+
},
85+
retentionIndicator: {
86+
display: 'flex',
87+
alignItems: 'center',
88+
width: 180 + PRESS_RETENTION_OFFSET * 2,
89+
borderRightWidth: StyleSheet.hairlineWidth,
90+
margin: 20,
91+
},
92+
});

0 commit comments

Comments
 (0)