From 51fcda646dfb341dc162e4f0b3de039db76d18bb Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 16 Dec 2025 10:09:10 +0200 Subject: [PATCH] feat(breadcrumb): add hoist option --- apps/client/src/menus/link_context_menu.ts | 23 ++++++++++------ apps/client/src/widgets/layout/Breadcrumb.tsx | 26 +++++++++++++++++-- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/apps/client/src/menus/link_context_menu.ts b/apps/client/src/menus/link_context_menu.ts index 0799a58ab..2cba3aab4 100644 --- a/apps/client/src/menus/link_context_menu.ts +++ b/apps/client/src/menus/link_context_menu.ts @@ -1,10 +1,11 @@ -import { t } from "../services/i18n.js"; -import contextMenu, { type ContextMenuEvent, type MenuItem } from "./context_menu.js"; +import type { LeafletMouseEvent } from "leaflet"; + import appContext, { type CommandNames } from "../components/app_context.js"; +import { t } from "../services/i18n.js"; import type { ViewScope } from "../services/link.js"; import utils, { isMobile } from "../services/utils.js"; import { getClosestNtxId } from "../widgets/widget_utils.js"; -import type { LeafletMouseEvent } from "leaflet"; +import contextMenu, { type ContextMenuEvent, type MenuItem } from "./context_menu.js"; function openContextMenu(notePath: string, e: ContextMenuEvent, viewScope: ViewScope = {}, hoistedNoteId: string | null = null) { contextMenu.show({ @@ -34,15 +35,21 @@ function handleLinkContextMenuItem(command: string | undefined, e: ContextMenuEv if (command === "openNoteInNewTab") { appContext.tabManager.openContextWithNote(notePath, { hoistedNoteId, viewScope }); + return true; } else if (command === "openNoteInNewSplit") { const ntxId = getNtxId(e); - if (!ntxId) return; + if (!ntxId) return false; appContext.triggerCommand("openNewNoteSplit", { ntxId, notePath, hoistedNoteId, viewScope }); + return true; } else if (command === "openNoteInNewWindow") { appContext.triggerCommand("openInWindow", { notePath, hoistedNoteId, viewScope }); + return true; } else if (command === "openNoteInPopup") { - appContext.triggerCommand("openInPopup", { noteIdOrPath: notePath }) + appContext.triggerCommand("openInPopup", { noteIdOrPath: notePath }); + return true; } + + return false; } function getNtxId(e: ContextMenuEvent | LeafletMouseEvent) { @@ -52,9 +59,9 @@ function getNtxId(e: ContextMenuEvent | LeafletMouseEvent) { return subContexts[subContexts.length - 1].ntxId; } else if (e.target instanceof HTMLElement) { return getClosestNtxId(e.target); - } else { - return null; - } + } + return null; + } export default { diff --git a/apps/client/src/widgets/layout/Breadcrumb.tsx b/apps/client/src/widgets/layout/Breadcrumb.tsx index 498159514..ed4e42c7c 100644 --- a/apps/client/src/widgets/layout/Breadcrumb.tsx +++ b/apps/client/src/widgets/layout/Breadcrumb.tsx @@ -1,6 +1,6 @@ import "./Breadcrumb.css"; -import { useRef, useState } from "preact/hooks"; +import { useContext, useRef, useState } from "preact/hooks"; import { Fragment } from "preact/jsx-runtime"; import appContext from "../../components/app_context"; @@ -19,6 +19,7 @@ import { FormListItem } from "../react/FormList"; import { useChildNotes, useNote, useNoteIcon, useNoteLabel, useNoteLabelBoolean, useNoteProperty, useStaticTooltip } from "../react/hooks"; import Icon from "../react/Icon"; import NoteLink from "../react/NoteLink"; +import { ParentComponent } from "../react/react_utils"; const COLLAPSE_THRESHOLD = 5; const INITIAL_ITEMS = 2; @@ -141,6 +142,8 @@ function BreadcrumbLastItem({ notePath }: { notePath: string }) { } function BreadcrumbItem({ index, notePath, noteContext, notePathLength }: { index: number, notePathLength: number, notePath: string, noteContext: NoteContext | undefined }) { + const parentComponent = useContext(ParentComponent); + if (index === 0) { return ; } @@ -162,13 +165,32 @@ function BreadcrumbItem({ index, notePath, noteContext, notePathLength }: { inde const note = await froca.getNote(noteId); if (!note) return; + const notSearch = note?.type !== "search"; + contextMenu.show({ items: [ ...link_context_menu.getItems(e), + { + title: `${t("tree-context-menu.hoist-note")}`, + command: "toggleNoteHoisting", + keyboardShortcut: "toggleNoteHoisting", + uiIcon: "bx bxs-chevrons-up", + enabled: notSearch + }, ], x: e.pageX, y: e.pageY, - selectMenuItemHandler: ({ command }) => link_context_menu.handleLinkContextMenuItem(command, e, note.noteId), + selectMenuItemHandler: ({ command }) => { + if (link_context_menu.handleLinkContextMenuItem(command, e, note.noteId)) { + return; + } + + if (command) { + parentComponent?.triggerCommand(command, { + noteId + }); + } + }, }); }} />;