diff --git a/apps/client/src/widgets/NoteDetail.tsx b/apps/client/src/widgets/NoteDetail.tsx index 32be6b824..44ed2ecd2 100644 --- a/apps/client/src/widgets/NoteDetail.tsx +++ b/apps/client/src/widgets/NoteDetail.tsx @@ -17,6 +17,7 @@ import File from "./type_widgets/File"; import Image from "./type_widgets/Image"; import { ReadOnlyCode, EditableCode } from "./type_widgets/code/Code"; import Mermaid from "./type_widgets/Mermaid"; +import MindMap from "./type_widgets/MindMap"; /** * A `NoteType` altered by the note detail widget, taking into consideration whether the note is editable or not and adding special note types such as an empty one, @@ -96,6 +97,7 @@ function getCorrespondingWidget(noteType: ExtendedNoteType | undefined, props: T case "readOnlyCode": return case "editableCode": return case "mermaid": return + case "mindMap": return default: break; } } diff --git a/apps/client/src/widgets/type_widgets/MindMap.css b/apps/client/src/widgets/type_widgets/MindMap.css new file mode 100644 index 000000000..0a14addf4 --- /dev/null +++ b/apps/client/src/widgets/type_widgets/MindMap.css @@ -0,0 +1,124 @@ +.note-detail-mind-map { + height: 100%; + overflow: hidden !important; +} + +.note-detail-mind-map .mind-map-container { + height: 100%; +} + +.map-container .node-menu { + position: absolute; + top: 60px; + right: 20px; + bottom: 80px; + overflow: auto; + background: var(--panel-bgcolor); + color: var(--main-color); + border-radius: 5px; + box-shadow: 0 1px 2px #0003; + width: 240px; + box-sizing: border-box; + padding: 0 15px 15px; + transition: .3s all +} + +.map-container .node-menu.close { + height: 29px; + width: 46px; + overflow: hidden +} + +.map-container .node-menu .button-container { + padding: 3px 0; + direction: rtl +} + +.map-container .node-menu #nm-tag { + margin-top: 20px +} + +.map-container .node-menu .nm-fontsize-container { + display: flex; + justify-content: space-around; + margin-bottom: 20px +} + +.map-container .node-menu .nm-fontsize-container div { + height: 36px; + width: 36px; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 1px 2px #0003; + background-color: #fff; + color: tomato; + border-radius: 100% +} + +.map-container .node-menu .nm-fontcolor-container { + margin-bottom: 10px +} + +.map-container .node-menu input, +.map-container .node-menu textarea { + background: var(--input-background-color); + border: 1px solid var(--panel-border-color); + border-radius: var(--bs-border-radius); + color: var(--main-color); + padding: 5px; + margin: 10px 0; + width: 100%; + box-sizing: border-box; +} + +.map-container .node-menu textarea { + resize: none +} + +.map-container .node-menu .split6 { + display: inline-block; + width: 16.66%; + margin-bottom: 5px +} + +.map-container .node-menu .palette { + border-radius: 100%; + width: 21px; + height: 21px; + border: 1px solid #edf1f2; + margin: auto +} + +.map-container .node-menu .nmenu-selected, +.map-container .node-menu .palette:hover { + box-shadow: tomato 0 0 0 2px; + background-color: #c7e9fa +} + +.map-container .node-menu .size-selected { + background-color: tomato !important; + border-color: tomato; + fill: #fff; + color: #fff +} + +.map-container .node-menu .size-selected svg { + color: #fff +} + +.map-container .node-menu .bof { + text-align: center +} + +.map-container .node-menu .bof span { + display: inline-block; + font-size: 14px; + border-radius: 4px; + padding: 2px 5px +} + +.map-container .node-menu .bof .selected { + background-color: tomato; + color: #fff +} \ No newline at end of file diff --git a/apps/client/src/widgets/type_widgets/MindMap.tsx b/apps/client/src/widgets/type_widgets/MindMap.tsx new file mode 100644 index 000000000..be369c4ad --- /dev/null +++ b/apps/client/src/widgets/type_widgets/MindMap.tsx @@ -0,0 +1,77 @@ +import { useCallback, useEffect, useRef } from "preact/hooks"; +import { TypeWidgetProps } from "./type_widget"; +import { MindElixirData, default as VanillaMindElixir } from "mind-elixir"; +import { HTMLAttributes } from "preact"; +// allow node-menu plugin css to be bundled by webpack +import nodeMenu from "@mind-elixir/node-menu"; +import "mind-elixir/style"; +import "@mind-elixir/node-menu/dist/style.css"; +import "./MindMap.css"; + +const NEW_TOPIC_NAME = ""; + +interface MindmapModel extends MindElixirData { + direction: number; +} + +interface MindElixirProps { + direction: number; + containerProps?: Omit, "ref">; + content: MindElixirData; +} + +export default function MindMap({ }: TypeWidgetProps) { + const content = VanillaMindElixir.new(NEW_TOPIC_NAME); + + const onKeyDown = useCallback((e: KeyboardEvent) => { + /* + * Some global shortcuts interfere with the default shortcuts of the mind map, + * as defined here: https://mind-elixir.com/docs/guides/shortcuts + */ + if (e.key === "F1") { + e.stopPropagation(); + } + + // Zoom controls + const isCtrl = e.ctrlKey && !e.altKey && !e.metaKey; + if (isCtrl && (e.key == "-" || e.key == "=" || e.key == "0")) { + e.stopPropagation(); + } + }, []); + + return ( +
+ +
+ ) +} + +function MindElixir({ content, containerProps, direction }: MindElixirProps) { + const containerRef = useRef(null); + + useEffect(() => { + if (!containerRef.current) return; + + const mind = new VanillaMindElixir({ + el: containerRef.current, + direction + }); + + mind.install(nodeMenu); + mind.init(content); + + return () => mind.destroy(); + }, []); + + return ( +
+ +
+ ) +} diff --git a/apps/client/src/widgets/type_widgets_old/mind_map.ts b/apps/client/src/widgets/type_widgets_old/mind_map.ts index 59e1a7a3e..a9673560b 100644 --- a/apps/client/src/widgets/type_widgets_old/mind_map.ts +++ b/apps/client/src/widgets/type_widgets_old/mind_map.ts @@ -1,154 +1,9 @@ import TypeWidget from "./type_widget.js"; import utils from "../../services/utils.js"; import type { MindElixirInstance } from "mind-elixir"; -import nodeMenu from "@mind-elixir/node-menu"; import type FNote from "../../entities/fnote.js"; import type { EventData } from "../../components/app_context.js"; -// allow node-menu plugin css to be bundled by webpack -import "mind-elixir/style"; -import "@mind-elixir/node-menu/dist/style.css"; - -const NEW_TOPIC_NAME = ""; - -const TPL = /*html*/` -
-
-
- - -
-`; - -interface MindmapModel { - direction: number; -} - export default class MindMapWidget extends TypeWidget { private $content!: JQuery; @@ -161,24 +16,6 @@ export default class MindMapWidget extends TypeWidget { } doRender() { - this.$widget = $(TPL); - this.$content = this.$widget.find(".mind-map-container"); - this.$content.on("keydown", (e) => { - /* - * Some global shortcuts interfere with the default shortcuts of the mind map, - * as defined here: https://mind-elixir.com/docs/guides/shortcuts - */ - if (e.key === "F1") { - e.stopPropagation(); - } - - // Zoom controls - const isCtrl = e.ctrlKey && !e.altKey && !e.metaKey; - if (isCtrl && (e.key == "-" || e.key == "=" || e.key == "0")) { - e.stopPropagation(); - } - }); - // Save the mind map if the user changes the layout direction. this.$content.on("click", ".mind-elixir-toolbar.lt", () => { this.spacedUpdate.scheduleUpdate(); @@ -213,13 +50,9 @@ export default class MindMapWidget extends TypeWidget { } async #initLibrary(direction?: number) { - this.MindElixir = (await import("mind-elixir")).default; - const mind = new this.MindElixir({ - el: this.$content[0], direction: direction ?? this.MindElixir.LEFT }); - mind.install(nodeMenu); this.mind = mind; mind.init(this.MindElixir.new(NEW_TOPIC_NAME)); @@ -230,12 +63,6 @@ export default class MindMapWidget extends TypeWidget { this.spacedUpdate.scheduleUpdate(); } }); - - // If the note is displayed directly after a refresh, the scroll ends up at (0,0), making it difficult for the user to see. - // Adding an arbitrary wait until the element is attached to the DOM seems to do the trick for now. - setTimeout(() => { - mind.toCenter(); - }, 200); } async getData() {