{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "destructive-gate",
  "title": "Destructive Gate",
  "description": "A dialog and mobile drawer confirmation that gates destructive actions behind a generated three-word phrase.",
  "dependencies": [
    "lucide-react",
    "radix-ui",
    "vaul"
  ],
  "registryDependencies": [
    "button",
    "input",
    "label"
  ],
  "files": [
    {
      "path": "registry/corr/components/destructive-gate.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { BoxIcon, ShieldAlertIcon } from \"lucide-react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from \"@/components/ui/dialog\"\nimport {\n  Drawer,\n  DrawerClose,\n  DrawerContent,\n  DrawerDescription,\n  DrawerFooter,\n  DrawerHeader,\n  DrawerTitle,\n  DrawerTrigger,\n} from \"@/components/ui/drawer\"\nimport { Input } from \"@/components/ui/input\"\nimport { Label } from \"@/components/ui/label\"\nimport { cn } from \"@/lib/utils\"\n\nconst defaultWords = [\n  \"anchor\",\n  \"bridge\",\n  \"copper\",\n  \"delta\",\n  \"ember\",\n  \"forest\",\n  \"harbor\",\n  \"index\",\n  \"kernel\",\n  \"ledger\",\n  \"matrix\",\n  \"orbit\",\n  \"pixel\",\n  \"quartz\",\n  \"signal\",\n  \"summit\",\n  \"vector\",\n  \"window\",\n]\n\nfunction useMediaQuery(query: string) {\n  const [matches, setMatches] = React.useState(false)\n\n  React.useEffect(() => {\n    const media = window.matchMedia(query)\n    const sync = () => setMatches(media.matches)\n\n    sync()\n    media.addEventListener(\"change\", sync)\n\n    return () => media.removeEventListener(\"change\", sync)\n  }, [query])\n\n  return matches\n}\n\nfunction generateGatePhrase(words: string[]) {\n  const pool = words.length >= 3 ? words : defaultWords\n  const selected = new Set<string>()\n\n  while (selected.size < 3) {\n    selected.add(pool[Math.floor(Math.random() * pool.length)])\n  }\n\n  return Array.from(selected).join(\" \")\n}\n\nfunction DestructiveGateBody({\n  entityLabel,\n  phrase,\n  value,\n  confirmLabel,\n  cancelLabel,\n  onValueChange,\n  onCancel,\n  onConfirm,\n}: {\n  entityLabel?: string\n  phrase: string\n  value: string\n  confirmLabel: string\n  cancelLabel: string\n  onValueChange: (value: string) => void\n  onCancel?: () => void\n  onConfirm: () => void\n}) {\n  const inputId = React.useId()\n  const canConfirm = value.trim() === phrase\n\n  return (\n    <div className=\"grid gap-4\">\n      {entityLabel ? (\n        <div className=\"flex items-center gap-3 rounded-md border border-red-200 bg-white/75 p-3 text-sm shadow-sm dark:border-red-900 dark:bg-black/15\">\n          <span className=\"inline-flex size-9 shrink-0 items-center justify-center rounded-md bg-red-100 text-destructive dark:bg-red-900/60\">\n            <BoxIcon className=\"size-4\" />\n          </span>\n          <span className=\"min-w-0\">\n            <span className=\"block text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n              Target\n            </span>\n            <span className=\"block truncate font-medium\">{entityLabel}</span>\n          </span>\n        </div>\n      ) : null}\n      <div className=\"grid gap-2\">\n        <Label htmlFor={inputId}>Type this phrase to continue</Label>\n        <div\n          className=\"select-none rounded-md border border-dashed border-red-300 bg-white/75 px-3 py-2 font-mono text-sm tracking-wide text-destructive dark:border-red-800 dark:bg-black/15\"\n          onCopy={(event) => event.preventDefault()}\n          onContextMenu={(event) => event.preventDefault()}\n          onCut={(event) => event.preventDefault()}\n          onDragStart={(event) => event.preventDefault()}\n          onMouseDown={(event) => {\n            if (event.detail > 1) event.preventDefault()\n          }}\n          onSelect={(event) => event.preventDefault()}\n        >\n          {phrase}\n        </div>\n        <Input\n          id={inputId}\n          value={value}\n          onChange={(event) => onValueChange(event.target.value)}\n          placeholder={phrase}\n          autoComplete=\"off\"\n          spellCheck={false}\n        />\n      </div>\n      <div className=\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\">\n        <Button type=\"button\" variant=\"outline\" onClick={onCancel}>\n          {cancelLabel}\n        </Button>\n        <Button\n          type=\"button\"\n          variant=\"destructive\"\n          disabled={!canConfirm}\n          onClick={onConfirm}\n          className=\"disabled:bg-destructive/10 disabled:text-destructive/50\"\n        >\n          {confirmLabel}\n        </Button>\n      </div>\n    </div>\n  )\n}\n\nexport function DestructiveGate({\n  trigger,\n  title = \"Confirm destructive action\",\n  description = \"This action cannot be undone.\",\n  entityLabel,\n  confirmLabel = \"Delete\",\n  cancelLabel = \"Cancel\",\n  words = defaultWords,\n  open,\n  onOpenChange,\n  onConfirm,\n  className,\n}: {\n  trigger: React.ReactNode\n  title?: string\n  description?: string\n  entityLabel?: string\n  confirmLabel?: string\n  cancelLabel?: string\n  words?: string[]\n  open?: boolean\n  onOpenChange?: (open: boolean) => void\n  onConfirm?: (phrase: string) => void\n  className?: string\n}) {\n  const isSmallScreen = useMediaQuery(\"(max-width: 639px)\")\n  const [internalOpen, setInternalOpen] = React.useState(false)\n  const [phrase, setPhrase] = React.useState(() => generateGatePhrase(words))\n  const [value, setValue] = React.useState(\"\")\n  const isOpen = open ?? internalOpen\n\n  function setOpen(nextOpen: boolean) {\n    if (nextOpen) {\n      setPhrase(generateGatePhrase(words))\n      setValue(\"\")\n    }\n\n    onOpenChange?.(nextOpen)\n    if (open === undefined) setInternalOpen(nextOpen)\n  }\n\n  function handleConfirm() {\n    onConfirm?.(phrase)\n    setOpen(false)\n  }\n\n  const body = (\n    <DestructiveGateBody\n      entityLabel={entityLabel}\n      phrase={phrase}\n      value={value}\n      confirmLabel={confirmLabel}\n      cancelLabel={cancelLabel}\n      onValueChange={setValue}\n      onCancel={() => setOpen(false)}\n      onConfirm={handleConfirm}\n    />\n  )\n\n  if (isSmallScreen) {\n    return (\n      <Drawer open={isOpen} onOpenChange={setOpen}>\n        <DrawerTrigger asChild>{trigger}</DrawerTrigger>\n        <DrawerContent\n          variant=\"destructive\"\n          className={cn(\n            className\n          )}\n        >\n          <div className=\"mx-auto w-full max-w-md\">\n            <DrawerHeader className=\"items-start text-left group-data-[vaul-drawer-direction=bottom]/drawer-content:text-left\">\n              <div className=\"mb-2 inline-flex size-10 items-center justify-center rounded-md bg-destructive/10 text-destructive\">\n                <ShieldAlertIcon className=\"size-5\" />\n              </div>\n              <DrawerTitle className=\"text-destructive\">{title}</DrawerTitle>\n              {description ? (\n                <DrawerDescription className=\"max-w-sm text-left text-destructive/80\">\n                  {description}\n                </DrawerDescription>\n              ) : null}\n            </DrawerHeader>\n            <div className=\"px-4 pb-4\">{body}</div>\n            <DrawerFooter className=\"sr-only\">\n              <DrawerClose asChild>\n                <Button variant=\"outline\">{cancelLabel}</Button>\n              </DrawerClose>\n            </DrawerFooter>\n          </div>\n        </DrawerContent>\n      </Drawer>\n    )\n  }\n\n  return (\n    <Dialog open={isOpen} onOpenChange={setOpen}>\n      <DialogTrigger asChild>{trigger}</DialogTrigger>\n      <DialogContent\n        variant=\"destructive\"\n        className={cn(\n          \"sm:max-w-md\",\n          className\n        )}\n      >\n        <DialogHeader>\n          <div className=\"mb-2 inline-flex size-10 items-center justify-center rounded-md bg-destructive/10 text-destructive\">\n            <ShieldAlertIcon className=\"size-5\" />\n          </div>\n          <DialogTitle className=\"text-destructive\">{title}</DialogTitle>\n          {description ? (\n            <DialogDescription className=\"text-destructive/80\">\n              {description}\n            </DialogDescription>\n          ) : null}\n        </DialogHeader>\n        {body}\n        <DialogFooter className=\"sr-only\">\n          <DialogClose asChild>\n            <Button variant=\"outline\">{cancelLabel}</Button>\n          </DialogClose>\n        </DialogFooter>\n      </DialogContent>\n    </Dialog>\n  )\n}\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/ui/dialog.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Dialog as DialogPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport { XIcon } from \"lucide-react\"\n\nfunction Dialog({\n  ...props\n}: React.ComponentProps<typeof DialogPrimitive.Root>) {\n  return <DialogPrimitive.Root data-slot=\"dialog\" {...props} />\n}\n\nfunction DialogTrigger({\n  ...props\n}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {\n  return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />\n}\n\nfunction DialogPortal({\n  ...props\n}: React.ComponentProps<typeof DialogPrimitive.Portal>) {\n  return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />\n}\n\nfunction DialogClose({\n  ...props\n}: 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 isolate z-50 bg-black/80 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction DialogContent({\n  className,\n  children,\n  showCloseButton = true,\n  variant = \"default\",\n  ...props\n}: React.ComponentProps<typeof DialogPrimitive.Content> & {\n  showCloseButton?: boolean\n  variant?: \"default\" | \"destructive\"\n}) {\n  return (\n    <DialogPortal>\n      <DialogOverlay />\n      <DialogPrimitive.Content\n        data-slot=\"dialog-content\"\n        className={cn(\n          \"t-modal fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-xs/relaxed text-popover-foreground ring-1 ring-foreground/10 outline-none sm:max-w-sm\",\n          variant === \"destructive\" &&\n            \"bg-red-50 text-red-950 ring-red-200 dark:bg-red-950 dark:text-red-50 dark:ring-red-900\",\n          className\n        )}\n        {...props}\n      >\n        {children}\n        {showCloseButton && (\n          <DialogPrimitive.Close data-slot=\"dialog-close\" asChild>\n            <Button\n              variant=\"ghost\"\n              className=\"absolute top-2 right-2\"\n              size=\"icon-sm\"\n            >\n              <XIcon />\n              <span className=\"sr-only\">Close</span>\n            </Button>\n          </DialogPrimitive.Close>\n        )}\n      </DialogPrimitive.Content>\n    </DialogPortal>\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\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction DialogFooter({\n  className,\n  showCloseButton = false,\n  children,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  showCloseButton?: boolean\n}) {\n  return (\n    <div\n      data-slot=\"dialog-footer\"\n      className={cn(\n        \"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\",\n        className\n      )}\n      {...props}\n    >\n      {children}\n      {showCloseButton && (\n        <DialogPrimitive.Close asChild>\n          <Button variant=\"outline\">Close</Button>\n        </DialogPrimitive.Close>\n      )}\n    </div>\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(\"font-heading text-sm font-medium\", 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(\n        \"text-xs/relaxed text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nexport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogOverlay,\n  DialogPortal,\n  DialogTitle,\n  DialogTrigger,\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "src/components/ui/drawer.tsx",
      "content": "import * as React from \"react\"\nimport { Drawer as DrawerPrimitive } from \"vaul\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Drawer({\n  ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Root>) {\n  return <DrawerPrimitive.Root data-slot=\"drawer\" {...props} />\n}\n\nfunction DrawerTrigger({\n  ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {\n  return <DrawerPrimitive.Trigger data-slot=\"drawer-trigger\" {...props} />\n}\n\nfunction DrawerPortal({\n  ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Portal>) {\n  return <DrawerPrimitive.Portal data-slot=\"drawer-portal\" {...props} />\n}\n\nfunction DrawerClose({\n  ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Close>) {\n  return <DrawerPrimitive.Close data-slot=\"drawer-close\" {...props} />\n}\n\nfunction DrawerOverlay({\n  className,\n  ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {\n  return (\n    <DrawerPrimitive.Overlay\n      data-slot=\"drawer-overlay\"\n      className={cn(\n        \"fixed inset-0 z-50 bg-black/80 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction DrawerContent({\n  className,\n  children,\n  variant = \"default\",\n  ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Content> & {\n  variant?: \"default\" | \"destructive\"\n}) {\n  return (\n    <DrawerPortal data-slot=\"drawer-portal\">\n      <DrawerOverlay />\n      <DrawerPrimitive.Content\n        data-slot=\"drawer-content\"\n        className={cn(\n          \"group/drawer-content fixed z-50 flex h-auto flex-col bg-transparent p-2 text-xs/relaxed text-popover-foreground before:absolute before:inset-2 before:-z-10 before:rounded-xl before:border before:border-border before:bg-popover data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm\",\n          variant === \"destructive\" &&\n            \"text-red-950 before:border-red-200 before:bg-red-50 dark:text-red-50 dark:before:border-red-900 dark:before:bg-red-950\",\n          className\n        )}\n        {...props}\n      >\n        <div className=\"mx-auto mt-4 hidden h-1.5 w-[100px] shrink-0 rounded-full bg-muted group-data-[vaul-drawer-direction=bottom]/drawer-content:block\" />\n        {children}\n      </DrawerPrimitive.Content>\n    </DrawerPortal>\n  )\n}\n\nfunction DrawerHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"drawer-header\"\n      className={cn(\n        \"flex flex-col gap-1 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:text-left\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction DrawerFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"drawer-footer\"\n      className={cn(\"mt-auto flex flex-col gap-2 p-4\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction DrawerTitle({\n  className,\n  ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Title>) {\n  return (\n    <DrawerPrimitive.Title\n      data-slot=\"drawer-title\"\n      className={cn(\n        \"font-heading text-sm font-medium text-foreground\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction DrawerDescription({\n  className,\n  ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Description>) {\n  return (\n    <DrawerPrimitive.Description\n      data-slot=\"drawer-description\"\n      className={cn(\"text-xs/relaxed text-muted-foreground\", className)}\n      {...props}\n    />\n  )\n}\n\nexport {\n  Drawer,\n  DrawerPortal,\n  DrawerOverlay,\n  DrawerTrigger,\n  DrawerClose,\n  DrawerContent,\n  DrawerHeader,\n  DrawerFooter,\n  DrawerTitle,\n  DrawerDescription,\n}\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:component"
}