diff --git a/apps/client/src/widgets/react/hooks.tsx b/apps/client/src/widgets/react/hooks.tsx index fc8099551..be62a5563 100644 --- a/apps/client/src/widgets/react/hooks.tsx +++ b/apps/client/src/widgets/react/hooks.tsx @@ -107,7 +107,10 @@ export function useEditorSpacedUpdate({ note, getData, onContentChange, dataSave }, [ blob ]); // React to update interval changes. - useEffect(() => spacedUpdate.setUpdateInterval(updateInterval), [ updateInterval ]); + useEffect(() => { + if (!updateInterval) return; + spacedUpdate.setUpdateInterval(updateInterval); + }, [ updateInterval ]); return spacedUpdate; } diff --git a/apps/client/src/widgets/type_widgets/MindMap.tsx b/apps/client/src/widgets/type_widgets/MindMap.tsx index be369c4ad..433bd80d4 100644 --- a/apps/client/src/widgets/type_widgets/MindMap.tsx +++ b/apps/client/src/widgets/type_widgets/MindMap.tsx @@ -1,12 +1,13 @@ import { useCallback, useEffect, useRef } from "preact/hooks"; import { TypeWidgetProps } from "./type_widget"; -import { MindElixirData, default as VanillaMindElixir } from "mind-elixir"; -import { HTMLAttributes } from "preact"; +import { MindElixirData, MindElixirInstance, Operation, default as VanillaMindElixir } from "mind-elixir"; +import { HTMLAttributes, RefObject } 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"; +import { useEditorSpacedUpdate } from "../react/hooks"; const NEW_TOPIC_NAME = ""; @@ -15,13 +16,34 @@ interface MindmapModel extends MindElixirData { } interface MindElixirProps { + apiRef?: RefObject; direction: number; containerProps?: Omit, "ref">; content: MindElixirData; + onChange?: () => void; } -export default function MindMap({ }: TypeWidgetProps) { +export default function MindMap({ note }: TypeWidgetProps) { const content = VanillaMindElixir.new(NEW_TOPIC_NAME); + const apiRef = useRef(null); + const spacedUpdate = useEditorSpacedUpdate({ + note, + getData: () => ({ content: apiRef.current?.getDataString() }), + onContentChange: (content) => { + let newContent: MindElixirData; + if (content) { + try { + newContent = JSON.parse(content) as MindmapModel; + } catch (e) { + console.warn(e); + console.debug("Wrong JSON content: ", content); + } + } else { + newContent = VanillaMindElixir.new(NEW_TOPIC_NAME) + } + apiRef.current?.init(newContent!); + } + }); const onKeyDown = useCallback((e: KeyboardEvent) => { /* @@ -42,7 +64,9 @@ export default function MindMap({ }: TypeWidgetProps) { return (
spacedUpdate.scheduleUpdate()} containerProps={{ className: "mind-map-container", onKeyDown @@ -52,8 +76,9 @@ export default function MindMap({ }: TypeWidgetProps) { ) } -function MindElixir({ content, containerProps, direction }: MindElixirProps) { +function MindElixir({ content, containerProps, direction, apiRef: externalApiRef, onChange }: MindElixirProps) { const containerRef = useRef(null); + const apiRef = useRef(null); useEffect(() => { if (!containerRef.current) return; @@ -66,12 +91,28 @@ function MindElixir({ content, containerProps, direction }: MindElixirProps) { mind.install(nodeMenu); mind.init(content); + apiRef.current = mind; + if (externalApiRef) { + externalApiRef.current = mind; + } + return () => mind.destroy(); }, []); - return ( -
+ // On change listener. + useEffect(() => { + if (!onChange) return; -
+ const listener = (operation: Operation) => { + if (operation.name !== "beginEdit") { + onChange(); + } + } + apiRef.current?.bus.addListener("operation", listener); + return () => apiRef.current?.bus?.removeListener("operation", listener); + }, [ onChange ]); + + 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 a9673560b..85d6f4f04 100644 --- a/apps/client/src/widgets/type_widgets_old/mind_map.ts +++ b/apps/client/src/widgets/type_widgets_old/mind_map.ts @@ -55,14 +55,6 @@ export default class MindMapWidget extends TypeWidget { }); this.mind = mind; - mind.init(this.MindElixir.new(NEW_TOPIC_NAME)); - // TODO: See why the typeof mindmap is not correct. - mind.bus.addListener("operation", (operation: { name: string }) => { - this.triggeredByUserOperation = true; - if (operation.name !== "beginEdit") { - this.spacedUpdate.scheduleUpdate(); - } - }); } async getData() {