|
|
|
|
@@ -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);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|