mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-04 13:39:01 +01:00 
			
		
		
		
	feat(book/table): store hidden columns
This commit is contained in:
		
							parent
							
								
									c7b16cd043
								
							
						
					
					
						commit
						ccb9b7e5fb
					
				@ -109,24 +109,22 @@ const CALENDAR_VIEWS = [
 | 
				
			|||||||
    "listMonth"
 | 
					    "listMonth"
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class CalendarView extends ViewMode {
 | 
					export default class CalendarView extends ViewMode<{}> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private $root: JQuery<HTMLElement>;
 | 
					    private $root: JQuery<HTMLElement>;
 | 
				
			||||||
    private $calendarContainer: JQuery<HTMLElement>;
 | 
					    private $calendarContainer: JQuery<HTMLElement>;
 | 
				
			||||||
    private noteIds: string[];
 | 
					    private noteIds: string[];
 | 
				
			||||||
    private parentNote: FNote;
 | 
					 | 
				
			||||||
    private calendar?: Calendar;
 | 
					    private calendar?: Calendar;
 | 
				
			||||||
    private isCalendarRoot: boolean;
 | 
					    private isCalendarRoot: boolean;
 | 
				
			||||||
    private lastView?: string;
 | 
					    private lastView?: string;
 | 
				
			||||||
    private debouncedSaveView?: DebouncedFunction<() => void>;
 | 
					    private debouncedSaveView?: DebouncedFunction<() => void>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(args: ViewModeArgs) {
 | 
					    constructor(args: ViewModeArgs) {
 | 
				
			||||||
        super(args);
 | 
					        super(args, "calendar");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.$root = $(TPL);
 | 
					        this.$root = $(TPL);
 | 
				
			||||||
        this.$calendarContainer = this.$root.find(".calendar-container");
 | 
					        this.$calendarContainer = this.$root.find(".calendar-container");
 | 
				
			||||||
        this.noteIds = args.noteIds;
 | 
					        this.noteIds = args.noteIds;
 | 
				
			||||||
        this.parentNote = args.parentNote;
 | 
					 | 
				
			||||||
        this.isCalendarRoot = false;
 | 
					        this.isCalendarRoot = false;
 | 
				
			||||||
        args.$parent.append(this.$root);
 | 
					        args.$parent.append(this.$root);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,7 @@ import treeService from "../../services/tree.js";
 | 
				
			|||||||
import utils from "../../services/utils.js";
 | 
					import utils from "../../services/utils.js";
 | 
				
			||||||
import type FNote from "../../entities/fnote.js";
 | 
					import type FNote from "../../entities/fnote.js";
 | 
				
			||||||
import ViewMode, { type ViewModeArgs } from "./view_mode.js";
 | 
					import ViewMode, { type ViewModeArgs } from "./view_mode.js";
 | 
				
			||||||
 | 
					import type { ViewTypeOptions } from "../../services/note_list_renderer.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const TPL = /*html*/`
 | 
					const TPL = /*html*/`
 | 
				
			||||||
<div class="note-list">
 | 
					<div class="note-list">
 | 
				
			||||||
@ -157,26 +158,22 @@ const TPL = /*html*/`
 | 
				
			|||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</div>`;
 | 
					</div>`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ListOrGridView extends ViewMode {
 | 
					class ListOrGridView extends ViewMode<{}> {
 | 
				
			||||||
    private $noteList: JQuery<HTMLElement>;
 | 
					    private $noteList: JQuery<HTMLElement>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private parentNote: FNote;
 | 
					 | 
				
			||||||
    private noteIds: string[];
 | 
					    private noteIds: string[];
 | 
				
			||||||
    private page?: number;
 | 
					    private page?: number;
 | 
				
			||||||
    private pageSize?: number;
 | 
					    private pageSize?: number;
 | 
				
			||||||
    private viewType?: string | null;
 | 
					 | 
				
			||||||
    private showNotePath?: boolean;
 | 
					    private showNotePath?: boolean;
 | 
				
			||||||
    private highlightRegex?: RegExp | null;
 | 
					    private highlightRegex?: RegExp | null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /*
 | 
					    /*
 | 
				
			||||||
     * We're using noteIds so that it's not necessary to load all notes at once when paging
 | 
					     * We're using noteIds so that it's not necessary to load all notes at once when paging
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    constructor(viewType: string, args: ViewModeArgs) {
 | 
					    constructor(viewType: ViewTypeOptions, args: ViewModeArgs) {
 | 
				
			||||||
        super(args);
 | 
					        super(args, viewType);
 | 
				
			||||||
        this.$noteList = $(TPL);
 | 
					        this.$noteList = $(TPL);
 | 
				
			||||||
        this.viewType = viewType;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.parentNote = args.parentNote;
 | 
					 | 
				
			||||||
        const includedNoteIds = this.getIncludedNoteIds();
 | 
					        const includedNoteIds = this.getIncludedNoteIds();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.noteIds = args.noteIds.filter((noteId) => !includedNoteIds.has(noteId) && noteId !== "_hidden");
 | 
					        this.noteIds = args.noteIds.filter((noteId) => !includedNoteIds.has(noteId) && noteId !== "_hidden");
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
import froca from "../../services/froca.js";
 | 
					import froca from "../../services/froca.js";
 | 
				
			||||||
import renderTable from "./table_view/renderer.js";
 | 
					import renderTable from "./table_view/renderer.js";
 | 
				
			||||||
 | 
					import type { StateInfo } from "./table_view/storage.js";
 | 
				
			||||||
import ViewMode, { ViewModeArgs } from "./view_mode";
 | 
					import ViewMode, { ViewModeArgs } from "./view_mode";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const TPL = /*html*/`
 | 
					const TPL = /*html*/`
 | 
				
			||||||
@ -24,14 +25,14 @@ const TPL = /*html*/`
 | 
				
			|||||||
</div>
 | 
					</div>
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class TableView extends ViewMode {
 | 
					export default class TableView extends ViewMode<StateInfo> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private $root: JQuery<HTMLElement>;
 | 
					    private $root: JQuery<HTMLElement>;
 | 
				
			||||||
    private $container: JQuery<HTMLElement>;
 | 
					    private $container: JQuery<HTMLElement>;
 | 
				
			||||||
    private args: ViewModeArgs;
 | 
					    private args: ViewModeArgs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(args: ViewModeArgs) {
 | 
					    constructor(args: ViewModeArgs) {
 | 
				
			||||||
        super(args);
 | 
					        super(args, "table");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.$root = $(TPL);
 | 
					        this.$root = $(TPL);
 | 
				
			||||||
        this.$container = this.$root.find(".table-view-container");
 | 
					        this.$container = this.$root.find(".table-view-container");
 | 
				
			||||||
@ -48,7 +49,7 @@ export default class TableView extends ViewMode {
 | 
				
			|||||||
        const notes = await froca.getNotes(noteIds);
 | 
					        const notes = await froca.getNotes(noteIds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.$container.empty();
 | 
					        this.$container.empty();
 | 
				
			||||||
        renderTable(this.$container[0], parentNote, notes);
 | 
					        renderTable(this.$container[0], parentNote, notes, this.viewStorage);
 | 
				
			||||||
        return this.$root;
 | 
					        return this.$root;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,35 @@
 | 
				
			|||||||
import { createGrid, AllCommunityModule, ModuleRegistry, columnDropStyleBordered, GridOptions } from "ag-grid-community";
 | 
					import { createGrid, AllCommunityModule, ModuleRegistry, GridOptions } from "ag-grid-community";
 | 
				
			||||||
import { buildData, type TableData } from "./data.js";
 | 
					import { buildData, type TableData } from "./data.js";
 | 
				
			||||||
import FNote from "../../../entities/fnote.js";
 | 
					import FNote from "../../../entities/fnote.js";
 | 
				
			||||||
import getPromotedAttributeInformation, { PromotedAttributeInformation } from "./parser.js";
 | 
					import getPromotedAttributeInformation from "./parser.js";
 | 
				
			||||||
import { setLabel } from "../../../services/attributes.js";
 | 
					import { setLabel } from "../../../services/attributes.js";
 | 
				
			||||||
import applyHeaderCustomization from "./header-customization.js";
 | 
					import applyHeaderCustomization from "./header-customization.js";
 | 
				
			||||||
 | 
					import ViewModeStorage from "../view_mode_storage.js";
 | 
				
			||||||
 | 
					import { type StateInfo } from "./storage.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ModuleRegistry.registerModules([ AllCommunityModule ]);
 | 
					ModuleRegistry.registerModules([ AllCommunityModule ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function renderTable(el: HTMLElement, parentNote: FNote, notes: FNote[]) {
 | 
					export default async function renderTable(el: HTMLElement, parentNote: FNote, notes: FNote[], storage: ViewModeStorage<StateInfo>) {
 | 
				
			||||||
    const info = getPromotedAttributeInformation(parentNote);
 | 
					    const info = getPromotedAttributeInformation(parentNote);
 | 
				
			||||||
 | 
					    const viewStorage = await storage.restore();
 | 
				
			||||||
 | 
					    const initialState = viewStorage?.gridState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    createGrid(el, {
 | 
					    createGrid(el, {
 | 
				
			||||||
        ...buildData(info, notes),
 | 
					        ...buildData(info, notes),
 | 
				
			||||||
        ...setupEditing(info),
 | 
					        ...setupEditing(),
 | 
				
			||||||
        onGridReady(event) {
 | 
					        initialState,
 | 
				
			||||||
 | 
					        async onGridReady(event) {
 | 
				
			||||||
            applyHeaderCustomization(el, event.api);
 | 
					            applyHeaderCustomization(el, event.api);
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        onStateUpdated(event) {
 | 
				
			||||||
 | 
					            storage.store({
 | 
				
			||||||
 | 
					                gridState: event.api.getState()
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function setupEditing(info: PromotedAttributeInformation[]): GridOptions<TableData> {
 | 
					function setupEditing(): GridOptions<TableData> {
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
        onCellValueChanged(event) {
 | 
					        onCellValueChanged(event) {
 | 
				
			||||||
            if (event.type !== "cellValueChanged") {
 | 
					            if (event.type !== "cellValueChanged") {
 | 
				
			||||||
@ -37,3 +47,4 @@ function setupEditing(info: PromotedAttributeInformation[]): GridOptions<TableDa
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					import { GridState } from "ag-grid-community";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface StateInfo {
 | 
				
			||||||
 | 
					    gridState: GridState;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1,5 +1,7 @@
 | 
				
			|||||||
import type { EventData } from "../../components/app_context.js";
 | 
					import type { EventData } from "../../components/app_context.js";
 | 
				
			||||||
import type FNote from "../../entities/fnote.js";
 | 
					import type FNote from "../../entities/fnote.js";
 | 
				
			||||||
 | 
					import type { ViewTypeOptions } from "../../services/note_list_renderer.js";
 | 
				
			||||||
 | 
					import ViewModeStorage from "./view_mode_storage.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ViewModeArgs {
 | 
					export interface ViewModeArgs {
 | 
				
			||||||
    $parent: JQuery<HTMLElement>;
 | 
					    $parent: JQuery<HTMLElement>;
 | 
				
			||||||
@ -8,11 +10,18 @@ export interface ViewModeArgs {
 | 
				
			|||||||
    showNotePath?: boolean;
 | 
					    showNotePath?: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default abstract class ViewMode {
 | 
					export default abstract class ViewMode<T extends object> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(args: ViewModeArgs) {
 | 
					    private _viewStorage: ViewModeStorage<T> | null;
 | 
				
			||||||
 | 
					    protected parentNote: FNote;
 | 
				
			||||||
 | 
					    protected viewType: ViewTypeOptions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(args: ViewModeArgs, viewType: ViewTypeOptions) {
 | 
				
			||||||
 | 
					        this.parentNote = args.parentNote;
 | 
				
			||||||
 | 
					        this._viewStorage = null;
 | 
				
			||||||
        // note list must be added to the DOM immediately, otherwise some functionality scripting (canvas) won't work
 | 
					        // note list must be added to the DOM immediately, otherwise some functionality scripting (canvas) won't work
 | 
				
			||||||
        args.$parent.empty();
 | 
					        args.$parent.empty();
 | 
				
			||||||
 | 
					        this.viewType = viewType;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    abstract renderList(): Promise<JQuery<HTMLElement> | undefined>;
 | 
					    abstract renderList(): Promise<JQuery<HTMLElement> | undefined>;
 | 
				
			||||||
@ -32,4 +41,13 @@ export default abstract class ViewMode {
 | 
				
			|||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    get viewStorage() {
 | 
				
			||||||
 | 
					        if (this._viewStorage) {
 | 
				
			||||||
 | 
					            return this._viewStorage;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this._viewStorage = new ViewModeStorage(this.parentNote, this.viewType);
 | 
				
			||||||
 | 
					        return this._viewStorage;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										43
									
								
								apps/client/src/widgets/view_widgets/view_mode_storage.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								apps/client/src/widgets/view_widgets/view_mode_storage.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					import type FNote from "../../entities/fnote";
 | 
				
			||||||
 | 
					import type { ViewTypeOptions } from "../../services/note_list_renderer";
 | 
				
			||||||
 | 
					import server from "../../services/server";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ATTACHMENT_ROLE = "viewConfig";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class ViewModeStorage<T extends object> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private note: FNote;
 | 
				
			||||||
 | 
					    private attachmentName: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(note: FNote, viewType: ViewTypeOptions) {
 | 
				
			||||||
 | 
					        this.note = note;
 | 
				
			||||||
 | 
					        this.attachmentName = viewType + ".json";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async store(data: T) {
 | 
				
			||||||
 | 
					        const payload = {
 | 
				
			||||||
 | 
					            role: ATTACHMENT_ROLE,
 | 
				
			||||||
 | 
					            title: this.attachmentName,
 | 
				
			||||||
 | 
					            mime: "application/json",
 | 
				
			||||||
 | 
					            content: JSON.stringify(data),
 | 
				
			||||||
 | 
					            position: 0
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        await server.post(`notes/${this.note.noteId}/attachments?matchBy=title`, payload);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async restore() {
 | 
				
			||||||
 | 
					        const existingAttachments = await this.note.getAttachmentsByRole(ATTACHMENT_ROLE);
 | 
				
			||||||
 | 
					        if (existingAttachments.length === 0) {
 | 
				
			||||||
 | 
					            return undefined;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const attachment = existingAttachments
 | 
				
			||||||
 | 
					            .find(a => a.title === this.attachmentName);
 | 
				
			||||||
 | 
					        if (!attachment) {
 | 
				
			||||||
 | 
					            return undefined;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const attachmentData = await server.get<{ content: string } | null>(`attachments/${attachment.attachmentId}/blob`);
 | 
				
			||||||
 | 
					        return JSON.parse(attachmentData?.content ?? "{}");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user