'use client'; import { useRef, type ReactNode } from 'react'; import { MoreHorizontal, Download, Image as ImageIcon } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { cn } from '@/lib/utils'; interface ChartCardProps { title: string; description?: string; /** Filename stem used for both CSV + PNG exports (no extension). */ exportFilename: string; /** Returns CSV content for the current chart data, or null when nothing to export. */ toCsv?: () => string | null; children: ReactNode; className?: string; } function downloadBlob(blob: Blob, filename: string) { const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url); } async function exportContainerAsPng(container: HTMLElement, filename: string) { const svg = container.querySelector('svg'); if (!svg) return; const clone = svg.cloneNode(true) as SVGSVGElement; const { width, height } = svg.getBoundingClientRect(); clone.setAttribute('width', String(width)); clone.setAttribute('height', String(height)); clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); const xml = new XMLSerializer().serializeToString(clone); const svgBlob = new Blob([xml], { type: 'image/svg+xml;charset=utf-8' }); const url = URL.createObjectURL(svgBlob); const img = new Image(); await new Promise((resolve, reject) => { img.onload = () => resolve(); img.onerror = () => reject(new Error('Failed to load chart for export')); img.src = url; }); const canvas = document.createElement('canvas'); const dpr = window.devicePixelRatio ?? 1; canvas.width = width * dpr; canvas.height = height * dpr; const ctx = canvas.getContext('2d'); if (!ctx) { URL.revokeObjectURL(url); return; } ctx.scale(dpr, dpr); ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, width, height); ctx.drawImage(img, 0, 0, width, height); URL.revokeObjectURL(url); canvas.toBlob((blob) => { if (blob) downloadBlob(blob, filename); }, 'image/png'); } export function ChartCard({ title, description, exportFilename, toCsv, children, className, }: ChartCardProps) { const containerRef = useRef(null); function onDownloadCsv() { const csv = toCsv?.(); if (!csv) return; downloadBlob(new Blob([csv], { type: 'text/csv;charset=utf-8' }), `${exportFilename}.csv`); } function onDownloadPng() { if (containerRef.current) { void exportContainerAsPng(containerRef.current, `${exportFilename}.png`); } } return (
{title} {description ?

{description}

: null}
{toCsv ? ( Download CSV ) : null} Download PNG
{children}
); }