Spaces:
Sleeping
Sleeping
File size: 2,839 Bytes
5575628 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | "use client";
import { useState, useRef, useEffect } from "react";
import { FileDown, ChevronDown, Loader2 } from "lucide-react";
import { EXPORT_FORMATS } from "@/lib/export-utils";
import type { AnalysisResult } from "@/lib/types";
export function ExportDropdown({ results }: { results: AnalysisResult }) {
const [open, setOpen] = useState(false);
const [exporting, setExporting] = useState<string | null>(null);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
function handleClickOutside(e: MouseEvent) {
if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
}
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
async function handleExport(key: string, fn: (r: AnalysisResult) => void | Promise<any>) {
setExporting(key);
try {
await fn(results);
} catch (e) {
console.error("Export failed:", e);
}
setExporting(null);
setOpen(false);
}
return (
<div ref={ref} className="relative">
<button
onClick={() => setOpen(!open)}
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-zinc-600 bg-white border border-zinc-200 rounded-lg hover:bg-zinc-50 hover:border-zinc-300 transition-all shadow-sm"
>
<FileDown className="w-3.5 h-3.5" />
Export
<ChevronDown className={`w-3 h-3 transition-transform ${open ? "rotate-180" : ""}`} />
</button>
{open && (
<div className="absolute right-0 top-full mt-1.5 w-64 bg-white border border-zinc-200 rounded-xl shadow-xl z-50 overflow-hidden animate-in fade-in slide-in-from-top-1 duration-150">
<div className="px-3 py-2 border-b border-zinc-100">
<p className="text-[10px] font-semibold text-zinc-400 uppercase tracking-wider">Export Report</p>
</div>
<div className="py-1">
{EXPORT_FORMATS.map((fmt) => (
<button
key={fmt.key}
onClick={() => handleExport(fmt.key, fmt.fn)}
disabled={exporting !== null}
className="w-full flex items-center gap-3 px-3 py-2.5 text-left hover:bg-zinc-50 transition-colors disabled:opacity-40"
>
<span className="text-base w-5 text-center">{fmt.icon}</span>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-zinc-700">{fmt.label}</p>
<p className="text-[10px] text-zinc-400">{fmt.description}</p>
</div>
{exporting === fmt.key && <Loader2 className="w-3.5 h-3.5 text-zinc-400 animate-spin" />}
</button>
))}
</div>
</div>
)}
</div>
);
}
|