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() {