Progresso do Upload
O Dropup oferece rastreamento detalhado de progresso para todos os uploads.
Rastreamento de Progresso
Progresso Individual do Arquivo
Cada arquivo tem uma propriedade progress (0-100):
const { files } = useDropup({
upload: { url: '/api/upload' },
});
{files.map(file => (
<div key={file.id}>
<span>{file.name}</span>
{file.status === 'uploading' && (
<progress value={file.progress} max={100} />
)}
</div>
))}
Progresso Geral
Rastreie o progresso de todos os arquivos:
const { state } = useDropup();
// state.progress - Progresso médio de todos os arquivos (0-100)
// state.isUploading - Se algum upload está em andamento
<div>
{state.isUploading && (
<p>Enviando: {state.progress}%</p>
)}
</div>
Callbacks de Progresso
Progresso Por Arquivo
useDropup({
upload: { url: '/api/upload' },
onUploadProgress: (file, progress) => {
console.log(`${file.name}: ${progress}%`);
},
});
Eventos de Upload
useDropup({
upload: { url: '/api/upload' },
onUploadStart: (file) => {
console.log(`Iniciado: ${file.name}`);
},
onUploadProgress: (file, progress) => {
console.log(`Progresso: ${file.name} - ${progress}%`);
},
onUploadComplete: (file, response) => {
console.log(`Completo: ${file.name}`);
console.log(`URL: ${file.uploadedUrl}`);
},
onUploadError: (file, error) => {
console.error(`Falhou: ${file.name}`, error);
},
onAllComplete: (files) => {
console.log(`Tudo pronto! ${files.length} arquivos enviados`);
},
});
Construindo UI de Progresso
Barra de Progresso Simples
function ProgressBar({ value }: { value: number }) {
return (
<div style={{
width: '100%',
height: 8,
backgroundColor: '#e0e0e0',
borderRadius: 4,
}}>
<div style={{
width: `${value}%`,
height: '100%',
backgroundColor: '#4caf50',
borderRadius: 4,
transition: 'width 0.2s',
}} />
</div>
);
}
// Uso
{file.status === 'uploading' && (
<ProgressBar value={file.progress} />
)}
Progresso Circular
function CircularProgress({ value, size = 40 }: { value: number; size?: number }) {
const strokeWidth = 4;
const radius = (size - strokeWidth) / 2;
const circumference = radius * 2 * Math.PI;
const offset = circumference - (value / 100) * circumference;
return (
<svg width={size} height={size}>
<circle
stroke="#e0e0e0"
fill="transparent"
strokeWidth={strokeWidth}
r={radius}
cx={size / 2}
cy={size / 2}
/>
<circle
stroke="#4caf50"
fill="transparent"
strokeWidth={strokeWidth}
strokeLinecap="round"
strokeDasharray={circumference}
strokeDashoffset={offset}
r={radius}
cx={size / 2}
cy={size / 2}
style={{
transform: 'rotate(-90deg)',
transformOrigin: '50% 50%',
transition: 'stroke-dashoffset 0.2s',
}}
/>
<text
x="50%"
y="50%"
textAnchor="middle"
dy=".3em"
fontSize={size / 4}
>
{value}%
</text>
</svg>
);
}
Card Completo de Upload de Arquivo
function FileUploadCard({ file }: { file: DropupFile }) {
const getStatusIcon = () => {
switch (file.status) {
case 'idle': return '⏳';
case 'uploading': return '📤';
case 'complete': return '✅';
case 'error': return '❌';
case 'paused': return '⏸️';
default: return '📄';
}
};
const getStatusColor = () => {
switch (file.status) {
case 'uploading': return '#2196f3';
case 'complete': return '#4caf50';
case 'error': return '#f44336';
default: return '#9e9e9e';
}
};
return (
<div style={{
display: 'flex',
alignItems: 'center',
padding: 12,
border: '1px solid #e0e0e0',
borderRadius: 8,
marginBottom: 8,
}}>
{/* Preview ou Ícone */}
{file.preview ? (
<img
src={file.preview}
alt=""
style={{ width: 48, height: 48, objectFit: 'cover', borderRadius: 4 }}
/>
) : (
<span style={{ fontSize: 32 }}>{getStatusIcon()}</span>
)}
{/* Informações do Arquivo */}
<div style={{ flex: 1, marginLeft: 12 }}>
<div style={{ fontWeight: 500 }}>{file.name}</div>
<div style={{ fontSize: 12, color: '#666' }}>
{(file.size / 1024).toFixed(1)} KB
</div>
{/* Barra de Progresso */}
{file.status === 'uploading' && (
<div style={{
marginTop: 4,
height: 4,
backgroundColor: '#e0e0e0',
borderRadius: 2,
}}>
<div style={{
width: `${file.progress}%`,
height: '100%',
backgroundColor: getStatusColor(),
borderRadius: 2,
}} />
</div>
)}
{/* Mensagem de Erro */}
{file.error && (
<div style={{ color: '#f44336', fontSize: 12, marginTop: 4 }}>
{file.error.message}
</div>
)}
</div>
{/* Badge de Status */}
<span style={{
padding: '4px 8px',
borderRadius: 4,
fontSize: 12,
color: 'white',
backgroundColor: getStatusColor(),
}}>
{file.status === 'uploading' ? `${file.progress}%` : file.status}
</span>
</div>
);
}
Contadores de Status
Rastreie contadores de status de upload:
const { files, state } = useDropup();
const contadores = {
total: files.length,
pendentes: files.filter(f => f.status === 'idle').length,
enviando: files.filter(f => f.status === 'uploading').length,
completos: files.filter(f => f.status === 'complete').length,
falhos: files.filter(f => f.status === 'error').length,
};
// Exibição
<div>
<p>Total: {contadores.total}</p>
<p>Enviando: {contadores.enviando}</p>
<p>Completos: {contadores.completos}</p>
<p>Falhos: {contadores.falhos}</p>
</div>
Velocidade de Upload & Tempo Estimado
Calcule velocidade de upload e tempo estimado:
import { useState, useRef, useEffect } from 'react';
function useUploadSpeed(file: DropupFile) {
const [speed, setSpeed] = useState(0);
const [eta, setEta] = useState<number | null>(null);
const lastProgress = useRef(0);
const lastTime = useRef(Date.now());
useEffect(() => {
if (file.status !== 'uploading') return;
const interval = setInterval(() => {
const now = Date.now();
const elapsed = (now - lastTime.current) / 1000;
const progressDiff = file.progress - lastProgress.current;
const bytesDiff = (progressDiff / 100) * file.size;
if (elapsed > 0) {
const currentSpeed = bytesDiff / elapsed;
setSpeed(currentSpeed);
const remaining = file.size * (1 - file.progress / 100);
if (currentSpeed > 0) {
setEta(remaining / currentSpeed);
}
}
lastProgress.current = file.progress;
lastTime.current = now;
}, 1000);
return () => clearInterval(interval);
}, [file.status, file.progress, file.size]);
return { speed, eta };
}
// Uso
function FileProgress({ file }: { file: DropupFile }) {
const { speed, eta } = useUploadSpeed(file);
return (
<div>
<p>Velocidade: {formatSpeed(speed)}</p>
<p>Tempo restante: {formatTime(eta)}</p>
</div>
);
}
function formatSpeed(bytesPerSecond: number): string {
if (bytesPerSecond < 1024) return `${bytesPerSecond.toFixed(0)} B/s`;
if (bytesPerSecond < 1024 * 1024) return `${(bytesPerSecond / 1024).toFixed(1)} KB/s`;
return `${(bytesPerSecond / 1024 / 1024).toFixed(1)} MB/s`;
}
function formatTime(seconds: number | null): string {
if (!seconds) return '--';
if (seconds < 60) return `${Math.ceil(seconds)}s`;
return `${Math.ceil(seconds / 60)}m`;
}