Skip to content

Commit

Permalink
Create gesturized pressable component (#2942)
Browse files Browse the repository at this point in the history
This PR adds our own `Pressable` component

Closes
#1221
  • Loading branch information
latekvo authored Jul 3, 2024
1 parent 2bf6b29 commit 4ce89e3
Show file tree
Hide file tree
Showing 15 changed files with 1,134 additions and 0 deletions.
4 changes: 4 additions & 0 deletions example/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import AsyncStorage from '@react-native-async-storage/async-storage';
import OverflowParent from './src/release_tests/overflowParent';
import DoublePinchRotate from './src/release_tests/doubleScalePinchAndRotate';
import DoubleDraggable from './src/release_tests/doubleDraggable';
import GesturizedPressable from './src/release_tests/gesturizedPressable';
import { ComboWithGHScroll } from './src/release_tests/combo';
import {
TouchablesIndex,
Expand Down Expand Up @@ -66,6 +67,7 @@ import Hover from './src/new_api/hover';
import HoverableIcons from './src/new_api/hoverable_icons';
import VelocityTest from './src/new_api/velocityTest';
import Swipeable from 'src/new_api/swipeable';
import Pressable from 'src/new_api/pressable';

import EmptyExample from './src/empty/EmptyExample';
import RectButtonBorders from './src/release_tests/rectButton';
Expand Down Expand Up @@ -143,6 +145,7 @@ const EXAMPLES: ExamplesSection[] = [
{ name: 'PointerType', component: PointerType },
{ name: 'Swipeable Reanimation', component: SwipeableReanimation },
{ name: 'RectButton (borders)', component: RectButtonBorders },
{ name: 'Gesturized pressable', component: GesturizedPressable },
],
},
{
Expand All @@ -164,6 +167,7 @@ const EXAMPLES: ExamplesSection[] = [
{ name: 'Chat Heads', component: ChatHeadsNewApi },
{ name: 'Drag and drop', component: DragNDrop },
{ name: 'Swipeable', component: Swipeable },
{ name: 'Pressable', component: Pressable },
{
name: 'Horizontal Drawer (Reanimated 2 & RNGH 2)',
component: BetterHorizontalDrawer,
Expand Down
90 changes: 90 additions & 0 deletions example/src/new_api/pressable/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Pressable } from 'react-native-gesture-handler';

export default function PressableExample() {
const pressIn = () => {
console.log('Pressable pressed in');
};

const pressOut = () => {
console.log('Pressable pressed out');
};

const press = () => {
console.log('Pressable pressed');
};

const hoverIn = () => {
console.log('Hovered in');
};

const hoverOut = () => {
console.log('Hovered out');
};

const longPress = () => {
console.log('Long pressed');
};
return (
<View style={styles.pressRectContainer}>
<View style={styles.hitRectContainer}>
<Pressable
style={styles.pressable}
onPressIn={pressIn}
onPressOut={pressOut}
onPress={press}
onHoverIn={hoverIn}
onHoverOut={hoverOut}
onLongPress={longPress}
hitSlop={20}
pressRetentionOffset={20}>
<View style={styles.textWrapper}>
<Text style={styles.text}>Pressable!</Text>
</View>
</Pressable>
<Text style={styles.rectText}>Hit Rect</Text>
</View>
<Text style={styles.rectText}>Press Rect</Text>
</View>
);
}

const BACKGROUND_COLOR = '#F5FCFF';

const styles = StyleSheet.create({
pressRectContainer: {
backgroundColor: '#FFD6E0',
padding: 20,
width: 200,
height: 200,
margin: 'auto',
},
hitRectContainer: {
backgroundColor: '#F29DC3',
padding: 20,
width: 160,
height: 160,
margin: 'auto',
},
rectText: {
color: BACKGROUND_COLOR,
fontWeight: '700',
position: 'absolute',
right: 5,
bottom: 2,
},
pressable: {
width: 120,
height: 120,
backgroundColor: 'mediumpurple',
},
textWrapper: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
text: {
color: BACKGROUND_COLOR,
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import { Platform, StyleSheet, View } from 'react-native';
import TestingBase from './testingBase';

export function RippleExample() {
const buttonOpacity =
Platform.OS === 'android' ? { opacity: 1 } : { opacity: 0.6 };

return (
<View style={styles.container}>
<TestingBase
style={[styles.pressable, buttonOpacity]}
android_ripple={{
color: 'green',
borderless: false,
foreground: false,
}}
/>
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
gap: 40,
padding: 20,
},
pressable: {
width: 100,
height: 100,
borderWidth: StyleSheet.hairlineWidth,
backgroundColor: 'mediumpurple',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React from 'react';
import { StyleSheet, View } from 'react-native';
import TestingBase from './testingBase';
import Animated, {
useAnimatedStyle,
useSharedValue,
withSequence,
withSpring,
} from 'react-native-reanimated';

const signalerConfig = {
duration: 200,
dampingRatio: 1,
stiffness: 500,
overshootClamping: true,
restDisplacementThreshold: 0.01,
restSpeedThreshold: 2,
};

export function DelayedPressExample() {
const startColor = '#fff';
const pressColor = '#ff0';
const longPressColor = '#f0f';
const animatedColor = useSharedValue(startColor);

const pressDelay = 1000;
const longPressDelay = 1000;

const onPressIn = () => {
console.log('Pressed with delay');
animatedColor.value = withSequence(
withSpring(pressColor, signalerConfig),
withSpring(startColor, signalerConfig)
);
};

const onLongPress = () => {
console.log('Long pressed with delay');
animatedColor.value = withSequence(
withSpring(longPressColor, signalerConfig),
withSpring(startColor, signalerConfig)
);
};

const signalerStyle = useAnimatedStyle(() => ({
backgroundColor: animatedColor.value,
}));

return (
<>
<Animated.View style={[signalerStyle, styles.signaler]} />
<View style={styles.container}>
<TestingBase
style={styles.pressable}
delayLongPress={longPressDelay}
unstable_pressDelay={pressDelay}
onPressIn={onPressIn}
onLongPress={onLongPress}
/>
</View>
</>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
gap: 40,
padding: 20,
},
pressable: {
width: 100,
height: 100,
backgroundColor: 'mediumpurple',
},
signaler: {
width: 50,
height: 50,
borderRadius: 25,
marginTop: 15,
borderWidth: StyleSheet.hairlineWidth,
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';
import {
PressableStateCallbackType,
StyleProp,
StyleSheet,
View,
ViewStyle,
} from 'react-native';
import TestingBase from './testingBase';

export function FunctionalStyleExample() {
const functionalStyle = (
state: PressableStateCallbackType
): StyleProp<ViewStyle> => {
if (state.pressed) {
return {
width: 100,
height: 100,
backgroundColor: 'red',
};
} else {
return {
width: 100,
height: 100,
backgroundColor: 'mediumpurple',
};
}
};
return (
<View style={styles.container}>
<TestingBase style={functionalStyle} />
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
gap: 40,
padding: 20,
},
});
92 changes: 92 additions & 0 deletions example/src/release_tests/gesturizedPressable/hitSlopExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import TestingBase from './testingBase';

const HIT_SLOP = 40;
const PRESS_RETENTION_OFFSET = HIT_SLOP;

export function HitSlopExample() {
const pressIn = () => {
console.log('Pressable pressed in');
};

const pressOut = () => {
console.log('Pressable pressed out');
};

const press = () => {
console.log('Pressable pressed');
};

const hoverIn = () => {
console.log('Hovered in');
};

const hoverOut = () => {
console.log('Hovered out');
};

const longPress = () => {
console.log('Long pressed');
};

return (
<View style={styles.retentionIndicator}>
<View style={styles.slopIndicator}>
<View style={styles.container}>
<TestingBase
style={styles.pressable}
hitSlop={HIT_SLOP}
pressRetentionOffset={PRESS_RETENTION_OFFSET}
onPressIn={() => pressIn()}
onPressOut={() => pressOut()}
onPress={() => press()}
onHoverIn={() => hoverIn()}
onHoverOut={() => hoverOut()}
onLongPress={() => longPress()}
/>
</View>
<Text style={styles.text}>Hit Slop</Text>
</View>
<Text style={styles.text}>Retention Offset</Text>
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'flex-start',
alignItems: 'center',
gap: 40,
},
pressable: {
backgroundColor: 'mediumpurple',
width: 100,
height: 100,
},
textWrapper: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
text: {
alignSelf: 'flex-end',
marginBottom: 4,
marginRight: 6,
marginTop: 12,
},
slopIndicator: {
display: 'flex',
alignItems: 'center',
width: 100 + HIT_SLOP * 2,
borderRightWidth: StyleSheet.hairlineWidth,
},
retentionIndicator: {
display: 'flex',
alignItems: 'center',
width: 180 + PRESS_RETENTION_OFFSET * 2,
borderRightWidth: StyleSheet.hairlineWidth,
margin: 20,
},
});
Loading

0 comments on commit 4ce89e3

Please sign in to comment.