mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	chore(react/collections): get list view to show something
This commit is contained in:
		| @@ -1,4 +1,4 @@ | ||||
| import { allViewTypes, ViewTypeOptions } from "./interface"; | ||||
| import { allViewTypes, ViewModeProps, ViewTypeOptions } from "./interface"; | ||||
| import { useNoteContext, useNoteLabel, useTriliumEvent } from "../react/hooks"; | ||||
| import FNote from "../../entities/fnote"; | ||||
| import "./NoteList.css"; | ||||
| @@ -13,27 +13,27 @@ export default function NoteList({ }: NoteListProps) { | ||||
|     const { note } = useNoteContext(); | ||||
|     const viewType = useNoteViewType(note); | ||||
|     const noteIds = useNoteIds(note, viewType); | ||||
|     const isEnabled = (!!viewType); | ||||
|     const isEnabled = (note && !!viewType); | ||||
|  | ||||
|     // Refresh note Ids | ||||
|     console.log("Got note ids", noteIds); | ||||
|  | ||||
|     return ( | ||||
|         <div className="note-list-widget"> | ||||
|             {isEnabled && ( | ||||
|                 <div className="note-list-widget-content"> | ||||
|                     {getComponentByViewType(viewType)} | ||||
|                     {getComponentByViewType(note, noteIds, viewType)} | ||||
|                 </div> | ||||
|             )} | ||||
|         </div> | ||||
|     ); | ||||
| } | ||||
|  | ||||
| function getComponentByViewType(viewType: ViewTypeOptions) { | ||||
|     console.log("Got ", viewType); | ||||
| function getComponentByViewType(note: FNote, noteIds: string[], viewType: ViewTypeOptions) { | ||||
|     const props: ViewModeProps = { note, noteIds }; | ||||
|      | ||||
|     switch (viewType) { | ||||
|         case "list": | ||||
|             return <ListView />; | ||||
|             return <ListView {...props} />; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -54,12 +54,13 @@ function useNoteIds(note: FNote | null | undefined, viewType: ViewTypeOptions | | ||||
|     const [ noteIds, setNoteIds ] = useState<string[]>([]); | ||||
|      | ||||
|     async function refreshNoteIds() { | ||||
|         console.log("Refreshed note IDs"); | ||||
|         if (!note) { | ||||
|             setNoteIds([]); | ||||
|         } else if (viewType === "list" || viewType === "grid") { | ||||
|             console.log("Refreshed note IDs"); | ||||
|             setNoteIds(note.getChildNoteIds()); | ||||
|         } else { | ||||
|             console.log("Refreshed note IDs"); | ||||
|             setNoteIds(await note.getSubtreeNoteIds()); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -1,5 +1,14 @@ | ||||
| import FNote from "../../entities/fnote"; | ||||
| import type { ViewModeArgs } from "../view_widgets/view_mode"; | ||||
|  | ||||
| export const allViewTypes = ["list", "grid", "calendar", "table", "geoMap", "board"] as const; | ||||
| export type ArgsWithoutNoteId = Omit<ViewModeArgs, "noteIds">; | ||||
| export type ViewTypeOptions = typeof allViewTypes[number]; | ||||
|  | ||||
| export interface ViewModeProps { | ||||
|     note: FNote; | ||||
|     /** | ||||
|      * We're using noteIds so that it's not necessary to load all notes at once when paging. | ||||
|      */ | ||||
|     noteIds: string[]; | ||||
| } | ||||
|   | ||||
| @@ -1,14 +1,80 @@ | ||||
| export default function ListView() { | ||||
| import { useEffect, useMemo, useState } from "preact/hooks"; | ||||
| import FNote from "../../../entities/fnote"; | ||||
| import Icon from "../../react/Icon"; | ||||
| import { ViewModeProps } from "../interface"; | ||||
| import { useNoteLabel, useNoteLabelBoolean } from "../../react/hooks"; | ||||
| import froca from "../../../services/froca"; | ||||
| import NoteLink from "../../react/NoteLink"; | ||||
|  | ||||
| export default function ListView({ note, noteIds }: ViewModeProps) { | ||||
|     const [ isExpanded ] = useNoteLabelBoolean(note, "expanded"); | ||||
|     const filteredNoteIds = useMemo(() => { | ||||
|         // Filters the note IDs for the legacy view to filter out subnotes that are already included in the note content such as images, included notes. | ||||
|         const includedLinks = note ? note.getRelations().filter((rel) => rel.name === "imageLink" || rel.name === "includeNoteLink") : []; | ||||
|         const includedNoteIds = new Set(includedLinks.map((rel) => rel.value));    | ||||
|         return noteIds.filter((noteId) => !includedNoteIds.has(noteId) && noteId !== "_hidden"); | ||||
|     }, noteIds); | ||||
|     const { pageNotes } = usePagination(note, filteredNoteIds); | ||||
|  | ||||
|     return ( | ||||
|         <div class="note-list"> | ||||
|             <div class="note-list-wrapper"> | ||||
|                 List view goes here. | ||||
|                 <div class="note-list-pager"></div> | ||||
|          | ||||
|                 <div class="note-list-container use-tn-links"></div> | ||||
|                 <div class="note-list-container use-tn-links"> | ||||
|                     {pageNotes?.map(note => ( | ||||
|                         <NoteCard note={note} expand={isExpanded} /> | ||||
|                     ))} | ||||
|                 </div> | ||||
|          | ||||
|                 <div class="note-list-pager"></div> | ||||
|             </div> | ||||
|         </div> | ||||
|     ); | ||||
| } | ||||
|  | ||||
| function NoteCard({ note, expand }: { note: FNote, expand?: boolean }) { | ||||
|     const isSearch = note.type === "search"; | ||||
|     const notePath = isSearch | ||||
|         ? note.noteId // for search note parent, we want to display a non-search path | ||||
|         : `${note.noteId}/${note.noteId}`; | ||||
|  | ||||
|     return ( | ||||
|         <div | ||||
|             className="note-book-card no-tooltip-preview" | ||||
|             data-note-id={note.noteId} | ||||
|         > | ||||
|             <h5 className="note-book-header"> | ||||
|                 <Icon className="note-icon" icon={note.getIcon()} /> | ||||
|                 <NoteLink notePath={notePath} noPreview showNotePath={isSearch} /> | ||||
|             </h5> | ||||
|         </div> | ||||
|     ) | ||||
| } | ||||
|  | ||||
| function usePagination(note: FNote, noteIds: string[]) { | ||||
|     const [ page, setPage ] = useState(1); | ||||
|     const [ pageNotes, setPageNotes ] = useState<FNote[]>(); | ||||
|  | ||||
|     // Parse page size. | ||||
|     const [ pageSize ] = useNoteLabel(note, "pageSize"); | ||||
|     const pageSizeNum = parseInt(pageSize ?? "", 10); | ||||
|     const normalizedPageSize = (pageSizeNum && pageSizeNum > 0 ? pageSizeNum : 20); | ||||
|  | ||||
|     // Calculate start/end index. | ||||
|     const startIdx = (page - 1) * normalizedPageSize; | ||||
|     const endIdx = startIdx + normalizedPageSize; | ||||
|  | ||||
|     // Obtain notes within the range. | ||||
|     const pageNoteIds = noteIds.slice(startIdx, Math.min(endIdx, noteIds.length)); | ||||
|  | ||||
|     useEffect(() => { | ||||
|         froca.getNotes(pageNoteIds).then(setPageNotes); | ||||
|     }, [ note, noteIds, page, pageSize ]); | ||||
|  | ||||
|     return { | ||||
|         page, | ||||
|         setPage, | ||||
|         pageNotes | ||||
|     } | ||||
| } | ||||
| @@ -1,7 +1,8 @@ | ||||
| interface IconProps { | ||||
|     icon?: string; | ||||
|     className?: string; | ||||
| } | ||||
|  | ||||
| export default function Icon({ icon }: IconProps) { | ||||
|     return <span class={icon ?? "bx bx-empty"}></span> | ||||
| export default function Icon({ icon, className }: IconProps) { | ||||
|     return <span class={`${icon ?? "bx bx-empty"} ${className ?? ""}`}></span> | ||||
| } | ||||
| @@ -17,40 +17,10 @@ class ListOrGridView extends ViewMode<{}> { | ||||
|     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(viewType: ViewTypeOptions, args: ViewModeArgs) { | ||||
|         super(args, viewType); | ||||
|         this.$noteList = $(TPL); | ||||
|  | ||||
|  | ||||
|         args.$parent.append(this.$noteList); | ||||
|  | ||||
|         this.page = 1; | ||||
|         this.pageSize = parseInt(args.parentNote.getLabelValue("pageSize") || ""); | ||||
|  | ||||
|         if (!this.pageSize || this.pageSize < 1) { | ||||
|             this.pageSize = 20; | ||||
|         } | ||||
|  | ||||
|         this.$noteList.addClass(`${this.viewType}-view`); | ||||
|  | ||||
|         this.showNotePath = args.showNotePath; | ||||
|     } | ||||
|  | ||||
|     /** @returns {Set<string>} 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)); | ||||
|     } | ||||
|  | ||||
|     async beforeRender() { | ||||
|         super.beforeRender(); | ||||
|         const includedNoteIds = this.getIncludedNoteIds(); | ||||
|         this.filteredNoteIds = this.noteIds.filter((noteId) => !includedNoteIds.has(noteId) && noteId !== "_hidden"); | ||||
|     } | ||||
|  | ||||
|     async renderList() { | ||||
| @@ -70,20 +40,6 @@ class ListOrGridView extends ViewMode<{}> { | ||||
|  | ||||
|         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.filteredNoteIds.slice(startIdx, Math.min(endIdx, this.filteredNoteIds.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; | ||||
| @@ -132,25 +88,15 @@ class ListOrGridView extends ViewMode<{}> { | ||||
|     } | ||||
|  | ||||
|     async renderNote(note: FNote, expand: boolean = false) { | ||||
|         const $expander = $('<span class="note-expander bx bx-chevron-right"></span>'); | ||||
|  | ||||
|         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 = $('<div class="note-book-card">') | ||||
|             .attr("data-note-id", note.noteId) | ||||
|             .addClass("no-tooltip-preview") | ||||
|             .append( | ||||
|                 $('<h5 class="note-book-header">') | ||||
|                     .append($expander) | ||||
|                     .append($('<span class="note-icon">').addClass(note.getIcon())) | ||||
|                     .append( | ||||
|                         this.viewType === "grid" | ||||
|                             ? $('<span class="note-book-title">').text(await treeService.getNoteTitle(note.noteId, this.parentNote.noteId)) | ||||
|                             : (await linkService.createLink(notePath, { showTooltip: false, showNotePath: this.showNotePath })).addClass("note-book-title") | ||||
|                             : (await linkService.createLink(notePath, { showNotePath: this.showNotePath })).addClass("note-book-title") | ||||
|                     ) | ||||
|                     .append($renderedAttributes) | ||||
|             ); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user