{
  "name": "FileUpload",
  "package": "@magicblocksai/ui",
  "file": "packages/ui/src/components/FileUpload.tsx",
  "chapterTag": "04 Forms & Inputs",
  "chapter": "05-forms-and-inputs.html",
  "sectionId": "file-upload",
  "elName": "FileUpload",
  "demoUrl": "https://brand.magicblocks.ai/components/05-forms-and-inputs#file-upload",
  "hasLiveDemo": false,
  "description": "File upload + drop zone. Click the zone (or press Enter / Space) to open\nthe native picker; drag files onto it to drop directly. Files are\nvalidated against `accept`, `maxSize`, and `maxFiles` before being passed\nto `onFiles` — rejections fire `onReject` (or surface as toast errors by\ndefault).",
  "useClient": true,
  "interactivity": "interactive",
  "namedExports": [
    {
      "name": "FileUpload",
      "isPrincipal": true,
      "isType": false
    },
    {
      "name": "DropZone",
      "isPrincipal": false,
      "isType": false
    },
    {
      "name": "FileUploadProps",
      "isPrincipal": false,
      "isType": true
    },
    {
      "name": "FileUploadPreviewMode",
      "isPrincipal": false,
      "isType": true
    },
    {
      "name": "FileRejection",
      "isPrincipal": false,
      "isType": false
    }
  ],
  "importStatement": "import { FileUpload } from \"@magicblocksai/ui\";",
  "props": [
    {
      "name": "accept",
      "optional": true,
      "type": "string",
      "doc": "Native `accept` attribute — comma-separated MIME types and/or extensions."
    },
    {
      "name": "multiple",
      "optional": true,
      "type": "boolean",
      "doc": "Allow multiple files. Default `true`."
    },
    {
      "name": "maxSize",
      "optional": true,
      "type": "number",
      "doc": "Max file size in bytes. Files exceeding this are rejected before being passed to `onFiles`."
    },
    {
      "name": "maxFiles",
      "optional": true,
      "type": "number",
      "doc": "Maximum number of files. Files past this limit are rejected."
    },
    {
      "name": "disabled",
      "optional": true,
      "type": "boolean",
      "doc": "Disable both the click trigger and drop interactions."
    },
    {
      "name": "label",
      "optional": true,
      "type": "ReactNode",
      "doc": "Headline copy inside the drop zone. Default `\"Drop files or click to upload\"`."
    },
    {
      "name": "hint",
      "optional": true,
      "type": "ReactNode",
      "doc": "Smaller hint text below the headline. Default reflects `accept` + `maxSize`."
    },
    {
      "name": "preview",
      "optional": true,
      "type": "FileUploadPreviewMode",
      "doc": "Preview mode: `thumbnails` (image previews), `list` (file rows), or `none`. Default `\"thumbnails\"`."
    },
    {
      "name": "onFiles",
      "optional": true,
      "type": "(files: File[]) => void",
      "doc": "Fires with the accepted file list (excludes rejects)."
    },
    {
      "name": "onReject",
      "optional": true,
      "type": "(rejections: FileRejection[]) => void",
      "doc": "Fires when files are rejected (size/type/count). Defaults to a `toast.error()` per file."
    },
    {
      "name": "progress",
      "optional": true,
      "type": "Record<string, number>",
      "doc": "Optional progress map keyed by `file.name` (0…1). Renders a slim bar inside each file row when `preview=\"list\"`."
    },
    {
      "name": "ariaLabel",
      "optional": true,
      "type": "string",
      "doc": "Render a button label / ARIA label for the trigger. Default `\"Upload files\"`."
    }
  ],
  "classesUsed": [
    "dropzone",
    "dropzone-cta",
    "dropzone-headline",
    "dropzone-hint",
    "dropzone-input",
    "file-upload",
    "file-upload-list",
    "file-upload-row",
    "file-upload-row-name",
    "file-upload-row-progress",
    "file-upload-row-progress-fill",
    "file-upload-row-remove",
    "file-upload-row-size",
    "file-upload-thumbnail",
    "file-upload-thumbnail-fallback",
    "file-upload-thumbnail-remove",
    "file-upload-thumbnails"
  ],
  "examples": {
    "react": "<FileUpload\n    accept=\"image/*,.pdf\"\n    maxSize={5 * 1024 * 1024}\n    onFiles={(files) => upload(files)}\n  />",
    "html": "<div class=\"file-upload\">\n  <div role=\"button\" tabindex=\"0\" aria-label=\"Upload files\" class=\"dropzone\">\n    <span class=\"dropzone-headline\">\n      <span class=\"dropzone-cta\">Choose files</span> or drag and drop\n    </span>\n    <span class=\"dropzone-hint\">Accepts image/*,.pdf · Up to 5 MB per file</span>\n    <input type=\"file\" class=\"dropzone-input\" accept=\"image/*,.pdf\" multiple tabindex=\"-1\" aria-hidden=\"true\">\n  </div>\n</div>",
    "css": ".dropzone {\n  display: flex; flex-direction: column; align-items: center; justify-content: center;\n  gap: var(--s-2);\n  padding: var(--s-6) var(--s-4);\n  border: 2px dashed var(--hair);\n  border-radius: var(--r-md);\n  background: var(--bg-paper);\n  cursor: pointer;\n  text-align: center;\n  color: var(--fg-soft);\n  font: 400 13.5px/1.4 var(--f-body);\n  transition: border-color var(--dur-2) var(--ease), background var(--dur-2) var(--ease), color var(--dur-2) var(--ease);\n}\n\n.dropzone-cta {\n  color: var(--accent);\n  text-decoration: underline;\n  text-underline-offset: 2px;\n}\n\n.dropzone-headline {\n  font: 600 14px/1.3 var(--f-display);\n  color: var(--fg);\n}\n\n.dropzone-hint {\n  font: 400 12px/1.4 var(--f-body);\n  color: var(--fg-dim);\n}\n\n.dropzone-input { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; pointer-events: none; }\n\n.file-upload { display: flex; flex-direction: column; gap: var(--s-3); }\n\n.file-upload-list {\n  list-style: none; margin: 0; padding: 0;\n  display: flex; flex-direction: column; gap: 4px;\n}\n\n.file-upload-row {\n  display: grid;\n  grid-template-columns: 1fr auto auto;\n  align-items: center;\n  gap: var(--s-3);\n  padding: var(--s-2) var(--s-3);\n  background: var(--bg-paper);\n  border: 1px solid var(--hair);\n  border-radius: var(--r-sm);\n  font: 400 13px/1.3 var(--f-body);\n}\n\n.file-upload-row-name {\n  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n  color: var(--fg);\n}\n\n.file-upload-row-progress {\n  grid-column: 1 / -1;\n  height: 2px; background: var(--warm-3); border-radius: 999px; overflow: hidden;\n}\n\n.file-upload-row-progress-fill {\n  height: 100%; background: var(--accent); border-radius: 999px;\n  transition: width var(--dur-3) var(--ease);\n}\n\n.file-upload-row-remove {\n  appearance: none; background: transparent; border: 0; cursor: pointer;\n  color: var(--fg-faint);\n  padding: 4px;\n  border-radius: var(--r-xs);\n}\n\n.file-upload-row-size {\n  font: 400 12px/1.3 var(--f-mono);\n  color: var(--fg-dim);\n}\n\n.file-upload-thumbnail {\n  position: relative;\n  aspect-ratio: 1;\n  border-radius: var(--r-sm);\n  background: var(--bg-sunken);\n  border: 1px solid var(--hair);\n  overflow: hidden;\n  display: flex; align-items: center; justify-content: center;\n}\n\n.file-upload-thumbnail-fallback {\n  font: 500 11px/1.3 var(--f-mono);\n  color: var(--fg-dim);\n  padding: var(--s-2);\n  text-align: center;\n  word-break: break-word;\n}\n\n.file-upload-thumbnail-remove {\n  position: absolute; top: 4px; right: 4px;\n  width: 22px; height: 22px;\n  border-radius: 50%;\n  background: color-mix(in oklab, var(--ink) 55%, transparent);\n  color: var(--paper);\n  border: 0; cursor: pointer;\n  display: inline-flex; align-items: center; justify-content: center;\n  font: 600 12px/1 var(--f-mono);\n}\n\n.file-upload-thumbnails {\n  display: grid; gap: var(--s-3);\n  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));\n  list-style: none; margin: 0; padding: 0;\n}"
  }
}
