{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "use-multiple-file-upload", "dependencies": [ "axios" ], "files": [ { "path": "registry/hooks/use-multiple-file-upload.tsx", "content": "\"use client\"\n\nimport { useEffect, useRef, useState } from \"react\"\nimport axios from \"axios\"\n\nexport type UploadStatus = \"PENDING\" | \"COMPLETE\" | \"ERROR\"\n\ninterface UploadProgress {\n key: string\n progress: number | UploadStatus\n}\n\ninterface UploadResult {\n url: string\n filename: string\n}\n\ninterface FileToUpload {\n key: string\n file: File\n}\n\ninterface UseMultipleFileUploadParams {\n onSuccess?: (urls: string[]) => void\n onError?: (error: string) => void\n}\n\nexport function useMultipleFileUpload({\n onSuccess,\n onError,\n}: UseMultipleFileUploadParams = {}) {\n const [uploadProgress, setUploadProgress] = useState([])\n const [uploadResults, setUploadResults] = useState([])\n const [isUploading, setIsUploading] = useState(false)\n const [error, setError] = useState(null)\n const abortControllersRef = useRef>(new Map())\n const imageUrlsRef = useRef([])\n\n useEffect(() => {\n return () => {\n // Cleanup URLs when component unmounts\n imageUrlsRef.current.forEach((url) => {\n if (url.startsWith(\"blob:\")) {\n URL.revokeObjectURL(url)\n }\n })\n }\n }, [])\n\n const uploadMultipleFiles = async (files: FileToUpload[]) => {\n setIsUploading(true)\n setError(null)\n\n // Initialize progress for all files\n setUploadProgress(\n files.map((file) => ({\n key: file.key,\n progress: \"PENDING\",\n }))\n )\n\n try {\n await Promise.all(\n files.map(async ({ key, file }) => {\n const abortController = new AbortController()\n abortControllersRef.current.set(key, abortController)\n\n try {\n const blobUrl = URL.createObjectURL(file)\n imageUrlsRef.current.push(blobUrl)\n\n const formData = new FormData()\n formData.append(\"file\", file)\n\n // TODO: Replace with your own API endpoint\n const response = await axios.post(\n `https://your-api-endpoint.com`,\n formData,\n {\n signal: abortController.signal,\n onUploadProgress: (progressEvent) => {\n const percentCompleted = Math.round(\n (progressEvent.loaded * 100) / progressEvent.total!\n )\n setUploadProgress((prev) =>\n prev.map((item) =>\n item.key === key ?\n { ...item, progress: percentCompleted }\n : item\n )\n )\n },\n }\n )\n // TODO: Replace with your own API response\n const uploadedUrl = response?.data?.secure_url\n\n // Add small delay to show complete state\n await new Promise((resolve) => setTimeout(resolve, 1000))\n\n setUploadProgress((prev) =>\n prev.map((item) =>\n item.key === key ? { ...item, progress: \"COMPLETE\" } : item\n )\n )\n\n setUploadResults((prev) => [\n ...prev,\n {\n url: uploadedUrl,\n filename: file.name,\n },\n ])\n\n // Cleanup blob URL after successful upload\n URL.revokeObjectURL(blobUrl)\n imageUrlsRef.current = imageUrlsRef.current.filter(\n (url) => url !== blobUrl\n )\n\n abortControllersRef.current.delete(key)\n } catch (error: any) {\n if (axios.isCancel(error)) {\n console.log(\"Upload cancelled for file:\", file.name)\n } else {\n const errorMessage =\n error.response?.data?.message || \"Upload failed\"\n setUploadProgress((prev) =>\n prev.map((item) =>\n item.key === key ? { ...item, progress: \"ERROR\" } : item\n )\n )\n onError?.(errorMessage)\n setError(errorMessage)\n }\n }\n })\n )\n\n const successfulUploads = uploadResults.map((result) => result.url)\n if (successfulUploads.length > 0) {\n onSuccess?.(successfulUploads)\n }\n } catch (error) {\n console.error(\"Error in multiple file upload:\", error)\n } finally {\n setIsUploading(false)\n }\n }\n\n const cancelAllUploads = () => {\n abortControllersRef.current.forEach((controller) => {\n controller.abort()\n })\n abortControllersRef.current.clear()\n setUploadProgress([])\n setUploadResults([])\n setIsUploading(false)\n setError(null)\n }\n\n const resetUploads = () => {\n setUploadProgress([])\n setUploadResults([])\n setIsUploading(false)\n setError(null)\n }\n\n return {\n uploadMultipleFiles,\n uploadProgress,\n uploadResults,\n isUploading,\n error,\n cancelAllUploads,\n resetUploads,\n }\n}\n", "type": "registry:hook", "target": "hooks/use-multiple-file-upload.tsx" } ], "type": "registry:hook" }