diff --git a/example/demo-components/src/DemoScreen.tsx b/example/demo-components/src/DemoScreen.tsx index cc2f93e..34dd2a4 100644 --- a/example/demo-components/src/DemoScreen.tsx +++ b/example/demo-components/src/DemoScreen.tsx @@ -18,6 +18,7 @@ import { useTheme } from './theme/colors'; import { IS_FABRIC } from './constants'; import { EmbeddedModal } from './modals/embedded/EmbeddedModal'; import { FlashModal } from './modals/flash-modal/FlashModal'; +import { EdgeToEdgeModal } from './modals/edge-to-edge/EdgeToEdgeModal'; type DemoCase = { id: string; @@ -30,6 +31,7 @@ type DemoCase = { export const DemoScreen = () => { const { colors } = useTheme(); + const { colors } = useTheme(); const [activeCases, setActiveCases] = useState([]); @@ -137,6 +139,13 @@ export const DemoScreen = () => { description: 'Tap Flash: a submodal is dismissed before it renders', Component: FlashModal, }, + { + id: 'edge-to-edge', + title: 'Edge To Edge', + description: + 'Content stays within the safe area while the backdrop covers system bars.', + Component: EdgeToEdgeModal, + }, ], [openModal], ); @@ -171,16 +180,22 @@ export const DemoScreen = () => { return ( + style={[styles.safeArea, { backgroundColor: colors.background }]} > + + contentContainerStyle={styles.scrollContent} > Current Architecture: {IS_FABRIC ? 'Fabric 🚀' : 'Paper ✈️'} + {demoCases.map(({ title, description, id }) => ( {demoCases.map(({ title, description, id }) => ( = props => { + return ( + + + + ↑ TOP EDGE ↑ + + + + + + ↓ BOTTOM EDGE ↓ + + + + ); +}; + +const styles = StyleSheet.create({ + contentContainer: { + flex: 1, + }, + body: { + flex: 1, + justifyContent: 'space-between', + alignItems: 'center', + }, + edge: { + alignSelf: 'stretch', + paddingVertical: 18, + alignItems: 'center', + backgroundColor: '#e53935', + }, + topEdge: {}, + bottomEdge: {}, + edgeText: { + color: 'white', + fontWeight: '700', + fontSize: 16, + }, +}); diff --git a/src/ModalView.tsx b/src/ModalView.tsx index bdac54b..3fc2923 100644 --- a/src/ModalView.tsx +++ b/src/ModalView.tsx @@ -5,6 +5,7 @@ import { Platform, Pressable, StatusBar, StyleSheet, View } from 'react-native'; import { LayoutInspectorProvider } from './LayoutInspectorProvider'; import { ScrollContextResetter } from './ScrollContextResetter'; import { GestureHandlerRootView } from './integrations/GestureHandlerRootView'; +import { SafeAreaProvider } from './integrations/SafeAreaProvider'; import RNTModalView from './newarch/NativeRNTModalView'; import type { ModalViewProps } from './types'; @@ -40,39 +41,41 @@ export const ModalView: FC = ({ animationType={animationType} > - - {isIOS && statusBar && !disableDefaultStatusBarIOS ? ( - - ) : null} - - {showBackdrop && ( - - onRequestDismiss?.(DismissalSource.Backdrop)} + + + {isIOS && statusBar && !disableDefaultStatusBarIOS ? ( + + ) : null} + + {showBackdrop && ( + + onRequestDismiss?.(DismissalSource.Backdrop)} + > + {renderBackdrop ? ( + renderBackdrop() + ) : ( + + )} + + + )} + + - {renderBackdrop ? ( - renderBackdrop() - ) : ( - - )} - - - )} - - - {children} - - - - + {children} + + + + + ); diff --git a/src/integrations/SafeAreaProvider.tsx b/src/integrations/SafeAreaProvider.tsx new file mode 100644 index 0000000..2891adb --- /dev/null +++ b/src/integrations/SafeAreaProvider.tsx @@ -0,0 +1,29 @@ +import { ReactNode } from 'react'; + +const importSafeAreaProvider = () => { + const Errors = { + SafeAreaProviderNotFound: new Error( + 'SafeAreaProvider not found. Please report the issue: https://github.com/paufau/react-native-multiple-modals/issues', + ), + }; + + try { + const SafeAreaProviderComponent = + require('react-native-safe-area-context').SafeAreaProvider; + + if (!SafeAreaProviderComponent) { + throw Errors.SafeAreaProviderNotFound; + } + + return SafeAreaProviderComponent; + } catch (e) { + if (e === Errors.SafeAreaProviderNotFound) { + console.error(e); + } + + // Fallback component used if the library is not found + return ({ children }: { children: ReactNode }) => children; + } +}; + +export const SafeAreaProvider = importSafeAreaProvider();