diff --git a/src/public/app/components/app_context.ts b/src/public/app/components/app_context.ts
index dbfe78cd0..2be0d986b 100644
--- a/src/public/app/components/app_context.ts
+++ b/src/public/app/components/app_context.ts
@@ -83,7 +83,7 @@ export type CommandMappings = {
};
showExportDialog: CommandData & {
notePath: string;
- defaultType: "single";
+ defaultType: "single" | "subtree";
};
showDeleteNotesDialog: CommandData & {
branchIdsToDelete: string[];
diff --git a/src/public/app/widgets/dialogs/export.js b/src/public/app/widgets/dialogs/export.ts
similarity index 81%
rename from src/public/app/widgets/dialogs/export.js
rename to src/public/app/widgets/dialogs/export.ts
index 5bc9b3707..8f1fe5306 100644
--- a/src/public/app/widgets/dialogs/export.js
+++ b/src/public/app/widgets/dialogs/export.ts
@@ -1,11 +1,12 @@
import treeService from "../../services/tree.js";
import utils from "../../services/utils.js";
import ws from "../../services/ws.js";
-import toastService from "../../services/toast.js";
+import toastService, { type ToastOptions } from "../../services/toast.js";
import froca from "../../services/froca.js";
import openService from "../../services/open.js";
import BasicWidget from "../basic_widget.js";
import { t } from "../../services/i18n.js";
+import type { EventData } from "../../components/app_context.js";
const TPL = `
@@ -105,6 +106,13 @@ const TPL = `
${t("export.format_markdown")}
+
+
+
+
`;
export default class ExportDialog extends BasicWidget {
+
+ private taskId: string;
+ private branchId: string | null;
+ private modal?: bootstrap.Modal;
+ private $form!: JQuery;
+ private $noteTitle!: JQuery;
+ private $subtreeFormats!: JQuery;
+ private $singleFormats!: JQuery;
+ private $subtreeType!: JQuery;
+ private $singleType!: JQuery;
+ private $exportButton!: JQuery;
+ private $opmlVersions!: JQuery;
+
constructor() {
super();
@@ -125,6 +146,8 @@ export default class ExportDialog extends BasicWidget {
doRender() {
this.$widget = $(TPL);
+ // Remove once bootstrap is fixed.
+ // @ts-ignore
this.modal = bootstrap.Modal.getOrCreateInstance(this.$widget);
this.$form = this.$widget.find(".export-form");
this.$noteTitle = this.$widget.find(".export-note-title");
@@ -136,7 +159,7 @@ export default class ExportDialog extends BasicWidget {
this.$opmlVersions = this.$widget.find(".opml-versions");
this.$form.on("submit", () => {
- this.modal.hide();
+ this.modal?.hide();
const exportType = this.$widget.find("input[name='export-type']:checked").val();
@@ -149,13 +172,15 @@ export default class ExportDialog extends BasicWidget {
const exportVersion = exportFormat === "opml" ? this.$widget.find("input[name='opml-version']:checked").val() : "1.0";
- this.exportBranch(this.branchId, exportType, exportFormat, exportVersion);
+ if (this.branchId) {
+ this.exportBranch(this.branchId, String(exportType), String(exportFormat), String(exportVersion));
+ }
return false;
});
this.$widget.find("input[name=export-type]").on("change", (e) => {
- if (e.currentTarget.value === "subtree") {
+ if ((e.currentTarget as HTMLInputElement).value === "subtree") {
if (this.$widget.find("input[name=export-subtree-format]:checked").length === 0) {
this.$widget.find("input[name=export-subtree-format]:first").prop("checked", true);
}
@@ -173,7 +198,7 @@ export default class ExportDialog extends BasicWidget {
});
this.$widget.find("input[name=export-subtree-format]").on("change", (e) => {
- if (e.currentTarget.value === "opml") {
+ if ((e.currentTarget as HTMLInputElement).value === "opml") {
this.$opmlVersions.slideDown();
} else {
this.$opmlVersions.slideUp();
@@ -181,7 +206,7 @@ export default class ExportDialog extends BasicWidget {
});
}
- async showExportDialogEvent({ notePath, defaultType }) {
+ async showExportDialogEvent({ notePath, defaultType }: EventData<"showExportDialog">) {
this.taskId = "";
this.$exportButton.removeAttr("disabled");
@@ -201,11 +226,15 @@ export default class ExportDialog extends BasicWidget {
const { noteId, parentNoteId } = treeService.getNoteIdAndParentIdFromUrl(notePath);
- this.branchId = await froca.getBranchId(parentNoteId, noteId);
- this.$noteTitle.text(await treeService.getNoteTitle(noteId));
+ if (parentNoteId) {
+ this.branchId = await froca.getBranchId(parentNoteId, noteId);
+ }
+ if (noteId) {
+ this.$noteTitle.text(await treeService.getNoteTitle(noteId));
+ }
}
- exportBranch(branchId, type, format, version) {
+ exportBranch(branchId: string, type: string, format: string, version: string) {
this.taskId = utils.randomString(10);
const url = openService.getUrlForDownload(`api/branches/${branchId}/export/${type}/${format}/${version}/${this.taskId}`);
@@ -215,12 +244,14 @@ export default class ExportDialog extends BasicWidget {
}
ws.subscribeToMessages(async (message) => {
- const makeToast = (id, message) => ({
- id: id,
- title: t("export.export_status"),
- message: message,
- icon: "arrow-square-up-right"
- });
+ function makeToast(id: string, message: string): ToastOptions {
+ return {
+ id: id,
+ title: t("export.export_status"),
+ message: message,
+ icon: "arrow-square-up-right"
+ };
+ };
if (message.taskType !== "export") {
return;
diff --git a/src/public/app/widgets/note_detail.js b/src/public/app/widgets/note_detail.js
index 21f84dbc1..11cc6f71d 100644
--- a/src/public/app/widgets/note_detail.js
+++ b/src/public/app/widgets/note_detail.js
@@ -261,6 +261,7 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
const { ipcRenderer } = utils.dynamicRequire("electron");
ipcRenderer.send("export-as-pdf", {
title: this.note.title,
+ pageSize: this.note.getAttributeValue("label", "pageSize") ?? "Letter",
landscape: this.note.hasAttribute("label", "printLandscape")
});
}
diff --git a/src/public/translations/en/translation.json b/src/public/translations/en/translation.json
index c0d7d0eb1..e29c258f0 100644
--- a/src/public/translations/en/translation.json
+++ b/src/public/translations/en/translation.json
@@ -109,7 +109,8 @@
"choose_export_type": "Choose export type first please",
"export_status": "Export status",
"export_in_progress": "Export in progress: {{progressCount}}",
- "export_finished_successfully": "Export finished successfully."
+ "export_finished_successfully": "Export finished successfully.",
+ "format_pdf": "PDF - for printing or sharing purposes."
},
"help": {
"fullDocumentation": "Help (full documentation is available online)",
diff --git a/src/services/export/pdf.ts b/src/services/export/pdf.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/services/window.ts b/src/services/window.ts
index ff391875b..8c3fd9118 100644
--- a/src/services/window.ts
+++ b/src/services/window.ts
@@ -51,6 +51,7 @@ ipcMain.on("create-extra-window", (event, arg) => {
interface ExportAsPdfOpts {
title: string;
landscape: boolean;
+ pageSize: "A0" | "A1" | "A2" | "A3" | "A4" | "A5" | "A6" | "Legal" | "Letter" | "Tabloid" | "Ledger";
}
ipcMain.on("export-as-pdf", async (e, opts: ExportAsPdfOpts) => {
@@ -76,6 +77,7 @@ ipcMain.on("export-as-pdf", async (e, opts: ExportAsPdfOpts) => {
try {
buffer = await browserWindow.webContents.printToPDF({
landscape: opts.landscape,
+ pageSize: opts.pageSize,
generateDocumentOutline: true,
generateTaggedPDF: true,
displayHeaderFooter: true,