chore(react/collections): calculate note Ids

This commit is contained in:
Elian Doran 2025-08-30 15:11:49 +03:00
parent 5fb843268f
commit ecf44deecf
No known key found for this signature in database
9 changed files with 245 additions and 207 deletions

View File

@ -0,0 +1,18 @@
.note-list-widget {
min-height: 0;
overflow: auto;
contain: none !important;
}
.note-list-widget .note-list {
padding: 10px;
}
.note-list-widget.full-height,
.note-list-widget.full-height .note-list-widget-content {
height: 100%;
}
.note-list-widget video {
height: 100%;
}

View File

@ -1,7 +1,80 @@
import { allViewTypes, ViewTypeOptions } from "./interface";
import { useNoteContext, useNoteLabel, useTriliumEvent } from "../react/hooks";
import FNote from "../../entities/fnote";
import "./NoteList.css";
import ListView from "./legacy/ListView";
import { useEffect, useState } from "preact/hooks";
interface NoteListProps {
displayOnlyCollections?: boolean;
}
export default function NoteList({ }: NoteListProps) {
return <p>Hi</p>
const { note } = useNoteContext();
const viewType = useNoteViewType(note);
const noteIds = useNoteIds(note, viewType);
const isEnabled = (!!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)}
</div>
)}
</div>
);
}
function getComponentByViewType(viewType: ViewTypeOptions) {
console.log("Got ", viewType);
switch (viewType) {
case "list":
return <ListView />;
}
}
function useNoteViewType(note?: FNote | null): ViewTypeOptions | undefined {
const [ viewType ] = useNoteLabel(note, "viewType");
if (!note) {
return undefined;
} else if (!(allViewTypes as readonly string[]).includes(viewType || "")) {
// when not explicitly set, decide based on the note type
return note.type === "search" ? "list" : "grid";
} else {
return viewType as ViewTypeOptions;
}
}
function useNoteIds(note: FNote | null | undefined, viewType: ViewTypeOptions | undefined) {
const [ noteIds, setNoteIds ] = useState<string[]>([]);
async function refreshNoteIds() {
console.log("Refreshed note IDs");
if (!note) {
setNoteIds([]);
} else if (viewType === "list" || viewType === "grid") {
setNoteIds(note.getChildNoteIds());
} else {
setNoteIds(await note.getSubtreeNoteIds());
}
}
// Refresh on note switch.
useEffect(() => { refreshNoteIds() }, [ note ]);
// Refresh on alterations to the note subtree.
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
if (note && loadResults.getBranchRows().some(branch =>
branch.parentNoteId === note.noteId
|| noteIds.includes(branch.parentNoteId ?? ""))) {
refreshNoteIds();
}
})
return noteIds;
}

View File

@ -1,5 +1,5 @@
import type { ViewModeArgs } from "../view_widgets/view_mode";
const allViewTypes = ["list", "grid", "calendar", "table", "geoMap", "board"] as const;
export const allViewTypes = ["list", "grid", "calendar", "table", "geoMap", "board"] as const;
export type ArgsWithoutNoteId = Omit<ViewModeArgs, "noteIds">;
export type ViewTypeOptions = typeof allViewTypes[number];

View File

