{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "dialog", "title": "Dialog (Base UI)", "description": "Animated modal dialog with spring-based scale and opacity transitions. Base UI flavor.", "dependencies": [ "framer-motion", "@base-ui/react" ], "registryDependencies": [ "https://zeron-ui.vercel.app/r/surfaces.json", "utils", "https://zeron-ui.vercel.app/r/springs.json", "https://zeron-ui.vercel.app/r/shape-context.json", "https://zeron-ui.vercel.app/r/surface-context.json", "https://zeron-ui.vercel.app/r/surface-classes.json", "https://zeron-ui.vercel.app/r/icon-context.json", "https://zeron-ui.vercel.app/r/button.json", "https://zeron-ui.vercel.app/r/portal-container-context.json" ], "files": [ { "path": "src/components/ui/dialog.tsx", "content": "\"use client\";\n\nimport {\n forwardRef,\n type ReactNode,\n type HTMLAttributes,\n} from \"react\";\nimport { Dialog as DialogPrimitive } from \"@base-ui/react/dialog\";\nimport { motion } from \"framer-motion\";\nimport { cn } from \"@/lib/utils\";\nimport { useIcon } from \"@/lib/icon-context\";\nimport { spring } from \"@/lib/springs\";\nimport { useShape } from \"@/lib/shape-context\";\nimport {\n resolveSurface,\n SurfaceProvider,\n useSurface,\n} from \"@/lib/surface-context\";\nimport { surfaceClasses } from \"@/lib/surface-classes\";\nimport { Button } from \"@/components/ui/button\";\nimport { usePortalContainer } from \"@/lib/portal-container-context\";\n\ninterface DialogProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n modal?: boolean;\n children?: ReactNode;\n}\n\nfunction Dialog({\n children,\n open,\n defaultOpen,\n onOpenChange,\n modal,\n}: DialogProps) {\n // Base UI's Root handles controlled/uncontrolled state internally. We only\n // narrow the (open, eventDetails) callback to (open) for our public prop.\n return (\n onOpenChange?.(next)}\n modal={modal}\n >\n {children}\n \n );\n}\n\nconst DialogTrigger = DialogPrimitive.Trigger;\nconst DialogClose = DialogPrimitive.Close;\n\ninterface DialogContentProps extends HTMLAttributes {\n size?: \"sm\" | \"lg\";\n /** Portal target. When set, the overlay and panel render inside this element\n * (positioned `absolute`) instead of covering the viewport (`fixed`). Pair\n * with a `position: relative; overflow: hidden` container — and usually\n * `` — to scope a dialog to a bounded region, e.g. a\n * docs preview. Defaults to the document body / full-viewport behaviour. */\n container?: HTMLElement | null;\n}\n\nconst DialogContent = forwardRef(\n ({ className, children, size = \"sm\", container, ...props }, ref) => {\n const XIcon = useIcon(\"x\");\n const shape = useShape();\n const substrate = useSurface();\n const dialogSurface = resolveSurface(substrate, \"overlay\");\n const ambientPortal = usePortalContainer();\n\n // No `if (!open) return null` here — Base UI's ``\n // handles mount/unmount itself, and waits for the framer-motion opacity\n // tween below to finish (via `element.getAnimations()`) before unmounting.\n // Returning null early would short-circuit the closing animation.\n return (\n \n {\n const exiting = state.transitionStatus === \"ending\";\n const {\n style: _style,\n onDrag: _onDrag,\n onDragStart: _onDragStart,\n onDragEnd: _onDragEnd,\n onAnimationStart: _onAnimationStart,\n onAnimationEnd: _onAnimationEnd,\n onAnimationIteration: _onAnimationIteration,\n ...rest\n } = backdropProps as React.HTMLAttributes;\n return (\n \n );\n }}\n />\n {\n const exiting = state.transitionStatus === \"ending\";\n const {\n style: baseStyle,\n onDrag: _onDrag,\n onDragStart: _onDragStart,\n onDragEnd: _onDragEnd,\n onAnimationStart: _onAnimationStart,\n onAnimationEnd: _onAnimationEnd,\n onAnimationIteration: _onAnimationIteration,\n ...rest\n } = popupProps as React.HTMLAttributes;\n return (\n ` props (className,\n // event handlers, data-*, etc.) land on the visible motion.div.\n {...(props as Omit<\n React.HTMLAttributes,\n | \"onDrag\"\n | \"onDragStart\"\n | \"onDragEnd\"\n | \"onAnimationStart\"\n | \"onAnimationEnd\"\n | \"onAnimationIteration\"\n >)}\n className={cn(\n container ? \"absolute\" : \"fixed\",\n \"left-1/2 top-1/2 z-popover w-[calc(100%-2rem)]\",\n surfaceClasses(dialogSurface, \"overlay\"),\n \"p-6 focus:outline-none\",\n size === \"sm\" && \"max-w-[400px]\",\n size === \"lg\" && \"max-w-[540px]\",\n shape.container,\n className\n )}\n style={{\n ...(baseStyle as React.CSSProperties | undefined),\n ...(props.style as React.CSSProperties | undefined),\n }}\n initial={{ opacity: 0, scale: 0.97, x: \"-50%\", y: \"-50%\" }}\n animate={{\n opacity: exiting ? 0 : 1,\n scale: exiting ? 0.97 : 1,\n x: \"-50%\",\n y: \"-50%\",\n }}\n transition={exiting ? spring.slow.exit : spring.slow}\n >\n \n {children}\n \n \n Close\n \n }\n />\n \n \n );\n }}\n />\n \n );\n }\n);\nDialogContent.displayName = \"DialogContent\";\n\nfunction DialogHeader({ className, ...props }: HTMLAttributes) {\n return (\n \n );\n}\n\nfunction DialogFooter({ className, ...props }: HTMLAttributes) {\n return (\n \n );\n}\n\nconst DialogTitle = forwardRef<\n HTMLHeadingElement,\n HTMLAttributes\n>(({ className, ...props }, ref) => (\n \n));\nDialogTitle.displayName = \"DialogTitle\";\n\nconst DialogDescription = forwardRef<\n HTMLParagraphElement,\n HTMLAttributes\n>(({ className, ...props }, ref) => (\n \n));\nDialogDescription.displayName = \"DialogDescription\";\n\nexport {\n Dialog,\n DialogTrigger,\n DialogContent,\n DialogHeader,\n DialogFooter,\n DialogTitle,\n DialogDescription,\n DialogClose,\n};\n", "type": "registry:ui", "target": "components/ui/dialog.tsx" } ], "type": "registry:ui" }