{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dialog",
  "title": "Dialog",
  "description": "Modal dialog on radix-ui with portal, overlay, focus trap, fade/zoom animations and 5 content sizes (sm, md, lg, xl, full) that cap the width from the sm breakpoint up while staying full width minus a margin on mobile. Exports Dialog, DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, DialogClose.",
  "dependencies": [
    "radix-ui@^1.6.1",
    "lucide-react@^1.23.0",
    "tw-animate-css@^1.4.0"
  ],
  "registryDependencies": [
    "@ai2/tokens",
    "@ai2/primitives"
  ],
  "files": [
    {
      "path": "registry/ai2/ui/dialog.tsx",
      "content": "\"use client\"\n\nimport type * as React from \"react\"\nimport { X } from \"lucide-react\"\nimport { Dialog as DialogPrimitive } from \"@/registry/ai2/ui/primitives\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* max-h + overflow-y-auto are REQUIRED, not decoration: the dialog is centred\n   with `fixed` + `-translate-y-1/2`, and while it is open Radix locks body\n   scrolling. Without its own scroll container, the bottom of content taller\n   than the viewport cannot be reached on mobile by ANY means (measured\n   2026-07-26: last row off-screen, the box would not scroll). If the confirm\n   button sits at the bottom, the whole flow dies.\n   `dvh` is used: on iOS `vh` does not account for the address bar. Radix\n   renders nested overlays (select, popover) into a portal, so this scroll\n   container does not clip them. */\nconst dialogContentVariants = cva(\n  \"fixed left-1/2 top-1/2 z-50 grid max-h-[calc(100dvh-2rem)] w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 overflow-y-auto overscroll-contain rounded-xl border border-border bg-popover p-6 text-popover-foreground shadow-lg duration-(--motion-base) data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 motion-reduce:animate-none\",\n  {\n    variants: {\n      size: {\n        sm: \"sm:max-w-sm\",\n        md: \"sm:max-w-lg\",\n        lg: \"sm:max-w-2xl\",\n        xl: \"sm:max-w-4xl\",\n        full: \"sm:max-w-[calc(100%-4rem)]\",\n      },\n    },\n    defaultVariants: { size: \"md\" },\n  }\n)\n\nfunction Dialog(props: React.ComponentProps<typeof DialogPrimitive.Root>) {\n  return <DialogPrimitive.Root data-slot=\"dialog\" {...props} />\n}\n\nfunction DialogTrigger(props: React.ComponentProps<typeof DialogPrimitive.Trigger>) {\n  return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />\n}\n\nfunction DialogClose(props: React.ComponentProps<typeof DialogPrimitive.Close>) {\n  return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />\n}\n\nfunction DialogOverlay({\n  className,\n  ...props\n}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {\n  return (\n    <DialogPrimitive.Overlay\n      data-slot=\"dialog-overlay\"\n      className={cn(\n        \"fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0 motion-reduce:animate-none\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction DialogContent({\n  className,\n  children,\n  showClose = true,\n  size,\n  ...props\n}: React.ComponentProps<typeof DialogPrimitive.Content> &\n  VariantProps<typeof dialogContentVariants> & {\n    showClose?: boolean\n  }) {\n  return (\n    <DialogPrimitive.Portal data-slot=\"dialog-portal\">\n      <DialogOverlay />\n      <DialogPrimitive.Content\n        data-slot=\"dialog-content\"\n        data-size={size ?? \"md\"}\n        className={cn(dialogContentVariants({ size, className }))}\n        {...props}\n      >\n        {children}\n        {showClose && (\n          <DialogPrimitive.Close className=\"absolute right-4 top-4 rounded-md opacity-70 outline-none transition-opacity duration-(--motion-fast) after:absolute after:-inset-2 hover:opacity-100 focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none [&_svg]:size-4 [&_i]:text-base [&_i]:leading-none\">\n            <X />\n            <span className=\"sr-only\">Close</span>\n          </DialogPrimitive.Close>\n        )}\n      </DialogPrimitive.Content>\n    </DialogPrimitive.Portal>\n  )\n}\n\nfunction DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"dialog-header\"\n      className={cn(\"flex flex-col gap-1.5 text-center sm:text-left\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction DialogFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"dialog-footer\"\n      className={cn(\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction DialogTitle({\n  className,\n  ...props\n}: React.ComponentProps<typeof DialogPrimitive.Title>) {\n  return (\n    <DialogPrimitive.Title\n      data-slot=\"dialog-title\"\n      className={cn(\"text-lg font-semibold leading-none\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction DialogDescription({\n  className,\n  ...props\n}: React.ComponentProps<typeof DialogPrimitive.Description>) {\n  return (\n    <DialogPrimitive.Description\n      data-slot=\"dialog-description\"\n      className={cn(\"text-sm text-muted-foreground\", className)}\n      {...props}\n    />\n  )\n}\n\nexport {\n  Dialog,\n  DialogTrigger,\n  DialogClose,\n  DialogContent,\n  DialogHeader,\n  DialogFooter,\n  DialogTitle,\n  DialogDescription,\n  DialogOverlay,\n  dialogContentVariants,\n}\n",
      "type": "registry:ui"
    }
  ],
  "docs": "showClose defaults to true. Always include DialogTitle (visually hidden with sr-only if needed) for accessibility.",
  "categories": [
    "overlay"
  ],
  "type": "registry:ui"
}