/** * 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 */ /* eslint-disable no-alert */ import type {RNTesterModuleExample} from '../../types/RNTesterTypes'; import type {ModalProps} from 'react-native'; import RNTesterButton from '../../components/RNTesterButton'; import RNTesterText from '../../components/RNTesterText'; import {RNTesterThemeContext} from '../../components/RNTesterTheme'; import RNTOption from '../../components/RNTOption'; import * as React from 'react'; import {useCallback, useContext, useState} from 'react'; import {Modal, Platform, StyleSheet, Switch, Text, View} from 'react-native'; const animationTypes = ['slide', 'none', 'fade'] as const; const presentationStyles = [ 'fullScreen', 'pageSheet', 'formSheet', 'overFullScreen', ] as const; const supportedOrientations = [ 'portrait', 'portrait-upside-down', 'landscape', 'landscape-left', 'landscape-right', ] as const; const backdropColors = ['red', 'blue', undefined]; function ModalPresentation() { const onDismiss = useCallback(() => { alert('onDismiss'); }, []); const onShow = useCallback(() => { alert('onShow'); }, []); const onRequestClose = useCallback(() => { console.log('onRequestClose'); setProps(prev => ({...prev, visible: false})); }, []); const [props, setProps] = useState({ animationType: 'none', transparent: false, hardwareAccelerated: false, statusBarTranslucent: false, navigationBarTranslucent: false, presentationStyle: Platform.select({ ios: 'fullScreen', default: undefined, }), allowSwipeDismissal: false, supportedOrientations: Platform.select({ ios: ['portrait'], default: undefined, }), onDismiss: undefined, onShow: undefined, visible: false, backdropColor: undefined, }); const presentationStyle = props.presentationStyle; const hardwareAccelerated = props.hardwareAccelerated; const statusBarTranslucent = props.statusBarTranslucent; const navigationBarTranslucent = props.navigationBarTranslucent; const allowSwipeDismissal = props.allowSwipeDismissal; const backdropColor = props.backdropColor; const backgroundColor = useContext(RNTesterThemeContext).BackgroundColor; const [currentOrientation, setCurrentOrientation] = useState('unknown'); type OrientationChangeEvent = Parameters< $NonMaybeType, >[0]; const onOrientationChange = (event: OrientationChangeEvent) => setCurrentOrientation(event.nativeEvent.orientation); const controls = ( <> Status Bar Translucent 🟢 setProps(prev => ({ ...prev, statusBarTranslucent: enabled, navigationBarTranslucent: false, })) } /> Navigation Bar Translucent 🟢 { setProps(prev => ({ ...prev, statusBarTranslucent: enabled, navigationBarTranslucent: enabled, })); }} /> Hardware Acceleration 🟢 setProps(prev => ({ ...prev, hardwareAccelerated: enabled, })) } /> Allow Swipe Dismissal ⚫️ setProps(prev => ({ ...prev, allowSwipeDismissal: enabled, })) } /> Presentation Style ⚫️ {presentationStyles.map(type => ( setProps(prev => { if (type === 'overFullScreen' && prev.transparent === true) { return { ...prev, presentationStyle: type, transparent: false, }; } return { ...prev, presentationStyle: type === prev.presentationStyle ? undefined : type, }; }) } selected={type === presentationStyle} /> ))} Transparent setProps(prev => ({...prev, transparent: enabled})) } /> {Platform.OS === 'ios' && presentationStyle !== 'overFullScreen' ? ( iOS Modal can only be transparent with 'overFullScreen' Presentation Style ) : null} Supported Orientation ⚫️ {supportedOrientations.map(orientation => ( setProps(prev => { if (prev.supportedOrientations?.includes(orientation)) { return { ...prev, supportedOrientations: prev.supportedOrientations?.filter( o => o !== orientation, ), }; } return { ...prev, supportedOrientations: [ ...(prev.supportedOrientations ?? []), orientation, ], }; }) } selected={props.supportedOrientations?.includes(orientation)} /> ))} Actions setProps(prev => ({ ...prev, onShow: prev.onShow ? undefined : onShow, })) } selected={!!props.onShow} /> setProps(prev => ({ ...prev, onDismiss: prev.onDismiss ? undefined : onDismiss, })) } selected={!!props.onDismiss} /> Backdrop Color ⚫️ {backdropColors.map(type => ( setProps(prev => ({ ...prev, backdropColor: type, })) } selected={type === backdropColor} /> ))} ); return ( setProps(prev => ({...prev, visible: true}))}> Show Modal This modal was presented with animationType: ' {props.animationType}' {Platform.OS === 'ios' ? ( It is currently displayed in {currentOrientation} mode. ) : null} setProps(prev => ({...prev, visible: false}))}> Close {controls} Animation Type {animationTypes.map(type => ( setProps(prev => ({...prev, animationType: type}))} selected={type === props.animationType} /> ))} {controls} ); } const styles = StyleSheet.create({ row: { flexWrap: 'wrap', flexDirection: 'row', }, rowWithSpaceBetween: { flexDirection: 'row', justifyContent: 'space-between', }, block: { borderColor: 'rgba(0,0,0, 0.1)', borderBottomWidth: 1, padding: 6, }, inlineBlock: { padding: 6, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', borderColor: 'rgba(0,0,0, 0.1)', borderBottomWidth: 1, }, title: { margin: 3, fontWeight: 'bold', }, option: { marginRight: 8, marginTop: 6, }, modalContainer: { flex: 1, justifyContent: 'center', padding: 20, }, modalInnerContainer: { borderRadius: 10, padding: 10, }, warning: { margin: 3, fontSize: 12, color: 'red', }, }); export default ({ title: 'Modal Presentation', name: 'basic', description: 'Modals can be presented with or without animation', render: (): React.Node => , }: RNTesterModuleExample);