{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "alert-dialog", "title": "Integrated Alert Dialog", "description": "Flat-props AlertDialog built on shadcn's base-ui AlertDialog: title / description slots, confirm / cancel footer with async handlers, controlled or uncontrolled open state, destructive variant sugar, and a full footer override.", "registryDependencies": [ "alert-dialog", "@easy-shadcn/async-button" ], "files": [ { "path": "registry/ui/alert-dialog.tsx", "content": "\"use client\";\n\nimport type { AlertDialog as AlertDialogPrimitive } from \"@base-ui/react/alert-dialog\";\nimport type { ClassValue } from \"clsx\";\nimport type React from \"react\";\nimport {\n type ComponentProps,\n type ReactElement,\n type ReactNode,\n useState,\n} from \"react\";\nimport {\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialog as AlertDialogRoot,\n AlertDialogTitle,\n AlertDialogTrigger,\n} from \"@/components/ui/alert-dialog\";\nimport { cn } from \"@/lib/utils\";\nimport { AsyncButton } from \"./async-button\";\n\n// Mirrors React's own rendering rules: null/undefined/boolean render nothing,\n// but valid falsy nodes like 0 and \"\" must not be swallowed.\nconst hasNode = (node: ReactNode): boolean =>\n node !== null && node !== undefined && typeof node !== \"boolean\";\n\nexport interface AlertDialogProps\n extends Omit<\n AlertDialogPrimitive.Root.Props,\n \"children\" | \"onOpenChange\" | \"render\"\n > {\n cancelProps?: Omit<\n ComponentProps,\n \"children\" | \"onClick\"\n >;\n cancelText?: ReactNode;\n className?: ClassValue;\n confirmProps?: Omit<\n ComponentProps,\n \"children\" | \"onClick\"\n >;\n confirmText?: ReactNode;\n description?: ReactNode;\n descriptionClassName?: ClassValue;\n footer?: ReactNode;\n footerClassName?: ClassValue;\n headerClassName?: ClassValue;\n onCancel?: () => void | Promise;\n onConfirm?: () => void | Promise;\n onOpenChange?: (open: boolean) => void;\n showCancel?: boolean;\n size?: \"default\" | \"sm\";\n title?: ReactNode;\n titleClassName?: ClassValue;\n trigger?: ReactElement;\n variant?: \"default\" | \"destructive\";\n}\n\nexport const AlertDialog: React.FC = ({\n open,\n defaultOpen,\n onOpenChange,\n trigger,\n title,\n titleClassName,\n description,\n descriptionClassName,\n headerClassName,\n cancelText,\n confirmText,\n showCancel = true,\n onCancel,\n onConfirm,\n cancelProps,\n confirmProps,\n footer,\n footerClassName,\n variant = \"default\",\n size,\n className,\n ...rootProps\n}) => {\n const [internalOpen, setInternalOpen] = useState(defaultOpen ?? false);\n const currentOpen = open === undefined ? internalOpen : open;\n\n const handleOpenChange = (next: boolean) => {\n if (open === undefined) {\n setInternalOpen(next);\n }\n onOpenChange?.(next);\n };\n\n const close = () => handleOpenChange(false);\n\n const confirmVariant =\n confirmProps?.variant ??\n (variant === \"destructive\" ? \"destructive\" : undefined);\n\n return (\n \n {trigger && }\n \n {(hasNode(title) || hasNode(description)) && (\n \n {hasNode(title) && (\n \n {title}\n \n )}\n {hasNode(description) && (\n }\n >\n {description}\n \n )}\n \n )}\n \n {hasNode(footer) ? (\n footer\n ) : (\n <>\n {showCancel && (\n {\n await onCancel?.();\n close();\n }}\n variant=\"outline\"\n {...cancelProps}\n >\n {cancelText ?? \"Cancel\"}\n \n )}\n {\n await onConfirm?.();\n close();\n }}\n {...confirmProps}\n variant={confirmVariant}\n >\n {confirmText ?? \"OK\"}\n \n \n )}\n \n \n \n );\n};\n\nexport default AlertDialog;\n", "type": "registry:component", "target": "components/easy/alert-dialog.tsx" } ], "type": "registry:component" }