{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "modal",
  "type": "registry:ui",
  "title": "Modal",
  "description": "Accessible dialog modal with backdrop blur, focus handling, and escape key dismissal.",
  "registryDependencies": [
    "https://ui.arpitbhayani.me/r/aui.json"
  ],
  "files": [
    {
      "path": "ui/modal.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface ModalProps\n  extends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\"> {\n  isOpen?: boolean;\n  open?: boolean;\n  onClose?: () => void;\n  title?: React.ReactNode;\n  footer?: React.ReactNode;\n  closeOnBackdropClick?: boolean;\n}\n\nexport function Modal({\n  isOpen,\n  open,\n  onClose,\n  title,\n  children,\n  footer,\n  className,\n  closeOnBackdropClick = true,\n  ...props\n}: ModalProps) {\n  const isVisible = open !== undefined ? open : Boolean(isOpen);\n  const [mounted, setMounted] = React.useState(false);\n  const modalRef = React.useRef<HTMLDivElement>(null);\n\n  React.useEffect(() => {\n    setMounted(true);\n  }, []);\n\n  React.useEffect(() => {\n    if (!isVisible) return;\n    const handleKeyDown = (e: KeyboardEvent) => {\n      if (e.key === \"Escape\") {\n        e.stopPropagation();\n        onClose?.();\n      }\n    };\n    document.addEventListener(\"keydown\", handleKeyDown);\n    const originalOverflow = document.body.style.overflow;\n    document.body.style.overflow = \"hidden\";\n    return () => {\n      document.removeEventListener(\"keydown\", handleKeyDown);\n      document.body.style.overflow = originalOverflow;\n    };\n  }, [isVisible, onClose]);\n\n  if (!isVisible || !mounted) return null;\n\n  const modalNode = (\n    <div\n      className=\"fixed inset-0 z-50 bg-black/55 backdrop-blur-[3px] flex items-center justify-center p-4 animate-in fade-in-0\"\n      onClick={(e) => {\n        if (closeOnBackdropClick && e.target === e.currentTarget) {\n          onClose?.();\n        }\n      }}\n      role=\"dialog\"\n      aria-modal=\"true\"\n    >\n      <div\n        ref={modalRef}\n        className={cn(\n          \"w-full max-w-md bg-card text-card-foreground border border-border rounded-xl shadow-2xl overflow-hidden animate-in zoom-in-95\",\n          className\n        )}\n        tabIndex={-1}\n        {...props}\n      >\n        {(title || onClose) && (\n          <div className=\"flex items-center justify-between px-5 py-4 border-b border-border\">\n            {title && (\n              <h3 className=\"font-heading font-semibold text-lg text-foreground\">\n                {title}\n              </h3>\n            )}\n            {onClose && (\n              <button\n                type=\"button\"\n                className=\"w-7 h-7 inline-flex items-center justify-center rounded-md text-muted-foreground hover:text-foreground hover:bg-muted transition-colors cursor-pointer ml-auto\"\n                onClick={onClose}\n                aria-label=\"Close dialog\"\n              >\n                ✕\n              </button>\n            )}\n          </div>\n        )}\n\n        <div className=\"p-5 text-sm\">{children}</div>\n\n        {footer && (\n          <div className=\"flex items-center justify-end gap-2 px-5 py-3 bg-muted/30 border-t border-border\">\n            {footer}\n          </div>\n        )}\n      </div>\n    </div>\n  );\n\n  return typeof document !== \"undefined\"\n    ? createPortal(modalNode, document.body)\n    : null;\n}\n",
      "type": "registry:ui"
    }
  ]
}