diff --git a/apps/client/src/widgets/type_widgets/relation_map/NoteBox.tsx b/apps/client/src/widgets/type_widgets/relation_map/NoteBox.tsx new file mode 100644 index 000000000..ad069afc0 --- /dev/null +++ b/apps/client/src/widgets/type_widgets/relation_map/NoteBox.tsx @@ -0,0 +1,109 @@ +import { useCallback, useEffect, useState } from "preact/hooks"; +import { useNoteProperty } from "../../react/hooks"; +import froca from "../../../services/froca"; +import contextMenu from "../../../menus/context_menu"; +import { t } from "../../../services/i18n"; +import appContext from "../../../components/app_context"; +import dialog from "../../../services/dialog"; +import server from "../../../services/server"; +import { JsPlumbItem } from "./jsplumb"; +import FNote from "../../../entities/fnote"; +import RelationMapApi, { MapDataNoteEntry } from "./api"; +import { RefObject } from "preact"; +import NoteLink from "../../react/NoteLink"; +import { idToNoteId, noteIdToId } from "./utils"; + +const NOTE_BOX_SOURCE_CONFIG = { + filter: ".endpoint", + anchor: "Continuous", + connectorStyle: { stroke: "#000", strokeWidth: 1 }, + connectionType: "basic", + extract: { + action: "the-action" + } +}; + +const NOTE_BOX_TARGET_CONFIG = { + dropOptions: { hoverClass: "dragHover" }, + anchor: "Continuous", + allowLoopback: true +}; + +interface NoteBoxProps extends MapDataNoteEntry { + mapApiRef: RefObject; +} + +export function NoteBox({ noteId, x, y, mapApiRef }: NoteBoxProps) { + const [ note, setNote ] = useState(); + const title = useNoteProperty(note, "title"); + useEffect(() => { + froca.getNote(noteId).then(setNote); + }, [ noteId ]); + + const contextMenuHandler = useCallback((e: MouseEvent) => { + e.preventDefault(); + contextMenu.show({ + x: e.pageX, + y: e.pageY, + items: [ + { + title: t("relation_map.open_in_new_tab"), + uiIcon: "bx bx-empty", + handler: () => appContext.tabManager.openTabWithNoteWithHoisting(noteId) + }, + { + title: t("relation_map.remove_note"), + uiIcon: "bx bx-trash", + handler: async () => { + if (!note) return; + const result = await dialog.confirmDeleteNoteBoxWithNote(note.title); + if (typeof result !== "object" || !result.confirmed) return; + + mapApiRef.current?.removeItem(noteId, result.isDeleteNoteChecked); + } + }, + { + title: t("relation_map.edit_title"), + uiIcon: "bx bx-pencil", + handler: async () => { + const title = await dialog.prompt({ + title: t("relation_map.rename_note"), + message: t("relation_map.enter_new_title"), + defaultValue: note?.title, + }); + + if (!title) { + return; + } + + await server.put(`notes/${noteId}/title`, { title }); + } + } + ], + selectMenuItemHandler() {} + }) + }, [ note ]); + + return note && ( + + +
+ + ) +} diff --git a/apps/client/src/widgets/type_widgets/relation_map/RelationMap.tsx b/apps/client/src/widgets/type_widgets/relation_map/RelationMap.tsx index ef98fa226..3739e353d 100644 --- a/apps/client/src/widgets/type_widgets/relation_map/RelationMap.tsx +++ b/apps/client/src/widgets/type_widgets/relation_map/RelationMap.tsx @@ -1,11 +1,9 @@ import { useCallback, useEffect, useRef, useState } from "preact/hooks"; import { TypeWidgetProps } from "../type_widget"; -import { Defaults, jsPlumb, jsPlumbInstance, OverlaySpec } from "jsplumb"; -import { useEditorSpacedUpdate, useNoteBlob, useNoteProperty, useTriliumEvent, useTriliumEvents } from "../../react/hooks"; +import { jsPlumbInstance } from "jsplumb"; +import { useEditorSpacedUpdate, useTriliumEvent, useTriliumEvents } from "../../react/hooks"; import FNote from "../../../entities/fnote"; -import { ComponentChildren, RefObject } from "preact"; -import froca from "../../../services/froca"; -import NoteLink from "../../react/NoteLink"; +import { RefObject } from "preact"; import "./RelationMap.css"; import { t } from "../../../services/i18n"; import panzoom, { PanZoomOptions } from "panzoom"; @@ -13,11 +11,11 @@ import dialog from "../../../services/dialog"; import server from "../../../services/server"; import toast from "../../../services/toast"; import { CreateChildrenResponse, RelationMapPostResponse, RelationMapRelation } from "@triliumnext/commons"; -import contextMenu from "../../../menus/context_menu"; -import appContext from "../../../components/app_context"; import RelationMapApi, { MapData, MapDataNoteEntry } from "./api"; import setupOverlays, { uniDirectionalOverlays } from "./overlays"; -import { JsPlumb, JsPlumbItem } from "./jsplumb"; +import { JsPlumb } from "./jsplumb"; +import { noteIdToId } from "./utils"; +import { NoteBox } from "./NoteBox"; interface Clipboard { noteId: string; @@ -307,105 +305,6 @@ function useNoteCreation({ ntxId, note, containerRef, mapApiRef }: { return onClickHandler; } -const NOTE_BOX_SOURCE_CONFIG = { - filter: ".endpoint", - anchor: "Continuous", - connectorStyle: { stroke: "#000", strokeWidth: 1 }, - connectionType: "basic", - extract: { - action: "the-action" - } -}; - -const NOTE_BOX_TARGET_CONFIG = { - dropOptions: { hoverClass: "dragHover" }, - anchor: "Continuous", - allowLoopback: true -}; - -function NoteBox({ noteId, x, y, mapApiRef }: MapDataNoteEntry & { mapApiRef: RefObject }) { - const [ note, setNote ] = useState(); - const title = useNoteProperty(note, "title"); - useEffect(() => { - froca.getNote(noteId).then(setNote); - }, [ noteId ]); - - const contextMenuHandler = useCallback((e: MouseEvent) => { - e.preventDefault(); - contextMenu.show({ - x: e.pageX, - y: e.pageY, - items: [ - { - title: t("relation_map.open_in_new_tab"), - uiIcon: "bx bx-empty", - handler: () => appContext.tabManager.openTabWithNoteWithHoisting(noteId) - }, - { - title: t("relation_map.remove_note"), - uiIcon: "bx bx-trash", - handler: async () => { - if (!note) return; - const result = await dialog.confirmDeleteNoteBoxWithNote(note.title); - if (typeof result !== "object" || !result.confirmed) return; - - mapApiRef.current?.removeItem(noteId, result.isDeleteNoteChecked); - } - }, - { - title: t("relation_map.edit_title"), - uiIcon: "bx bx-pencil", - handler: async () => { - const title = await dialog.prompt({ - title: t("relation_map.rename_note"), - message: t("relation_map.enter_new_title"), - defaultValue: note?.title, - }); - - if (!title) { - return; - } - - await server.put(`notes/${noteId}/title`, { title }); - } - } - ], - selectMenuItemHandler() {} - }) - }, [ note ]); - - return note && ( - - -
- - ) -} - -function noteIdToId(noteId: string) { - return `rel-map-note-${noteId}`; -} - -function idToNoteId(id: string) { - return id.substr(13); -} - function getZoom(container: HTMLDivElement) { const transform = window.getComputedStyle(container).transform; if (transform === "none") { diff --git a/apps/client/src/widgets/type_widgets/relation_map/utils.ts b/apps/client/src/widgets/type_widgets/relation_map/utils.ts new file mode 100644 index 000000000..0564628ad --- /dev/null +++ b/apps/client/src/widgets/type_widgets/relation_map/utils.ts @@ -0,0 +1,7 @@ +export function noteIdToId(noteId: string) { + return `rel-map-note-${noteId}`; +} + +export function idToNoteId(id: string) { + return id.substr(13); +}