{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "confirm-dialog",
  "title": "Confirm dialog",
  "description": "Confirmation on the native dialog: both buttons name the action, the safe one has focus, red only when something is destroyed, optional type-to-confirm.",
  "dependencies": [],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/manniche/confirm-dialog/confirm-dialog.tsx",
      "content": "import { useEffect, useId, useRef, useState, type ReactNode } from 'react'\nimport { cn } from '@/lib/utils'\n\nexport type ConfirmDialogProps = {\n  open: boolean\n  onOpenChange: (open: boolean) => void\n  title: string\n  description?: ReactNode\n  /** The verb and the thing, e.g. “Delete project”. Never “Yes” or “OK”. */\n  confirmLabel: string\n  /** What happens if they back out, e.g. “Keep project”. Never “No” or “Cancel” alone. */\n  cancelLabel: string\n  onConfirm: () => void\n  /** Red, for actions that destroy something. Leave it off for everything else. */\n  destructive?: boolean\n  /** When set, the confirm button stays off until this exact text is typed, e.g. the project name. */\n  typeToConfirm?: string\n  className?: string\n}\n\n/**\n * A confirmation on the native <dialog> that follows the rules for dangerous actions:\n * both buttons say what they do, the safe one has focus and sits where “OK” usually is (right),\n * and the destructive one sits apart on the left, so a habitual Enter or click keeps the data.\n */\nexport function ConfirmDialog({\n  open,\n  onOpenChange,\n  title,\n  description,\n  confirmLabel,\n  cancelLabel,\n  onConfirm,\n  destructive = false,\n  typeToConfirm,\n  className,\n}: ConfirmDialogProps) {\n  const dialog = useRef<HTMLDialogElement>(null)\n  const keep = useRef<HTMLButtonElement>(null)\n  const [typed, setTyped] = useState('')\n  const titleId = useId()\n  const descId = useId()\n  const inputId = useId()\n\n  useEffect(() => {\n    const d = dialog.current\n    if (!d) return\n    if (open && !d.open) {\n      setTyped('')\n      d.showModal()\n      keep.current?.focus()\n    } else if (!open && d.open) d.close()\n  }, [open])\n\n  const close = () => onOpenChange(false)\n  const ready = typeToConfirm === undefined || typed === typeToConfirm\n\n  return (\n    <dialog\n      ref={dialog}\n      aria-labelledby={titleId}\n      aria-describedby={description ? descId : undefined}\n      onClose={close}\n      onClick={(e) => e.target === dialog.current && close()}\n      className={cn(\n        'm-auto w-[min(26rem,calc(100vw-2rem))] rounded-3xl border bg-card p-0 text-card-foreground shadow-2xl',\n        'backdrop:bg-black/30 backdrop:backdrop-blur-[2px]',\n        'opacity-100 transition-[opacity,transform] duration-200 ease-out-quint starting:scale-95 starting:opacity-0 motion-reduce:transition-none',\n        className,\n      )}\n    >\n      <form\n        method=\"dialog\"\n        className=\"grid gap-4 p-6\"\n        onSubmit={(e) => {\n          e.preventDefault()\n          if (!ready) return\n          onConfirm()\n          close()\n        }}\n      >\n        <h2 id={titleId} className=\"text-lg font-semibold text-balance\">\n          {title}\n        </h2>\n        {description && (\n          <div id={descId} className=\"text-sm text-pretty text-muted-foreground\">\n            {description}\n          </div>\n        )}\n        {typeToConfirm !== undefined && (\n          <div className=\"grid gap-1.5\">\n            <label htmlFor={inputId} className=\"text-sm\">\n              Type <strong className=\"font-mono font-medium select-all\">{typeToConfirm}</strong> to confirm\n            </label>\n            <input\n              id={inputId}\n              value={typed}\n              onChange={(e) => setTyped(e.target.value)}\n              autoComplete=\"off\"\n              spellCheck={false}\n              className=\"min-h-11 rounded-xl border bg-background px-3 font-mono text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n            />\n          </div>\n        )}\n        <div className=\"mt-2 flex flex-wrap-reverse items-center justify-between gap-3\">\n          <button\n            type=\"submit\"\n            disabled={!ready}\n            className={cn(\n              'inline-flex min-h-11 items-center rounded-xl px-4 text-sm font-medium transition-[background-color,opacity,transform] duration-150 ease-out-quint active:scale-[0.97] disabled:pointer-events-none disabled:opacity-40',\n              destructive ? 'bg-destructive text-white hover:bg-destructive/90' : 'bg-primary text-primary-foreground hover:bg-primary/90',\n            )}\n          >\n            {confirmLabel}\n          </button>\n          <button\n            ref={keep}\n            type=\"button\"\n            onClick={close}\n            className=\"inline-flex min-h-11 items-center rounded-xl border bg-card px-4 text-sm font-medium transition-[background-color,transform] duration-150 ease-out-quint hover:bg-muted active:scale-[0.97]\"\n          >\n            {cancelLabel}\n          </button>\n        </div>\n      </form>\n    </dialog>\n  )\n}\n",
      "type": "registry:component"
    }
  ],
  "categories": [
    "controls"
  ],
  "type": "registry:component"
}