@ -0,0 +1,138 @@
.note-list {
overflow: hidden;
position: relative;
height: 100%;
}
.note-book-card {
border-radius: 10px;
background-color: var(--accented-background-color);
padding: 10px 15px 15px 8px;
margin: 5px 5px 5px 5px;
overflow: hidden;
display: flex;
flex-direction: column;
flex-shrink: 0;
flex-grow: 1;
}
.note-book-card:not(.expanded) .note-book-content {
display: none !important;
padding: 10px
}
.note-book-card.expanded .note-book-content {
display: block;
min-height: 0;
height: 100%;
padding-top: 10px;
}
.note-book-content .rendered-content {
height: 100%;
}
.note-book-header {
border-bottom: 1px solid var(--main-border-color);
margin-bottom: 0;
padding-bottom: .5rem;
word-break: break-all;
}
/* not-expanded title is limited to one line only */
.note-book-card:not(.expanded) .note-book-header {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.note-book-header .rendered-note-attributes {
font-size: medium;
}
.note-book-header .rendered-note-attributes:before {
content: "\\00a0\\00a0";
}
.note-book-header .note-icon {
font-size: 100%;
display: inline-block;
padding-right: 7px;
position: relative;
}
.note-book-card .note-book-card {
border: 1px solid var(--main-border-color);
}
.note-book-content.type-image, .note-book-content.type-file, .note-book-content.type-protectedSession {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: 10px;
}
.note-book-content.type-image img, .note-book-content.type-canvas svg {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
.note-book-card.type-image .note-book-content img,
.note-book-card.type-text .note-book-content img,
.note-book-card.type-canvas .note-book-content img {
max-width: 100%;
max-height: 100%;
}
.note-book-header {
flex-grow: 0;
}
.note-list-wrapper {
height: 100%;
overflow: auto;
}
.note-expander {
font-size: x-large;
position: relative;
top: 3px;
cursor: pointer;
}
.note-list-pager {
text-align: center;
}
/* #region Grid view */
.note-list.grid-view .note-list-container {
display: flex;
flex-wrap: wrap;
}
.note-list.grid-view .note-book-card {
flex-basis: 300px;
border: 1px solid transparent;
}
.note-list.grid-view .note-expander {
display: none;
}
.note-list.grid-view .note-book-card {
max-height: 300px;
}
.note-list.grid-view .note-book-card img {
max-height: 220px;
object-fit: contain;
}
.note-list.grid-view .note-book-card:hover {
cursor: pointer;
border: 1px solid var(--main-border-color);
background: var(--more-accented-background-color);
}
/* #endregion */

View File

@ -0,0 +1,14 @@
export default function ListView() {
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-pager"></div>
</div>
</div>
);
}

View File

@ -4,32 +4,6 @@ import type FNote from "../../entities/fnote.js";
import type { CommandListener, CommandListenerData, CommandMappings, CommandNames, EventData, EventNames } from "../../components/app_context.js";
import type ViewMode from "../view_widgets/view_mode.js";
const TPL = /*html*/`
<div class="note-list-widget">
<style>
.note-list-widget {
min-height: 0;
overflow: auto;
}
.note-list-widget .note-list {
padding: 10px;
}
.note-list-widget.full-height,
.note-list-widget.full-height .note-list-widget-content {
height: 100%;
}
.note-list-widget video {
height: 100%;
}
</style>
<div class="note-list-widget-content">
</div>
</div>`;
export default class NoteListWidget extends NoteContextAwareWidget {
private $content!: JQuery<HTMLElement>;
@ -49,10 +23,6 @@ export default class NoteListWidget extends NoteContextAwareWidget {
}
isEnabled() {
if (!super.isEnabled()) {
return false;
}
if (this.displayOnlyCollections && this.note?.type !== "book") {
const viewType = this.note?.getLabelValue("viewType");
if (!viewType || ["grid", "list"].includes(viewType)) {

View File

@ -17,17 +17,6 @@ export default class NoteListRenderer {
this.viewType = this.#getViewType(args.parentNote);
}
#getViewType(parentNote: FNote): ViewTypeOptions {
const viewType = parentNote.getLabelValue("viewType");
if (!(allViewTypes as readonly string[]).includes(viewType || "")) {
// when not explicitly set, decide based on the note type
return parentNote.type === "search" ? "list" : "grid";
} else {
return viewType as ViewTypeOptions;
}
}
get isFullHeight() {
switch (this.viewType) {
case "list":

View File

@ -8,156 +8,6 @@ import type FNote from "../../entities/fnote.js";
import ViewMode, { type ViewModeArgs } from "./view_mode.js";
import { ViewTypeOptions } from "../collections/interface.js";
const TPL = /*html*/`
<div class="note-list">
<style>
.note-list {
overflow: hidden;
position: relative;
height: 100%;
}
.note-list.grid-view .note-list-container {
display: flex;
flex-wrap: wrap;
}
.note-list.grid-view .note-book-card {
flex-basis: 300px;
border: 1px solid transparent;
}
.note-list.grid-view .note-expander {
display: none;
}
.note-list.grid-view .note-book-card {
max-height: 300px;
}
.note-list.grid-view .note-book-card img {
max-height: 220px;
object-fit: contain;
}
.note-list.grid-view .note-book-card:hover {
cursor: pointer;
border: 1px solid var(--main-border-color);
background: var(--more-accented-background-color);
}
.note-book-card {
border-radius: 10px;
background-color: var(--accented-background-color);
padding: 10px 15px 15px 8px;
margin: 5px 5px 5px 5px;
overflow: hidden;
display: flex;
flex-direction: column;
flex-shrink: 0;
flex-grow: 1;
}
.note-book-card:not(.expanded) .note-book-content {
display: none !important;
padding: 10px
}
.note-book-card.expanded .note-book-content {
display: block;
min-height: 0;
height: 100%;
padding-top: 10px;
}
.note-book-content .rendered-content {
height: 100%;
}
.note-book-header {
border-bottom: 1px solid var(--main-border-color);
margin-bottom: 0;
padding-bottom: .5rem;
word-break: break-all;
}
/* not-expanded title is limited to one line only */
.note-book-card:not(.expanded) .note-book-header {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.note-book-header .rendered-note-attributes {
font-size: medium;
}
.note-book-header .rendered-note-attributes:before {
content: "\\00a0\\00a0";
}
.note-book-header .note-icon {
font-size: 100%;
display: inline-block;
padding-right: 7px;
position: relative;
}
.note-book-card .note-book-card {
border: 1px solid var(--main-border-color);
}
.note-book-content.type-image, .note-book-content.type-file, .note-book-content.type-protectedSession {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: 10px;
}
.note-book-content.type-image img, .note-book-content.type-canvas svg {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
.note-book-card.type-image .note-book-content img,
.note-book-card.type-text .note-book-content img,
.note-book-card.type-canvas .note-book-content img {
max-width: 100%;
max-height: 100%;
}
.note-book-header {
flex-grow: 0;
}
.note-list-wrapper {
height: 100%;
overflow: auto;
}
.note-expander {
font-size: x-large;
position: relative;
top: 3px;
cursor: pointer;
}
.note-list-pager {
text-align: center;
}
</style>
<div class="note-list-wrapper">
<div class="note-list-pager"></div>
<div class="note-list-container use-tn-links"></div>
<div class="note-list-pager"></div>
</div>
</div>`;
class ListOrGridView extends ViewMode<{}> {
private $noteList: JQuery<HTMLElement>;

View File

@ -48,10 +48,6 @@ export default abstract class ViewMode<T extends object> extends Component {
}
async entitiesReloadedEvent(e: EventData<"entitiesReloaded">) {
if (e.loadResults.getBranchRows().some(branch => branch.parentNoteId === this.parentNote.noteId || this.noteIds.includes(branch.parentNoteId ?? ""))) {
this.#refreshNoteIds();
}
if (await this.onEntitiesReloaded(e)) {
appContext.triggerEvent("refreshNoteList", { noteId: this.parentNote.noteId });
}
@ -70,14 +66,4 @@ export default abstract class ViewMode<T extends object> extends Component {
return this._viewStorage;
}
async #refreshNoteIds() {
let noteIds: string[];
if (this.viewType === "list" || this.viewType === "grid") {
noteIds = this.args.parentNote.getChildNoteIds();
} else {
noteIds = await this.args.parentNote.getSubtreeNoteIds();
}
this.noteIds = noteIds;
}
}