"content": "import * as React from \"react\";\nimport {\n View,\n Text,\n Pressable,\n Modal,\n TouchableWithoutFeedback,\n Platform,\n Animated,\n Dimensions,\n KeyboardAvoidingView,\n} from \"react-native\";\nimport { cn } from \"@/lib/utils\";\n\ninterface AlertDialogProps {\n children: React.ReactNode;\n className?: string;\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n}\n\ninterface AlertDialogTriggerProps {\n children: React.ReactNode;\n className?: string;\n disabled?: boolean;\n asChild?: boolean;\n}\n\ninterface AlertDialogContentProps {\n children: React.ReactNode;\n className?: string;\n onInteractOutside?: () => void;\n}\n\ninterface AlertDialogHeaderProps {\n className?: string;\n children: React.ReactNode;\n}\n\ninterface AlertDialogFooterProps {\n className?: string;\n children: React.ReactNode;\n}\n\ninterface AlertDialogTitleProps {\n className?: string;\n children: React.ReactNode;\n}\n\ninterface AlertDialogDescriptionProps {\n className?: string;\n children: React.ReactNode;\n}\n\ninterface AlertDialogActionProps {\n children: React.ReactElement<{ onPress?: (e: any) => void }>;\n className?: string;\n}\n\ninterface AlertDialogCancelProps {\n children: React.ReactElement<{ onPress?: (e: any) => void }>;\n className?: string;\n}\n\nconst AlertDialogContext = React.createContext<{\n open: boolean;\n setOpen: (open: boolean) => void;\n handleClose?: () => void;\n}>({\n open: false,\n setOpen: () => { },\n});\n\nconst AlertDialog = React.forwardRef<View, AlertDialogProps>(\n ({ children, className, open = false, onOpenChange, ...props }, ref) => {\n const [internalOpen, setInternalOpen] = React.useState(false);\n\n const isControlled = open !== undefined;\n const isOpen = isControlled ? open : internalOpen;\n\n const setOpen = React.useCallback(\n (value: boolean) => {\n if (!isControlled) {\n setInternalOpen(value);\n }\n onOpenChange?.(value);\n },\n [isControlled, onOpenChange]\n );\n\n return (\n <AlertDialogContext.Provider value={{ open: isOpen, setOpen }}>\n <View ref={ref} className={cn(\"\", className)} {...props}>\n {children}\n </View>\n </AlertDialogContext.Provider>\n );\n }\n);\n\nAlertDialog.displayName = \"AlertDialog\";\n\nconst AlertDialogTrigger = React.forwardRef<View, AlertDialogTriggerProps>(\n (\n { children, className, disabled = false, asChild = false, ...props },\n ref\n ) => {\n const { setOpen } = React.useContext(AlertDialogContext);\n\n if (asChild) {\n const child = React.Children.only(children) as React.ReactElement<{\n onPress?: (e: any) => void;\n ref?: React.Ref<any>;\n disabled?: boolean;\n }>;\n return React.cloneElement(child, {\n ...props,\n ref,\n onPress: (e: any) => {\n child.props?.onPress?.(e);\n setOpen(true);\n },\n disabled,\n });\n }\n\n return (\n <Pressable\n ref={ref}\n className={cn(\"\", className)}\n disabled={disabled}\n onPress={() => setOpen(true)}\n accessibilityRole=\"button\"\n {...props}\n >\n {children}\n </Pressable>\n );\n }\n);\n\nAlertDialogTrigger.displayName = \"AlertDialogTrigger\";\n\nconst AlertDialogContent = React.forwardRef<View, AlertDialogContentProps>(\n ({ children, className, onInteractOutside, ...props }, ref) => {\n const { open, setOpen } = React.useContext(AlertDialogContext);\n const fadeAnim = React.useRef(new Animated.Value(0)).current;\n const scaleAnim = React.useRef(new Animated.Value(0.95)).current;\n const { height: SCREEN_HEIGHT } = Dimensions.get(\"window\");\n const [isVisible, setIsVisible] = React.useState(open);\n\n React.useEffect(() => {\n if (open && !isVisible) {\n setIsVisible(true);\n }\n }, [open, isVisible]);\n\n React.useEffect(() => {\n if (isVisible) {\n Animated.parallel([\n Animated.timing(fadeAnim, {\n toValue: 1,\n duration: 200,\n useNativeDriver: true,\n }),\n Animated.spring(scaleAnim, {\n toValue: 1,\n damping: 20,\n stiffness: 300,\n useNativeDriver: true,\n }),\n ]).start();\n }\n }, [isVisible, fadeAnim, scaleAnim]);\n\n const handleClose = React.useCallback(() => {\n Animated.parallel([\n Animated.timing(fadeAnim, {\n toValue: 0,\n duration: 150,\n useNativeDriver: true,\n }),\n Animated.timing(scaleAnim, {\n toValue: 0.95,\n duration: 150,\n useNativeDriver: true,\n }),\n ]).start(() => {\n setIsVisible(false);\n setOpen(false);\n });\n }, [fadeAnim, scaleAnim, setOpen]);\n\n if (!isVisible) return null;\n\n return (\n <AlertDialogContext.Provider\n value={{ open: isVisible, setOpen, handleClose }}\n >\n <Modal\n visible={isVisible}\n transparent\n statusBarTranslucent\n animationType=\"none\"\n onRequestClose={handleClose}\n >\n <TouchableWithoutFeedback\n onPress={() => {\n onInteractOutside?.();\n }}\n >\n <Animated.View\n className=\"flex-1 justify-center items-center bg-black/50\"\n style={{ opacity: fadeAnim }}\n >\n <TouchableWithoutFeedback>\n <KeyboardAvoidingView\n behavior={Platform.OS === \"ios\" ? \"padding\" : undefined}\n keyboardVerticalOffset={\n Platform.OS === \"ios\" ? -SCREEN_HEIGHT * 0.2 : 0\n }\n >\n <Animated.View\n ref={ref}\n className={cn(\n \"bg-background m-6 rounded-2xl\",\n \"w-[85%] max-w-sm\",\n Platform.OS === \"ios\"\n ? \"ios:shadow-xl\"\n : \"android:elevation-8\",\n className\n )}\n style={{\n transform: [{ scale: scaleAnim }],\n }}\n {...props}\n >\n {children}\n </Animated.View>\n </KeyboardAvoidingView>\n </TouchableWithoutFeedback>\n </Animated.View>\n </TouchableWithoutFeedback>\n </Modal>\n </AlertDialogContext.Provider>\n );\n }\n);\n\nAlertDialogContent.displayName = \"AlertDialogContent\";\n\nconst AlertDialogHeader = React.forwardRef<View, AlertDialogHeaderProps>(\n ({ className, children, ...props }, ref) => (\n <View\n ref={ref}\n className={cn(\"flex-col gap-2 p-6 pb-0\", className)}\n {...props}\n >\n {children}\n </View>\n )\n);\n\nAlertDialogHeader.displayName = \"AlertDialogHeader\";\n\nconst AlertDialogFooter = React.forwardRef<View, AlertDialogFooterProps>(\n ({ className, children, ...props }, ref) => (\n <View\n ref={ref}\n className={cn(\n \"flex-row justify-end items-center gap-3 p-6 pt-4\",\n className\n )}\n {...props}\n >\n {children}\n </View>\n )\n);\n\nAlertDialogFooter.displayName = \"AlertDialogFooter\";\n\nconst AlertDialogTitle = React.forwardRef<Text, AlertDialogTitleProps>(\n ({ className, children, ...props }, ref) => (\n <Text\n ref={ref}\n className={cn(\n \"text-foreground text-xl font-semibold leading-none tracking-tight text-center\",\n className\n )}\n {...props}\n >\n {children}\n </Text>\n )\n);\n\nAlertDialogTitle.displayName = \"AlertDialogTitle\";\n\nconst AlertDialogDescription = React.forwardRef<\n Text,\n AlertDialogDescriptionProps\n>(({ className, children, ...props }, ref) => (\n <Text\n ref={ref}\n className={cn(\n \"text-muted-foreground text-base mt-2 text-center\",\n className\n )}\n {...props}\n >\n {children}\n </Text>\n));\n\nAlertDialogDescription.displayName = \"AlertDialogDescription\";\n\nconst AlertDialogAction = React.forwardRef<View, AlertDialogActionProps>(\n ({ children, ...props }, ref) => {\n const { handleClose } = React.useContext(AlertDialogContext);\n\n return React.cloneElement(children, {\n ...children.props,\n ...props,\n onPress: (e: any) => {\n children.props?.onPress?.(e);\n handleClose?.();\n },\n });\n }\n);\n\nAlertDialogAction.displayName = \"AlertDialogAction\";\n\nconst AlertDialogCancel = React.forwardRef<View, AlertDialogCancelProps>(\n ({ children, ...props }, ref) => {\n const { handleClose } = React.useContext(AlertDialogContext);\n\n return React.cloneElement(children, {\n ...children.props,\n ...props,\n onPress: (e: any) => {\n children.props?.onPress?.(e);\n handleClose?.();\n },\n });\n }\n);\n\nAlertDialogCancel.displayName = \"AlertDialogCancel\";\n\nexport {\n AlertDialog,\n AlertDialogTrigger,\n AlertDialogContent,\n AlertDialogHeader,\n AlertDialogFooter,\n AlertDialogTitle,\n AlertDialogDescription,\n AlertDialogAction,\n AlertDialogCancel,\n};\n",
0 commit comments