{"$schema":"https://ui.logic2b.com/schema/registry-item.json","name":"file-dropzone","type":"registry:ui","title":"File Dropzone","description":"A drag-and-drop file surface with click/keyboard to browse. Native DnD (no dependencies): highlights while a file is dragged over it and calls onFiles with the dropped or selected files. Supports accept, multiple and disabled; rendering the file list is left to the consumer.","dependencies":["lucide-react"],"registryDependencies":["utils"],"files":[{"path":"ui/file-dropzone.tsx","type":"registry:ui","content":"\"use client\"\n\nimport * as React from \"react\"\nimport { UploadIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface FileDropzoneProps\n  extends Omit<React.ComponentProps<\"div\">, \"onDrop\" | \"children\"> {\n  /** Called with the accepted files when they are dropped or selected. */\n  onFiles?: (files: File[]) => void\n  /** `accept` attribute forwarded to the file input (e.g. `\"image/*\"`). */\n  accept?: string\n  /** Allow selecting more than one file. Default `false`. */\n  multiple?: boolean\n  disabled?: boolean\n  /** Accessible name for the native file input. Default `\"Upload files\"`. */\n  inputLabel?: string\n  /** Replace the default icon/prompt with your own content. */\n  children?: React.ReactNode\n}\n\n/**\n * A drag-and-drop file surface with click / keyboard to browse. Native DnD (no\n * dependencies): highlights while a file is dragged over it and calls `onFiles`\n * with the dropped or selected files. Rendering the file list is left to the\n * consumer.\n */\nfunction FileDropzone({\n  className,\n  onFiles,\n  accept,\n  multiple = false,\n  disabled,\n  inputLabel = \"Upload files\",\n  children,\n  ...props\n}: FileDropzoneProps) {\n  const inputRef = React.useRef<HTMLInputElement>(null)\n  const [dragging, setDragging] = React.useState(false)\n\n  const emit = (list: FileList | null) => {\n    if (!list || list.length === 0) return\n    onFiles?.(Array.from(list))\n  }\n\n  const open = () => {\n    if (!disabled) inputRef.current?.click()\n  }\n\n  return (\n    <>\n      <input\n        ref={inputRef}\n        type=\"file\"\n        tabIndex={-1}\n        aria-label={inputLabel}\n        accept={accept}\n        multiple={multiple}\n        disabled={disabled}\n        className=\"sr-only\"\n        onChange={(e) => {\n          emit(e.target.files)\n          e.target.value = \"\"\n        }}\n      />\n      <div\n        data-slot=\"file-dropzone\"\n        data-dragging={dragging ? \"\" : undefined}\n        data-disabled={disabled ? \"\" : undefined}\n        role=\"button\"\n        tabIndex={disabled ? -1 : 0}\n        aria-disabled={disabled}\n        onClick={open}\n        onKeyDown={(e) => {\n          if (e.key === \"Enter\" || e.key === \" \") {\n            e.preventDefault()\n            open()\n          }\n        }}\n        onDragOver={(e) => {\n          e.preventDefault()\n          if (!disabled) setDragging(true)\n        }}\n        onDragLeave={() => setDragging(false)}\n        onDrop={(e) => {\n          e.preventDefault()\n          setDragging(false)\n          if (!disabled) emit(e.dataTransfer.files)\n        }}\n        className={cn(\n          \"border-input flex flex-col items-center justify-center gap-2 rounded-lg border border-dashed p-8 text-center transition-colors outline-none\",\n          \"hover:bg-accent/40 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]\",\n          \"data-dragging:border-ring data-dragging:bg-accent/60\",\n          \"data-disabled:pointer-events-none data-disabled:opacity-50\",\n          className\n        )}\n        {...props}\n      >\n        {children ?? (\n          <>\n            <UploadIcon className=\"text-muted-foreground size-6\" />\n            <div className=\"text-sm\">\n              <span className=\"font-medium\">Click to upload</span>\n              <span className=\"text-muted-foreground\"> or drag and drop</span>\n            </div>\n          </>\n        )}\n      </div>\n    </>\n  )\n}\n\nexport { FileDropzone }\n"}],"categories":["form"],"accessibility":{"support":"authored","pattern":"file upload","primitive":"HTMLInputElement type=file","keyboard":[{"keys":["Enter","Space"],"action":"Activate the focused control."}],"aria":["A hidden native file input owns the picker; the visible drop surface is keyboard activatable."],"consumer":["Provide contextual instructions, accepted formats and validation feedback."]},"api":{"source":"src/ui/file-dropzone.tsx","exports":[{"name":"FileDropzoneProps","kind":"type","definition":"interface FileDropzoneProps extends Omit<React.ComponentProps<\"div\">, \"onDrop\" | \"children\"> { /** Called with the accepted files when they are dropped or selected. */ onFiles?: (files: File[]) => void /** `accept` attribute forwarded to the file input (e.g. `\"image/*\"`). */ accept?: string /** Allow selecting more than one file. Default `false`. */ multiple?: boolean disabled?: boolean /** Accessible name for the native file input. Default `\"Upload files\"`. */ inputLabel?: string /** Replace the default icon/prompt with your own content. */ children?: React.ReactNode }"},{"name":"FileDropzone","kind":"component","description":"A drag-and-drop file surface with click / keyboard to browse. Native DnD (no dependencies): highlights while a file is dragged over it and calls `onFiles` with the dropped or selected files. Rendering the file list is left to the consumer.","propsType":"FileDropzoneProps","props":[{"name":"onFiles","type":"(files: File[]) => void","required":false,"description":"Called with the accepted files when they are dropped or selected."},{"name":"accept","type":"string","required":false,"description":"`accept` attribute forwarded to the file input (e.g. `\"image/*\"`)."},{"name":"multiple","type":"boolean","required":false,"default":"false","description":"Allow selecting more than one file. Default `false`."},{"name":"disabled","type":"boolean","required":false},{"name":"inputLabel","type":"string","required":false,"default":"\"Upload files\"","description":"Accessible name for the native file input. Default `\"Upload files\"`."},{"name":"children","type":"React.ReactNode","required":false,"description":"Replace the default icon/prompt with your own content."}]}]}}