db8b43b0e8
The press page interleaved active downloads, queued items, and errors in one long list — hard to see what's actually pressing. Split the non-imported rows into three tabs (Active / In queue / Errors) with live counts; the default tab follows the work (Active if anything's running, else Queue, else Errors) until the user picks one. Move the press-level actions (Pause/Resume, Clear queue, Stop now) off the toolbar into a vertical "⋮" kebab menu on the right of the header — Clear queue disables when the queue is empty, Stop now only shows while downloading. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
48 lines
1.4 KiB
TypeScript
48 lines
1.4 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useRef, useState, type ReactNode } from "react";
|
|
|
|
/** A vertical "⋮" trigger that opens a small popup menu. Closes on outside click, Escape, or any
|
|
* click inside the popup (so menu items don't each need to manage it). Children are the items —
|
|
* use <button className="menu-item">…</button>. */
|
|
export function KebabMenu({ label = "Actions", children }: { label?: string; children: ReactNode }) {
|
|
const [open, setOpen] = useState(false);
|
|
const ref = useRef<HTMLDivElement>(null);
|
|
|
|
useEffect(() => {
|
|
if (!open) return;
|
|
function onDoc(e: MouseEvent) {
|
|
if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
|
|
}
|
|
function onKey(e: KeyboardEvent) {
|
|
if (e.key === "Escape") setOpen(false);
|
|
}
|
|
document.addEventListener("mousedown", onDoc);
|
|
document.addEventListener("keydown", onKey);
|
|
return () => {
|
|
document.removeEventListener("mousedown", onDoc);
|
|
document.removeEventListener("keydown", onKey);
|
|
};
|
|
}, [open]);
|
|
|
|
return (
|
|
<div className="menu" ref={ref}>
|
|
<button
|
|
type="button"
|
|
className="menu-trigger"
|
|
aria-haspopup="menu"
|
|
aria-expanded={open}
|
|
aria-label={label}
|
|
onClick={() => setOpen((o) => !o)}
|
|
>
|
|
⋮
|
|
</button>
|
|
{open ? (
|
|
<div className="menu-pop" role="menu" onClick={() => setOpen(false)}>
|
|
{children}
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|