{ "name": "file-upload-chat-input-demo", "type": "registry:example", "dependencies": [ "lucide-react", "sonner", "uploadthing", "@uploadthing/react" ], "registryDependencies": [ "button", "file-upload", "textarea" ], "files": [ { "path": "examples/file-upload-chat-input-demo.tsx", "type": "registry:example", "content": "\"use client\";\n\nimport { ArrowUp, Paperclip, Upload, X } from \"lucide-react\";\nimport * as React from \"react\";\nimport { toast } from \"sonner\";\nimport { Button } from \"@/registry/bases/base/ui/button\";\nimport {\n FileUpload,\n FileUploadDropzone,\n FileUploadItem,\n FileUploadItemDelete,\n FileUploadItemMetadata,\n FileUploadItemPreview,\n FileUploadItemProgress,\n FileUploadList,\n type FileUploadProps,\n FileUploadTrigger,\n} from \"@/registry/bases/base/ui/file-upload\";\nimport { Textarea } from \"@/registry/bases/base/ui/textarea\";\n\nexport default function FileUploadChatInputDemo() {\n const [input, setInput] = React.useState(\"\");\n const [files, setFiles] = React.useState([]);\n const [isUploading, setIsUploading] = React.useState(false);\n\n const onInputChange = React.useCallback(\n (event: React.ChangeEvent) => {\n setInput(event.target.value);\n },\n [],\n );\n\n const onUpload: NonNullable = React.useCallback(\n async (files, { onProgress, onSuccess, onError }) => {\n try {\n setIsUploading(true);\n // Process each file individually\n const uploadPromises = files.map(async (file) => {\n try {\n // Simulate file upload with progress\n const totalChunks = 10;\n let uploadedChunks = 0;\n\n // Simulate chunk upload with delays\n for (let i = 0; i < totalChunks; i++) {\n // Simulate network delay (100-300ms per chunk)\n await new Promise((resolve) =>\n setTimeout(resolve, Math.random() * 200 + 100),\n );\n\n // Update progress for this specific file\n uploadedChunks++;\n const progress = (uploadedChunks / totalChunks) * 100;\n onProgress(file, progress);\n }\n\n // Simulate server processing delay\n await new Promise((resolve) => setTimeout(resolve, 500));\n onSuccess(file);\n } catch (error) {\n onError(\n file,\n error instanceof Error ? error : new Error(\"Upload failed\"),\n );\n } finally {\n setIsUploading(false);\n }\n });\n\n // Wait for all uploads to complete\n await Promise.all(uploadPromises);\n } catch (error) {\n // This handles any error that might occur outside the individual upload processes\n console.error(\"Unexpected error during upload:\", error);\n }\n },\n [],\n );\n\n const onFileReject = React.useCallback((file: File, message: string) => {\n toast(message, {\n description: `\"${file.name.length > 20 ? `${file.name.slice(0, 20)}...` : file.name}\" has been rejected`,\n });\n }, []);\n\n const onSubmit = React.useCallback(\n (event: React.FormEvent) => {\n event.preventDefault();\n setInput(\"\");\n setFiles([]);\n },\n [],\n );\n\n return (\n \n event.preventDefault()}\n className=\"absolute top-0 left-0 z-0 flex size-full items-center justify-center rounded-none border-none bg-background/50 p-0 opacity-0 backdrop-blur transition-opacity duration-200 ease-out data-dragging:z-10 data-dragging:opacity-100\"\n >\n
\n
\n \n
\n

Drag & drop files here

\n

\n Upload max 5 files each up to 5MB\n

\n
\n \n \n \n {files.map((file, index) => (\n \n svg]:size-5\">\n \n \n \n (\n \n \n \n )}\n />\n \n ))}\n \n \n
\n (\n \n \n Attach file\n \n )}\n />\n \n \n Send message\n \n
\n \n \n );\n}\n", "target": "" } ] }