diff --git a/src/public/app/services/note_list_renderer.ts b/src/public/app/services/note_list_renderer.ts
index 06d2c1a48..6fd9d2ec7 100644
--- a/src/public/app/services/note_list_renderer.ts
+++ b/src/public/app/services/note_list_renderer.ts
@@ -1,404 +1,17 @@
-import linkService from "./link.js";
-import contentRenderer from "./content_renderer.js";
-import froca from "./froca.js";
-import attributeRenderer from "./attribute_renderer.js";
-import libraryLoader from "./library_loader.js";
-import treeService from "./tree.js";
-import utils from "./utils.js";
import type FNote from "../entities/fnote.js";
+import ListOrGridView from "../widgets/view_widgets/list_or_grid_view.js";
+import type ViewMode from "../widgets/view_widgets/view_mode.js";
-const TPL = `
-
`;
-
-class NoteListRenderer {
- private $noteList: JQuery;
-
- private parentNote: FNote;
- private noteIds: string[];
- private page?: number;
- private pageSize?: number;
- private viewType?: string | null;
- private showNotePath?: boolean;
- private highlightRegex?: RegExp | null;
-
- /*
- * We're using noteIds so that it's not necessary to load all notes at once when paging
- */
constructor($parent: JQuery, parentNote: FNote, noteIds: string[], showNotePath: boolean = false) {
- this.$noteList = $(TPL);
-
- // note list must be added to the DOM immediately, otherwise some functionality scripting (canvas) won't work
- $parent.empty();
-
- this.parentNote = parentNote;
- const includedNoteIds = this.getIncludedNoteIds();
-
- this.noteIds = noteIds.filter((noteId) => !includedNoteIds.has(noteId) && noteId !== "_hidden");
-
- if (this.noteIds.length === 0) {
- return;
- }
-
- $parent.append(this.$noteList);
-
- this.page = 1;
- this.pageSize = parseInt(parentNote.getLabelValue("pageSize") || "");
-
- if (!this.pageSize || this.pageSize < 1) {
- this.pageSize = 20;
- }
-
- this.viewType = parentNote.getLabelValue("viewType");
-
- if (!["list", "grid"].includes(this.viewType || "")) {
- // when not explicitly set, decide based on the note type
- this.viewType = parentNote.type === "search" ? "list" : "grid";
- }
-
- this.$noteList.addClass(`${this.viewType}-view`);
-
- this.showNotePath = showNotePath;
- }
-
- /** @returns {Set} list of noteIds included (images, included notes) in the parent note and which
- * don't have to be shown in the note list. */
- getIncludedNoteIds() {
- const includedLinks = this.parentNote ? this.parentNote.getRelations().filter((rel) => rel.name === "imageLink" || rel.name === "includeNoteLink") : [];
-
- return new Set(includedLinks.map((rel) => rel.value));
+ this.viewMode = new ListOrGridView($parent, parentNote, noteIds, showNotePath);
}
async renderList() {
- if (this.noteIds.length === 0 || !this.page || !this.pageSize) {
- this.$noteList.hide();
- return;
- }
-
- const highlightedTokens = this.parentNote.highlightedTokens || [];
- if (highlightedTokens.length > 0) {
- await libraryLoader.requireLibrary(libraryLoader.MARKJS);
-
- const regex = highlightedTokens.map((token) => utils.escapeRegExp(token)).join("|");
-
- this.highlightRegex = new RegExp(regex, "gi");
- } else {
- this.highlightRegex = null;
- }
-
- this.$noteList.show();
-
- const $container = this.$noteList.find(".note-list-container").empty();
-
- const startIdx = (this.page - 1) * this.pageSize;
- const endIdx = startIdx + this.pageSize;
-
- const pageNoteIds = this.noteIds.slice(startIdx, Math.min(endIdx, this.noteIds.length));
- const pageNotes = await froca.getNotes(pageNoteIds);
-
- for (const note of pageNotes) {
- const $card = await this.renderNote(note, this.parentNote.isLabelTruthy("expanded"));
-
- $container.append($card);
- }
-
- this.renderPager();
-
- return this.$noteList;
+ return await this.viewMode.renderList();
}
- renderPager() {
- const $pager = this.$noteList.find(".note-list-pager").empty();
- if (!this.page || !this.pageSize) {
- return;
- }
-
- const pageCount = Math.ceil(this.noteIds.length / this.pageSize);
-
- $pager.toggle(pageCount > 1);
-
- let lastPrinted;
-
- for (let i = 1; i <= pageCount; i++) {
- if (pageCount < 20 || i <= 5 || pageCount - i <= 5 || Math.abs(this.page - i) <= 2) {
- lastPrinted = true;
-
- const startIndex = (i - 1) * this.pageSize + 1;
- const endIndex = Math.min(this.noteIds.length, i * this.pageSize);
-
- $pager.append(
- i === this.page
- ? $("").text(i).css("text-decoration", "underline").css("font-weight", "bold")
- : $('')
- .text(i)
- .attr("title", `Page of ${startIndex} - ${endIndex}`)
- .on("click", () => {
- this.page = i;
- this.renderList();
- }),
- " "
- );
- } else if (lastPrinted) {
- $pager.append("... ");
-
- lastPrinted = false;
- }
- }
-
- // no need to distinguish "note" vs "notes" since in case of one result, there's no paging at all
- $pager.append(``);
- }
-
- async renderNote(note: FNote, expand: boolean = false) {
- const $expander = $('');
-
- const { $renderedAttributes } = await attributeRenderer.renderNormalAttributes(note);
- const notePath =
- this.parentNote.type === "search"
- ? note.noteId // for search note parent, we want to display a non-search path
- : `${this.parentNote.noteId}/${note.noteId}`;
-
- const $card = $('')
- .attr("data-note-id", note.noteId)
- .append(
- $('