{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "multiple-image-upload-form-demo", "dependencies": [ "react-hook-form", "@hookform/resolvers/zod", "zod" ], "registryDependencies": [ "https://reusables.vercel.app/r/multiple-image-upload", "button", "input", "form", "card" ], "files": [ { "path": "registry/example/multiple-image-upload-form-demo.tsx", "content": "\"use client\"\n\nimport React from \"react\"\nimport { toast } from \"@/packages/notify/src\"\nimport { zodResolver } from \"@hookform/resolvers/zod\"\nimport { useForm } from \"react-hook-form\"\nimport { z } from \"zod\"\n\nimport { useMultipleFileUpload } from \"@/hooks/use-multiple-file-upload\"\nimport { Button } from \"@/components/ui/button\"\nimport { Card, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\"\nimport {\n Form,\n FormControl,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"@/components/ui/form\"\nimport { Input } from \"@/components/ui/input\"\n\nimport {\n FileState,\n MultiImageDropzone,\n} from \"@/components/multiple-image-upload\"\n\nconst formSchema = z.object({\n title: z.string().min(2).max(50),\n images: z.array(z.string()).min(1, \"At least one image is required\"),\n})\n\ntype FormValues = z.infer\n\nexport default function MultipleImageUploadFormDemo() {\n const {\n uploadMultipleFiles,\n isUploading,\n error,\n uploadProgress,\n uploadResults,\n } = useMultipleFileUpload({\n onSuccess: (urls) => {\n // Update form with uploaded URLs\n form.setValue(\"images\", urls)\n toast.success(\"Images uploaded successfully!\")\n },\n onError: (error) => {\n toast.error(error)\n },\n })\n\n const form = useForm({\n resolver: zodResolver(formSchema),\n defaultValues: {\n title: \"\",\n images: [],\n },\n })\n\n const [fileStates, setFileStates] = React.useState([])\n\n // Update progress and completion status\n React.useEffect(() => {\n if (uploadProgress.length || uploadResults.length) {\n setFileStates((fileStates) => {\n const newFileStates = structuredClone(fileStates)\n\n // Update progress\n uploadProgress.forEach(({ key, progress }) => {\n const fileState = newFileStates.find(\n (fileState) => fileState.key === key\n )\n if (fileState) {\n fileState.progress = progress\n }\n })\n\n // Update completed files\n uploadResults.forEach(({ url, filename }, index) => {\n const fileState = newFileStates.find(\n (fileState) =>\n typeof fileState.file !== \"string\" &&\n fileState.file.name === filename\n )\n if (fileState) {\n ;((fileState.file = url), (fileState.progress = \"COMPLETE\"))\n }\n })\n\n return newFileStates\n })\n }\n }, [uploadProgress, uploadResults])\n\n // Update the onUpload handler\n const handleUpload = async (files: File[]) => {\n // Create file states first to show immediate preview\n const newFileStates = files.map((file) => ({\n file,\n key: Math.random().toString(36).slice(2),\n progress: 0, // Start with 0 progress instead of PENDING\n }))\n\n // Update state to show previews immediately\n setFileStates((prev) => [...prev, ...newFileStates])\n\n // Start upload\n const filesToUpload = newFileStates.map(({ key, file }) => ({\n key,\n file: file as File,\n }))\n\n await uploadMultipleFiles(filesToUpload)\n }\n\n async function onSubmit(values: FormValues) {\n toast.promise(() => new Promise((resolve) => setTimeout(resolve, 2000)), {\n loading: \"Creating gallery...\",\n success: () => `Gallery created successfully!`,\n error: \"Failed to create gallery\",\n })\n console.log(values)\n form.reset()\n setFileStates([])\n }\n\n return (\n \n \n Create Image Gallery\n \n \n
\n \n (\n \n Gallery Title\n \n \n \n \n \n )}\n />\n (\n \n Gallery Images\n \n \n \n \n \n )}\n />\n \n \n \n
\n
\n )\n}\n", "type": "registry:example", "target": "components/multiple-image-upload-form-demo.tsx" } ], "type": "registry:example" }