/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow strict-local * @format */ import type {RNTesterModule} from '../../types/RNTesterTypes'; import * as PressableExampleFbInternal from './PressableExampleFbInternal'; import * as React from 'react'; import { Alert, Animated, Image, Platform, Pressable, StyleSheet, Text, View, } from 'react-native'; const {useEffect, useRef, useState} = React; function onPressablePress(pressableName: string) { Alert.alert(`Your application has been ${pressableName}!`); } 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 ( <> { setTimesPressed(current => current + 1); }}> {({pressed}) => ( {pressed ? 'Pressed!' : 'Press Me'} )} {textLog} ); } 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 ( <> { setTimesPressed(prev => prev + 1); }}> Text has built-in onPress handling {textLog} ); } function PressableAriaLabel() { return ( onPressablePress('pressed')}> Press Me ); } function PressableFeedbackEvents() { const [eventLog, setEventLog] = useState>([]); function appendEvent(eventName: string) { const limit = 6; setEventLog(current => { return [eventName].concat(current.slice(0, limit - 1)); }); } return ( appendEvent('press')} onPressIn={() => appendEvent('pressIn')} onPressOut={() => appendEvent('pressOut')} onLongPress={() => appendEvent('longPress')}> Press Me {eventLog.map((e, ii) => ( {e} ))} ); } function PressableDelayEvents() { const [eventLog, setEventLog] = useState>([]); function appendEvent(eventName: string) { const limit = 6; const newEventLog = eventLog.slice(0, limit - 1); newEventLog.unshift(eventName); setEventLog(newEventLog); } return ( appendEvent('press')} onPressIn={() => appendEvent('pressIn')} onPressOut={() => appendEvent('pressOut')} delayLongPress={800} onLongPress={() => appendEvent('longPress - 800ms delay')}> Press Me {eventLog.map((e, ii) => ( {e} ))} ); } function ForceTouchExample() { const [force, setForce] = useState(0); const consoleText = forceTouchAvailable ? 'Force: ' + force.toFixed(3) : '3D Touch is not available on this device'; return ( {consoleText} true} // $FlowFixMe[sketchy-null-number] onResponderMove={event => setForce(event.nativeEvent?.force || 1)} onResponderRelease={event => setForce(0)}> Press Me ); } function PressableHitSlop() { const [timesPressed, setTimesPressed] = useState(0); let log = ''; if (timesPressed > 1) { log = timesPressed + 'x onPress'; } else if (timesPressed > 0) { log = 'onPress'; } return ( setTimesPressed(num => num + 1)} style={styles.hitSlopWrapper} hitSlop={{top: 30, bottom: 30, left: 60, right: 60}} testID="pressable_hit_slop_button"> Press Outside This View {log} ); } function PressableNativeMethods() { const [status, setStatus] = useState(null); const ref = useRef<$FlowFixMe>(null); useEffect(() => { setStatus(ref.current != null && typeof ref.current.measure === 'function'); }, []); return ( <> {status == null ? 'Missing Ref!' : status === true ? 'Native Methods Exist' : 'Native Methods Missing!'} ); } function PressableDisabled() { return ( <> Disabled Pressable [ {opacity: pressed ? 0.5 : 1}, styles.row, styles.block, ]}> Enabled 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, width: '100%', justifyContent: 'center', alignItems: 'center', }, 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', }, image: { height: 100, width: 100, }, }); const examples = [ { title: 'Change content based on Press', render(): React.Node { return ; }, }, { title: 'Change style based on Press', render(): React.Node { return ( [ { backgroundColor: pressed ? 'rgb(210, 230, 255)' : 'white', }, styles.wrapperCustom, ]}> Press Me ); }, }, { title: 'Change child based on Press', description: ('You should be able to press the button, move your finger while pressing, and release it with the proper status updates.': string), render(): React.Node { return ( [ { backgroundColor: pressed ? 'rgb(210, 230, 255)' : 'white', }, styles.wrapperCustom, ]}> {({pressed}) => ( <> {pressed && Pressed!} {!pressed && ( Press me and move your finger )} )} ); }, }, { title: 'Pressable feedback events', description: (' components accept onPress, onPressIn, ' + 'onPressOut, and onLongPress as props.': string), render: function (): React.Node { return ; }, }, { 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 ( ); }, }, { 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 ( {/* $FlowFixMe[incompatible-type] Natural Inference rollout. * See https://fburl.com/workplace/6291gfvu */} radius 30 {/* $FlowFixMe[incompatible-type] Natural Inference rollout. * See https://fburl.com/workplace/6291gfvu */} radius 150 {/* $FlowFixMe[incompatible-type] Natural Inference rollout. * See https://fburl.com/workplace/6291gfvu */} radius 70, with border {/* $FlowFixMe[incompatible-type] Natural Inference rollout. See * https://fburl.com/workplace/6291gfvu */} with border, default color and radius use foreground ); }, }, { title: ' with highlight', render: function (): React.Node { return ; }, }, { title: 'Pressable delay for events', description: (' also accept delayPressIn, ' + 'delayPressOut, and delayLongPress as props. These props impact the ' + 'timing of feedback events.': string), render: function (): React.Node { return ; }, }, { 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 ; }, platform: 'ios', }, { title: 'Pressable Hit Slop', description: (' components accept hitSlop prop which extends the touch area ' + 'without changing the view bounds.': string), render: function (): React.Node { return ; }, }, { title: 'Pressable Native Methods', description: (' components expose native methods like `measure`.': string), render: function (): React.Node { return ; }, }, { title: 'Disabled Pressable', description: (' components accept disabled prop which prevents ' + 'any interaction with component': string), render: function (): React.Node { return ; }, }, { title: 'Pressable with aria-label="label"', description: ('Note: This prop changes the text that a screen ' + 'reader announces (there are no visual differences).': string), render: function (): React.Node { return ; }, }, ...PressableExampleFbInternal.examples, ]; module.exports = ({ title: 'Pressable', documentationURL: 'https://reactnative.dev/docs/pressable', category: 'UI', description: 'Component for making views pressable.', displayName: 'Pressable', /* $FlowFixMe[incompatible-type] Natural Inference rollout. See * https://fburl.com/workplace/6291gfvu */ examples, }: RNTesterModule);