feat(print/pdf): add toggle for landscape

This commit is contained in:
Elian Doran
2026-04-14 22:58:15 +03:00
parent 2db1e2d750
commit 4d4e63998f
4 changed files with 87 additions and 17 deletions

View File

@@ -2308,7 +2308,8 @@
"print_preview": {
"title": "Print preview",
"close": "Close",
"save": "Save as PDF"
"save": "Save as PDF",
"landscape": "Landscape"
},
"pdf": {
"attachments_one": "{{count}} attachment",

View File

@@ -147,9 +147,9 @@ export default function NoteDetail() {
toast.closePersistent("printing");
handlePrintReport(printReport);
};
const onPreviewResult = (_e: any, { buffer, title }: { buffer: Uint8Array; title: string }) => {
const onPreviewResult = (_e: any, { buffer, title, notePath, pageSize, landscape }: { buffer: Uint8Array; title: string; notePath: string; pageSize: string; landscape: boolean }) => {
toast.closePersistent("printing");
appContext.triggerCommand("showPrintPreview", { pdfBuffer: buffer, title });
appContext.triggerCommand("showPrintPreview", { pdfBuffer: buffer, title, notePath, pageSize, landscape });
};
ipcRenderer.on("print-progress", onPrintProgress);
ipcRenderer.on("print-done", onPrintDone);

View File

@@ -1,29 +1,52 @@
import { useRef, useState } from "preact/hooks";
import Modal from "../react/Modal";
import PdfViewer from "../type_widgets/file/PdfViewer";
import { useCallback, useRef, useState } from "preact/hooks";
import { t } from "../../services/i18n";
import toast from "../../services/toast";
import { dynamicRequire, isElectron } from "../../services/utils";
import Button from "../react/Button";
import { useTriliumEvent } from "../react/hooks";
import { t } from "../../services/i18n";
import { dynamicRequire } from "../../services/utils";
import Modal from "../react/Modal";
import PdfViewer from "../type_widgets/file/PdfViewer";
import { OptionsRowWithToggle } from "../type_widgets/options/components/OptionsRow";
import OptionsSection from "../type_widgets/options/components/OptionsSection";
export interface PrintPreviewData {
pdfBuffer: Uint8Array;
title: string;
notePath: string;
pageSize: string;
landscape: boolean;
}
export default function PrintPreviewDialog() {
const [shown, setShown] = useState(false);
const [pdfUrl, setPdfUrl] = useState<string>();
const [landscape, setLandscape] = useState(false);
const [loading, setLoading] = useState(false);
const bufferRef = useRef<Uint8Array>();
const titleRef = useRef("");
const notePathRef = useRef("");
const pageSizeRef = useRef("");
const updatePreview = useCallback((buffer: Uint8Array) => {
bufferRef.current = buffer;
// Revoke old URL before creating new one.
if (pdfUrl) {
URL.revokeObjectURL(pdfUrl);
}
const blob = new Blob([buffer as BlobPart], { type: "application/pdf" });
setPdfUrl(URL.createObjectURL(blob));
setLoading(false);
}, [pdfUrl]);
useTriliumEvent("showPrintPreview", (data: PrintPreviewData) => {
bufferRef.current = data.pdfBuffer;
titleRef.current = data.title;
const blob = new Blob([data.pdfBuffer as BlobPart], { type: "application/pdf" });
const url = URL.createObjectURL(blob);
setPdfUrl(url);
notePathRef.current = data.notePath;
pageSizeRef.current = data.pageSize;
setLandscape(data.landscape);
updatePreview(data.pdfBuffer);
setShown(true);
});
@@ -34,6 +57,7 @@ export default function PrintPreviewDialog() {
setPdfUrl(undefined);
}
bufferRef.current = undefined;
setLoading(false);
}
function handleSave() {
@@ -47,6 +71,32 @@ export default function PrintPreviewDialog() {
handleClose();
}
function handleLandscapeToggle(newValue: boolean) {
setLandscape(newValue);
regeneratePreview(newValue);
}
function regeneratePreview(newLandscape: boolean) {
if (!isElectron()) return;
setLoading(true);
const { ipcRenderer } = dynamicRequire("electron");
// Listen for the result once.
const onResult = (_e: any, { buffer }: { buffer: Uint8Array }) => {
toast.closePersistent("printing");
updatePreview(buffer);
};
ipcRenderer.once("export-as-pdf-preview-result", onResult);
ipcRenderer.send("export-as-pdf-preview", {
title: titleRef.current,
notePath: notePathRef.current,
pageSize: pageSizeRef.current,
landscape: newLandscape
});
}
return (
<Modal
className="print-preview-dialog"
@@ -54,15 +104,34 @@ export default function PrintPreviewDialog() {
size="xl"
show={shown}
onHidden={handleClose}
bodyStyle={{ height: "80vh", padding: 0 }}
bodyStyle={{ height: "78vh", padding: 0, display: "flex" }}
footer={
<>
<Button text={t("print_preview.close")} onClick={handleClose} />
<Button text={t("print_preview.save")} className="btn-primary" onClick={handleSave} />
<Button text={t("print_preview.save")} className="btn-primary" onClick={handleSave} disabled={loading} />
</>
}
>
{pdfUrl && <PdfViewer pdfUrl={pdfUrl} />}
<div style={{ padding: "16px", minWidth: "250px", overflowY: "auto" }}>
<OptionsSection>
<OptionsRowWithToggle
name="printLandscape"
label={t("print_preview.landscape")}
currentValue={landscape}
onChange={handleLandscapeToggle}
disabled={loading}
/>
</OptionsSection>
</div>
<div style={{ flex: 1, position: "relative" }}>
{loading && (
<div style={{ position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", zIndex: 1, backgroundColor: "var(--modal-bg-color, rgba(255,255,255,0.8))" }}>
<span class="bx bx-loader-circle bx-spin" style={{ fontSize: "2rem" }} />
</div>
)}
{pdfUrl && <PdfViewer pdfUrl={pdfUrl} />}
</div>
</Modal>
);
}

View File

@@ -187,7 +187,7 @@ electron.ipcMain.on("export-as-pdf-preview", async (e, { title, notePath, landsc
`
});
e.sender.send("export-as-pdf-preview-result", { buffer, title });
e.sender.send("export-as-pdf-preview-result", { buffer, title, notePath, pageSize, landscape });
} catch (_e) {
electron.dialog.showErrorBox(t("pdf.unable-to-export-title"), t("pdf.unable-to-export-message"));
} finally {