feat(breadcrumb): get tree menu to show

This commit is contained in:
Elian Doran 2025-12-16 09:35:19 +02:00
parent 587ea42700
commit 96a6ea4c7a
No known key found for this signature in database
2 changed files with 276 additions and 245 deletions

View File

@ -1,21 +1,23 @@
import NoteColorPicker from "./custom-items/NoteColorPicker.jsx";
import treeService from "../services/tree.js";
import froca from "../services/froca.js";
import clipboard from "../services/clipboard.js";
import noteCreateService from "../services/note_create.js";
import contextMenu, { type MenuCommandItem, type MenuItem } from "./context_menu.js";
import appContext, { type ContextMenuCommandData, type FilteredCommandNames } from "../components/app_context.js"; import appContext, { type ContextMenuCommandData, type FilteredCommandNames } from "../components/app_context.js";
import type { SelectMenuItemEventListener } from "../components/events.js";
import type FAttachment from "../entities/fattachment.js";
import FBranch from "../entities/fbranch.js";
import FNote from "../entities/fnote.js";
import attributes from "../services/attributes.js";
import { executeBulkActions } from "../services/bulk_action.js";
import clipboard from "../services/clipboard.js";
import dialogService from "../services/dialog.js";
import froca from "../services/froca.js";
import { t } from "../services/i18n.js";
import noteCreateService from "../services/note_create.js";
import noteTypesService from "../services/note_types.js"; import noteTypesService from "../services/note_types.js";
import server from "../services/server.js"; import server from "../services/server.js";
import toastService from "../services/toast.js"; import toastService from "../services/toast.js";
import dialogService from "../services/dialog.js"; import treeService from "../services/tree.js";
import { t } from "../services/i18n.js";
import type NoteTreeWidget from "../widgets/note_tree.js";
import type FAttachment from "../entities/fattachment.js";
import type { SelectMenuItemEventListener } from "../components/events.js";
import utils from "../services/utils.js"; import utils from "../services/utils.js";
import attributes from "../services/attributes.js"; import type NoteTreeWidget from "../widgets/note_tree.js";
import { executeBulkActions } from "../services/bulk_action.js"; import contextMenu, { type MenuCommandItem, type MenuItem } from "./context_menu.js";
import NoteColorPicker from "./custom-items/NoteColorPicker.jsx";
// TODO: Deduplicate once client/server is well split. // TODO: Deduplicate once client/server is well split.
interface ConvertToAttachmentResponse { interface ConvertToAttachmentResponse {
@ -53,227 +55,16 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
lastTargetNode.classList.add('fancytree-menu-target'); lastTargetNode.classList.add('fancytree-menu-target');
} }
async getMenuItems(): Promise<MenuItem<TreeCommandNames>[]> { async getMenuItems() {
const note = this.node.data.noteId ? await froca.getNote(this.node.data.noteId) : null; const note = this.node.data.noteId ? await froca.getNote(this.node.data.noteId) : null;
const branch = froca.getBranch(this.node.data.branchId);
const isNotRoot = note?.noteId !== "root";
const isHoisted = note?.noteId === appContext.tabManager.getActiveContext()?.hoistedNoteId;
const parentNote = isNotRoot && branch ? await froca.getNote(branch.parentNoteId) : null;
// some actions don't support multi-note, so they are disabled when notes are selected,
// the only exception is when the only selected note is the one that was right-clicked, then
// it's clear what the user meant to do.
const selNodes = this.treeWidget.getSelectedNodes(); const selNodes = this.treeWidget.getSelectedNodes();
const selectedNotes = await froca.getNotes(selNodes.map(node => node.data.noteId));
if (note && !selectedNotes.includes(note)) selectedNotes.push(note);
const isArchived = selectedNotes.every(note => note.isArchived);
const canToggleArchived = !selectedNotes.some(note => note.isArchived !== isArchived);
const noSelectedNotes = selNodes.length === 0 || (selNodes.length === 1 && selNodes[0] === this.node); return buildTreeMenuItems({
note,
const notSearch = note?.type !== "search"; branch: froca.getBranch(this.node.data.branchId),
const notOptionsOrHelp = !note?.noteId.startsWith("_options") && !note?.noteId.startsWith("_help"); selectedNotes: await froca.getNotes(selNodes.map(node => node.data.noteId)),
const parentNotSearch = !parentNote || parentNote.type !== "search"; noSelectedNotes: selNodes.length === 0 || (selNodes.length === 1 && selNodes[0] === this.node)
const insertNoteAfterEnabled = isNotRoot && !isHoisted && parentNotSearch; });
const items: (MenuItem<TreeCommandNames> | null)[] = [
{ title: t("tree-context-menu.open-in-a-new-tab"), command: "openInTab", shortcut: "Ctrl+Click", uiIcon: "bx bx-link-external", enabled: noSelectedNotes },
{ title: t("tree-context-menu.open-in-a-new-split"), command: "openNoteInSplit", uiIcon: "bx bx-dock-right", enabled: noSelectedNotes },
{ title: t("tree-context-menu.open-in-popup"), command: "openNoteInPopup", uiIcon: "bx bx-edit", enabled: noSelectedNotes },
isHoisted
? null
: {
title: `${t("tree-context-menu.hoist-note")}`,
command: "toggleNoteHoisting",
keyboardShortcut: "toggleNoteHoisting",
uiIcon: "bx bxs-chevrons-up",
enabled: noSelectedNotes && notSearch
},
!isHoisted || !isNotRoot
? null
: { title: t("tree-context-menu.unhoist-note"), command: "toggleNoteHoisting", keyboardShortcut: "toggleNoteHoisting", uiIcon: "bx bx-door-open" },
{ kind: "separator" },
{
title: t("tree-context-menu.insert-note-after"),
command: "insertNoteAfter",
keyboardShortcut: "createNoteAfter",
uiIcon: "bx bx-plus",
items: insertNoteAfterEnabled ? await noteTypesService.getNoteTypeItems("insertNoteAfter") : null,
enabled: insertNoteAfterEnabled && noSelectedNotes && notOptionsOrHelp,
columns: 2
},
{
title: t("tree-context-menu.insert-child-note"),
command: "insertChildNote",
keyboardShortcut: "createNoteInto",
uiIcon: "bx bx-plus",
items: notSearch ? await noteTypesService.getNoteTypeItems("insertChildNote") : null,
enabled: notSearch && noSelectedNotes && notOptionsOrHelp,
columns: 2
},
{ kind: "separator" },
{ title: t("tree-context-menu.protect-subtree"), command: "protectSubtree", uiIcon: "bx bx-check-shield", enabled: noSelectedNotes },
{ title: t("tree-context-menu.unprotect-subtree"), command: "unprotectSubtree", uiIcon: "bx bx-shield", enabled: noSelectedNotes },
{ kind: "separator" },
{
title: t("tree-context-menu.advanced"),
uiIcon: "bx bxs-wrench",
enabled: true,
items: [
{ title: t("tree-context-menu.apply-bulk-actions"), command: "openBulkActionsDialog", uiIcon: "bx bx-list-plus", enabled: true },
{ kind: "separator" },
{
title: t("tree-context-menu.edit-branch-prefix"),
command: "editBranchPrefix",
keyboardShortcut: "editBranchPrefix",
uiIcon: "bx bx-rename",
enabled: isNotRoot && parentNotSearch && notOptionsOrHelp
},
{
title:
t("tree-context-menu.convert-to-attachment"),
command: "convertNoteToAttachment",
uiIcon: "bx bx-paperclip",
enabled: isNotRoot && !isHoisted && notOptionsOrHelp && selectedNotes.some(note => note.isEligibleForConversionToAttachment())
},
{ kind: "separator" },
{ title: t("tree-context-menu.expand-subtree"), command: "expandSubtree", keyboardShortcut: "expandSubtree", uiIcon: "bx bx-expand", enabled: noSelectedNotes },
{ title: t("tree-context-menu.collapse-subtree"), command: "collapseSubtree", keyboardShortcut: "collapseSubtree", uiIcon: "bx bx-collapse", enabled: noSelectedNotes },
{
title: t("tree-context-menu.sort-by"),
command: "sortChildNotes",
keyboardShortcut: "sortChildNotes",
uiIcon: "bx bx-sort-down",
enabled: noSelectedNotes && notSearch
},
{ kind: "separator" },
{ title: t("tree-context-menu.copy-note-path-to-clipboard"), command: "copyNotePathToClipboard", uiIcon: "bx bx-directions", enabled: true },
{ title: t("tree-context-menu.recent-changes-in-subtree"), command: "recentChangesInSubtree", uiIcon: "bx bx-history", enabled: noSelectedNotes && notOptionsOrHelp }
]
},
{ kind: "separator" },
{
title: t("tree-context-menu.cut"),
command: "cutNotesToClipboard",
keyboardShortcut: "cutNotesToClipboard",
uiIcon: "bx bx-cut",
enabled: isNotRoot && !isHoisted && parentNotSearch
},
{ title: t("tree-context-menu.copy-clone"), command: "copyNotesToClipboard", keyboardShortcut: "copyNotesToClipboard", uiIcon: "bx bx-copy", enabled: isNotRoot && !isHoisted },
{
title: t("tree-context-menu.paste-into"),
command: "pasteNotesFromClipboard",
keyboardShortcut: "pasteNotesFromClipboard",
uiIcon: "bx bx-paste",
enabled: !clipboard.isClipboardEmpty() && notSearch && noSelectedNotes
},
{
title: t("tree-context-menu.paste-after"),
command: "pasteNotesAfterFromClipboard",
uiIcon: "bx bx-paste",
enabled: !clipboard.isClipboardEmpty() && isNotRoot && !isHoisted && parentNotSearch && noSelectedNotes
},
{
title: t("tree-context-menu.move-to"),
command: "moveNotesTo",
keyboardShortcut: "moveNotesTo",
uiIcon: "bx bx-transfer",
enabled: isNotRoot && !isHoisted && parentNotSearch
},
{ title: t("tree-context-menu.clone-to"), command: "cloneNotesTo", keyboardShortcut: "cloneNotesTo", uiIcon: "bx bx-duplicate", enabled: isNotRoot && !isHoisted },
{
title: t("tree-context-menu.duplicate"),
command: "duplicateSubtree",
keyboardShortcut: "duplicateSubtree",
uiIcon: "bx bx-outline",
enabled: parentNotSearch && isNotRoot && !isHoisted && notOptionsOrHelp
},
{
title: !isArchived ? t("tree-context-menu.archive") : t("tree-context-menu.unarchive"),
uiIcon: !isArchived ? "bx bx-archive" : "bx bx-archive-out",
enabled: canToggleArchived,
handler: () => {
if (!selectedNotes.length) return;
if (selectedNotes.length == 1) {
const note = selectedNotes[0];
if (!isArchived) {
attributes.addLabel(note.noteId, "archived");
} else {
attributes.removeOwnedLabelByName(note, "archived");
}
} else {
const noteIds = selectedNotes.map(note => note.noteId);
if (!isArchived) {
executeBulkActions(noteIds, [{
name: "addLabel", labelName: "archived"
}]);
} else {
executeBulkActions(noteIds, [{
name: "deleteLabel", labelName: "archived"
}]);
}
}
}
},
{
title: t("tree-context-menu.delete"),
command: "deleteNotes",
keyboardShortcut: "deleteNotes",
uiIcon: "bx bx-trash destructive-action-icon",
enabled: isNotRoot && !isHoisted && parentNotSearch && notOptionsOrHelp
},
{ kind: "separator"},
(notOptionsOrHelp && selectedNotes.length === 1) ? {
kind: "custom",
componentFn: () => {
return NoteColorPicker({note});
}
} : null,
{ kind: "separator" },
{ title: t("tree-context-menu.import-into-note"), command: "importIntoNote", uiIcon: "bx bx-import", enabled: notSearch && noSelectedNotes && notOptionsOrHelp },
{ title: t("tree-context-menu.export"), command: "exportNote", uiIcon: "bx bx-export", enabled: notSearch && noSelectedNotes && notOptionsOrHelp },
{ kind: "separator" },
{
title: t("tree-context-menu.search-in-subtree"),
command: "searchInSubtree",
keyboardShortcut: "searchInSubtree",
uiIcon: "bx bx-search",
enabled: notSearch && noSelectedNotes
}
];
return items.filter((row) => row !== null) as MenuItem<TreeCommandNames>[];
} }
async selectMenuItemHandler({ command, type, templateNoteId }: MenuCommandItem<TreeCommandNames>) { async selectMenuItemHandler({ command, type, templateNoteId }: MenuCommandItem<TreeCommandNames>) {
@ -292,17 +83,17 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
noteCreateService.createNote(parentNotePath, { noteCreateService.createNote(parentNotePath, {
target: "after", target: "after",
targetBranchId: this.node.data.branchId, targetBranchId: this.node.data.branchId,
type: type, type,
isProtected: isProtected, isProtected,
templateNoteId: templateNoteId templateNoteId
}); });
} else if (command === "insertChildNote") { } else if (command === "insertChildNote") {
const parentNotePath = treeService.getNotePath(this.node); const parentNotePath = treeService.getNotePath(this.node);
noteCreateService.createNote(parentNotePath, { noteCreateService.createNote(parentNotePath, {
type: type, type,
isProtected: this.node.data.isProtected, isProtected: this.node.data.isProtected,
templateNoteId: templateNoteId templateNoteId
}); });
} else if (command === "openNoteInSplit") { } else if (command === "openNoteInSplit") {
const subContexts = appContext.tabManager.getActiveContext()?.getSubContexts(); const subContexts = appContext.tabManager.getActiveContext()?.getSubContexts();
@ -310,7 +101,7 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
this.treeWidget.triggerCommand("openNewNoteSplit", { ntxId, notePath }); this.treeWidget.triggerCommand("openNewNoteSplit", { ntxId, notePath });
} else if (command === "openNoteInPopup") { } else if (command === "openNoteInPopup") {
appContext.triggerCommand("openInPopup", { noteIdOrPath: notePath }) appContext.triggerCommand("openInPopup", { noteIdOrPath: notePath });
} else if (command === "convertNoteToAttachment") { } else if (command === "convertNoteToAttachment") {
if (!(await dialogService.confirm(t("tree-context-menu.convert-to-attachment-confirm")))) { if (!(await dialogService.confirm(t("tree-context-menu.convert-to-attachment-confirm")))) {
return; return;
@ -332,11 +123,11 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
toastService.showMessage(t("tree-context-menu.converted-to-attachments", { count: converted })); toastService.showMessage(t("tree-context-menu.converted-to-attachments", { count: converted }));
} else if (command === "copyNotePathToClipboard") { } else if (command === "copyNotePathToClipboard") {
navigator.clipboard.writeText("#" + notePath); navigator.clipboard.writeText(`#${ notePath}`);
} else if (command) { } else if (command) {
this.treeWidget.triggerCommand<TreeCommandNames>(command, { this.treeWidget.triggerCommand<TreeCommandNames>(command, {
node: this.node, node: this.node,
notePath: notePath, notePath,
noteId: this.node.data.noteId, noteId: this.node.data.noteId,
selectedOrActiveBranchIds: this.treeWidget.getSelectedOrActiveBranchIds(this.node), selectedOrActiveBranchIds: this.treeWidget.getSelectedOrActiveBranchIds(this.node),
selectedOrActiveNoteIds: this.treeWidget.getSelectedOrActiveNoteIds(this.node) selectedOrActiveNoteIds: this.treeWidget.getSelectedOrActiveNoteIds(this.node)
@ -344,3 +135,225 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
} }
} }
} }
export async function buildTreeMenuItems({ note, branch, selectedNotes, noSelectedNotes }: {
note: FNote | null;
branch: FBranch | undefined;
selectedNotes: FNote[];
noSelectedNotes: boolean;
}): Promise<MenuItem<TreeCommandNames>[]> {
const isNotRoot = note?.noteId !== "root";
const isHoisted = note?.noteId === appContext.tabManager.getActiveContext()?.hoistedNoteId;
const parentNote = isNotRoot && branch ? await froca.getNote(branch.parentNoteId) : null;
// some actions don't support multi-note, so they are disabled when notes are selected,
// the only exception is when the only selected note is the one that was right-clicked, then
// it's clear what the user meant to do.
if (note && !selectedNotes.includes(note)) selectedNotes.push(note);
const isArchived = selectedNotes.every(note => note.isArchived);
const canToggleArchived = !selectedNotes.some(note => note.isArchived !== isArchived);
const notSearch = note?.type !== "search";
const notOptionsOrHelp = !note?.noteId.startsWith("_options") && !note?.noteId.startsWith("_help");
const parentNotSearch = !parentNote || parentNote.type !== "search";
const insertNoteAfterEnabled = isNotRoot && !isHoisted && parentNotSearch;
const items: (MenuItem<TreeCommandNames> | null)[] = [
{ title: t("tree-context-menu.open-in-a-new-tab"), command: "openInTab", shortcut: "Ctrl+Click", uiIcon: "bx bx-link-external", enabled: noSelectedNotes },
{ title: t("tree-context-menu.open-in-a-new-split"), command: "openNoteInSplit", uiIcon: "bx bx-dock-right", enabled: noSelectedNotes },
{ title: t("tree-context-menu.open-in-popup"), command: "openNoteInPopup", uiIcon: "bx bx-edit", enabled: noSelectedNotes },
isHoisted
? null
: {
title: `${t("tree-context-menu.hoist-note")}`,
command: "toggleNoteHoisting",
keyboardShortcut: "toggleNoteHoisting",
uiIcon: "bx bxs-chevrons-up",
enabled: noSelectedNotes && notSearch
},
!isHoisted || !isNotRoot
? null
: { title: t("tree-context-menu.unhoist-note"), command: "toggleNoteHoisting", keyboardShortcut: "toggleNoteHoisting", uiIcon: "bx bx-door-open" },
{ kind: "separator" },
{
title: t("tree-context-menu.insert-note-after"),
command: "insertNoteAfter",
keyboardShortcut: "createNoteAfter",
uiIcon: "bx bx-plus",
items: insertNoteAfterEnabled ? await noteTypesService.getNoteTypeItems("insertNoteAfter") : null,
enabled: insertNoteAfterEnabled && noSelectedNotes && notOptionsOrHelp,
columns: 2
},
{
title: t("tree-context-menu.insert-child-note"),
command: "insertChildNote",
keyboardShortcut: "createNoteInto",
uiIcon: "bx bx-plus",
items: notSearch ? await noteTypesService.getNoteTypeItems("insertChildNote") : null,
enabled: notSearch && noSelectedNotes && notOptionsOrHelp,
columns: 2
},
{ kind: "separator" },
{ title: t("tree-context-menu.protect-subtree"), command: "protectSubtree", uiIcon: "bx bx-check-shield", enabled: noSelectedNotes },
{ title: t("tree-context-menu.unprotect-subtree"), command: "unprotectSubtree", uiIcon: "bx bx-shield", enabled: noSelectedNotes },
{ kind: "separator" },
{
title: t("tree-context-menu.advanced"),
uiIcon: "bx bxs-wrench",
enabled: true,
items: [
{ title: t("tree-context-menu.apply-bulk-actions"), command: "openBulkActionsDialog", uiIcon: "bx bx-list-plus", enabled: true },
{ kind: "separator" },
{
title: t("tree-context-menu.edit-branch-prefix"),
command: "editBranchPrefix",
keyboardShortcut: "editBranchPrefix",
uiIcon: "bx bx-rename",
enabled: isNotRoot && parentNotSearch && notOptionsOrHelp
},
{
title:
t("tree-context-menu.convert-to-attachment"),
command: "convertNoteToAttachment",
uiIcon: "bx bx-paperclip",
enabled: isNotRoot && !isHoisted && notOptionsOrHelp && selectedNotes.some(note => note.isEligibleForConversionToAttachment())
},
{ kind: "separator" },
{ title: t("tree-context-menu.expand-subtree"), command: "expandSubtree", keyboardShortcut: "expandSubtree", uiIcon: "bx bx-expand", enabled: noSelectedNotes },
{ title: t("tree-context-menu.collapse-subtree"), command: "collapseSubtree", keyboardShortcut: "collapseSubtree", uiIcon: "bx bx-collapse", enabled: noSelectedNotes },
{
title: t("tree-context-menu.sort-by"),
command: "sortChildNotes",
keyboardShortcut: "sortChildNotes",
uiIcon: "bx bx-sort-down",
enabled: noSelectedNotes && notSearch
},
{ kind: "separator" },
{ title: t("tree-context-menu.copy-note-path-to-clipboard"), command: "copyNotePathToClipboard", uiIcon: "bx bx-directions", enabled: true },
{ title: t("tree-context-menu.recent-changes-in-subtree"), command: "recentChangesInSubtree", uiIcon: "bx bx-history", enabled: noSelectedNotes && notOptionsOrHelp }
]
},
{ kind: "separator" },
{
title: t("tree-context-menu.cut"),
command: "cutNotesToClipboard",
keyboardShortcut: "cutNotesToClipboard",
uiIcon: "bx bx-cut",
enabled: isNotRoot && !isHoisted && parentNotSearch
},
{ title: t("tree-context-menu.copy-clone"), command: "copyNotesToClipboard", keyboardShortcut: "copyNotesToClipboard", uiIcon: "bx bx-copy", enabled: isNotRoot && !isHoisted },
{
title: t("tree-context-menu.paste-into"),
command: "pasteNotesFromClipboard",
keyboardShortcut: "pasteNotesFromClipboard",
uiIcon: "bx bx-paste",
enabled: !clipboard.isClipboardEmpty() && notSearch && noSelectedNotes
},
{
title: t("tree-context-menu.paste-after"),
command: "pasteNotesAfterFromClipboard",
uiIcon: "bx bx-paste",
enabled: !clipboard.isClipboardEmpty() && isNotRoot && !isHoisted && parentNotSearch && noSelectedNotes
},
{
title: t("tree-context-menu.move-to"),
command: "moveNotesTo",
keyboardShortcut: "moveNotesTo",
uiIcon: "bx bx-transfer",
enabled: isNotRoot && !isHoisted && parentNotSearch
},
{ title: t("tree-context-menu.clone-to"), command: "cloneNotesTo", keyboardShortcut: "cloneNotesTo", uiIcon: "bx bx-duplicate", enabled: isNotRoot && !isHoisted },
{
title: t("tree-context-menu.duplicate"),
command: "duplicateSubtree",
keyboardShortcut: "duplicateSubtree",
uiIcon: "bx bx-outline",
enabled: parentNotSearch && isNotRoot && !isHoisted && notOptionsOrHelp
},
{
title: !isArchived ? t("tree-context-menu.archive") : t("tree-context-menu.unarchive"),
uiIcon: !isArchived ? "bx bx-archive" : "bx bx-archive-out",
enabled: canToggleArchived,
handler: () => {
if (!selectedNotes.length) return;
if (selectedNotes.length == 1) {
const note = selectedNotes[0];
if (!isArchived) {
attributes.addLabel(note.noteId, "archived");
} else {
attributes.removeOwnedLabelByName(note, "archived");
}
} else {
const noteIds = selectedNotes.map(note => note.noteId);
if (!isArchived) {
executeBulkActions(noteIds, [{
name: "addLabel", labelName: "archived"
}]);
} else {
executeBulkActions(noteIds, [{
name: "deleteLabel", labelName: "archived"
}]);
}
}
}
},
{
title: t("tree-context-menu.delete"),
command: "deleteNotes",
keyboardShortcut: "deleteNotes",
uiIcon: "bx bx-trash destructive-action-icon",
enabled: isNotRoot && !isHoisted && parentNotSearch && notOptionsOrHelp
},
{ kind: "separator"},
(notOptionsOrHelp && selectedNotes.length === 1) ? {
kind: "custom",
componentFn: () => {
return NoteColorPicker({note});
}
} : null,
{ kind: "separator" },
{ title: t("tree-context-menu.import-into-note"), command: "importIntoNote", uiIcon: "bx bx-import", enabled: notSearch && noSelectedNotes && notOptionsOrHelp },
{ title: t("tree-context-menu.export"), command: "exportNote", uiIcon: "bx bx-export", enabled: notSearch && noSelectedNotes && notOptionsOrHelp },
{ kind: "separator" },
{
title: t("tree-context-menu.search-in-subtree"),
command: "searchInSubtree",
keyboardShortcut: "searchInSubtree",
uiIcon: "bx bx-search",
enabled: notSearch && noSelectedNotes
}
];
return items.filter((row) => row !== null) as MenuItem<TreeCommandNames>[];
}

View File

@ -8,6 +8,7 @@ import NoteContext from "../../components/note_context";
import FNote from "../../entities/fnote"; import FNote from "../../entities/fnote";
import contextMenu from "../../menus/context_menu"; import contextMenu from "../../menus/context_menu";
import link_context_menu from "../../menus/link_context_menu"; import link_context_menu from "../../menus/link_context_menu";
import { buildTreeMenuItems } from "../../menus/tree_context_menu";
import { getReadableTextColor } from "../../services/css_class_manager"; import { getReadableTextColor } from "../../services/css_class_manager";
import froca from "../../services/froca"; import froca from "../../services/froca";
import hoisted_note from "../../services/hoisted_note"; import hoisted_note from "../../services/hoisted_note";
@ -154,14 +155,31 @@ function BreadcrumbItem({ index, notePath, noteContext, notePathLength }: { inde
return <NoteLink return <NoteLink
notePath={notePath} notePath={notePath}
noContextMenu noContextMenu
onContextMenu={(e) => { onContextMenu={async (e) => {
e.preventDefault(); e.preventDefault();
const notePathArray = notePath.split("/");
const parentNoteId = notePathArray.at(-2);
const childNoteId = notePathArray.at(-1);
console.log(parentNoteId, childNoteId);
if (!parentNoteId || !childNoteId) return;
const branchId = await froca.getBranchId(parentNoteId, childNoteId);
if (!branchId) return;
const branch = froca.getBranch(branchId);
const note = await branch?.getNote();
if (!branch || !note) return;
const items = await buildTreeMenuItems({
branch,
note,
noSelectedNotes: true,
selectedNotes: []
});
contextMenu.show({ contextMenu.show({
items: [ items,
{
title: "Foo"
}
],
x: e.pageX, x: e.pageX,
y: e.pageY y: e.pageY
}); });