init
This commit is contained in:
80
src/components/ExportDashboardButton.tsx
Normal file
80
src/components/ExportDashboardButton.tsx
Normal file
@ -0,0 +1,80 @@
|
||||
import { useState, type RefObject } from 'react'
|
||||
import html2canvas from 'html2canvas'
|
||||
import { jsPDF } from 'jspdf'
|
||||
|
||||
type Props = {
|
||||
targetRef: RefObject<HTMLElement | null>
|
||||
}
|
||||
|
||||
export function ExportDashboardButton({ targetRef }: Props) {
|
||||
const [busy, setBusy] = useState<'png' | 'pdf' | null>(null)
|
||||
|
||||
const runExport = async (mode: 'png' | 'pdf') => {
|
||||
const el = targetRef.current
|
||||
if (!el) return
|
||||
setBusy(mode)
|
||||
try {
|
||||
const canvas = await html2canvas(el, {
|
||||
scale: 2,
|
||||
useCORS: true,
|
||||
logging: false,
|
||||
backgroundColor: '#020617',
|
||||
})
|
||||
const stamp = new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-')
|
||||
if (mode === 'png') {
|
||||
canvas.toBlob((blob) => {
|
||||
if (!blob) return
|
||||
const url = URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = `dashboard-copil-${stamp}.png`
|
||||
a.click()
|
||||
URL.revokeObjectURL(url)
|
||||
}, 'image/png')
|
||||
} else {
|
||||
const imgData = canvas.toDataURL('image/png')
|
||||
const pdf = new jsPDF({ orientation: 'landscape', unit: 'pt', format: 'a4' })
|
||||
const pageW = pdf.internal.pageSize.getWidth()
|
||||
const pageH = pdf.internal.pageSize.getHeight()
|
||||
const img = new Image()
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
img.onload = () => resolve()
|
||||
img.onerror = () => reject(new Error('image'))
|
||||
img.src = imgData
|
||||
})
|
||||
const ratio = Math.min(pageW / img.width, pageH / img.height)
|
||||
const w = img.width * ratio
|
||||
const h = img.height * ratio
|
||||
const x = (pageW - w) / 2
|
||||
const y = (pageH - h) / 2
|
||||
pdf.addImage(imgData, 'PNG', x, y, w, h)
|
||||
pdf.save(`dashboard-copil-${stamp}.pdf`)
|
||||
}
|
||||
} catch {
|
||||
alert('Export impossible (vérifiez les bloqueurs ou réessayez).')
|
||||
} finally {
|
||||
setBusy(null)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<button
|
||||
type="button"
|
||||
disabled={busy !== null}
|
||||
onClick={() => void runExport('png')}
|
||||
className="rounded-lg border border-violet-500/50 bg-violet-500/15 px-3 py-2 text-xs font-semibold text-violet-100 transition hover:bg-violet-500/25 disabled:opacity-50"
|
||||
>
|
||||
{busy === 'png' ? 'Export…' : 'Exporter PNG (Copil)'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
disabled={busy !== null}
|
||||
onClick={() => void runExport('pdf')}
|
||||
className="rounded-lg border border-sky-500/50 bg-sky-500/15 px-3 py-2 text-xs font-semibold text-sky-100 transition hover:bg-sky-500/25 disabled:opacity-50"
|
||||
>
|
||||
{busy === 'pdf' ? 'Export…' : 'Exporter PDF (Copil)'}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user