Compare commits

...

6 Commits

Author SHA1 Message Date
Elian Doran
aff4f7e010 feat(tree): disable animation for performance 2026-01-06 01:34:02 +02:00
Elian Doran
dec4dafba6 feat(tree): avoid async 2026-01-06 01:26:56 +02:00
Elian Doran
d0cdcfc32c refactor(tree): use loop for mini optimisation 2026-01-06 01:21:43 +02:00
Elian Doran
0867b81c7a feat(tree): use template for create child to improve performance 2026-01-06 01:13:31 +02:00
Elian Doran
bde6068f2d refactor(tree): extract enchance title into separate method 2026-01-06 01:07:37 +02:00
Elian Doran
47fd2affa4 feat(tree): use direct DOM manipulation instead of jQuery 2026-01-06 00:59:32 +02:00

View File

@@ -153,7 +153,7 @@ const TPL = /*html*/`
const MAX_SEARCH_RESULTS_IN_TREE = 100;
// this has to be hanged on the actual elements to effectively intercept and stop click event
const cancelClickPropagation: (e: JQuery.ClickEvent) => void = (e) => e.stopPropagation();
const cancelClickPropagation: (e: JQuery.ClickEvent | MouseEvent) => void = (e) => e.stopPropagation();
// TODO: Fix once we remove Node.js API from public
type Timeout = NodeJS.Timeout | string | number | undefined;
@@ -353,6 +353,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
this.$tree.fancytree({
titlesTabbable: true,
keyboard: true,
toggleEffect: false,
extensions: ["dnd5", "clones", "filter"],
source: treeData,
scrollOfs: {
@@ -598,102 +599,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
clones: {
highlightActiveClones: true
},
async enhanceTitle (
event: Event,
data: {
node: Fancytree.FancytreeNode;
noteId: string;
}
) {
const node = data.node;
if (!node.data.noteId) {
// if there's "non-note" node, then don't enhance
// this can happen for e.g. "Load error!" node
return;
}
const note = await froca.getNote(node.data.noteId, true);
if (!note) {
return;
}
const activeNoteContext = appContext.tabManager.getActiveContext();
const $span = $(node.span);
$span.find(".tree-item-button").remove();
$span.find(".note-indicator-icon").remove();
const isHoistedNote = activeNoteContext && activeNoteContext.hoistedNoteId === note.noteId && note.noteId !== "root";
if (note.hasLabel("workspace") && !isHoistedNote) {
const $enterWorkspaceButton = $(`<span class="tree-item-button tn-icon enter-workspace-button bx bx-door-open" title="${t("note_tree.hoist-this-note-workspace")}"></span>`).on(
"click",
cancelClickPropagation
);
$span.append($enterWorkspaceButton);
}
if (note.type === "search") {
const $refreshSearchButton = $(`<span class="tree-item-button tn-icon refresh-search-button bx bx-refresh" title="${t("note_tree.refresh-saved-search-results")}"></span>`).on(
"click",
cancelClickPropagation
);
$span.append($refreshSearchButton);
}
// TODO: Deduplicate with server's notes.ts#getAndValidateParent
if (!["search", "launcher"].includes(note.type)
&& !note.isOptions()
&& !note.isLaunchBarConfig()
&& !note.noteId.startsWith("_help")
) {
const $createChildNoteButton = $(`<span class="tree-item-button tn-icon add-note-button bx bx-plus" title="${t("note_tree.create-child-note")}"></span>`).on(
"click",
cancelClickPropagation
);
$span.append($createChildNoteButton);
}
if (isHoistedNote) {
const $unhoistButton = $(`<span class="tree-item-button tn-icon unhoist-button bx bx-door-open" title="${t("note_tree.unhoist")}"></span>`).on("click", cancelClickPropagation);
$span.append($unhoistButton);
}
// Add clone indicator with tooltip if note has multiple parents
const parentNotes = note.getParentNotes();
const realParents = parentNotes.filter(
(parent) => !["_share", "_lbBookmarks"].includes(parent.noteId) && parent.type !== "search"
);
if (realParents.length > 1) {
const parentTitles = realParents.map((p) => p.title).join(", ");
const tooltipText = realParents.length === 2
? t("note_tree.clone-indicator-tooltip-single", { parent: realParents[1].title })
: t("note_tree.clone-indicator-tooltip", { count: realParents.length, parents: parentTitles });
const $cloneIndicator = $(`<span class="note-indicator-icon clone-indicator"></span>`);
$cloneIndicator.attr("title", tooltipText);
$span.find(".fancytree-title").append($cloneIndicator);
}
// Add shared indicator with tooltip if note is shared
if (note.isShared()) {
const shareId = note.getOwnedLabelValue("shareAlias") || note.noteId;
const shareUrl = `${location.origin}${location.pathname}share/${shareId}`;
const tooltipText = t("note_tree.shared-indicator-tooltip-with-url", { url: shareUrl });
const $sharedIndicator = $(`<span class="note-indicator-icon shared-indicator"></span>`);
$sharedIndicator.attr("title", tooltipText);
$span.find(".fancytree-title").append($sharedIndicator);
}
},
enhanceTitle: buildEnhanceTitle(),
// this is done to automatically lazy load all expanded notes after tree load
loadChildren: (event, data) => {
data.node.visit((subNode) => {
@@ -1882,3 +1788,100 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
return items;
}
}
function buildEnhanceTitle() {
const createChildTemplate = document.createElement("span");
createChildTemplate.className = "tree-item-button tn-icon add-note-button bx bx-plus";
createChildTemplate.title = t("note_tree.create-child-note");
createChildTemplate.addEventListener("click", cancelClickPropagation);
return async function enhanceTitle(event: Event,
data: {
node: Fancytree.FancytreeNode;
noteId: string;
}) {
const node = data.node;
if (!node.data.noteId) {
// if there's "non-note" node, then don't enhance
// this can happen for e.g. "Load error!" node
return;
}
const note = froca.getNoteFromCache(node.data.noteId);
if (!note) return;
const activeNoteContext = appContext.tabManager.getActiveContext();
const $span = $(node.span);
$span.find(".tree-item-button").remove();
$span.find(".note-indicator-icon").remove();
const isHoistedNote = activeNoteContext && activeNoteContext.hoistedNoteId === note.noteId && note.noteId !== "root";
if (note.hasLabel("workspace") && !isHoistedNote) {
const $enterWorkspaceButton = $(`<span class="tree-item-button tn-icon enter-workspace-button bx bx-door-open" title="${t("note_tree.hoist-this-note-workspace")}"></span>`).on(
"click",
cancelClickPropagation
);
$span.append($enterWorkspaceButton);
}
if (note.type === "search") {
const $refreshSearchButton = $(`<span class="tree-item-button tn-icon refresh-search-button bx bx-refresh" title="${t("note_tree.refresh-saved-search-results")}"></span>`).on(
"click",
cancelClickPropagation
);
$span.append($refreshSearchButton);
}
// TODO: Deduplicate with server's notes.ts#getAndValidateParent
if (!["search", "launcher"].includes(note.type)
&& !note.isOptions()
&& !note.isLaunchBarConfig()
&& !note.noteId.startsWith("_help")
) {
node.span.append(createChildTemplate.cloneNode());
}
if (isHoistedNote) {
const $unhoistButton = $(`<span class="tree-item-button tn-icon unhoist-button bx bx-door-open" title="${t("note_tree.unhoist")}"></span>`).on("click", cancelClickPropagation);
$span.append($unhoistButton);
}
// Add clone indicator with tooltip if note has multiple parents
const parentNotes = note.getParentNotes();
const realParents: FNote[] = [];
for (const parent of parentNotes) {
if (parent.noteId !== "_share" && parent.noteId !== "_lbBookmarks" && parent.type !== "search") {
realParents.push(parent);
}
}
if (realParents.length > 1) {
const parentTitles = realParents.map((p) => p.title).join(", ");
const tooltipText = realParents.length === 2
? t("note_tree.clone-indicator-tooltip-single", { parent: realParents[1].title })
: t("note_tree.clone-indicator-tooltip", { count: realParents.length, parents: parentTitles });
const $cloneIndicator = $(`<span class="note-indicator-icon clone-indicator"></span>`);
$cloneIndicator.attr("title", tooltipText);
$span.find(".fancytree-title").append($cloneIndicator);
}
// Add shared indicator with tooltip if note is shared
if (note.isShared()) {
const shareId = note.getOwnedLabelValue("shareAlias") || note.noteId;
const shareUrl = `${location.origin}${location.pathname}share/${shareId}`;
const tooltipText = t("note_tree.shared-indicator-tooltip-with-url", { url: shareUrl });
const $sharedIndicator = $(`<span class="note-indicator-icon shared-indicator"></span>`);
$sharedIndicator.attr("title", tooltipText);
$span.find(".fancytree-title").append($sharedIndicator);
}
};
}