Upload Progress Queue
retab-ui/dropzone-upload-progress-queueFreeBlock
A dropzone-backed upload queue with per-file loading bars keyed by the admitted file ids.
Install it
npx shadcn@latest add https://ui.retab.com/r/dropzone-upload-progress-queue.jsonSource
1"use client";23import * as React from "react";4import { CheckCircle2, FileText, UploadCloud, X } from "lucide-react";5import { useKeyedMountEffect } from "@/hooks/use-keyed-mount-effect";6import { useMountEffect } from "@/hooks/use-mount-effect";78import { cn } from "@/lib/utils";9import { useDropzone } from "@/components/ui/dropzone";10import { formatFileSize } from "@/components/ui/file-size-format";1112import {13 RejectionRows,14 type DropzoneExampleProps,15} from "./dropzone-example-shared";1617type UploadState = {18 progress: number;19 status: "uploading" | "done";20};2122/**23 * Dropzone owns file intake; the upload transport is the consumer's job. This24 * example simulates a per-file upload and renders its progress. Real code would25 * swap the interval for `fetch`/`XMLHttpRequest` with an `onprogress` handler.26 */27export function UploadProgressQueue({ className }: DropzoneExampleProps) {28 const dropzone = useDropzone({ maxFiles: 5, multiple: true });29 const [uploads, setUploads] = React.useState<Record<string, UploadState>>({});30 const timers = React.useRef<Record<string, ReturnType<typeof setInterval>>>(31 {},32 );33 const dropzoneFilesKey = React.useMemo(34 () => dropzone.files.map((item) => item.id).join("\0"),35 [dropzone.files],36 );3738 // Start a simulated upload for every file the dropzone admits that we have39 // not seen yet. The dropzone owns the canonical file id, so we key off it.40 useKeyedMountEffect(dropzoneFilesKey, () => {41 dropzone.files.forEach((item) => {42 if (timers.current[item.id] !== undefined) return;4344 setUploads((prev) =>45 prev[item.id]46 ? prev47 : { ...prev, [item.id]: { progress: 0, status: "uploading" } },48 );4950 timers.current[item.id] = setInterval(() => {51 setUploads((prev) => {52 const current = prev[item.id];53 if (!current || current.status === "done") return prev;5455 const progress = Math.min(56 100,57 current.progress + 9 + Math.random() * 11,58 );59 if (progress >= 100) {60 clearInterval(timers.current[item.id]);61 delete timers.current[item.id];62 return { ...prev, [item.id]: { progress: 100, status: "done" } };63 }64 return { ...prev, [item.id]: { progress, status: "uploading" } };65 });66 }, 280);67 });68 });6970 useMountEffect(() => {71 const pending = timers.current;72 return () => {73// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from retab-ui
All 130 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Classify Consensusclassify-consensus-viewer-block | retab-ui | Blocks | Free | 1 dep · 4 files | |
| CSV Sourcescsv-sources-block | retab-ui | Blocks | Free | no deps · 2 files | |
| DOCX Sourcesdocx-sources-block | retab-ui | Blocks | Free | no deps · 2 files | |
| Avatar Image Slotdropzone-avatar-image-slot | retab-ui | Blocks | Free | 1 dep · 2 files | |
| Dropzonedropzone-block | retab-ui | Blocks | Free | 1 dep · 26 files | |
| Comparison Pairdropzone-comparison-pair-upload | retab-ui | Blocks | Free | 1 dep · 2 files | |
| Controlled Queuedropzone-controlled-queue | retab-ui | Blocks | Free | 1 dep · 2 files | |
| Custom Thumbnail Griddropzone-custom-thumbnail-grid | retab-ui | Blocks | Free | 1 dep · 2 files |
