import * as React from 'react'; import { Modal, View, Text, Pressable, Animated } from 'react-native'; import { cn } from '@/lib/utils'; interface DialogProps { open?: boolean; onOpenChange?: (open: boolean) => void; children: React.ReactNode; } interface DialogContentProps { children: React.ReactNode; className?: string; } interface DialogHeaderProps { children: React.ReactNode; className?: string; } interface DialogTitleProps { children: React.ReactNode; className?: string; } interface DialogDescriptionProps { children: React.ReactNode; className?: string; } interface DialogFooterProps { children: React.ReactNode; className?: string; } const DialogContext = React.createContext<{ open: boolean; onOpenChange: (open: boolean) => void; } | null>(null); function useDialog() { const context = React.useContext(DialogContext); if (!context) { throw new Error('Dialog components must be used within Dialog'); } return context; } export function Dialog({ open: controlledOpen, onOpenChange: controlledOnOpenChange, children }: DialogProps) { const [internalOpen, setInternalOpen] = React.useState(false); const hasDialogTrigger = React.Children.toArray(children).some( (child) => React.isValidElement(child) && child.type === DialogTrigger ); const open = controlledOpen !== undefined ? controlledOpen : internalOpen; const onOpenChange = controlledOnOpenChange || setInternalOpen; return ( {children} ); } export function DialogTrigger({ children }: { children: React.ReactNode }) { const { onOpenChange } = useDialog(); if (React.isValidElement(children)) { return React.cloneElement(children as React.ReactElement, { onPress: () => onOpenChange(true), }); } return ( onOpenChange(true)}> {children} ); } export function DialogContent({ children, className }: DialogContentProps) { const { open, onOpenChange } = useDialog(); // DONE: Track visible state separately for animation const [visible, setVisible] = React.useState(false); const scaleAnim = React.useRef(new Animated.Value(0.9)).current; const opacityAnim = React.useRef(new Animated.Value(0)).current; React.useEffect(() => { if (open) { // Show modal first setVisible(true); // Then animate in Animated.parallel([ Animated.spring(scaleAnim, { toValue: 1, useNativeDriver: true, tension: 80, friction: 10, }), Animated.timing(opacityAnim, { toValue: 1, duration: 200, useNativeDriver: true, }), ]).start(); } else { // Animate out first Animated.parallel([ Animated.timing(scaleAnim, { toValue: 0.9, duration: 150, useNativeDriver: true, }), Animated.timing(opacityAnim, { toValue: 0, duration: 150, useNativeDriver: true, }), ]).start(() => { // Then hide modal after animation completes setVisible(false); }); } }, [open]); if (!visible) return null; return ( onOpenChange(false)} > onOpenChange(false)} className="flex-1 bg-black/50 items-center justify-center p-4" > e.stopPropagation()} className={cn( 'bg-white rounded-lg p-6 shadow-lg', className )} > {children} ); } export function DialogHeader({ children, className }: DialogHeaderProps) { return ( {children} ); } export function DialogTitle({ children, className }: DialogTitleProps) { return ( {children} ); } export function DialogDescription({ children, className }: DialogDescriptionProps) { return ( {children} ); } export function DialogFooter({ children, className }: DialogFooterProps) { return ( {children} ); } export function DialogClose({ children }: { children: React.ReactNode }) { const { onOpenChange } = useDialog(); if (React.isValidElement(children)) { return React.cloneElement(children as React.ReactElement, { onPress: () => onOpenChange(false), }); } return ( onOpenChange(false)}> {children} ); }