import React, {useImperativeHandle} from 'react' import {View, Dimensions, Keyboard, Pressable} from 'react-native' import BottomSheet, { BottomSheetBackdropProps, BottomSheetScrollView, BottomSheetTextInput, BottomSheetView, useBottomSheet, WINDOW_HEIGHT, } from '@gorhom/bottom-sheet' import {useSafeAreaInsets} from 'react-native-safe-area-context' import Animated, {useAnimatedStyle} from 'react-native-reanimated' import {useTheme, atoms as a, flatten} from '#/alf' import {Portal} from '#/components/Portal' import {createInput} from '#/components/forms/TextField' import {logger} from '#/logger' import {useDialogStateControlContext} from '#/state/dialogs' import { DialogOuterProps, DialogControlProps, DialogInnerProps, } from '#/components/Dialog/types' import {Context} from '#/components/Dialog/context' export {useDialogControl, useDialogContext} from '#/components/Dialog/context' export * from '#/components/Dialog/types' // @ts-ignore export const Input = createInput(BottomSheetTextInput) function Backdrop(props: BottomSheetBackdropProps) { const t = useTheme() const bottomSheet = useBottomSheet() const animatedStyle = useAnimatedStyle(() => { const opacity = (Math.abs(WINDOW_HEIGHT - props.animatedPosition.value) - 50) / 1000 return { opacity: Math.min(Math.max(opacity, 0), 0.55), } }) const onPress = React.useCallback(() => { bottomSheet.close() }, [bottomSheet]) return ( ) } export function Outer({ children, control, onClose, nativeOptions, }: React.PropsWithChildren) { const t = useTheme() const sheet = React.useRef(null) const sheetOptions = nativeOptions?.sheet || {} const hasSnapPoints = !!sheetOptions.snapPoints const insets = useSafeAreaInsets() const closeCallback = React.useRef<() => void>() const {setDialogIsOpen} = useDialogStateControlContext() /* * Used to manage open/closed, but index is otherwise handled internally by `BottomSheet` */ const [openIndex, setOpenIndex] = React.useState(-1) /* * `openIndex` is the index of the snap point to open the bottom sheet to. If >0, the bottom sheet is open. */ const isOpen = openIndex > -1 const open = React.useCallback( ({index} = {}) => { setDialogIsOpen(control.id, true) // can be set to any index of `snapPoints`, but `0` is the first i.e. "open" setOpenIndex(index || 0) }, [setOpenIndex, setDialogIsOpen, control.id], ) const close = React.useCallback(cb => { if (cb && typeof cb === 'function') { closeCallback.current = cb } sheet.current?.close() }, []) useImperativeHandle( control.ref, () => ({ open, close, }), [open, close], ) const onCloseInner = React.useCallback(() => { Keyboard.dismiss() try { closeCallback.current?.() } catch (e: any) { logger.error(`Dialog closeCallback failed`, { message: e.message, }) } finally { closeCallback.current = undefined } setDialogIsOpen(control.id, false) onClose?.() setOpenIndex(-1) }, [control.id, onClose, setDialogIsOpen]) const context = React.useMemo(() => ({close}), [close]) return ( isOpen && ( {children} ) ) } export function Inner({children, style}: DialogInnerProps) { const insets = useSafeAreaInsets() return ( {children} ) } export function ScrollableInner({children, style}: DialogInnerProps) { const insets = useSafeAreaInsets() return ( {children} ) } export function Handle() { const t = useTheme() const onTouchStart = React.useCallback(() => { Keyboard.dismiss() }, []) return ( ) } export function Close() { return null }