mirror of
https://github.com/zadam/trilium.git
synced 2025-11-26 02:24:23 +01:00
feat(print/table): basic implementation using export module
This commit is contained in:
parent
4552b2b158
commit
6f83b932b0
@ -14,6 +14,7 @@ import { WebSocketMessage } from "@triliumnext/commons";
|
|||||||
import froca from "../../services/froca";
|
import froca from "../../services/froca";
|
||||||
import PresentationView from "./presentation";
|
import PresentationView from "./presentation";
|
||||||
import { ListPrintView } from "./legacy/ListPrintView";
|
import { ListPrintView } from "./legacy/ListPrintView";
|
||||||
|
import TablePrintView from "./table/TablePrintView";
|
||||||
|
|
||||||
interface NoteListProps {
|
interface NoteListProps {
|
||||||
note: FNote | null | undefined;
|
note: FNote | null | undefined;
|
||||||
@ -117,9 +118,13 @@ function getComponentByViewType(viewType: ViewTypeOptions, props: ViewModeProps<
|
|||||||
case "geoMap":
|
case "geoMap":
|
||||||
return <GeoView {...props} />;
|
return <GeoView {...props} />;
|
||||||
case "calendar":
|
case "calendar":
|
||||||
return <CalendarView {...props} />
|
return <CalendarView {...props} />;
|
||||||
case "table":
|
case "table":
|
||||||
return <TableView {...props} />
|
if (props.media !== "print") {
|
||||||
|
return <TableView {...props} />;
|
||||||
|
} else {
|
||||||
|
return <TablePrintView {...props} />;
|
||||||
|
}
|
||||||
case "board":
|
case "board":
|
||||||
return <BoardView {...props} />
|
return <BoardView {...props} />
|
||||||
case "presentation":
|
case "presentation":
|
||||||
|
|||||||
38
apps/client/src/widgets/collections/table/TablePrintView.tsx
Normal file
38
apps/client/src/widgets/collections/table/TablePrintView.tsx
Normal file
@ -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<TableConfig>) {
|
||||||
|
const tabulatorRef = useRef<VanillaTabulator>(null);
|
||||||
|
const { columnDefs, rowData, movableRows, hasChildren } = useData(note, noteIds, viewConfig, undefined, () => {});
|
||||||
|
const [ html, setHtml ] = useState<string>();
|
||||||
|
|
||||||
|
return rowData && (
|
||||||
|
<div className="table-print-view">
|
||||||
|
{!html ? (
|
||||||
|
<Tabulator
|
||||||
|
tabulatorRef={tabulatorRef}
|
||||||
|
className="table-print-view-container"
|
||||||
|
modules={[ PrintModule, ExportModule ]}
|
||||||
|
columns={columnDefs ?? []}
|
||||||
|
data={rowData}
|
||||||
|
index="branchId"
|
||||||
|
dataTree={hasChildren}
|
||||||
|
printAsHtml={true}
|
||||||
|
printStyled={true}
|
||||||
|
onReady={() => {
|
||||||
|
const tabulator = tabulatorRef.current;
|
||||||
|
if (!tabulator) return;
|
||||||
|
setHtml(tabulator.getHtml());
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<RawHtmlBlock html={html} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
77
apps/client/src/widgets/collections/table/data.tsx
Normal file
77
apps/client/src/widgets/collections/table/data.tsx
Normal file
@ -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<number | undefined> | undefined, resetNewAttributePosition: () => void) {
|
||||||
|
const [ maxDepth ] = useNoteLabelInt(note, "maxNestingDepth") ?? -1;
|
||||||
|
const [ includeArchived ] = useNoteLabelBoolean(note, "includeArchived");
|
||||||
|
|
||||||
|
const [ columnDefs, setColumnDefs ] = useState<ColumnDefinition[]>();
|
||||||
|
const [ rowData, setRowData ] = useState<TableData[]>();
|
||||||
|
const [ hasChildren, setHasChildren ] = useState<boolean>();
|
||||||
|
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 };
|
||||||
|
}
|
||||||
@ -1,10 +1,9 @@
|
|||||||
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "preact/hooks";
|
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "preact/hooks";
|
||||||
import { ViewModeProps } from "../interface";
|
import { ViewModeProps } from "../interface";
|
||||||
import { buildColumnDefinitions } from "./columns";
|
import { TableData } from "./rows";
|
||||||
import getAttributeDefinitionInformation, { buildRowDefinitions, TableData } from "./rows";
|
import { useLegacyWidget } from "../../react/hooks";
|
||||||
import { useLegacyWidget, useNoteLabelBoolean, useNoteLabelInt, useTriliumEvent } from "../../react/hooks";
|
|
||||||
import Tabulator from "./tabulator";
|
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 { useContextMenu } from "./context_menu";
|
||||||
import { ParentComponent } from "../../react/react_utils";
|
import { ParentComponent } from "../../react/react_utils";
|
||||||
import FNote from "../../../entities/fnote";
|
import FNote from "../../../entities/fnote";
|
||||||
@ -14,16 +13,8 @@ import "./index.css";
|
|||||||
import useRowTableEditing from "./row_editing";
|
import useRowTableEditing from "./row_editing";
|
||||||
import useColTableEditing from "./col_editing";
|
import useColTableEditing from "./col_editing";
|
||||||
import AttributeDetailWidget from "../../attribute_widgets/attribute_detail";
|
import AttributeDetailWidget from "../../attribute_widgets/attribute_detail";
|
||||||
import attributes from "../../../services/attributes";
|
|
||||||
import { RefObject } from "preact";
|
|
||||||
import SpacedUpdate from "../../../services/spaced_update";
|
import SpacedUpdate from "../../../services/spaced_update";
|
||||||
import froca from "../../../services/froca";
|
import useData, { TableConfig } from "./data";
|
||||||
|
|
||||||
interface TableConfig {
|
|
||||||
tableData: {
|
|
||||||
columns?: ColumnDefinition[];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function TableView({ note, noteIds, notePath, viewConfig, saveConfig }: ViewModeProps<TableConfig>) {
|
export default function TableView({ note, noteIds, notePath, viewConfig, saveConfig }: ViewModeProps<TableConfig>) {
|
||||||
const tabulatorRef = useRef<VanillaTabulator>(null);
|
const tabulatorRef = useRef<VanillaTabulator>(null);
|
||||||
@ -118,67 +109,7 @@ function usePersistence(viewConfig: TableConfig | null | undefined, saveConfig:
|
|||||||
return () => {
|
return () => {
|
||||||
spacedUpdate.updateNowIfNecessary();
|
spacedUpdate.updateNowIfNecessary();
|
||||||
};
|
};
|
||||||
}, [ viewConfig, saveConfig ])
|
}, [ viewConfig, saveConfig ]);
|
||||||
|
|
||||||
return persistenceProps;
|
return persistenceProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
function useData(note: FNote, noteIds: string[], viewConfig: TableConfig | undefined, newAttributePosition: RefObject<number | undefined>, resetNewAttributePosition: () => void) {
|
|
||||||
const [ maxDepth ] = useNoteLabelInt(note, "maxNestingDepth") ?? -1;
|
|
||||||
const [ includeArchived ] = useNoteLabelBoolean(note, "includeArchived");
|
|
||||||
|
|
||||||
const [ columnDefs, setColumnDefs ] = useState<ColumnDefinition[]>();
|
|
||||||
const [ rowData, setRowData ] = useState<TableData[]>();
|
|
||||||
const [ hasChildren, setHasChildren ] = useState<boolean>();
|
|
||||||
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 };
|
|
||||||
}
|
|
||||||
|
|||||||
@ -14,9 +14,10 @@ interface TableProps<T> extends Omit<Options, "data" | "footerElement" | "index"
|
|||||||
events?: Partial<EventCallBackMethods>;
|
events?: Partial<EventCallBackMethods>;
|
||||||
index: keyof T;
|
index: keyof T;
|
||||||
footerElement?: string | HTMLElement | JSX.Element;
|
footerElement?: string | HTMLElement | JSX.Element;
|
||||||
|
onReady?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Tabulator<T>({ className, columns, data, modules, tabulatorRef: externalTabulatorRef, footerElement, events, index, dataTree, ...restProps }: TableProps<T>) {
|
export default function Tabulator<T>({ className, columns, data, modules, tabulatorRef: externalTabulatorRef, footerElement, events, index, dataTree, onReady, ...restProps }: TableProps<T>) {
|
||||||
const parentComponent = useContext(ParentComponent);
|
const parentComponent = useContext(ParentComponent);
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const tabulatorRef = useRef<VanillaTabulator>(null);
|
const tabulatorRef = useRef<VanillaTabulator>(null);
|
||||||
@ -43,6 +44,7 @@ export default function Tabulator<T>({ className, columns, data, modules, tabula
|
|||||||
tabulator.on("tableBuilt", () => {
|
tabulator.on("tableBuilt", () => {
|
||||||
tabulatorRef.current = tabulator;
|
tabulatorRef.current = tabulator;
|
||||||
externalTabulatorRef.current = tabulator;
|
externalTabulatorRef.current = tabulator;
|
||||||
|
onReady?.();
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => tabulator.destroy();
|
return () => tabulator.destroy();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user