import { useCallback, useEffect, useMemo, useRef } from "react"; import type { ReactNode } from "react"; import { createPortal } from "react-dom"; import { Modal, Platform, Pressable, ScrollView, Text, View, } from "react-native"; import { StyleSheet, UnistylesRuntime, useUnistyles } from "react-native-unistyles"; import { getOverlayRoot, OVERLAY_Z } from "../lib/overlay-root"; import { BottomSheetModal, BottomSheetBackdrop, BottomSheetScrollView, type BottomSheetBackgroundProps, } from "@gorhom/bottom-sheet"; import { X } from "lucide-react-native"; const styles = StyleSheet.create((theme) => ({ desktopOverlay: { ...StyleSheet.absoluteFillObject, backgroundColor: "rgba(0,0,0,0.55)", justifyContent: "center", alignItems: "center", padding: theme.spacing[6], zIndex: OVERLAY_Z.modal, pointerEvents: "auto" as const, }, desktopCard: { width: "100%", maxWidth: 520, maxHeight: "85%", backgroundColor: theme.colors.surface1, borderRadius: theme.borderRadius.xl, borderWidth: 1, borderColor: theme.colors.surface2, overflow: "hidden", }, header: { paddingHorizontal: theme.spacing[6], paddingVertical: theme.spacing[4], flexDirection: "row", alignItems: "center", justifyContent: "space-between", borderBottomWidth: 1, borderBottomColor: theme.colors.surface2, }, title: { color: theme.colors.foreground, fontSize: theme.fontSize.lg, fontWeight: theme.fontWeight.semibold, }, closeButton: { padding: theme.spacing[2], borderRadius: theme.borderRadius.lg, backgroundColor: theme.colors.surface2, }, desktopScroll: { flex: 1, }, desktopContent: { padding: theme.spacing[6], gap: theme.spacing[4], }, bottomSheetHandle: { backgroundColor: theme.colors.surface2, }, bottomSheetHeader: { paddingHorizontal: theme.spacing[6], paddingTop: theme.spacing[4], paddingBottom: theme.spacing[3], flexDirection: "row", justifyContent: "space-between", alignItems: "center", borderBottomWidth: 1, borderBottomColor: theme.colors.surface2, }, bottomSheetContent: { padding: theme.spacing[6], gap: theme.spacing[4], }, })); function SheetBackground({ style }: BottomSheetBackgroundProps) { const { theme } = useUnistyles(); return ( ); } export interface AdaptiveModalSheetProps { title: string; visible: boolean; onClose: () => void; children: ReactNode; snapPoints?: string[]; testID?: string; } export function AdaptiveModalSheet({ title, visible, onClose, children, snapPoints, testID, }: AdaptiveModalSheetProps) { const { theme } = useUnistyles(); const isMobile = UnistylesRuntime.breakpoint === "xs" || UnistylesRuntime.breakpoint === "sm"; const sheetRef = useRef(null); const resolvedSnapPoints = useMemo(() => snapPoints ?? ["65%", "90%"], [snapPoints]); useEffect(() => { if (!isMobile) return; if (visible) { sheetRef.current?.present(); } else { sheetRef.current?.dismiss(); } }, [visible, isMobile]); const handleSheetChange = useCallback( (index: number) => { if (index === -1) { onClose(); } }, [onClose] ); const renderBackdrop = useCallback( (props: React.ComponentProps) => ( ), [] ); if (isMobile) { return ( {title} {children} ); } const desktopContent = ( {title} {children} ); // On web, use portal to overlay root for consistent stacking with toasts if (Platform.OS === "web" && typeof document !== "undefined") { if (!visible) return null; return createPortal(desktopContent, getOverlayRoot()); } return ( {desktopContent} ); }