Spaces:
Sleeping
Sleeping
Download frontend/src/components/ExportButton.tsx from Hamza4100/cookie-har-analyzer: direct link, hf CLI and curl.
- Browser
- Download file 2.58 kB
-
https://huggingface.co/spaces/Hamza4100/cookie-har-analyzer/resolve/main/frontend/src/components/ExportButton.tsx
- Command line
-
hf download hf://spaces/Hamza4100/cookie-har-analyzer/frontend/src/components/ExportButton.tsx
-
curl -L -o ExportButton.tsx https://huggingface.co/spaces/Hamza4100/cookie-har-analyzer/resolve/main/frontend/src/components/ExportButton.tsx
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> | |
| ); | |
| } | |