import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { useColor } from '@/hooks/useColor'; import React, { useEffect } from 'react'; import { Modal, StyleSheet, TouchableWithoutFeedback, View, ViewStyle, } from 'react-native'; import Animated, { runOnJS, useAnimatedStyle, useSharedValue, withTiming, } from 'react-native-reanimated'; export type AlertDialogProps = { isVisible: boolean; onClose: () => void; title?: string; description?: string; children?: React.ReactNode; confirmText?: string; cancelText?: string; onConfirm?: () => void; onCancel?: () => void; dismissible?: boolean; showCancelButton?: boolean; style?: ViewStyle; }; // A simple card-like dialog overlay with fade-in animation similar to BottomSheet's backdrop export function AlertDialog({ isVisible, onClose, title, description, children, confirmText = 'OK', cancelText = 'Cancel', onConfirm, onCancel, dismissible = true, showCancelButton = true, style, }: AlertDialogProps) { const cardColor = useColor('card'); const [modalVisible, setModalVisible] = React.useState(false); const backdropOpacity = useSharedValue(0); const cardOpacity = useSharedValue(0); useEffect(() => { if (isVisible) { setModalVisible(true); backdropOpacity.value = withTiming(1, { duration: 250 }); cardOpacity.value = withTiming(1, { duration: 200 }); } else { backdropOpacity.value = withTiming(0, { duration: 250 }, (finished) => { if (finished) { runOnJS(setModalVisible)(false); } }); cardOpacity.value = withTiming(0, { duration: 200 }); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [isVisible]); const rBackdropStyle = useAnimatedStyle(() => ({ opacity: backdropOpacity.value, })); const rCardFadeStyle = useAnimatedStyle(() => ({ opacity: cardOpacity.value, })); const animateClose = () => { 'worklet'; backdropOpacity.value = withTiming(0, { duration: 300 }, (finished) => { if (finished) { runOnJS(onClose)(); } }); cardOpacity.value = withTiming(0, { duration: 200 }); }; const handleBackdropPress = () => { if (dismissible) { animateClose(); if (onCancel) onCancel(); } }; const handleCancel = () => { if (onCancel) onCancel(); animateClose(); }; const handleConfirm = () => { if (onConfirm) onConfirm(); animateClose(); }; return ( {/* Non-animated outer wrapper: handles rounded corners and clipping */} {/* Only fade the inner content */} {(title || description) && ( {title ? {title} : null} {description ? ( {description} ) : null} )} {children ? {children} : null} {showCancelButton && ( )} ); } const styles = StyleSheet.create({ backdrop: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.8)', alignItems: 'center', justifyContent: 'center', padding: 24, }, backdropTouchableArea: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, }, // Rounded corners and clipping consolidated here (non-animated) roundedWrapper: { width: '100%', borderRadius: 16, overflow: 'hidden', }, // Inner content can render freely (only opacity is animated) innerContent: { width: '100%', }, }); export function useAlertDialog() { const [isVisible, setIsVisible] = React.useState(false); const open = React.useCallback(() => setIsVisible(true), []); const close = React.useCallback(() => setIsVisible(false), []); const toggle = React.useCallback(() => setIsVisible((v) => !v), []); return { isVisible, open, close, toggle }; }