Photonix

UploadQueueModal

A floating modal docked in the bottom-right corner of the screen that tracks ongoing file uploads. Mimics the behavior of Google Drive's upload progress widget with minimize/expand transitions.

Preview

Usage
"use client";

import { UploadQueueModal } from '@photonix/ultimate';

const files = [
    { id: '1', name: 'brand-assets.zip', size: 4_200_000, status: 'uploading' as const, progress: 64 },
    { id: '2', name: 'cover.png', size: 820_000, status: 'success' as const, progress: 100 },
    { id: '3', name: 'brief.pdf', size: 380_000, status: 'pending' as const, progress: 0 },
];

export default function UploadQueueModalBasicExample() {
    return <UploadQueueModal open files={files} title="Uploading 3 items" />;
}

Component API

UploadQueueModal

Prop
Type
Default
Description
open
boolean
-
Whether the modal is open
onClose
(() => void)
-
Callback when the modal is closed
files
UploadQueueItem[]
[]
List of files in the upload queue
onCancelItem
((id: string) => void)
-
Callback when a file is cancelled/removed
title
string
-
Optional title to override the auto-generated one
defaultMinimized
boolean
false
Whether the modal should start minimized
className
string
-
Additional class name

Variants

Basic Queue

Displays the upload queue in various states (Uploading, Success, Error, Pending).

Basic Queue
"use client";

import { useState } from 'react';
import { Button, Flex, UploadQueueModal, type UploadQueueItem } from '@photonix/ultimate';

export default function UploadQueueModalBasicQueueExample() {
    const [open, setOpen] = useState(false);
    const [files, setFiles] = useState<UploadQueueItem[]>([
        { id: '1', name: 'quarterly_report.pdf', size: 5242880, status: 'success', progress: 100 },
        { id: '2', name: 'corporate_video.mp4', size: 104857600, status: 'uploading', progress: 45 },
        { id: '3', name: 'unsupported_archive.rar', size: 26214400, status: 'error', progress: 0, error: 'File size limit exceeded' },
        { id: '4', name: 'profile_picture.png', size: 1572864, status: 'pending', progress: 0 },
    ]);

    return (
        <Flex gap="md">
            <Button onClick={() => setOpen(true)}>Open Static Queue</Button>
            <UploadQueueModal open={open} onClose={() => setOpen(false)} files={files} onCancelItem={(id) => setFiles((prev) => prev.filter((f) => f.id !== id))} />
        </Flex>
    );
}

Interactive Simulator

Trigger mock uploads in real-time. Files progress automatically and show success/error states.

Interactive Simulator
"use client";

import { useState } from 'react';
import { Button, Flex, UploadQueueModal, type UploadQueueItem } from '@photonix/ultimate';

export default function UploadQueueModalInteractiveExample() {
    const [open, setOpen] = useState(false);
    const [files, setFiles] = useState<UploadQueueItem[]>([]);

    const simulateUpload = (id: string) => {
        setFiles((prev) => prev.map((f) => f.id === id ? { ...f, status: 'uploading' } : f));
        let progress = 0;
        const interval = setInterval(() => {
            progress += Math.floor(Math.random() * 15) + 5;
            if (progress >= 100) {
                progress = 100;
                clearInterval(interval);
                setFiles((prev) => prev.map((f) => {
                    if (f.id === id) {
                        const isError = f.name.includes('corrupted');
                        return { ...f, status: isError ? 'error' : 'success', progress: 100, error: isError ? 'Forbidden file signature detected' : undefined };
                    }
                    return f;
                }));
            } else {
                setFiles((prev) => prev.map((f) => f.id === id ? { ...f, progress } : f));
            }
        }, 200);
    };

    const handleAddUploads = () => {
        setOpen(true);
        const newItems: UploadQueueItem[] = [
            { id: Math.random().toString(36).slice(2), name: 'spreadsheet_data.xlsx', size: 327680, status: 'pending', progress: 0 },
            { id: Math.random().toString(36).slice(2), name: 'project_intro.mov', size: 52428800, status: 'pending', progress: 0 },
            { id: Math.random().toString(36).slice(2), name: 'corrupted_archive.7z', size: 15728640, status: 'pending', progress: 0 },
        ];
        setFiles((prev) => [...prev, ...newItems]);
        newItems.forEach((item, index) => setTimeout(() => simulateUpload(item.id), index * 800));
    };

    return (
        <Flex gap="md" align="center">
            <Button onClick={() => setOpen(true)}>Open Upload Queue</Button>
            <Button variant="secondary" onClick={handleAddUploads}>Simulate New Uploads</Button>
            {files.length > 0 && <Button variant="tertiary" onClick={() => setFiles([])}>Clear Queue</Button>}
            <UploadQueueModal open={open} onClose={() => setOpen(false)} files={files} onCancelItem={(id) => setFiles((prev) => prev.filter((f) => f.id !== id))} />
        </Flex>
    );
}

Minimized Default

Open the upload status panel minimized by default (showing only the header at the bottom-right).

Minimized Default
"use client";

import { useState } from 'react';
import { Button, Flex, UploadQueueModal, type UploadQueueItem } from '@photonix/ultimate';

const files: UploadQueueItem[] = [
    { id: '1', name: 'minimized_upload.docx', size: 1024 * 128, status: 'uploading', progress: 30 },
];

export default function UploadQueueModalMinimizedExample() {
    const [open, setOpen] = useState(false);

    return (
        <Flex gap="md">
            <Button onClick={() => setOpen(true)}>Open Minimized Queue</Button>
            <UploadQueueModal open={open} onClose={() => setOpen(false)} files={files} defaultMinimized />
        </Flex>
    );
}

On this page

Preview
Component API
Variants
Basic Queue
Interactive Simulator
Minimized Default
Photonix UI - React Components, Templates & Figma Design System