export to pdf hook update

This commit is contained in:
Nate Kelley 2025-08-08 10:33:28 -06:00
parent 078ea21086
commit 1317190edf
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
4 changed files with 29 additions and 74 deletions

View File

@ -22,7 +22,7 @@ export function ExportToolbarButton({ children, ...props }: DropdownMenuProps) {
const editor = useEditorRef();
const [open, setOpen] = React.useState(false);
const { exportToHtml, exportToPdf, exportToImage, exportToMarkdown } = useExportReport(editor);
const { exportToHtml, exportToPdf, exportToImage, exportToMarkdown } = useExportReport();
return (
<DropdownMenu open={open} onOpenChange={setOpen} modal={false}>
@ -34,16 +34,16 @@ export function ExportToolbarButton({ children, ...props }: DropdownMenuProps) {
<DropdownMenuContent align="start">
<DropdownMenuGroup>
<DropdownMenuItem onSelect={exportToHtml}>
<DropdownMenuItem onSelect={() => exportToHtml(editor)}>
{NodeTypeLabels.exportAsHtml.label}
</DropdownMenuItem>
<DropdownMenuItem onSelect={exportToPdf}>
<DropdownMenuItem onSelect={() => exportToPdf(editor)}>
{NodeTypeLabels.exportAsPdf.label}
</DropdownMenuItem>
<DropdownMenuItem onSelect={exportToImage}>
<DropdownMenuItem onSelect={() => exportToImage(editor)}>
{NodeTypeLabels.exportAsImage.label}
</DropdownMenuItem>
<DropdownMenuItem onSelect={exportToMarkdown}>
<DropdownMenuItem onSelect={() => exportToMarkdown(editor)}>
{NodeTypeLabels.exportAsMarkdown.label}
</DropdownMenuItem>
</DropdownMenuGroup>

View File

@ -0,0 +1 @@
export * from './useExportReport';

View File

@ -4,11 +4,12 @@ import type { PlateEditor } from 'platejs/react';
import { NodeTypeLabels } from '../config/labels';
import { createSlateEditor, serializeHtml } from 'platejs';
import { MarkdownPlugin } from '@platejs/markdown';
import { useMemo } from 'react';
export const useExportReport = (editor: PlateEditor) => {
export const useExportReport = () => {
const { openErrorMessage, openInfoMessage } = useBusterNotifications();
const getCanvas = async () => {
const getCanvas = async (editor: PlateEditor) => {
const { default: html2canvas } = await import('html2canvas-pro');
const style = document.createElement('style');
@ -69,9 +70,9 @@ export const useExportReport = (editor: PlateEditor) => {
window.URL.revokeObjectURL(blobUrl);
};
const exportToPdf = async () => {
const exportToPdf = async (editor: PlateEditor) => {
try {
const canvas = await getCanvas();
const canvas = await getCanvas(editor);
const PDFLib = await import('pdf-lib');
const pdfDoc = await PDFLib.PDFDocument.create();
const page = pdfDoc.addPage([canvas.width, canvas.height]);
@ -92,9 +93,9 @@ export const useExportReport = (editor: PlateEditor) => {
}
};
const exportToImage = async () => {
const exportToImage = async (editor: PlateEditor) => {
try {
const canvas = await getCanvas();
const canvas = await getCanvas(editor);
await downloadFile(canvas.toDataURL('image/png'), 'plate.png');
openInfoMessage(NodeTypeLabels.imageExportedSuccessfully.label);
} catch (error) {
@ -102,7 +103,7 @@ export const useExportReport = (editor: PlateEditor) => {
}
};
const exportToHtml = async () => {
const exportToHtml = async (editor: PlateEditor) => {
try {
const BaseEditorKit = await import('../editor-base-kit').then(
(module) => module.BaseEditorKit
@ -157,7 +158,7 @@ export const useExportReport = (editor: PlateEditor) => {
}
};
const exportToMarkdown = async () => {
const exportToMarkdown = async (editor: PlateEditor) => {
try {
const md = editor.getApi(MarkdownPlugin).markdown.serialize();
const url = `data:text/markdown;charset=utf-8,${encodeURIComponent(md)}`;
@ -168,10 +169,13 @@ export const useExportReport = (editor: PlateEditor) => {
}
};
return {
exportToPdf,
exportToImage,
exportToHtml,
exportToMarkdown
};
return useMemo(
() => ({
exportToPdf,
exportToImage,
exportToHtml,
exportToMarkdown
}),
[]
);
};

View File

@ -28,6 +28,7 @@ import type { VerificationStatus } from '@buster/server-shared/share';
import { useSaveToCollectionsDropdownContent } from '@/components/features/dropdowns/SaveToCollectionsDropdown';
import { getReportEditor } from '@/components/ui/report/editorRegistry';
import { NodeTypeLabels } from '@/components/ui/report/config/labels';
import { useExportReport } from '@/components/ui/report/hooks';
export const ReportThreeDotMenu = React.memo(
({
@ -313,69 +314,18 @@ const useDuplicateReportSelectMenu = (): DropdownItem => {
// Download as PDF
const useDownloadPdfSelectMenu = ({ reportId }: { reportId: string }): DropdownItem => {
const { openErrorMessage, openInfoMessage } = useBusterNotifications();
const editor = getReportEditor(reportId);
const { exportToPdf } = useExportReport();
const onClick = useMemoizedFn(async () => {
const editor = getReportEditor(reportId);
if (!editor) {
openErrorMessage(NodeTypeLabels.failedToExportPdf.label);
return;
}
try {
const html2canvas = await import('html2canvas-pro').then((m) => m.default);
const standardWidth = '850px';
const node = editor.api.toDOMNode(editor);
if (!node) throw new Error('Editor not found');
await exportToPdf(editor);
const style = document.createElement('style');
document.head.append(style);
const canvas = await html2canvas(node, {
onclone: (document: Document) => {
const editorElement = document.querySelector('[contenteditable="true"]');
if (editorElement) {
const existingStyle = editorElement.getAttribute('style') || '';
editorElement.setAttribute(
'style',
`${existingStyle}; width: ${standardWidth} !important; max-width: ${standardWidth} !important; min-width: ${standardWidth} !important;`
);
Array.from(editorElement.querySelectorAll('*')).forEach((element) => {
const elementStyle = element.getAttribute('style') || '';
element.setAttribute(
'style',
`${elementStyle}; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif !important`
);
});
} else {
throw new Error('Editor element not found');
}
}
});
style.remove();
const PDFLib = await import('pdf-lib');
const pdfDoc = await PDFLib.PDFDocument.create();
const page = pdfDoc.addPage([canvas.width, canvas.height]);
const imageEmbed = await pdfDoc.embedPng(canvas.toDataURL('PNG'));
const { height, width } = imageEmbed.scale(1);
page.drawImage(imageEmbed, { height, width, x: 0, y: 0 });
const pdfBase64 = await pdfDoc.saveAsBase64({ dataUri: true });
// download
const response = await fetch(pdfBase64);
const blob = await response.blob();
const blobUrl = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = blobUrl;
link.download = 'report.pdf';
document.body.append(link);
link.click();
link.remove();
window.URL.revokeObjectURL(blobUrl);
openInfoMessage(NodeTypeLabels.pdfExportedSuccessfully.label);
} catch (error) {
openErrorMessage(NodeTypeLabels.failedToExportPdf.label);
}
//
});
return useMemo(