Hamza4100's picture
Upload 54 files
5aaf491 verified
Raw History Blame Contribute Delete
2.58 kB
import { Download, FileJson, FileText, FileSpreadsheet, ChevronDown } from 'lucide-react';
import { useState, useRef, useEffect } from 'react';
interface ExportButtonProps {
onExport: (format: 'html' | 'json' | 'csv') => Promise<void>;
}
export function ExportButton({ onExport }: ExportButtonProps) {
const [open, setOpen] = useState(false);
const [exporting, setExporting] = useState<string | null>(null);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const handler = (e: MouseEvent) => {
if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
};
document.addEventListener('mousedown', handler);
return () => document.removeEventListener('mousedown', handler);
}, []);
const handleExport = async (format: 'html' | 'json' | 'csv') => {
setOpen(false);
setExporting(format);
try {
await onExport(format);
} finally {
setExporting(null);
}
};
const options = [
{ key: 'html' as const, label: 'HTML Report', desc: 'Self-contained, shareable', icon: <FileText className="w-4 h-4" /> },
{ key: 'json' as const, label: 'JSON Export', desc: 'Full structured data', icon: <FileJson className="w-4 h-4" /> },
{ key: 'csv' as const, label: 'CSV Summary', desc: 'Spreadsheet-friendly', icon: <FileSpreadsheet className="w-4 h-4" /> },
];
return (
<div className="relative" ref={ref}>
<button
id="export-button"
onClick={() => setOpen(o => !o)}
disabled={!!exporting}
className="btn-secondary gap-1.5"
>
<Download className="w-4 h-4" />
{exporting ? `Exporting ${exporting}…` : 'Export'}
<ChevronDown className={`w-3 h-3 transition-transform ${open ? 'rotate-180' : ''}`} />
</button>
{open && (
<div className="absolute right-0 mt-2 w-52 card shadow-xl z-40 overflow-hidden animate-fade-in">
{options.map(opt => (
<button
key={opt.key}
id={`export-${opt.key}`}
onClick={() => handleExport(opt.key)}
className="w-full flex items-center gap-3 px-4 py-3 text-left hover:bg-white/5 transition-colors border-b border-white/[0.05] last:border-0"
>
<span className="text-blue-400">{opt.icon}</span>
<div>
<p className="text-sm font-medium text-gray-200">{opt.label}</p>
<p className="text-xs text-gray-500">{opt.desc}</p>
</div>
</button>
))}
</div>
)}
</div>
);
}