{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"0n-modal-box","type":"registry:block","title":"Modal Box","description":"Pre-composed centered dialog with title, description, body, and footer slots. Drop in a trigger, get a working modal — no Dialog plumbing.","dependencies":[],"registryDependencies":["button","dialog"],"files":[{"path":"components/0n/modal-box.tsx","type":"registry:block","target":"components/0n/modal-box.tsx","content":"'use client'\n\n/**\n * 0n Modal Box — pre-composed dialog with title + description + body\n * + footer slots. Branded primary button. Close on Esc or overlay click.\n *\n * Install:\n *   npx shadcn@latest add https://0nmcp.com/r/0n-modal-box.json\n *\n * Use:\n *   <ModalBox\n *     trigger={<Button>Open</Button>}\n *     title=\"Save your work?\"\n *     description=\"Changes will be applied immediately.\"\n *     primaryAction={{ label: 'Save', onClick: () => save() }}\n *     secondaryAction={{ label: 'Cancel' }}\n *   >\n *     Optional body content goes here.\n *   </ModalBox>\n */\n\nimport { useState, type ReactNode } from 'react'\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from '@/components/ui/dialog'\nimport { Button } from '@/components/ui/button'\n\ninterface Action {\n  label: string\n  onClick?: () => void\n  /** Variant of the button; passed straight through to shadcn Button. */\n  variant?: 'default' | 'outline' | 'ghost' | 'destructive'\n}\n\nexport interface ModalBoxProps {\n  trigger: ReactNode\n  title: string\n  description?: string\n  children?: ReactNode\n  primaryAction?: Action\n  secondaryAction?: Action\n  /** Controlled open state. Omit for uncontrolled. */\n  open?: boolean\n  onOpenChange?: (next: boolean) => void\n}\n\nexport function ModalBox({\n  trigger,\n  title,\n  description,\n  children,\n  primaryAction,\n  secondaryAction,\n  open,\n  onOpenChange,\n}: ModalBoxProps) {\n  const [internal, setInternal] = useState(false)\n  const isOpen = open ?? internal\n  const setOpen = (v: boolean) => {\n    if (open === undefined) setInternal(v)\n    onOpenChange?.(v)\n  }\n\n  return (\n    <Dialog open={isOpen} onOpenChange={setOpen}>\n      <DialogTrigger asChild>{trigger}</DialogTrigger>\n      <DialogContent>\n        <DialogHeader>\n          <DialogTitle>{title}</DialogTitle>\n          {description ? <DialogDescription>{description}</DialogDescription> : null}\n        </DialogHeader>\n        {children}\n        {(primaryAction || secondaryAction) && (\n          <DialogFooter>\n            {secondaryAction ? (\n              <Button\n                variant={secondaryAction.variant ?? 'outline'}\n                onClick={() => {\n                  secondaryAction.onClick?.()\n                  setOpen(false)\n                }}\n              >\n                {secondaryAction.label}\n              </Button>\n            ) : null}\n            {primaryAction ? (\n              <Button\n                variant={primaryAction.variant ?? 'default'}\n                onClick={() => {\n                  primaryAction.onClick?.()\n                  setOpen(false)\n                }}\n              >\n                {primaryAction.label}\n              </Button>\n            ) : null}\n          </DialogFooter>\n        )}\n      </DialogContent>\n    </Dialog>\n  )\n}\n"}]}