{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "image-upload",
  "type": "registry:ui",
  "title": "Image upload",
  "description": "A single image picker that starts as a drag and drop zone, then swaps to a large preview with a remove button. Returns an object URL and the File in React.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "https://ui.minidev.pro/r/file-dropzone.json",
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/image-upload.tsx",
      "type": "registry:ui",
      "target": "components/ui/image-upload.tsx",
      "content": "\"use client\"\nimport * as React from \"react\"\nimport { ImageIcon, XIcon } from \"lucide-react\"\nimport { cn } from \"@/lib/utils\"\nimport { FileDropzone } from \"@/components/ui/file-dropzone\"\n\ntype ImageUploadProps = {\n  value?: string | null\n  onChange?: (url: string | null, file?: File | null) => void\n  disabled?: boolean\n  className?: string\n}\n\nfunction ImageUpload({ value, onChange, disabled, className }: ImageUploadProps) {\n  const [preview, setPreview] = React.useState<string | null>(value ?? null)\n  React.useEffect(() => { setPreview(value ?? null) }, [value])\n  return (\n    <div data-slot=\"image-upload\" className={cn(\"relative w-full\", className)}>\n      {preview ? (\n        <div className=\"relative overflow-hidden rounded-xl border border-border\">\n          {/* eslint-disable-next-line @next/next/no-img-element */}\n          <img src={preview} alt=\"Upload preview\" className=\"h-48 w-full object-cover\" />\n          <button\n            type=\"button\"\n            aria-label=\"Remove image\"\n            disabled={disabled}\n            className=\"absolute top-2 right-2 inline-flex size-8 items-center justify-center rounded-lg border border-border bg-surface text-fg\"\n            onClick={() => { setPreview(null); onChange?.(null, null) }}\n          >\n            <XIcon className=\"size-4\" />\n          </button>\n        </div>\n      ) : (\n        <FileDropzone\n          disabled={disabled}\n          accept=\"image/*\"\n          label=\"Drop an image\"\n          hint=\"PNG, JPG up to 5MB\"\n          onFiles={(files) => {\n            const file = Array.from(files)[0]\n            if (!file) return\n            const url = URL.createObjectURL(file)\n            setPreview(url)\n            onChange?.(url, file)\n          }}\n        />\n      )}\n      {!preview ? <ImageIcon className=\"pointer-events-none absolute top-4 left-4 size-4 text-fg-subtle opacity-0\" /> : null}\n    </div>\n  )\n}\nexport { ImageUpload }\n"
    }
  ]
}
