mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	feat(book/table): store hidden columns
This commit is contained in:
		| @@ -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 ?? "{}"); | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user