diff --git a/app/(site)/docs/components/alert-dialog/page.tsx b/app/(site)/docs/components/alert-dialog/page.tsx new file mode 100644 index 0000000..82e07db --- /dev/null +++ b/app/(site)/docs/components/alert-dialog/page.tsx @@ -0,0 +1,397 @@ +import { ComponentPreview } from "@/components/docs/component-preview"; + +export default function Alert-dialogPage() { + return ( + \n Click me\n \n );\n}", + "language": "tsx" + } +]} + componentCode={`import * as React from "react"; +import { + View, + Text, + Pressable, + Modal, + TouchableWithoutFeedback, + Platform, + Animated, + Dimensions, + KeyboardAvoidingView, +} from "react-native"; +import { cn } from "@/lib/utils"; + +interface AlertDialogProps { + children: React.ReactNode; + className?: string; + open?: boolean; + onOpenChange?: (open: boolean) => void; +} + +interface AlertDialogTriggerProps { + children: React.ReactNode; + className?: string; + disabled?: boolean; + asChild?: boolean; +} + +interface AlertDialogContentProps { + children: React.ReactNode; + className?: string; + onInteractOutside?: () => void; +} + +interface AlertDialogHeaderProps { + className?: string; + children: React.ReactNode; +} + +interface AlertDialogFooterProps { + className?: string; + children: React.ReactNode; +} + +interface AlertDialogTitleProps { + className?: string; + children: React.ReactNode; +} + +interface AlertDialogDescriptionProps { + className?: string; + children: React.ReactNode; +} + +interface AlertDialogActionProps { + children: React.ReactElement<{ onPress?: (e: any) => void }>; + className?: string; +} + +interface AlertDialogCancelProps { + children: React.ReactElement<{ onPress?: (e: any) => void }>; + className?: string; +} + +const AlertDialogContext = React.createContext<{ + open: boolean; + setOpen: (open: boolean) => void; + handleClose?: () => void; +}>({ + open: false, + setOpen: () => {}, +}); + +const AlertDialog = React.forwardRef( + ({ children, className, open = false, onOpenChange, ...props }, ref) => { + const [internalOpen, setInternalOpen] = React.useState(false); + + const isControlled = open !== undefined; + const isOpen = isControlled ? open : internalOpen; + + const setOpen = React.useCallback( + (value: boolean) => { + if (!isControlled) { + setInternalOpen(value); + } + onOpenChange?.(value); + }, + [isControlled, onOpenChange] + ); + + return ( + + + {children} + + + ); + } +); + +AlertDialog.displayName = "AlertDialog"; + +const AlertDialogTrigger = React.forwardRef( + ( + { children, className, disabled = false, asChild = false, ...props }, + ref + ) => { + const { setOpen } = React.useContext(AlertDialogContext); + + if (asChild) { + const child = React.Children.only(children) as React.ReactElement<{ + onPress?: (e: any) => void; + ref?: React.Ref; + disabled?: boolean; + }>; + return React.cloneElement(child, { + ...props, + ref, + onPress: (e: any) => { + child.props?.onPress?.(e); + setOpen(true); + }, + disabled, + }); + } + + return ( + setOpen(true)} + accessibilityRole="button" + {...props} + > + {children} + + ); + } +); + +AlertDialogTrigger.displayName = "AlertDialogTrigger"; + +const AlertDialogContent = React.forwardRef( + ({ children, className, onInteractOutside, ...props }, ref) => { + const { open, setOpen } = React.useContext(AlertDialogContext); + const fadeAnim = React.useRef(new Animated.Value(0)).current; + const scaleAnim = React.useRef(new Animated.Value(0.95)).current; + const { height: SCREEN_HEIGHT } = Dimensions.get("window"); + const [isVisible, setIsVisible] = React.useState(open); + + React.useEffect(() => { + if (open && !isVisible) { + setIsVisible(true); + } + }, [open, isVisible]); + + React.useEffect(() => { + if (isVisible) { + Animated.parallel([ + Animated.timing(fadeAnim, { + toValue: 1, + duration: 200, + useNativeDriver: true, + }), + Animated.spring(scaleAnim, { + toValue: 1, + damping: 20, + stiffness: 300, + useNativeDriver: true, + }), + ]).start(); + } + }, [isVisible, fadeAnim, scaleAnim]); + + const handleClose = React.useCallback(() => { + Animated.parallel([ + Animated.timing(fadeAnim, { + toValue: 0, + duration: 150, + useNativeDriver: true, + }), + Animated.timing(scaleAnim, { + toValue: 0.95, + duration: 150, + useNativeDriver: true, + }), + ]).start(() => { + setIsVisible(false); + setOpen(false); + }); + }, [fadeAnim, scaleAnim, setOpen]); + + if (!isVisible) return null; + + return ( + + + { + onInteractOutside?.(); + }} + > + + + + + {children} + + + + + + + + ); + } +); + +AlertDialogContent.displayName = "AlertDialogContent"; + +const AlertDialogHeader = React.forwardRef( + ({ className, children, ...props }, ref) => ( + + {children} + + ) +); + +AlertDialogHeader.displayName = "AlertDialogHeader"; + +const AlertDialogFooter = React.forwardRef( + ({ className, children, ...props }, ref) => ( + + {children} + + ) +); + +AlertDialogFooter.displayName = "AlertDialogFooter"; + +const AlertDialogTitle = React.forwardRef( + ({ className, children, ...props }, ref) => ( + + {children} + + ) +); + +AlertDialogTitle.displayName = "AlertDialogTitle"; + +const AlertDialogDescription = React.forwardRef< + Text, + AlertDialogDescriptionProps +>(({ className, children, ...props }, ref) => ( + + {children} + +)); + +AlertDialogDescription.displayName = "AlertDialogDescription"; + +const AlertDialogAction = React.forwardRef( + ({ children, ...props }, ref) => { + const { handleClose } = React.useContext(AlertDialogContext); + + return React.cloneElement(children, { + ...children.props, + ...props, + onPress: (e: any) => { + children.props?.onPress?.(e); + handleClose?.(); + }, + }); + } +); + +AlertDialogAction.displayName = "AlertDialogAction"; + +const AlertDialogCancel = React.forwardRef( + ({ children, ...props }, ref) => { + const { handleClose } = React.useContext(AlertDialogContext); + + return React.cloneElement(children, { + ...children.props, + ...props, + onPress: (e: any) => { + children.props?.onPress?.(e); + handleClose?.(); + }, + }); + } +); + +AlertDialogCancel.displayName = "AlertDialogCancel"; + +export { + AlertDialog, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +}; +`} + previewCode={`import { Alert-dialog } from "@nativeui/ui"; + +export default function Alert-dialogDemo() { + return ( +
+ Default Alert-dialog + Delete + Outline + Secondary + Ghost + Link +
+ ); +}`} + registryName="alert-dialog" + packageName="@nativeui/ui" + /> + ); +} diff --git a/public/r/alert-dialog.json b/public/r/alert-dialog.json new file mode 100644 index 0000000..ae1bd0b --- /dev/null +++ b/public/r/alert-dialog.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "alert-dialog", + "type": "registry:component", + "title": "Alert Dialog", + "description": "An alert dialog component for React Native applications.", + "dependencies": [ + "react-native" + ], + "registryDependencies": [], + "files": [ + { + "path": "registry/alert-dialog/alert-dialog.tsx", + "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(\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 \n \n {children}\n \n \n );\n }\n);\n\nAlertDialog.displayName = \"AlertDialog\";\n\nconst AlertDialogTrigger = React.forwardRef(\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;\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 setOpen(true)}\n accessibilityRole=\"button\"\n {...props}\n >\n {children}\n \n );\n }\n);\n\nAlertDialogTrigger.displayName = \"AlertDialogTrigger\";\n\nconst AlertDialogContent = React.forwardRef(\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 \n \n {\n onInteractOutside?.();\n }}\n >\n \n \n \n \n {children}\n \n \n \n \n \n \n \n );\n }\n);\n\nAlertDialogContent.displayName = \"AlertDialogContent\";\n\nconst AlertDialogHeader = React.forwardRef(\n ({ className, children, ...props }, ref) => (\n \n {children}\n \n )\n);\n\nAlertDialogHeader.displayName = \"AlertDialogHeader\";\n\nconst AlertDialogFooter = React.forwardRef(\n ({ className, children, ...props }, ref) => (\n \n {children}\n \n )\n);\n\nAlertDialogFooter.displayName = \"AlertDialogFooter\";\n\nconst AlertDialogTitle = React.forwardRef(\n ({ className, children, ...props }, ref) => (\n \n {children}\n \n )\n);\n\nAlertDialogTitle.displayName = \"AlertDialogTitle\";\n\nconst AlertDialogDescription = React.forwardRef<\n Text,\n AlertDialogDescriptionProps\n>(({ className, children, ...props }, ref) => (\n \n {children}\n \n));\n\nAlertDialogDescription.displayName = \"AlertDialogDescription\";\n\nconst AlertDialogAction = React.forwardRef(\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(\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", + "type": "registry:component" + } + ] +} \ No newline at end of file diff --git a/registry.json b/registry.json index ced91a3..0ba9e8e 100644 --- a/registry.json +++ b/registry.json @@ -427,6 +427,20 @@ ], "dependencies": ["react-native", "@expo/vector-icons"], "registryDependencies": [] + }, + { + "name": "alert-dialog", + "type": "registry:component", + "title": "Alert Dialog", + "description": "An alert dialog component for React Native applications.", + "files": [ + { + "path": "registry/alert-dialog/alert-dialog.tsx", + "type": "registry:component" + } + ], + "dependencies": ["react-native"], + "registryDependencies": [] } ] } diff --git a/registry/alert-dialog/alert-dialog.tsx b/registry/alert-dialog/alert-dialog.tsx new file mode 100644 index 0000000..8336c27 --- /dev/null +++ b/registry/alert-dialog/alert-dialog.tsx @@ -0,0 +1,362 @@ +import * as React from "react"; +import { + View, + Text, + Pressable, + Modal, + TouchableWithoutFeedback, + Platform, + Animated, + Dimensions, + KeyboardAvoidingView, +} from "react-native"; +import { cn } from "@/lib/utils"; + +interface AlertDialogProps { + children: React.ReactNode; + className?: string; + open?: boolean; + onOpenChange?: (open: boolean) => void; +} + +interface AlertDialogTriggerProps { + children: React.ReactNode; + className?: string; + disabled?: boolean; + asChild?: boolean; +} + +interface AlertDialogContentProps { + children: React.ReactNode; + className?: string; + onInteractOutside?: () => void; +} + +interface AlertDialogHeaderProps { + className?: string; + children: React.ReactNode; +} + +interface AlertDialogFooterProps { + className?: string; + children: React.ReactNode; +} + +interface AlertDialogTitleProps { + className?: string; + children: React.ReactNode; +} + +interface AlertDialogDescriptionProps { + className?: string; + children: React.ReactNode; +} + +interface AlertDialogActionProps { + children: React.ReactElement<{ onPress?: (e: any) => void }>; + className?: string; +} + +interface AlertDialogCancelProps { + children: React.ReactElement<{ onPress?: (e: any) => void }>; + className?: string; +} + +const AlertDialogContext = React.createContext<{ + open: boolean; + setOpen: (open: boolean) => void; + handleClose?: () => void; +}>({ + open: false, + setOpen: () => {}, +}); + +const AlertDialog = React.forwardRef( + ({ children, className, open = false, onOpenChange, ...props }, ref) => { + const [internalOpen, setInternalOpen] = React.useState(false); + + const isControlled = open !== undefined; + const isOpen = isControlled ? open : internalOpen; + + const setOpen = React.useCallback( + (value: boolean) => { + if (!isControlled) { + setInternalOpen(value); + } + onOpenChange?.(value); + }, + [isControlled, onOpenChange] + ); + + return ( + + + {children} + + + ); + } +); + +AlertDialog.displayName = "AlertDialog"; + +const AlertDialogTrigger = React.forwardRef( + ( + { children, className, disabled = false, asChild = false, ...props }, + ref + ) => { + const { setOpen } = React.useContext(AlertDialogContext); + + if (asChild) { + const child = React.Children.only(children) as React.ReactElement<{ + onPress?: (e: any) => void; + ref?: React.Ref; + disabled?: boolean; + }>; + return React.cloneElement(child, { + ...props, + ref, + onPress: (e: any) => { + child.props?.onPress?.(e); + setOpen(true); + }, + disabled, + }); + } + + return ( + setOpen(true)} + accessibilityRole="button" + {...props} + > + {children} + + ); + } +); + +AlertDialogTrigger.displayName = "AlertDialogTrigger"; + +const AlertDialogContent = React.forwardRef( + ({ children, className, onInteractOutside, ...props }, ref) => { + const { open, setOpen } = React.useContext(AlertDialogContext); + const fadeAnim = React.useRef(new Animated.Value(0)).current; + const scaleAnim = React.useRef(new Animated.Value(0.95)).current; + const { height: SCREEN_HEIGHT } = Dimensions.get("window"); + const [isVisible, setIsVisible] = React.useState(open); + + React.useEffect(() => { + if (open && !isVisible) { + setIsVisible(true); + } + }, [open, isVisible]); + + React.useEffect(() => { + if (isVisible) { + Animated.parallel([ + Animated.timing(fadeAnim, { + toValue: 1, + duration: 200, + useNativeDriver: true, + }), + Animated.spring(scaleAnim, { + toValue: 1, + damping: 20, + stiffness: 300, + useNativeDriver: true, + }), + ]).start(); + } + }, [isVisible, fadeAnim, scaleAnim]); + + const handleClose = React.useCallback(() => { + Animated.parallel([ + Animated.timing(fadeAnim, { + toValue: 0, + duration: 150, + useNativeDriver: true, + }), + Animated.timing(scaleAnim, { + toValue: 0.95, + duration: 150, + useNativeDriver: true, + }), + ]).start(() => { + setIsVisible(false); + setOpen(false); + }); + }, [fadeAnim, scaleAnim, setOpen]); + + if (!isVisible) return null; + + return ( + + + { + onInteractOutside?.(); + }} + > + + + + + {children} + + + + + + + + ); + } +); + +AlertDialogContent.displayName = "AlertDialogContent"; + +const AlertDialogHeader = React.forwardRef( + ({ className, children, ...props }, ref) => ( + + {children} + + ) +); + +AlertDialogHeader.displayName = "AlertDialogHeader"; + +const AlertDialogFooter = React.forwardRef( + ({ className, children, ...props }, ref) => ( + + {children} + + ) +); + +AlertDialogFooter.displayName = "AlertDialogFooter"; + +const AlertDialogTitle = React.forwardRef( + ({ className, children, ...props }, ref) => ( + + {children} + + ) +); + +AlertDialogTitle.displayName = "AlertDialogTitle"; + +const AlertDialogDescription = React.forwardRef< + Text, + AlertDialogDescriptionProps +>(({ className, children, ...props }, ref) => ( + + {children} + +)); + +AlertDialogDescription.displayName = "AlertDialogDescription"; + +const AlertDialogAction = React.forwardRef( + ({ children, ...props }, ref) => { + const { handleClose } = React.useContext(AlertDialogContext); + + return React.cloneElement(children, { + ...children.props, + ...props, + onPress: (e: any) => { + children.props?.onPress?.(e); + handleClose?.(); + }, + }); + } +); + +AlertDialogAction.displayName = "AlertDialogAction"; + +const AlertDialogCancel = React.forwardRef( + ({ children, ...props }, ref) => { + const { handleClose } = React.useContext(AlertDialogContext); + + return React.cloneElement(children, { + ...children.props, + ...props, + onPress: (e: any) => { + children.props?.onPress?.(e); + handleClose?.(); + }, + }); + } +); + +AlertDialogCancel.displayName = "AlertDialogCancel"; + +export { + AlertDialog, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +};