mirror of https://github.com/buster-so/buster.git
export to pdf hook update
This commit is contained in:
parent
078ea21086
commit
1317190edf
|
@ -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>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export * from './useExportReport';
|
|
@ -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 {
|
||||
return useMemo(
|
||||
() => ({
|
||||
exportToPdf,
|
||||
exportToImage,
|
||||
exportToHtml,
|
||||
exportToMarkdown
|
||||
};
|
||||
}),
|
||||
[]
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue