Add hover popover to nav download indicator
Shows each active download (title + progress bar) and background task (label, phase, done/total + bar) on hover. Pure CSS group-hover, no JS state. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -114,10 +114,10 @@ function DownloadIndicator() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className="relative group shrink-0">
|
||||||
<Link
|
<Link
|
||||||
to="/downloads"
|
to="/downloads"
|
||||||
className="flex items-center gap-1.5 px-2 py-1 rounded-lg bg-zinc-800 hover:bg-zinc-700 transition-colors text-xs text-zinc-300 shrink-0"
|
className="flex items-center gap-1.5 px-2 py-1 rounded-lg bg-zinc-800 hover:bg-zinc-700 transition-colors text-xs text-zinc-300"
|
||||||
title={`${totalActive} task${totalActive > 1 ? "s" : ""} in progress`}
|
|
||||||
>
|
>
|
||||||
<svg className="w-3 h-3 animate-spin text-zinc-400 shrink-0" fill="none" viewBox="0 0 24 24">
|
<svg className="w-3 h-3 animate-spin text-zinc-400 shrink-0" fill="none" viewBox="0 0 24 24">
|
||||||
<circle className="opacity-20" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="3" />
|
<circle className="opacity-20" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="3" />
|
||||||
@@ -128,6 +128,43 @@ function DownloadIndicator() {
|
|||||||
<span className="hidden sm:inline text-zinc-500">+{totalActive - 1}</span>
|
<span className="hidden sm:inline text-zinc-500">+{totalActive - 1}</span>
|
||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
{/* Hover popover */}
|
||||||
|
<div className="absolute top-[calc(100%+6px)] right-0 z-50
|
||||||
|
invisible opacity-0 group-hover:visible group-hover:opacity-100
|
||||||
|
transition-all duration-100 pointer-events-none">
|
||||||
|
<div className="bg-zinc-900 border border-zinc-800 rounded-xl shadow-2xl p-3 min-w-[220px] max-w-[280px] flex flex-col gap-2">
|
||||||
|
{activeDownloads.map((d) => (
|
||||||
|
<div key={d.id}>
|
||||||
|
<div className="flex items-center justify-between gap-2 mb-1">
|
||||||
|
<p className="text-xs text-zinc-200 truncate">{d.video_title || "Downloading…"}</p>
|
||||||
|
<span className="text-[10px] text-zinc-400 tabular-nums shrink-0">{(d.progress_percent ?? 0).toFixed(0)}%</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-0.5 bg-zinc-800 rounded-full overflow-hidden">
|
||||||
|
<div className="h-full bg-accent rounded-full transition-all duration-300" style={{ width: `${d.progress_percent ?? 0}%` }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{tasks.map((task) => {
|
||||||
|
const pct = task.total > 0 ? (task.done / task.total) * 100 : 0;
|
||||||
|
return (
|
||||||
|
<div key={task.id}>
|
||||||
|
<div className="flex items-center justify-between gap-2 mb-1">
|
||||||
|
<p className="text-xs text-zinc-200 truncate">{task.label}</p>
|
||||||
|
{task.total > 0 && (
|
||||||
|
<span className="text-[10px] text-zinc-400 tabular-nums shrink-0">{task.done}/{task.total}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<p className="text-[10px] text-zinc-500 mb-1">{task.phase || "Running…"}</p>
|
||||||
|
<div className="h-0.5 bg-zinc-800 rounded-full overflow-hidden">
|
||||||
|
<div className="h-full bg-accent rounded-full transition-all duration-300" style={{ width: `${pct}%` }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user