/**
* 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);