create-expo-cljs-app
Version:
Create a react native application with Expo and Shadow-CLJS!
482 lines (449 loc) • 12.6 kB
JavaScript
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow strict-local
*/
;
import * as React from 'react';
import {
Animated,
Pressable,
StyleSheet,
Text,
Platform,
View,
} from 'react-native';
const {useEffect, useRef, useState} = React;
const forceTouchAvailable =
(Platform.OS === 'ios' && Platform.constants.forceTouchAvailable) || false;
function ContentPress() {
const [timesPressed, setTimesPressed] = useState(0);
let textLog = '';
if (timesPressed > 1) {
textLog = timesPressed + 'x onPress';
} else if (timesPressed > 0) {
textLog = 'onPress';
}
return (
<>
<View style={styles.row}>
<Pressable
onPress={() => {
setTimesPressed(current => current + 1);
}}>
{({pressed}) => (
<Text style={styles.text}>{pressed ? 'Pressed!' : 'Press Me'}</Text>
)}
</Pressable>
</View>
<View style={styles.logBox}>
<Text testID="pressable_press_console">{textLog}</Text>
</View>
</>
);
}
function TextOnPressBox() {
const [timesPressed, setTimesPressed] = useState(0);
let textLog = '';
if (timesPressed > 1) {
textLog = timesPressed + 'x text onPress';
} else if (timesPressed > 0) {
textLog = 'text onPress';
}
return (
<>
<Text
style={styles.textBlock}
testID="tappable_text"
onPress={() => {
setTimesPressed(prev => prev + 1);
}}>
Text has built-in onPress handling
</Text>
<View style={styles.logBox}>
<Text testID="tappable_text_console">{textLog}</Text>
</View>
</>
);
}
function PressableFeedbackEvents() {
const [eventLog, setEventLog] = useState([]);
function appendEvent(eventName) {
const limit = 6;
setEventLog(current => {
return [eventName].concat(current.slice(0, limit - 1));
});
}
return (
<View testID="pressable_feedback_events">
<View style={[styles.row, styles.centered]}>
<Pressable
style={styles.wrapper}
testID="pressable_feedback_events_button"
accessibilityLabel="pressable feedback events"
accessibilityRole="button"
onPress={() => appendEvent('press')}
onPressIn={() => appendEvent('pressIn')}
onPressOut={() => appendEvent('pressOut')}
onLongPress={() => appendEvent('longPress')}>
<Text style={styles.button}>Press Me</Text>
</Pressable>
</View>
<View
testID="pressable_feedback_events_console"
style={styles.eventLogBox}>
{eventLog.map((e, ii) => (
<Text key={ii}>{e}</Text>
))}
</View>
</View>
);
}
function PressableDelayEvents() {
const [eventLog, setEventLog] = useState([]);
function appendEvent(eventName) {
const limit = 6;
const newEventLog = eventLog.slice(0, limit - 1);
newEventLog.unshift(eventName);
setEventLog(newEventLog);
}
return (
<View testID="pressable_delay_events">
<View style={[styles.row, styles.centered]}>
<Pressable
style={styles.wrapper}
testID="pressable_delay_events_button"
onPress={() => appendEvent('press')}
onPressIn={() => appendEvent('pressIn')}
onPressOut={() => appendEvent('pressOut')}
delayLongPress={800}
onLongPress={() => appendEvent('longPress - 800ms delay')}>
<Text style={styles.button}>Press Me</Text>
</Pressable>
</View>
<View style={styles.eventLogBox} testID="pressable_delay_events_console">
{eventLog.map((e, ii) => (
<Text key={ii}>{e}</Text>
))}
</View>
</View>
);
}
function ForceTouchExample() {
const [force, setForce] = useState(0);
const consoleText = forceTouchAvailable
? 'Force: ' + force.toFixed(3)
: '3D Touch is not available on this device';
return (
<View testID="pressable_3dtouch_event">
<View style={styles.forceTouchBox} testID="pressable_3dtouch_output">
<Text>{consoleText}</Text>
</View>
<View style={[styles.row, styles.centered]}>
<View
style={styles.wrapper}
testID="pressable_3dtouch_button"
onStartShouldSetResponder={() => true}
onResponderMove={event => setForce(event.nativeEvent?.force || 1)}
onResponderRelease={event => setForce(0)}>
<Text style={styles.button}>Press Me</Text>
</View>
</View>
</View>
);
}
function PressableHitSlop() {
const [timesPressed, setTimesPressed] = useState(0);
let log = '';
if (timesPressed > 1) {
log = timesPressed + 'x onPress';
} else if (timesPressed > 0) {
log = 'onPress';
}
return (
<View testID="pressable_hit_slop">
<View style={[styles.row, styles.centered]}>
<Pressable
onPress={() => setTimesPressed(num => num + 1)}
style={styles.hitSlopWrapper}
hitSlop={{top: 30, bottom: 30, left: 60, right: 60}}
testID="pressable_hit_slop_button">
<Text style={styles.hitSlopButton}>Press Outside This View</Text>
</Pressable>
</View>
<View style={styles.logBox}>
<Text>{log}</Text>
</View>
</View>
);
}
function PressableNativeMethods() {
const [status, setStatus] = useState<?boolean>(null);
const ref = useRef(null);
useEffect(() => {
setStatus(ref.current != null && typeof ref.current.measure === 'function');
}, []);
return (
<>
<View style={[styles.row, styles.block]}>
<Pressable ref={ref}>
<View />
</Pressable>
<Text>
{status == null
? 'Missing Ref!'
: status === true
? 'Native Methods Exist'
: 'Native Methods Missing!'}
</Text>
</View>
</>
);
}
function PressableDisabled() {
return (
<>
<Pressable disabled={true} style={[styles.row, styles.block]}>
<Text style={styles.disabledButton}>Disabled Pressable</Text>
</Pressable>
<Pressable
disabled={false}
style={({pressed}) => [
{opacity: pressed ? 0.5 : 1},
styles.row,
styles.block,
]}>
<Text style={styles.button}>Enabled Pressable</Text>
</Pressable>
</>
);
}
const styles = StyleSheet.create({
row: {
justifyContent: 'center',
flexDirection: 'row',
},
centered: {
justifyContent: 'center',
},
text: {
fontSize: 16,
},
block: {
padding: 10,
},
button: {
color: '#007AFF',
},
disabledButton: {
color: '#007AFF',
opacity: 0.5,
},
hitSlopButton: {
color: 'white',
},
wrapper: {
borderRadius: 8,
},
wrapperCustom: {
borderRadius: 8,
padding: 6,
},
hitSlopWrapper: {
backgroundColor: 'red',
marginVertical: 30,
},
logBox: {
padding: 20,
margin: 10,
borderWidth: StyleSheet.hairlineWidth,
borderColor: '#f0f0f0',
backgroundColor: '#f9f9f9',
},
eventLogBox: {
padding: 10,
margin: 10,
height: 120,
borderWidth: StyleSheet.hairlineWidth,
borderColor: '#f0f0f0',
backgroundColor: '#f9f9f9',
},
forceTouchBox: {
padding: 10,
margin: 10,
borderWidth: StyleSheet.hairlineWidth,
borderColor: '#f0f0f0',
backgroundColor: '#f9f9f9',
alignItems: 'center',
},
textBlock: {
fontWeight: '500',
color: 'blue',
},
});
exports.displayName = (undefined: ?string);
exports.description = 'Component for making views pressable.';
exports.title = 'Pressable';
exports.category = 'UI';
exports.documentationURL = 'https://reactnative.dev/docs/pressable';
exports.examples = [
{
title: 'Change content based on Press',
render(): React.Node {
return <ContentPress />;
},
},
{
title: 'Change style based on Press',
render(): React.Node {
return (
<View style={styles.row}>
<Pressable
style={({pressed}) => [
{
backgroundColor: pressed ? 'rgb(210, 230, 255)' : 'white',
},
styles.wrapperCustom,
]}>
<Text style={styles.text}>Press Me</Text>
</Pressable>
</View>
);
},
},
{
title: 'Pressable feedback events',
description: ('<Pressable> components accept onPress, onPressIn, ' +
'onPressOut, and onLongPress as props.': string),
render: function(): React.Node {
return <PressableFeedbackEvents />;
},
},
{
title: 'Pressable with Ripple and Animated child',
description: ('Pressable can have an AnimatedComponent as a direct child.': string),
platform: 'android',
render: function(): React.Node {
const mScale = new Animated.Value(1);
Animated.timing(mScale, {
toValue: 0.3,
duration: 1000,
useNativeDriver: false,
}).start();
const style = {
backgroundColor: 'rgb(180, 64, 119)',
width: 200,
height: 100,
transform: [{scale: mScale}],
};
return (
<View style={styles.row}>
<Pressable android_ripple={{color: 'green'}}>
<Animated.View style={style} />
</Pressable>
</View>
);
},
},
{
title: 'Pressable with custom Ripple',
description: ("Pressable can specify ripple's radius, color and borderless params": string),
platform: 'android',
render: function(): React.Node {
const nativeFeedbackButton = {
textAlign: 'center',
margin: 10,
};
return (
<View>
<View
style={[
styles.row,
{justifyContent: 'space-around', alignItems: 'center'},
]}>
<Pressable
android_ripple={{color: 'orange', borderless: true, radius: 30}}>
<View>
<Text style={[styles.button, nativeFeedbackButton]}>
radius 30
</Text>
</View>
</Pressable>
<Pressable android_ripple={{borderless: true, radius: 150}}>
<View>
<Text style={[styles.button, nativeFeedbackButton]}>
radius 150
</Text>
</View>
</Pressable>
<Pressable android_ripple={{borderless: false, radius: 70}}>
<View style={styles.block}>
<Text style={[styles.button, nativeFeedbackButton]}>
radius 70, with border
</Text>
</View>
</Pressable>
</View>
<Pressable android_ripple={{borderless: false}}>
<View style={styles.block}>
<Text style={[styles.button, nativeFeedbackButton]}>
with border, default color and radius
</Text>
</View>
</Pressable>
</View>
);
},
},
{
title: '<Text onPress={fn}> with highlight',
render: function(): React.Node {
return <TextOnPressBox />;
},
},
{
title: 'Pressable delay for events',
description: ('<Pressable> also accept delayPressIn, ' +
'delayPressOut, and delayLongPress as props. These props impact the ' +
'timing of feedback events.': string),
render: function(): React.Node {
return <PressableDelayEvents />;
},
},
{
title: '3D Touch / Force Touch',
description:
'iPhone 8 and 8 plus support 3D touch, which adds a force property to touches',
render: function(): React.Node {
return <ForceTouchExample />;
},
platform: 'ios',
},
{
title: 'Pressable Hit Slop',
description: ('<Pressable> components accept hitSlop prop which extends the touch area ' +
'without changing the view bounds.': string),
render: function(): React.Node {
return <PressableHitSlop />;
},
},
{
title: 'Pressable Native Methods',
description: ('<Pressable> components expose native methods like `measure`.': string),
render: function(): React.Node {
return <PressableNativeMethods />;
},
},
{
title: 'Disabled Pressable',
description: ('<Pressable> components accept disabled prop which prevents ' +
'any interaction with component': string),
render: function(): React.Node {
return <PressableDisabled />;
},
},
];