--- title: Dialog description: A modal window overlaid on the primary window, rendering content that requires user interaction. --- ## Installation CLI Manual ```bash npx lightswind@latest add dialog ``` Copy and paste the following code into your project. ```tsx import * as React from "react"; import * as ReactDOM from "react-dom"; import { cn } from "@/components/lib/utils"; // Assuming a utility for classnames import { X } from "lucide-react"; import { motion, AnimatePresence, HTMLMotionProps } from "framer-motion"; // Helper for cn if not provided const dummyCn = (...inputs) => { return inputs.filter(Boolean).join(' '); }; const cnFunction = typeof cn !== 'undefined' ? cn : dummyCn; interface DialogContextType { open: boolean; setOpen: React.Dispatch>; } const DialogContext = React.createContext(undefined); interface DialogProps { children: React.ReactNode; defaultOpen?: boolean; open?: boolean; onOpenChange?: (open: boolean) => void; } const Dialog: React.FC = ({ children, defaultOpen = false, open: controlledOpen, onOpenChange, }) => { const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen); const isControlled = controlledOpen !== undefined; const open = isControlled ? controlledOpen : uncontrolledOpen; const setOpen = React.useCallback((value: boolean | ((prev: boolean) => boolean)) => { if (!isControlled) { setUncontrolledOpen(value); } if (onOpenChange) { const newValue = typeof value === "function" ? value(open) : value; onOpenChange(newValue); } }, [isControlled, onOpenChange, open]); return ( {children} ); }; interface DialogTriggerProps { children: React.ReactNode; asChild?: boolean; } const DialogTrigger = React.forwardRef>( ({ children, asChild = false, ...props }, ref) => { const context = React.useContext(DialogContext); if (!context) { throw new Error("DialogTrigger must be used within a Dialog"); } const { setOpen } = context; const handleClick = (e: React.MouseEvent) => { e.preventDefault(); setOpen(true); if (props.onClick) { props.onClick(e); } }; const { onClick, ...otherProps } = props; if (asChild) { return ( {React.Children.map(children, child => { if (React.isValidElement(child)) { const element = child as React.ReactElement; return React.cloneElement(element, { ...element.props }); } return child; })} ); } return ( {children} ); } ); DialogTrigger.displayName = "DialogTrigger"; type OmittedDialogContentHTMLAttributes = Omit, 'onAnimationStart' | 'onAnimationEnd' | 'onAnimationIteration' | 'onTransitionEnd' | 'onDrag' | 'onDragEnd' | 'onDragEnter' | 'onDragExit' | 'onDragLeave' | 'onDragOver' | 'onDragStart' | 'onDrop' | 'onMouseDown' | 'onMouseEnter' | 'onMouseLeave' | 'onMouseMove' | 'onMouseOut' | 'onMouseOver' | 'onMouseUp' | 'onTouchCancel' | 'onTouchEnd' | 'onTouchMove' | 'onTouchStart' | 'onPointerDown' | 'onPointerMove' | 'onPointerUp' | 'onPointerCancel' | 'onPointerEnter' | 'onPointerLeave' | 'onPointerOver' | 'onPointerOut' | 'onGotPointerCapture' | 'onLostPointerCapture' >; // Define Props Interface interface DialogContentProps extends OmittedDialogContentHTMLAttributes { hideCloseButton?: boolean; } const DialogContent = React.forwardRef( ({ className, children, hideCloseButton, ...props }, ref) => { const context = React.useContext(DialogContext); if (!context) { throw new Error("DialogContent must be used within a Dialog"); } const { open, setOpen } = context; const [mounted, setMounted] = React.useState(false); React.useEffect(() => { setMounted(true); }, []); React.useEffect(() => { if (open) { document.body.style.overflow = "hidden"; } else { document.body.style.overflow = ""; } return () => { document.body.style.overflow = ""; }; }, [open]); if (!mounted) return null; return ReactDOM.createPortal( {open && ( {/* Backdrop Overlay */} setOpen(false)} /> {/* Dialog Content */} } > {children} {!hideCloseButton && ( setOpen(false)} className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none" aria-label="Close dialog" > Close )} )} , document.body ); } ); DialogContent.displayName = "DialogContent"; const DialogHeader = React.forwardRef>( ({ className, ...props }, ref) => ( ) ); DialogHeader.displayName = "DialogHeader"; const DialogTitle = React.forwardRef>( ({ className, ...props }, ref) => ( ) ); DialogTitle.displayName = "DialogTitle"; const DialogDescription = React.forwardRef>( ({ className, ...props }, ref) => ( ) ); DialogDescription.displayName = "DialogDescription"; const DialogFooter = React.forwardRef>( ({ className, ...props }, ref) => ( ) ); DialogFooter.displayName = "DialogFooter"; const DialogClose = React.forwardRef>( ({ className, ...props }, ref) => { const context = React.useContext(DialogContext); const setOpen = context ? context.setOpen : () => { }; return ( { setOpen(false); props.onClick?.(e); }} {...props} /> ); } ); DialogClose.displayName = "DialogClose"; export { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose }; ``` ## Usage ```tsx import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription } from "@/components/lightswind/dialog" ``` ```tsx Show Dialog Dialog Title Dialog Description ```