mirror of
https://github.com/hanzoai/shadcn.git
synced 2026-11-03 03:18:52 +00:00
14 lines
3.5 KiB
JSON
14 lines
3.5 KiB
JSON
{
|
|
"name": "dropzone",
|
|
"description": "File upload dropzone",
|
|
"dependencies": [
|
|
"react-dropzone"
|
|
],
|
|
"files": [
|
|
{
|
|
"name": "dropzone.tsx",
|
|
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { File, Upload, X } from \"lucide-react\"\nimport { useDropzone } from \"react-dropzone\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface DropzoneProps extends React.HTMLAttributes<HTMLDivElement> {\n onFilesAccepted?: (files: File[]) => void\n maxFiles?: number\n maxSize?: number\n accept?: Record<string, string[]>\n disabled?: boolean\n}\n\nconst Dropzone = React.forwardRef<HTMLDivElement, DropzoneProps>(\n (\n {\n className,\n onFilesAccepted,\n maxFiles = 5,\n maxSize = 5242880, // 5MB\n accept = {\n \"image/*\": [\".png\", \".jpg\", \".jpeg\", \".gif\", \".webp\"],\n \"application/pdf\": [\".pdf\"],\n },\n disabled = false,\n ...props\n },\n ref\n ) => {\n const [files, setFiles] = React.useState<File[]>([])\n\n const { getRootProps, getInputProps, isDragActive } = useDropzone({\n onDrop: (acceptedFiles) => {\n setFiles((prev) => [...prev, ...acceptedFiles].slice(0, maxFiles))\n onFilesAccepted?.(acceptedFiles)\n },\n maxFiles,\n maxSize,\n accept,\n disabled,\n })\n\n const removeFile = (index: number) => {\n setFiles((prev) => prev.filter((_, i) => i !== index))\n }\n\n return (\n <div ref={ref} className={cn(\"w-full\", className)} {...props}>\n <div\n {...getRootProps()}\n className={cn(\n \"cursor-pointer rounded-lg border-2 border-dashed p-8 transition-colors\",\n isDragActive\n ? \"border-primary bg-primary/5\"\n : \"border-muted-foreground/25 hover:border-muted-foreground/50\",\n disabled && \"cursor-not-allowed opacity-50\"\n )}\n >\n <input {...getInputProps()} />\n <div className=\"flex flex-col items-center justify-center gap-2 text-center\">\n <Upload className=\"h-8 w-8 text-muted-foreground\" />\n <div>\n <p className=\"text-sm font-medium\">\n {isDragActive ? \"Drop files here\" : \"Drag & drop files here\"}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n or click to browse (max {maxFiles} files,{\" \"}\n {Math.round(maxSize / 1024 / 1024)}MB each)\n </p>\n </div>\n </div>\n </div>\n\n {files.length > 0 && (\n <div className=\"mt-4 space-y-2\">\n {files.map((file, index) => (\n <div\n key={index}\n className=\"flex items-center gap-2 rounded-lg border p-2\"\n >\n <File className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"flex-1 truncate text-sm\">{file.name}</span>\n <span className=\"text-xs text-muted-foreground\">\n {(file.size / 1024).toFixed(1)} KB\n </span>\n <button\n onClick={() => removeFile(index)}\n className=\"rounded p-1 hover:bg-muted\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n ))}\n </div>\n )}\n </div>\n )\n }\n)\nDropzone.displayName = \"Dropzone\"\n\nexport { Dropzone }\n"
|
|
}
|
|
],
|
|
"type": "components:ui"
|
|
} |