From 6f83b932b0d0890d44d008416d9ef6fe0768f7cd Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Fri, 21 Nov 2025 21:28:31 +0200
Subject: [PATCH 01/11] feat(print/table): basic implementation using export
module
---
.../src/widgets/collections/NoteList.tsx | 9 ++-
.../collections/table/TablePrintView.tsx | 38 +++++++++
.../src/widgets/collections/table/data.tsx | 77 ++++++++++++++++++
.../src/widgets/collections/table/index.tsx | 79 ++-----------------
.../widgets/collections/table/tabulator.tsx | 4 +-
5 files changed, 130 insertions(+), 77 deletions(-)
create mode 100644 apps/client/src/widgets/collections/table/TablePrintView.tsx
create mode 100644 apps/client/src/widgets/collections/table/data.tsx
diff --git a/apps/client/src/widgets/collections/NoteList.tsx b/apps/client/src/widgets/collections/NoteList.tsx
index c613f8d39..bd007a40f 100644
--- a/apps/client/src/widgets/collections/NoteList.tsx
+++ b/apps/client/src/widgets/collections/NoteList.tsx
@@ -14,6 +14,7 @@ import { WebSocketMessage } from "@triliumnext/commons";
import froca from "../../services/froca";
import PresentationView from "./presentation";
import { ListPrintView } from "./legacy/ListPrintView";
+import TablePrintView from "./table/TablePrintView";
interface NoteListProps {
note: FNote | null | undefined;
@@ -117,9 +118,13 @@ function getComponentByViewType(viewType: ViewTypeOptions, props: ViewModeProps<
case "geoMap":
return ;
case "calendar":
- return
+ return ;
case "table":
- return
+ if (props.media !== "print") {
+ return ;
+ } else {
+ return ;
+ }
case "board":
return
case "presentation":
diff --git a/apps/client/src/widgets/collections/table/TablePrintView.tsx b/apps/client/src/widgets/collections/table/TablePrintView.tsx
new file mode 100644
index 000000000..a7e36dfb3
--- /dev/null
+++ b/apps/client/src/widgets/collections/table/TablePrintView.tsx
@@ -0,0 +1,38 @@
+import { useRef, useState } from "preact/hooks";
+import { ViewModeProps } from "../interface";
+import useData, { TableConfig } from "./data";
+import { ExportModule, PrintModule, Tabulator as VanillaTabulator} from 'tabulator-tables';
+import Tabulator from "./tabulator";
+import { RawHtmlBlock } from "../../react/RawHtml";
+
+export default function TablePrintView({ note, noteIds, viewConfig }: ViewModeProps) {
+ const tabulatorRef = useRef(null);
+ const { columnDefs, rowData, movableRows, hasChildren } = useData(note, noteIds, viewConfig, undefined, () => {});
+ const [ html, setHtml ] = useState();
+
+ return rowData && (
+
+ {!html ? (
+ {
+ const tabulator = tabulatorRef.current;
+ if (!tabulator) return;
+ setHtml(tabulator.getHtml());
+ }}
+ />
+ ) : (
+
+ )}
+
+
+ )
+}
diff --git a/apps/client/src/widgets/collections/table/data.tsx b/apps/client/src/widgets/collections/table/data.tsx
new file mode 100644
index 000000000..b77a1545e
--- /dev/null
+++ b/apps/client/src/widgets/collections/table/data.tsx
@@ -0,0 +1,77 @@
+import type { ColumnDefinition } from "tabulator-tables";
+import FNote from "../../../entities/fnote";
+import { useNoteLabelBoolean, useNoteLabelInt, useTriliumEvent } from "../../react/hooks";
+import { useEffect, useState } from "preact/hooks";
+import getAttributeDefinitionInformation, { buildRowDefinitions, TableData } from "./rows";
+import froca from "../../../services/froca";
+import { buildColumnDefinitions } from "./columns";
+import attributes from "../../../services/attributes";
+import { RefObject } from "preact";
+
+export interface TableConfig {
+ tableData: {
+ columns?: ColumnDefinition[];
+ };
+}
+
+export default function useData(note: FNote, noteIds: string[], viewConfig: TableConfig | undefined, newAttributePosition: RefObject | undefined, resetNewAttributePosition: () => void) {
+ const [ maxDepth ] = useNoteLabelInt(note, "maxNestingDepth") ?? -1;
+ const [ includeArchived ] = useNoteLabelBoolean(note, "includeArchived");
+
+ const [ columnDefs, setColumnDefs ] = useState();
+ const [ rowData, setRowData ] = useState();
+ const [ hasChildren, setHasChildren ] = useState();
+ const [ isSorted ] = useNoteLabelBoolean(note, "sorted");
+ const [ movableRows, setMovableRows ] = useState(false);
+
+ async function refresh() {
+ const info = getAttributeDefinitionInformation(note);
+
+ // Ensure all note IDs are loaded.
+ await froca.getNotes(noteIds);
+
+ const { definitions: rowData, hasSubtree: hasChildren, rowNumber } = await buildRowDefinitions(note, info, includeArchived, maxDepth);
+ const columnDefs = buildColumnDefinitions({
+ info,
+ movableRows,
+ existingColumnData: viewConfig?.tableData?.columns,
+ rowNumberHint: rowNumber,
+ position: newAttributePosition?.current ?? undefined
+ });
+ setColumnDefs(columnDefs);
+ setRowData(rowData);
+ setHasChildren(hasChildren);
+ resetNewAttributePosition();
+ }
+
+ useEffect(() => { refresh() }, [ note, noteIds, maxDepth, movableRows ]);
+
+ useTriliumEvent("entitiesReloaded", ({ loadResults}) => {
+ if (glob.device === "print") return;
+
+ // React to column changes.
+ if (loadResults.getAttributeRows().find(attr =>
+ attr.type === "label" &&
+ (attr.name?.startsWith("label:") || attr.name?.startsWith("relation:")) &&
+ attributes.isAffecting(attr, note))) {
+ refresh();
+ return;
+ }
+
+ // React to external row updates.
+ if (loadResults.getBranchRows().some(branch => branch.parentNoteId === note.noteId || noteIds.includes(branch.parentNoteId ?? ""))
+ || loadResults.getNoteIds().some(noteId => noteIds.includes(noteId))
+ || loadResults.getAttributeRows().some(attr => noteIds.includes(attr.noteId!))
+ || loadResults.getAttributeRows().some(attr => attr.name === "archived" && attr.noteId && noteIds.includes(attr.noteId))) {
+ refresh();
+ return;
+ }
+ });
+
+ // Identify if movable rows.
+ useEffect(() => {
+ setMovableRows(!isSorted && note.type !== "search" && !hasChildren);
+ }, [ isSorted, note, hasChildren ]);
+
+ return { columnDefs, rowData, movableRows, hasChildren };
+}
diff --git a/apps/client/src/widgets/collections/table/index.tsx b/apps/client/src/widgets/collections/table/index.tsx
index f6ae82009..d557f12d3 100644
--- a/apps/client/src/widgets/collections/table/index.tsx
+++ b/apps/client/src/widgets/collections/table/index.tsx
@@ -1,10 +1,9 @@
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "preact/hooks";
import { ViewModeProps } from "../interface";
-import { buildColumnDefinitions } from "./columns";
-import getAttributeDefinitionInformation, { buildRowDefinitions, TableData } from "./rows";
-import { useLegacyWidget, useNoteLabelBoolean, useNoteLabelInt, useTriliumEvent } from "../../react/hooks";
+import { TableData } from "./rows";
+import { useLegacyWidget } from "../../react/hooks";
import Tabulator from "./tabulator";
-import { Tabulator as VanillaTabulator, SortModule, FormatModule, InteractionModule, EditModule, ResizeColumnsModule, FrozenColumnsModule, PersistenceModule, MoveColumnsModule, MoveRowsModule, ColumnDefinition, DataTreeModule, Options, RowComponent} from 'tabulator-tables';
+import { Tabulator as VanillaTabulator, SortModule, FormatModule, InteractionModule, EditModule, ResizeColumnsModule, FrozenColumnsModule, PersistenceModule, MoveColumnsModule, MoveRowsModule, DataTreeModule, Options, RowComponent} from 'tabulator-tables';
import { useContextMenu } from "./context_menu";
import { ParentComponent } from "../../react/react_utils";
import FNote from "../../../entities/fnote";
@@ -14,16 +13,8 @@ import "./index.css";
import useRowTableEditing from "./row_editing";
import useColTableEditing from "./col_editing";
import AttributeDetailWidget from "../../attribute_widgets/attribute_detail";
-import attributes from "../../../services/attributes";
-import { RefObject } from "preact";
import SpacedUpdate from "../../../services/spaced_update";
-import froca from "../../../services/froca";
-
-interface TableConfig {
- tableData: {
- columns?: ColumnDefinition[];
- };
-}
+import useData, { TableConfig } from "./data";
export default function TableView({ note, noteIds, notePath, viewConfig, saveConfig }: ViewModeProps) {
const tabulatorRef = useRef(null);
@@ -118,67 +109,7 @@ function usePersistence(viewConfig: TableConfig | null | undefined, saveConfig:
return () => {
spacedUpdate.updateNowIfNecessary();
};
- }, [ viewConfig, saveConfig ])
+ }, [ viewConfig, saveConfig ]);
return persistenceProps;
}
-
-function useData(note: FNote, noteIds: string[], viewConfig: TableConfig | undefined, newAttributePosition: RefObject, resetNewAttributePosition: () => void) {
- const [ maxDepth ] = useNoteLabelInt(note, "maxNestingDepth") ?? -1;
- const [ includeArchived ] = useNoteLabelBoolean(note, "includeArchived");
-
- const [ columnDefs, setColumnDefs ] = useState();
- const [ rowData, setRowData ] = useState();
- const [ hasChildren, setHasChildren ] = useState();
- const [ isSorted ] = useNoteLabelBoolean(note, "sorted");
- const [ movableRows, setMovableRows ] = useState(false);
-
- async function refresh() {
- const info = getAttributeDefinitionInformation(note);
-
- // Ensure all note IDs are loaded.
- await froca.getNotes(noteIds);
-
- const { definitions: rowData, hasSubtree: hasChildren, rowNumber } = await buildRowDefinitions(note, info, includeArchived, maxDepth);
- const columnDefs = buildColumnDefinitions({
- info,
- movableRows,
- existingColumnData: viewConfig?.tableData?.columns,
- rowNumberHint: rowNumber,
- position: newAttributePosition.current ?? undefined
- });
- setColumnDefs(columnDefs);
- setRowData(rowData);
- setHasChildren(hasChildren);
- resetNewAttributePosition();
- }
-
- useEffect(() => { refresh() }, [ note, noteIds, maxDepth, movableRows ]);
-
- useTriliumEvent("entitiesReloaded", ({ loadResults}) => {
- // React to column changes.
- if (loadResults.getAttributeRows().find(attr =>
- attr.type === "label" &&
- (attr.name?.startsWith("label:") || attr.name?.startsWith("relation:")) &&
- attributes.isAffecting(attr, note))) {
- refresh();
- return;
- }
-
- // React to external row updates.
- if (loadResults.getBranchRows().some(branch => branch.parentNoteId === note.noteId || noteIds.includes(branch.parentNoteId ?? ""))
- || loadResults.getNoteIds().some(noteId => noteIds.includes(noteId))
- || loadResults.getAttributeRows().some(attr => noteIds.includes(attr.noteId!))
- || loadResults.getAttributeRows().some(attr => attr.name === "archived" && attr.noteId && noteIds.includes(attr.noteId))) {
- refresh();
- return;
- }
- });
-
- // Identify if movable rows.
- useEffect(() => {
- setMovableRows(!isSorted && note.type !== "search" && !hasChildren);
- }, [ isSorted, note, hasChildren ]);
-
- return { columnDefs, rowData, movableRows, hasChildren };
-}
diff --git a/apps/client/src/widgets/collections/table/tabulator.tsx b/apps/client/src/widgets/collections/table/tabulator.tsx
index 6301d5b38..31fb8d4f8 100644
--- a/apps/client/src/widgets/collections/table/tabulator.tsx
+++ b/apps/client/src/widgets/collections/table/tabulator.tsx
@@ -14,9 +14,10 @@ interface TableProps extends Omit;
index: keyof T;
footerElement?: string | HTMLElement | JSX.Element;
+ onReady?: () => void;
}
-export default function Tabulator({ className, columns, data, modules, tabulatorRef: externalTabulatorRef, footerElement, events, index, dataTree, ...restProps }: TableProps) {
+export default function Tabulator({ className, columns, data, modules, tabulatorRef: externalTabulatorRef, footerElement, events, index, dataTree, onReady, ...restProps }: TableProps) {
const parentComponent = useContext(ParentComponent);
const containerRef = useRef(null);
const tabulatorRef = useRef(null);
@@ -43,6 +44,7 @@ export default function Tabulator({ className, columns, data, modules, tabula
tabulator.on("tableBuilt", () => {
tabulatorRef.current = tabulator;
externalTabulatorRef.current = tabulator;
+ onReady?.();
});
return () => tabulator.destroy();
From f864746b5493220c1dfaa3602b1e71db4a94b8c2 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Fri, 21 Nov 2025 22:05:22 +0200
Subject: [PATCH 02/11] feat(print/table): render using internal mechanism
---
.../src/widgets/collections/table/TablePrintView.css | 7 +++++++
.../src/widgets/collections/table/TablePrintView.tsx | 10 +++++++---
2 files changed, 14 insertions(+), 3 deletions(-)
create mode 100644 apps/client/src/widgets/collections/table/TablePrintView.css
diff --git a/apps/client/src/widgets/collections/table/TablePrintView.css b/apps/client/src/widgets/collections/table/TablePrintView.css
new file mode 100644
index 000000000..09c6cae07
--- /dev/null
+++ b/apps/client/src/widgets/collections/table/TablePrintView.css
@@ -0,0 +1,7 @@
+.tabulator-print-table table,
+.tabulator-print-table th,
+.tabulator-print-table tr,
+.tabulator-print-table td {
+ border: 1px solid black !important;
+ border-collapse: collapse;
+}
\ No newline at end of file
diff --git a/apps/client/src/widgets/collections/table/TablePrintView.tsx b/apps/client/src/widgets/collections/table/TablePrintView.tsx
index a7e36dfb3..cc73d7cd2 100644
--- a/apps/client/src/widgets/collections/table/TablePrintView.tsx
+++ b/apps/client/src/widgets/collections/table/TablePrintView.tsx
@@ -4,6 +4,7 @@ import useData, { TableConfig } from "./data";
import { ExportModule, PrintModule, Tabulator as VanillaTabulator} from 'tabulator-tables';
import Tabulator from "./tabulator";
import { RawHtmlBlock } from "../../react/RawHtml";
+import "./TablePrintView.css";
export default function TablePrintView({ note, noteIds, viewConfig }: ViewModeProps) {
const tabulatorRef = useRef(null);
@@ -21,16 +22,19 @@ export default function TablePrintView({ note, noteIds, viewConfig }: ViewModePr
data={rowData}
index="branchId"
dataTree={hasChildren}
- printAsHtml={true}
printStyled={true}
onReady={() => {
const tabulator = tabulatorRef.current;
if (!tabulator) return;
- setHtml(tabulator.getHtml());
+ const generatedTable = tabulator.modules.export.generateTable(tabulator.options.printConfig, tabulator.options.printStyled, tabulator.options.printRowRange, "print");
+ if(tabulator.options.printFormatter){
+ tabulator.options.printFormatter(tabulator.element, generatedTable);
+ }
+ setHtml(generatedTable.outerHTML);
}}
/>
) : (
-
+
)}
From 749740242efcb19c3454a796e719f7f5a3154727 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Fri, 21 Nov 2025 22:09:07 +0200
Subject: [PATCH 03/11] fix(print/table) formatters not rendering
---
.../client/src/widgets/collections/table/TablePrintView.tsx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/apps/client/src/widgets/collections/table/TablePrintView.tsx b/apps/client/src/widgets/collections/table/TablePrintView.tsx
index cc73d7cd2..09f551969 100644
--- a/apps/client/src/widgets/collections/table/TablePrintView.tsx
+++ b/apps/client/src/widgets/collections/table/TablePrintView.tsx
@@ -1,14 +1,14 @@
import { useRef, useState } from "preact/hooks";
import { ViewModeProps } from "../interface";
import useData, { TableConfig } from "./data";
-import { ExportModule, PrintModule, Tabulator as VanillaTabulator} from 'tabulator-tables';
+import { ExportModule, FormatModule, PrintModule, Tabulator as VanillaTabulator} from 'tabulator-tables';
import Tabulator from "./tabulator";
import { RawHtmlBlock } from "../../react/RawHtml";
import "./TablePrintView.css";
export default function TablePrintView({ note, noteIds, viewConfig }: ViewModeProps) {
const tabulatorRef = useRef(null);
- const { columnDefs, rowData, movableRows, hasChildren } = useData(note, noteIds, viewConfig, undefined, () => {});
+ const { columnDefs, rowData, hasChildren } = useData(note, noteIds, viewConfig, undefined, () => {});
const [ html, setHtml ] = useState();
return rowData && (
@@ -17,7 +17,7 @@ export default function TablePrintView({ note, noteIds, viewConfig }: ViewModePr
Date: Fri, 21 Nov 2025 22:12:05 +0200
Subject: [PATCH 04/11] fix(print/table): missing title
---
.../collections/table/TablePrintView.tsx | 55 ++++++++++---------
1 file changed, 30 insertions(+), 25 deletions(-)
diff --git a/apps/client/src/widgets/collections/table/TablePrintView.tsx b/apps/client/src/widgets/collections/table/TablePrintView.tsx
index 09f551969..278df6413 100644
--- a/apps/client/src/widgets/collections/table/TablePrintView.tsx
+++ b/apps/client/src/widgets/collections/table/TablePrintView.tsx
@@ -12,31 +12,36 @@ export default function TablePrintView({ note, noteIds, viewConfig }: ViewModePr
const [ html, setHtml ] = useState();
return rowData && (
-
- {!html ? (
- {
- const tabulator = tabulatorRef.current;
- if (!tabulator) return;
- const generatedTable = tabulator.modules.export.generateTable(tabulator.options.printConfig, tabulator.options.printStyled, tabulator.options.printRowRange, "print");
- if(tabulator.options.printFormatter){
- tabulator.options.printFormatter(tabulator.element, generatedTable);
- }
- setHtml(generatedTable.outerHTML);
- }}
- />
- ) : (
-
- )}
-
+ <>
+ {note.title}
+
+
+
+ {!html ? (
+ {
+ const tabulator = tabulatorRef.current;
+ if (!tabulator) return;
+ const generatedTable = tabulator.modules.export.generateTable(tabulator.options.printConfig, tabulator.options.printStyled, tabulator.options.printRowRange, "print");
+ if(tabulator.options.printFormatter){
+ tabulator.options.printFormatter(tabulator.element, generatedTable);
+ }
+ setHtml(generatedTable.outerHTML);
+ }}
+ />
+ ) : (
+
+ )}
+
+ >
)
}
From 670cc474a47316ceba3afbc4db1bd914fe564564 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Fri, 21 Nov 2025 22:13:55 +0200
Subject: [PATCH 05/11] chore(print/table): grayed out table headers
---
apps/client/src/widgets/collections/table/TablePrintView.css | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/apps/client/src/widgets/collections/table/TablePrintView.css b/apps/client/src/widgets/collections/table/TablePrintView.css
index 09c6cae07..ce7eec9ec 100644
--- a/apps/client/src/widgets/collections/table/TablePrintView.css
+++ b/apps/client/src/widgets/collections/table/TablePrintView.css
@@ -4,4 +4,8 @@
.tabulator-print-table td {
border: 1px solid black !important;
border-collapse: collapse;
+}
+
+.tabulator-print-table th {
+ background-color: #f0f0f0 !important;
}
\ No newline at end of file
From 644e3e200da10f09d38b609ddb3e204284ff277f Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Fri, 21 Nov 2025 22:15:01 +0200
Subject: [PATCH 06/11] chore(print/table): stop copying styles and apply own
---
apps/client/src/widgets/collections/table/TablePrintView.css | 5 +++++
apps/client/src/widgets/collections/table/TablePrintView.tsx | 3 ++-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/apps/client/src/widgets/collections/table/TablePrintView.css b/apps/client/src/widgets/collections/table/TablePrintView.css
index ce7eec9ec..14c1e6dc4 100644
--- a/apps/client/src/widgets/collections/table/TablePrintView.css
+++ b/apps/client/src/widgets/collections/table/TablePrintView.css
@@ -8,4 +8,9 @@
.tabulator-print-table th {
background-color: #f0f0f0 !important;
+}
+
+.tabulator-print-table th,
+.tabulator-print-table td {
+ padding: 0.25rem 0.5rem !important;
}
\ No newline at end of file
diff --git a/apps/client/src/widgets/collections/table/TablePrintView.tsx b/apps/client/src/widgets/collections/table/TablePrintView.tsx
index 278df6413..abf73f1ee 100644
--- a/apps/client/src/widgets/collections/table/TablePrintView.tsx
+++ b/apps/client/src/widgets/collections/table/TablePrintView.tsx
@@ -26,7 +26,8 @@ export default function TablePrintView({ note, noteIds, viewConfig }: ViewModePr
data={rowData}
index="branchId"
dataTree={hasChildren}
- printStyled={true}
+ printAsHtml={true}
+ printStyled={false}
onReady={() => {
const tabulator = tabulatorRef.current;
if (!tabulator) return;
From 779e2f4633751e5e60f66c05bde0cae4cc2ad922 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Fri, 21 Nov 2025 22:15:56 +0200
Subject: [PATCH 07/11] chore(print/table): monochrome checkboxes
---
apps/client/src/widgets/collections/table/TablePrintView.css | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/apps/client/src/widgets/collections/table/TablePrintView.css b/apps/client/src/widgets/collections/table/TablePrintView.css
index 14c1e6dc4..74e282aaf 100644
--- a/apps/client/src/widgets/collections/table/TablePrintView.css
+++ b/apps/client/src/widgets/collections/table/TablePrintView.css
@@ -13,4 +13,8 @@
.tabulator-print-table th,
.tabulator-print-table td {
padding: 0.25rem 0.5rem !important;
+}
+
+.tabulator-print-table td[aria-checked] svg path {
+ fill: currentColor !important;
}
\ No newline at end of file
From 0c5a6a75481bc816768d593f664c92240cb8e764 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Fri, 21 Nov 2025 22:17:54 +0200
Subject: [PATCH 08/11] feat(print/table): integrate with the printing
mechanism
---
.../src/widgets/collections/table/TablePrintView.tsx | 9 +++++++--
apps/client/src/widgets/ribbon/NoteActions.tsx | 2 +-
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/apps/client/src/widgets/collections/table/TablePrintView.tsx b/apps/client/src/widgets/collections/table/TablePrintView.tsx
index abf73f1ee..9582b4360 100644
--- a/apps/client/src/widgets/collections/table/TablePrintView.tsx
+++ b/apps/client/src/widgets/collections/table/TablePrintView.tsx
@@ -1,4 +1,4 @@
-import { useRef, useState } from "preact/hooks";
+import { useEffect, useRef, useState } from "preact/hooks";
import { ViewModeProps } from "../interface";
import useData, { TableConfig } from "./data";
import { ExportModule, FormatModule, PrintModule, Tabulator as VanillaTabulator} from 'tabulator-tables';
@@ -6,11 +6,16 @@ import Tabulator from "./tabulator";
import { RawHtmlBlock } from "../../react/RawHtml";
import "./TablePrintView.css";
-export default function TablePrintView({ note, noteIds, viewConfig }: ViewModeProps) {
+export default function TablePrintView({ note, noteIds, viewConfig, onReady }: ViewModeProps) {
const tabulatorRef = useRef(null);
const { columnDefs, rowData, hasChildren } = useData(note, noteIds, viewConfig, undefined, () => {});
const [ html, setHtml ] = useState();
+ useEffect(() => {
+ if (!html) return;
+ onReady?.();
+ }, [ html ]);
+
return rowData && (
<>
{note.title}
diff --git a/apps/client/src/widgets/ribbon/NoteActions.tsx b/apps/client/src/widgets/ribbon/NoteActions.tsx
index d7e344dd8..33017221b 100644
--- a/apps/client/src/widgets/ribbon/NoteActions.tsx
+++ b/apps/client/src/widgets/ribbon/NoteActions.tsx
@@ -49,7 +49,7 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not
const canBeConvertedToAttachment = note?.isEligibleForConversionToAttachment();
const isSearchable = ["text", "code", "book", "mindMap", "doc"].includes(note.type);
const isInOptionsOrHelp = note?.noteId.startsWith("_options") || note?.noteId.startsWith("_help");
- const isPrintable = ["text", "code"].includes(note.type) || (note.type === "book" && ["presentation", "list"].includes(note.getLabelValue("viewType") ?? ""));
+ const isPrintable = ["text", "code"].includes(note.type) || (note.type === "book" && ["presentation", "list", "table"].includes(note.getLabelValue("viewType") ?? ""));
const isElectron = getIsElectron();
const isMac = getIsMac();
const hasSource = ["text", "code", "relationMap", "mermaid", "canvas", "mindMap", "aiChat"].includes(note.type);
From 8dc43dab59203dffcbdfff87fee35fe05b645f64 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Fri, 21 Nov 2025 22:28:43 +0200
Subject: [PATCH 09/11] docs(user): update printing documentation
---
.../Notes/Printing & Exporting as PDF.html | 63 +++++++++++--------
.../User Guide/Collections/List View.html | 11 ++--
.../Developer Guide/Documentation.md | 2 +-
docs/User Guide/!!!meta.json | 43 +++++++------
.../Notes/Printing & Exporting as PDF.md | 9 ++-
5 files changed, 74 insertions(+), 54 deletions(-)
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.html
index 35ae5862b..2e3b2c92c 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.html
@@ -59,9 +59,9 @@ class="admonition note">
orientation, size. However, there are a few Attributes to adjust some of the settings:
- - To print in landscape mode instead of portrait (useful for big diagrams
+
- To print in landscape mode instead of portrait (useful for big diagrams
or slides), add
#printLandscape.
- - By default, the resulting PDF will be in Letter format. It is possible
+
- By default, the resulting PDF will be in Letter format. It is possible
to adjust it to another page size via the
#printPageSize attribute,
with one of the following values: A0, A1, A2, A3, A4, A5, A6, Legal, Letter, Tabloid, Ledger.
@@ -71,12 +71,12 @@ class="admonition note">
Printing multiple notes
Since v0.100.0, it is possible to print more than one note at the time
- by using Collections:
+ by using Collections:
- - First create a collection.
- - Configure it to use List View.
+ - First create a collection.
+ - Configure it to use List View.
- Print the collection note normally.
+ data-list-item-id="ecaad5f90dcbfd65da21689c54b063ff9">Print the collection note normally.
The resulting collection will contain all the children of the collection,
while maintaining the hierarchy.
@@ -86,9 +86,9 @@ class="admonition note">
href="#root/_help_4TIF1oA4VQRO">Options and assigning a key combination
for:
- - Print Active Note
+
- Print Active Note
- - Export Active Note as PDF
+
- Export Active Note as PDF
Constraints & limitations
@@ -96,28 +96,39 @@ class="admonition note">
supported when printing, in which case the Print and Export as PDF options
will be disabled.
- - For Code notes:
+
- For Code notes:
- - Line numbers are not printed.
- - Syntax highlighting is enabled, however a default theme (Visual Studio)
+
- Line numbers are not printed.
+ - Syntax highlighting is enabled, however a default theme (Visual Studio)
is enforced.
- - For Collections:
+
- For Collections,
+ the following are supported:
- - List View is
- supported, allowing to print multiple notes at once while preserving hierarchy
- (similar to a book).
- - Presentation is
- also supported, where each slide/subnote is displayed.
- - The rest of the collections are not supported, but we plan to add support
+
- List View, allowing
+ to print multiple notes at once while preserving hierarchy (similar to
+ a book).
+ - Presentation,
+ where each slide/sub-note is displayed.
+ - Table,
+ where the table is rendered in a print-friendly way.
+
+ - Tables that are too complex (especially if they have multiple columns)
+ might not fit properly, however tables with a large number of rows are
+ supported thanks to pagination.
+ - Consider printing in landscape mode, or using
#printLandscape if
+ exporting to PDF.
+
+
+ - The rest of the collections are not supported, but we plan to add support
for all the collection types at some point.
- - Using Custom app-wide CSS for
+
- Using Custom app-wide CSS for
printing is not longer supported, due to a more stable but isolated mechanism.
- - We plan to introduce a new mechanism specifically for a print CSS.
+ - We plan to introduce a new mechanism specifically for a print CSS.
@@ -128,10 +139,10 @@ class="admonition note">
printing.
To do so:
- - Create a CSS code note.
- - On the note being printed, apply the
~printCss relation to
+ - Create a CSS code note.
+ - On the note being printed, apply the
~printCss relation to
point to the newly created CSS code note.
- - To apply the CSS to multiple notes, consider using inheritable attributes or
+
- To apply the CSS to multiple notes, consider using inheritable attributes or
Templates.
@@ -142,11 +153,11 @@ class="admonition note">
}
To remark:
- - Multiple CSS notes can be add by using multiple
~printCss relations.
+ - Multiple CSS notes can be add by using multiple
~printCss relations.
- If the note pointing to the
printCss doesn't have the right
+ data-list-item-id="e5989879c4432018bf90fecc5e31e090b">If the note pointing to the printCss doesn't have the right
note type or mime type, it will be ignored.
- - If migrating from a previous version where If migrating from a previous version where Custom app-wide CSS, there's no need for
@media print { since
the style-sheet is used only for printing.
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/List View.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/List View.html
index 64c09e024..4c8a97257 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/List View.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/List View.html
@@ -12,22 +12,21 @@
as a single continuous document.
Interaction
- - Each note can be expanded or collapsed by clicking on the arrow to the
+
- Each note can be expanded or collapsed by clicking on the arrow to the
left of the title.
- - In the Ribbon,
+
- In the Ribbon,
in the Collection tab there are options to expand and to collapse
all notes easily.
Printing and exporting to PDF
-Since v0.100.0, list collections can be printed or exported to PDF.
+Since v0.100.0, list collections can be printed or exported to PDF.
A printed list collection will print all the notes in the collection,
in the right order and preserving the full hierarchy.
If exported to PDF within the desktop application, there is additional
functionality:
- - The table of contents of the PDF will reflect the structure of the notes.
- - Reference and inline links to other notes within the same hierarchy will
+
- The table of contents of the PDF will reflect the structure of the notes.
+ - Reference and inline links to other notes within the same hierarchy will
be functional (will jump to the corresponding page). If a link refers to
a note that is not in the printed hierarchy, it will be unlinked.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Documentation.md b/docs/Developer Guide/Developer Guide/Documentation.md
index b8eca125b..6e7ea215f 100644
--- a/docs/Developer Guide/Developer Guide/Documentation.md
+++ b/docs/Developer Guide/Developer Guide/Documentation.md
@@ -1,5 +1,5 @@
# Documentation
-There are multiple types of documentation for Trilium:
+There are multiple types of documentation for Trilium:
* The _User Guide_ represents the user-facing documentation. This documentation can be browsed by users directly from within Trilium, by pressing F1.
* The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers.
diff --git a/docs/User Guide/!!!meta.json b/docs/User Guide/!!!meta.json
index 7b77cb46a..014f86614 100644
--- a/docs/User Guide/!!!meta.json
+++ b/docs/User Guide/!!!meta.json
@@ -4062,66 +4062,73 @@
{
"type": "relation",
"name": "internalLink",
- "value": "4TIF1oA4VQRO",
+ "value": "GTwFsgaA0lCt",
"isInheritable": false,
"position": 40
},
{
"type": "relation",
"name": "internalLink",
- "value": "KSZ04uQ2D1St",
+ "value": "mULW0Q3VojwY",
"isInheritable": false,
"position": 50
},
{
"type": "relation",
"name": "internalLink",
- "value": "6f9hih2hXXZk",
+ "value": "4TIF1oA4VQRO",
"isInheritable": false,
"position": 60
},
{
"type": "relation",
"name": "internalLink",
- "value": "GTwFsgaA0lCt",
+ "value": "KSZ04uQ2D1St",
"isInheritable": false,
"position": 70
},
{
"type": "relation",
"name": "internalLink",
- "value": "zP3PMqaG71Ct",
+ "value": "6f9hih2hXXZk",
"isInheritable": false,
"position": 80
},
{
"type": "relation",
"name": "internalLink",
- "value": "AlhDUqhENtH7",
+ "value": "zP3PMqaG71Ct",
"isInheritable": false,
"position": 90
},
{
"type": "relation",
"name": "internalLink",
- "value": "bwZpz2ajCEwO",
+ "value": "AlhDUqhENtH7",
"isInheritable": false,
"position": 100
},
{
"type": "relation",
"name": "internalLink",
- "value": "KC1HB96bqqHX",
+ "value": "bwZpz2ajCEwO",
"isInheritable": false,
"position": 110
},
{
"type": "relation",
"name": "internalLink",
- "value": "0ESUbbAxVnoK",
+ "value": "KC1HB96bqqHX",
"isInheritable": false,
"position": 120
},
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "0ESUbbAxVnoK",
+ "isInheritable": false,
+ "position": 130
+ },
{
"type": "label",
"name": "iconClass",
@@ -4139,9 +4146,9 @@
{
"type": "relation",
"name": "internalLink",
- "value": "mULW0Q3VojwY",
+ "value": "2FvYrpmOXm29",
"isInheritable": false,
- "position": 130
+ "position": 140
}
],
"format": "markdown",
@@ -10472,6 +10479,13 @@
"isInheritable": false,
"position": 20
},
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "NRnIZmSMc5sj",
+ "isInheritable": false,
+ "position": 30
+ },
{
"type": "label",
"name": "iconClass",
@@ -10485,13 +10499,6 @@
"value": "list",
"isInheritable": false,
"position": 30
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "NRnIZmSMc5sj",
- "isInheritable": false,
- "position": 40
}
],
"format": "markdown",
diff --git a/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.md b/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.md
index 083ad6ec7..56b0adb74 100644
--- a/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.md
+++ b/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.md
@@ -73,9 +73,12 @@ Not all Note Types
* For Code notes:
* Line numbers are not printed.
* Syntax highlighting is enabled, however a default theme (Visual Studio) is enforced.
-* For Collections:
- * List View is supported, allowing to print multiple notes at once while preserving hierarchy (similar to a book).
- * Presentation is also supported, where each slide/subnote is displayed.
+* For Collections, the following are supported:
+ * List View, allowing to print multiple notes at once while preserving hierarchy (similar to a book).
+ * Presentation, where each slide/sub-note is displayed.
+ * Table, where the table is rendered in a print-friendly way.
+ * Tables that are too complex (especially if they have multiple columns) might not fit properly, however tables with a large number of rows are supported thanks to pagination.
+ * Consider printing in landscape mode, or using `#printLandscape` if exporting to PDF.
* The rest of the collections are not supported, but we plan to add support for all the collection types at some point.
* Using Custom app-wide CSS for printing is not longer supported, due to a more stable but isolated mechanism.
* We plan to introduce a new mechanism specifically for a print CSS.
From ab14bdbb18f1a2a8243bbf3fa8a8f2a3c91571b3 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Fri, 21 Nov 2025 22:33:48 +0200
Subject: [PATCH 10/11] chore(print/table): address review
---
.../collections/table/TablePrintView.css | 24 +++++++++----------
.../src/widgets/collections/table/data.tsx | 2 +-
2 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/apps/client/src/widgets/collections/table/TablePrintView.css b/apps/client/src/widgets/collections/table/TablePrintView.css
index 74e282aaf..fef53c299 100644
--- a/apps/client/src/widgets/collections/table/TablePrintView.css
+++ b/apps/client/src/widgets/collections/table/TablePrintView.css
@@ -1,20 +1,20 @@
-.tabulator-print-table table,
-.tabulator-print-table th,
-.tabulator-print-table tr,
-.tabulator-print-table td {
- border: 1px solid black !important;
+.table-print-view .tabulator-print-table table,
+.table-print-view .tabulator-print-table th,
+.table-print-view .tabulator-print-table tr,
+.table-print-view .tabulator-print-table td {
+ border: 1px solid black;
border-collapse: collapse;
}
-.tabulator-print-table th {
- background-color: #f0f0f0 !important;
+.table-print-view .tabulator-print-table th {
+ background-color: #f0f0f0;
}
-.tabulator-print-table th,
-.tabulator-print-table td {
- padding: 0.25rem 0.5rem !important;
+.table-print-view .tabulator-print-table th,
+.table-print-view .tabulator-print-table td {
+ padding: 0.25rem 0.5rem;
}
-.tabulator-print-table td[aria-checked] svg path {
- fill: currentColor !important;
+.table-print-view .tabulator-print-table td[aria-checked] svg path {
+ fill: currentColor;
}
\ No newline at end of file
diff --git a/apps/client/src/widgets/collections/table/data.tsx b/apps/client/src/widgets/collections/table/data.tsx
index b77a1545e..94909939d 100644
--- a/apps/client/src/widgets/collections/table/data.tsx
+++ b/apps/client/src/widgets/collections/table/data.tsx
@@ -15,7 +15,7 @@ export interface TableConfig {
}
export default function useData(note: FNote, noteIds: string[], viewConfig: TableConfig | undefined, newAttributePosition: RefObject | undefined, resetNewAttributePosition: () => void) {
- const [ maxDepth ] = useNoteLabelInt(note, "maxNestingDepth") ?? -1;
+ const [ maxDepth ] = useNoteLabelInt(note, "maxNestingDepth");
const [ includeArchived ] = useNoteLabelBoolean(note, "includeArchived");
const [ columnDefs, setColumnDefs ] = useState();
From 5c0cf09c425b742389e1cc1878e8e56ef4deeb5e Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Fri, 21 Nov 2025 22:35:29 +0200
Subject: [PATCH 11/11] chore(print/table): revert back to using the export
module only
---
.../src/widgets/collections/table/TablePrintView.tsx | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/apps/client/src/widgets/collections/table/TablePrintView.tsx b/apps/client/src/widgets/collections/table/TablePrintView.tsx
index 9582b4360..534ba5764 100644
--- a/apps/client/src/widgets/collections/table/TablePrintView.tsx
+++ b/apps/client/src/widgets/collections/table/TablePrintView.tsx
@@ -1,7 +1,7 @@
import { useEffect, useRef, useState } from "preact/hooks";
import { ViewModeProps } from "../interface";
import useData, { TableConfig } from "./data";
-import { ExportModule, FormatModule, PrintModule, Tabulator as VanillaTabulator} from 'tabulator-tables';
+import { ExportModule, FormatModule, Tabulator as VanillaTabulator} from 'tabulator-tables';
import Tabulator from "./tabulator";
import { RawHtmlBlock } from "../../react/RawHtml";
import "./TablePrintView.css";
@@ -26,7 +26,7 @@ export default function TablePrintView({ note, noteIds, viewConfig, onReady }: V
{
const tabulator = tabulatorRef.current;
if (!tabulator) return;
- const generatedTable = tabulator.modules.export.generateTable(tabulator.options.printConfig, tabulator.options.printStyled, tabulator.options.printRowRange, "print");
- if(tabulator.options.printFormatter){
- tabulator.options.printFormatter(tabulator.element, generatedTable);
- }
- setHtml(generatedTable.outerHTML);
+ setHtml(tabulator.getHtml());
}}
/>
) : (