import { BottomSheetModal, BottomSheetModalProps, BottomSheetModalProvider, BottomSheetView as RNBottomSheetView, } from '@gorhom/bottom-sheet'; import { ParamListBase, useTheme } from '@react-navigation/native'; import * as React from 'react'; import { Platform, StyleSheet } from 'react-native'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { FullWindowOverlay } from 'react-native-screens'; import type { BottomSheetDescriptorMap, BottomSheetNavigationConfig, BottomSheetNavigationHelpers, BottomSheetNavigationProp, BottomSheetNavigationState, } from './types'; type BottomSheetModalScreenProps = BottomSheetModalProps & { navigation: BottomSheetNavigationProp; }; function Overlay({ children }: { children: React.ReactNode }) { if (Platform.OS === 'ios') { return ( {children} ); } else { return children; } } function BottomSheetModalScreen({ index, navigation, enableDynamicSizing, children, ...props }: BottomSheetModalScreenProps) { const ref = React.useRef(null); const lastIndexRef = React.useRef(index); // Present on mount. React.useEffect(() => { ref.current?.present(); }, []); const isMounted = React.useRef(true); React.useEffect(() => { return () => { isMounted.current = false; }; }, []); React.useEffect(() => { if (index != null && lastIndexRef.current !== index) { ref.current?.snapToIndex(index); } }, [index]); const onChange = React.useCallback( (newIndex: number) => { lastIndexRef.current = newIndex; if (newIndex >= 0) { navigation.snapTo(newIndex); } }, [navigation], ); const onDismiss = React.useCallback(() => { // BottomSheetModal will call onDismiss on unmount, be we do not want that since // we already popped the screen. if (isMounted.current) { navigation.goBack(); } }, [navigation]); return ( {enableDynamicSizing ? ( {children as React.ReactNode} ) : ( children )} ); } const DEFAULT_SNAP_POINTS = ['66%']; type Props = BottomSheetNavigationConfig & { state: BottomSheetNavigationState; navigation: BottomSheetNavigationHelpers; descriptors: BottomSheetDescriptorMap; }; export function BottomSheetView({ state, descriptors }: Props) { const { colors } = useTheme(); const themeBackgroundStyle = React.useMemo( () => ({ backgroundColor: colors.card, }), [colors.card], ); const themeHandleIndicatorStyle = React.useMemo( () => ({ backgroundColor: colors.border, }), [colors.border], ); // Avoid rendering provider if we only have one screen. const shouldRenderProvider = React.useRef(false); shouldRenderProvider.current = shouldRenderProvider.current || state.routes.length > 1; const firstScreen = descriptors[state.routes[0].key]; return ( <> {firstScreen.render()} {shouldRenderProvider.current && ( {state.routes.slice(1).map((route) => { const { options, navigation, render } = descriptors[route.key]; const { index, backgroundStyle, handleIndicatorStyle, snapPoints, enableDynamicSizing, ...sheetProps } = options; return ( {render()} ); })} )} ); } const styles = StyleSheet.create({ safeAreaProvider: { flex: 1, pointerEvents: 'box-none' }, });