{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "dialog", "type": "registry:ui", "title": "Dialog", "description": "A dialog component that can be used to display a modal window.", "files": [ { "path": "registry/tra-kit/components/dialog.tsx", "content": "/* eslint-disable tailwindcss/no-custom-classname */\r\nimport React, {\r\n Fragment,\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useEffect,\r\n useState,\r\n} from 'react';\r\nimport { createPortal } from 'react-dom';\r\nimport { cva } from 'class-variance-authority';\r\nimport { XIcon } from '@phosphor-icons/react';\r\nimport type { VariantProps } from 'class-variance-authority';\r\nimport { cn, preventScrollShift } from '@/lib/utils';\r\nimport Button from '@/components/ui/button';\r\n\r\nexport type DialogSize = 'sm' | 'default' | 'lg' | 'xl' | 'full';\r\n\r\ninterface DialogContextProps {\r\n open: boolean;\r\n setOpen: (open: boolean) => void;\r\n size?: DialogSize;\r\n disableBackdropClick?: boolean;\r\n disableEscapeKeyDown?: boolean;\r\n showCloseButton?: boolean;\r\n}\r\n\r\nconst DialogContext = createContext(undefined);\r\n\r\nconst useDialog = () => {\r\n const context = useContext(DialogContext);\r\n if (!context) {\r\n throw new Error('useDialog must be used within a component.');\r\n }\r\n return context;\r\n};\r\n\r\ninterface DialogProps {\r\n children: React.ReactNode;\r\n open?: boolean;\r\n defaultOpen?: boolean;\r\n onOpenChange?: (open: boolean) => void;\r\n size?: DialogSize;\r\n disableBackdropClick?: boolean;\r\n disableEscapeKeyDown?: boolean;\r\n showCloseButton?: boolean;\r\n}\r\n\r\nconst Dialog: React.FC = ({\r\n children,\r\n open: controlledOpen,\r\n defaultOpen = false,\r\n onOpenChange,\r\n size,\r\n disableBackdropClick = false,\r\n disableEscapeKeyDown = false,\r\n showCloseButton = true,\r\n}) => {\r\n const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);\r\n const isControlled = controlledOpen !== undefined;\r\n const open = isControlled ? controlledOpen : uncontrolledOpen;\r\n\r\n const setOpen = useCallback(\r\n (value: boolean) => {\r\n if (!isControlled) {\r\n setUncontrolledOpen(value);\r\n }\r\n onOpenChange?.(value);\r\n },\r\n [isControlled, onOpenChange],\r\n );\r\n\r\n return (\r\n \r\n {children}\r\n \r\n );\r\n};\r\n\r\ninterface DialogTriggerProps {\r\n children?: React.ReactNode;\r\n asChild?: boolean;\r\n className?: string;\r\n}\r\n\r\nconst DialogTrigger: React.FC = ({ children, asChild = false, className }) => {\r\n const { setOpen } = useDialog();\r\n\r\n if (asChild) {\r\n if (!React.isValidElement(children)) {\r\n throw new Error('DialogTrigger: when using asChild, children must be a single React element');\r\n }\r\n\r\n return React.cloneElement(children as React.ReactElement, {\r\n onClick: (e: React.MouseEvent) => {\r\n (children as React.ReactElement).props.onClick?.(e);\r\n setOpen(true);\r\n },\r\n });\r\n }\r\n\r\n return (\r\n setOpen(true)}\r\n >\r\n {children}\r\n \r\n );\r\n};\r\n\r\nconst overlayVariants = cva(\r\n 'fixed inset-0 z-50 bg-black/50 duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',\r\n);\r\n\r\nconst contentVariants = cva(\r\n `fixed left-[50%] top-[50%] z-50 grid w-full translate-x-[-50%] translate-y-[-50%] gap-4 border-none bg-background p-6 shadow-soft-grey duration-200 \r\n data-[state=open]:animate-in \r\n data-[state=closed]:animate-out \r\n data-[state=closed]:fade-out-0 \r\n data-[state=open]:fade-in-0 \r\n data-[state=closed]:zoom-out-95 \r\n data-[state=open]:zoom-in-95 \r\n data-[state=closed]:slide-out-to-left-1/2 \r\n data-[state=closed]:slide-out-to-top-[48%] \r\n data-[state=open]:slide-in-from-left-1/2 \r\n data-[state=open]:slide-in-from-top-[48%] \r\n sm:rounded-lg`,\r\n {\r\n variants: {\r\n size: {\r\n default: 'max-w-[calc(100%-2rem)] sm:max-w-[425px]',\r\n sm: 'max-w-sm',\r\n lg: 'max-w-2xl',\r\n xl: 'max-w-4xl',\r\n full: 'max-w-[95vw] h-[95vh] overflow-y-auto',\r\n },\r\n },\r\n defaultVariants: {\r\n size: 'default',\r\n },\r\n },\r\n);\r\n\r\ninterface DialogContentProps\r\n extends React.HTMLAttributes, VariantProps {\r\n className?: string;\r\n children: React.ReactNode;\r\n}\r\n\r\nconst DialogContent: React.FC = ({ className, children, ...props }) => {\r\n const { open, setOpen, size, disableBackdropClick, disableEscapeKeyDown, showCloseButton } =\r\n useDialog();\r\n const [isRendered, setIsRendered] = useState(open);\r\n\r\n useEffect(() => {\r\n let timeoutId: NodeJS.Timeout;\r\n if (open) {\r\n setIsRendered(true);\r\n } else {\r\n timeoutId = setTimeout(() => {\r\n setIsRendered(false);\r\n }, 150);\r\n }\r\n return () => clearTimeout(timeoutId);\r\n }, [open]);\r\n\r\n useEffect(() => {\r\n if (isRendered) {\r\n preventScrollShift.lock();\r\n }\r\n\r\n const handleEscape = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape' && !disableEscapeKeyDown) {\r\n setOpen(false);\r\n }\r\n };\r\n document.addEventListener('keydown', handleEscape);\r\n\r\n return () => {\r\n if (!open) {\r\n preventScrollShift.unlock();\r\n }\r\n document.removeEventListener('keydown', handleEscape);\r\n };\r\n }, [isRendered, disableEscapeKeyDown, setOpen, open]);\r\n\r\n if (!isRendered) return null;\r\n\r\n return createPortal(\r\n \r\n !disableBackdropClick && setOpen(false)}\r\n />\r\n\r\n \r\n {children}\r\n\r\n {showCloseButton && (\r\n setOpen(false)}\r\n className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground\"\r\n >\r\n \r\n Close\r\n \r\n )}\r\n \r\n ,\r\n document.body,\r\n );\r\n};\r\n\r\nconst DialogHeader = ({ className, ...props }: React.HTMLAttributes) => (\r\n \r\n);\r\n\r\nconst DialogFooter = ({ className, ...props }: React.HTMLAttributes) => (\r\n \r\n);\r\n\r\nconst DialogTitle = ({ className, ...props }: React.HTMLAttributes) => (\r\n \r\n);\r\n\r\nconst DialogDescription = ({ className, ...props }: React.HTMLAttributes) => (\r\n \r\n);\r\n\r\nexport {\r\n Dialog,\r\n DialogTrigger,\r\n DialogContent,\r\n DialogHeader,\r\n DialogFooter,\r\n DialogTitle,\r\n DialogDescription,\r\n};\r\n", "type": "registry:ui" } ] }