0.1.03 mindmap问题修改
This commit is contained in:
@@ -11,7 +11,14 @@ import React, {
|
|||||||
} from "react";
|
} from "react";
|
||||||
import { createPortal } from "react-dom";
|
import { createPortal } from "react-dom";
|
||||||
import { createReactBlockSpec } from "@blocknote/react";
|
import { createReactBlockSpec } from "@blocknote/react";
|
||||||
import type { Block, BlockNoteEditor } from "@blocknote/core";
|
import type {
|
||||||
|
BlockConfig,
|
||||||
|
BlockNoteEditor,
|
||||||
|
DefaultInlineContentSchema,
|
||||||
|
DefaultStyleSchema,
|
||||||
|
PropSchema,
|
||||||
|
SpecificBlock,
|
||||||
|
} from "@blocknote/core";
|
||||||
import { useDebouncedCallback } from "@/hooks/use-debounced-callback";
|
import { useDebouncedCallback } from "@/hooks/use-debounced-callback";
|
||||||
import type { CustomBlockSchema } from "../schema";
|
import type { CustomBlockSchema } from "../schema";
|
||||||
import { MindmapToolbar } from "./MindmapToolbar";
|
import { MindmapToolbar } from "./MindmapToolbar";
|
||||||
@@ -21,17 +28,16 @@ import { MindmapNavigator } from "./MindmapNavigator";
|
|||||||
import { MindmapMiniMap } from "./MindmapMiniMap";
|
import { MindmapMiniMap } from "./MindmapMiniMap";
|
||||||
import { MindmapCount } from "./MindmapCount";
|
import { MindmapCount } from "./MindmapCount";
|
||||||
import type { SidebarPanel } from "./mindmapSidebarConfig";
|
import type { SidebarPanel } from "./mindmapSidebarConfig";
|
||||||
|
import type { MindMapNode } from "./mindmapTypes";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { ArrowUp, ArrowDown, ArrowLeft, ArrowRight } from "lucide-react";
|
import { ArrowUp, ArrowDown, ArrowLeft, ArrowRight } from "lucide-react";
|
||||||
import iconConfig from "./mindmapIconConfig";
|
import iconConfig from "./mindmapIconConfig";
|
||||||
import { emitAssetsChanged } from "@/lib/events";
|
import { emitAssetsChanged } from "@/lib/events";
|
||||||
import { useEditorBridgeStore } from "@/store/editor-bridge";
|
|
||||||
|
|
||||||
// 避免 SSR/Node 环境提前评估 simple-mind-map 内部依赖 document
|
// 避免 SSR/Node 环境提前评估 simple-mind-map 内部依赖 document
|
||||||
// @ts-expect-error 第三方库缺少类型定义
|
|
||||||
const loadIconModules = async () => {
|
const loadIconModules = async () => {
|
||||||
const { nodeIconList } = await import("simple-mind-map/src/svg/icons.js");
|
const { nodeIconList } = await import("simple-mind-map/src/svg/icons.js");
|
||||||
const { mergerIconList } = await import("simple-mind-map/src/utils/index.js");
|
const { mergerIconList } = await import("simple-mind-map/src/utils/index.js");
|
||||||
return { nodeIconList, mergerIconList };
|
return { nodeIconList, mergerIconList };
|
||||||
};
|
};
|
||||||
@@ -47,17 +53,37 @@ const getImageSizeSafe = (url: string): Promise<{ width: number; height: number
|
|||||||
});
|
});
|
||||||
|
|
||||||
type MindMapInstance = {
|
type MindMapInstance = {
|
||||||
execCommand: (...args: unknown[]) => void;
|
execCommand: (command: string, ...args: unknown[]) => void;
|
||||||
destroy: () => void;
|
destroy: () => void;
|
||||||
setData: (data: unknown) => void;
|
setData: (data: unknown) => void;
|
||||||
getData: (withConfig?: boolean) => unknown;
|
getData: (withConfig?: boolean) => unknown;
|
||||||
on?: (event: string, handler: (...args: unknown[]) => void) => void;
|
on?: <TArgs extends unknown[]>(event: string, handler: (...args: TArgs) => void) => void;
|
||||||
emit?: (event: string, ...args: unknown[]) => void;
|
emit?: (event: string, ...args: unknown[]) => void;
|
||||||
off?: (event: string, handler: (...args: unknown[]) => void) => void;
|
off?: <TArgs extends unknown[]>(event: string, handler: (...args: TArgs) => void) => void;
|
||||||
setMode?: (mode: string) => void;
|
setMode?: (mode: string) => void;
|
||||||
command: { clearHistory: () => void };
|
command: { clearHistory: () => void };
|
||||||
view: { fit: () => void; scale: number; enlarge: () => void; narrow: () => void; setScale: (scale: number, cx: number, cy: number) => void };
|
editNodeClassList?: string[];
|
||||||
renderer: { activeNodeList: unknown[]; renderTree: { _node: unknown }; setRootNodeCenter: () => void };
|
view: {
|
||||||
|
fit: () => void;
|
||||||
|
scale: number;
|
||||||
|
enlarge: () => void;
|
||||||
|
narrow: () => void;
|
||||||
|
setScale: (scale: number, cx: number, cy: number) => void;
|
||||||
|
};
|
||||||
|
renderer: {
|
||||||
|
activeNodeList: unknown[];
|
||||||
|
lastActiveNodeList?: unknown[];
|
||||||
|
root?: unknown;
|
||||||
|
renderTree: { _node: unknown };
|
||||||
|
setRootNodeCenter?: () => void;
|
||||||
|
clearActiveNodeList?: () => void;
|
||||||
|
addNodeToActiveList?: (node: unknown, isActive?: boolean) => void;
|
||||||
|
emitNodeActiveEvent?: (node: unknown) => void;
|
||||||
|
findNodeByUid?: (uid: string) => unknown;
|
||||||
|
copy?: () => void;
|
||||||
|
paste?: () => void;
|
||||||
|
beingCopyData?: unknown;
|
||||||
|
};
|
||||||
painter?: { startPainter: () => void };
|
painter?: { startPainter: () => void };
|
||||||
doExport?: { export: (type: string, isDownload?: boolean, name?: string) => Promise<unknown> };
|
doExport?: { export: (type: string, isDownload?: boolean, name?: string) => Promise<unknown> };
|
||||||
width: number;
|
width: number;
|
||||||
@@ -69,13 +95,6 @@ type MindMapInstance = {
|
|||||||
setLayout: (layout: string) => void;
|
setLayout: (layout: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type MindMapNode = {
|
|
||||||
getStyle?: (key: string, inherit?: boolean) => unknown;
|
|
||||||
getData?: (key: string) => unknown;
|
|
||||||
nodeData?: { data?: Record<string, unknown> };
|
|
||||||
data?: Record<string, unknown>;
|
|
||||||
};
|
|
||||||
|
|
||||||
type MindMapData = {
|
type MindMapData = {
|
||||||
data: Record<string, unknown>;
|
data: Record<string, unknown>;
|
||||||
children?: unknown[];
|
children?: unknown[];
|
||||||
@@ -109,22 +128,27 @@ const patchSvgRbox = async () => {
|
|||||||
// 仅在浏览器环境生效
|
// 仅在浏览器环境生效
|
||||||
if (typeof window === "undefined") return;
|
if (typeof window === "undefined") return;
|
||||||
const svgModule = await import("@svgdotjs/svg.js");
|
const svgModule = await import("@svgdotjs/svg.js");
|
||||||
const candidates = [(svgModule as any).Element, (svgModule as any).G];
|
type SvgCtorPrototype = {
|
||||||
|
rbox?: (ref?: unknown) => unknown;
|
||||||
|
__wolaiRboxPatched?: boolean;
|
||||||
|
};
|
||||||
|
type SvgCtor = { prototype?: SvgCtorPrototype } | undefined;
|
||||||
|
const svgModuleTyped = svgModule as unknown as { Element?: SvgCtor; G?: SvgCtor };
|
||||||
|
const candidates = [svgModuleTyped.Element, svgModuleTyped.G];
|
||||||
let warned = false;
|
let warned = false;
|
||||||
candidates.forEach((Ctor) => {
|
candidates.forEach((Ctor) => {
|
||||||
if (!Ctor?.prototype) return;
|
if (!Ctor?.prototype) return;
|
||||||
if (Ctor.prototype.__wolaiRboxPatched) return;
|
if (Ctor.prototype.__wolaiRboxPatched) return;
|
||||||
const original = Ctor.prototype.rbox;
|
const original = Ctor.prototype.rbox;
|
||||||
if (typeof original !== "function") return;
|
if (typeof original !== "function") return;
|
||||||
// @ts-expect-error 动态扩展第三方原型
|
|
||||||
Ctor.prototype.rbox = function patchedRbox(ref?: unknown) {
|
Ctor.prototype.rbox = function patchedRbox(ref?: unknown) {
|
||||||
try {
|
try {
|
||||||
return original.call(this, ref);
|
return original.call(this, ref);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 退化到 DOM 的 getBoundingClientRect 计算,避免初次渲染时出现 (0,0)
|
// 退化到 DOM 的 getBoundingClientRect 计算,避免初次渲染时出现 (0,0)
|
||||||
const el =
|
type SvgElementLike = { node?: Element | null; el?: Element | null };
|
||||||
// svg.js Element 实例通常持有 node 属性
|
const maybeThis = this as unknown as SvgElementLike;
|
||||||
(this as any)?.node ?? (this as any)?.el ?? null;
|
const el = maybeThis?.node ?? maybeThis?.el ?? null;
|
||||||
const rect =
|
const rect =
|
||||||
el && typeof el.getBoundingClientRect === "function"
|
el && typeof el.getBoundingClientRect === "function"
|
||||||
? el.getBoundingClientRect()
|
? el.getBoundingClientRect()
|
||||||
@@ -178,7 +202,7 @@ const applyToActiveNodes = (
|
|||||||
const list = renderer?.activeNodeList;
|
const list = renderer?.activeNodeList;
|
||||||
if (!list || list.length === 0) {
|
if (!list || list.length === 0) {
|
||||||
// 优先尝试 lastActiveNodeList(部分操作会短暂清空 activeNodeList)
|
// 优先尝试 lastActiveNodeList(部分操作会短暂清空 activeNodeList)
|
||||||
const lastActive = (renderer as any)?.lastActiveNodeList?.[0];
|
const lastActive = renderer?.lastActiveNodeList?.[0];
|
||||||
const root = renderer?.root ?? renderer?.renderTree?._node;
|
const root = renderer?.root ?? renderer?.renderTree?._node;
|
||||||
const fallback = lastActive ?? root;
|
const fallback = lastActive ?? root;
|
||||||
if (fallback) {
|
if (fallback) {
|
||||||
@@ -202,15 +226,15 @@ const ensureActiveBefore = (mindmap?: MindMapInstance | null) => {
|
|||||||
const renderer = mm.renderer;
|
const renderer = mm.renderer;
|
||||||
const list = renderer?.activeNodeList ?? [];
|
const list = renderer?.activeNodeList ?? [];
|
||||||
if (!list || list.length === 0) {
|
if (!list || list.length === 0) {
|
||||||
const lastActive = (renderer as any)?.lastActiveNodeList?.[0];
|
const lastActive = renderer?.lastActiveNodeList?.[0];
|
||||||
const root = renderer?.root ?? renderer?.renderTree?._node;
|
const root = renderer?.root ?? renderer?.renderTree?._node;
|
||||||
const fallback = lastActive ?? root;
|
const fallback = lastActive ?? root;
|
||||||
if (fallback) {
|
if (fallback) {
|
||||||
// 主动维护激活列表,避免首次插入子节点时因无激活节点导致插入位置错误
|
// 主动维护激活列表,避免首次插入子节点时因无激活节点导致插入位置错误
|
||||||
// 先确保根节点居中,避免后续插入节点跑到 (0,0)
|
// 先确保根节点居中,避免后续插入节点跑到 (0,0)
|
||||||
renderer?.setRootNodeCenter && renderer.setRootNodeCenter();
|
if (renderer?.setRootNodeCenter) renderer.setRootNodeCenter();
|
||||||
mm.view?.fit?.();
|
mm.view?.fit?.();
|
||||||
renderer?.clearActiveNodeList && renderer.clearActiveNodeList();
|
if (renderer?.clearActiveNodeList) renderer.clearActiveNodeList();
|
||||||
if (renderer?.addNodeToActiveList) renderer.addNodeToActiveList(fallback, true);
|
if (renderer?.addNodeToActiveList) renderer.addNodeToActiveList(fallback, true);
|
||||||
if (renderer?.emitNodeActiveEvent) renderer.emitNodeActiveEvent(fallback);
|
if (renderer?.emitNodeActiveEvent) renderer.emitNodeActiveEvent(fallback);
|
||||||
mm.execCommand?.("SET_NODE_ACTIVE", fallback, true);
|
mm.execCommand?.("SET_NODE_ACTIVE", fallback, true);
|
||||||
@@ -227,23 +251,25 @@ const MindmapBlockView = ({
|
|||||||
editor,
|
editor,
|
||||||
fullscreen = false,
|
fullscreen = false,
|
||||||
}: {
|
}: {
|
||||||
block: Block<CustomBlockSchema, "mindmap">;
|
block: SpecificBlock<
|
||||||
|
CustomBlockSchema,
|
||||||
|
"mindmap",
|
||||||
|
DefaultInlineContentSchema,
|
||||||
|
DefaultStyleSchema
|
||||||
|
>;
|
||||||
editor: BlockNoteEditor<CustomBlockSchema>;
|
editor: BlockNoteEditor<CustomBlockSchema>;
|
||||||
fullscreen?: boolean;
|
fullscreen?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const currentDocumentId = useEditorBridgeStore(
|
|
||||||
(state) => state.currentDocumentId,
|
|
||||||
);
|
|
||||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||||
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
||||||
const [mindmap, setMindmap] = useState<MindMapInstance | null>(null);
|
const [mindmap, setMindmap] = useState<MindMapInstance | null>(null);
|
||||||
const mindmapReadyRef = useRef(false);
|
const mindmapReadyRef = useRef(false);
|
||||||
const mindmapRef = useRef<MindMapInstance | null>(null);
|
const mindmapRef = useRef<MindMapInstance | null>(null);
|
||||||
const hasLocalEditsRef = useRef(false);
|
const hasLocalEditsRef = useRef(false);
|
||||||
const applyingRemoteRef = useRef(false);
|
const applyingRemoteRef = useRef(false);
|
||||||
const [canBack, setCanBack] = useState(false);
|
const [canBack, setCanBack] = useState(false);
|
||||||
const [canForward, setCanForward] = useState(false);
|
const [canForward, setCanForward] = useState(false);
|
||||||
const [activeNodes, setActiveNodes] = useState<unknown[]>([]);
|
const [activeNodes, setActiveNodes] = useState<MindMapNode[]>([]);
|
||||||
const [painterMode, setPainterMode] = useState(false);
|
const [painterMode, setPainterMode] = useState(false);
|
||||||
const [showMiniMap, setShowMiniMap] = useState(false);
|
const [showMiniMap, setShowMiniMap] = useState(false);
|
||||||
const [activeSidebar, setActiveSidebar] = useState<SidebarPanel | null>(null);
|
const [activeSidebar, setActiveSidebar] = useState<SidebarPanel | null>(null);
|
||||||
@@ -264,11 +290,10 @@ const MindmapBlockView = ({
|
|||||||
const docId = useMemo(
|
const docId = useMemo(
|
||||||
() =>
|
() =>
|
||||||
block.props.docId ||
|
block.props.docId ||
|
||||||
currentDocumentId ||
|
|
||||||
(typeof window !== "undefined"
|
(typeof window !== "undefined"
|
||||||
? window.location.pathname.split("/").pop() ?? ""
|
? window.location.pathname.split("/").pop() ?? ""
|
||||||
: ""),
|
: ""),
|
||||||
[block.props.docId, currentDocumentId],
|
[block.props.docId],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -387,8 +412,10 @@ const MindmapBlockView = ({
|
|||||||
if (document.fullscreenElement) return;
|
if (document.fullscreenElement) return;
|
||||||
// 必须在“用户手势回调”中同步调用,避免 Fullscreen API 被浏览器拒绝
|
// 必须在“用户手势回调”中同步调用,避免 Fullscreen API 被浏览器拒绝
|
||||||
try {
|
try {
|
||||||
const target = document.documentElement as any;
|
const target = document.documentElement as unknown as {
|
||||||
const p = target?.requestFullscreen?.();
|
requestFullscreen?: () => Promise<void>;
|
||||||
|
};
|
||||||
|
const p = target.requestFullscreen?.();
|
||||||
if (p && typeof p.catch === "function") {
|
if (p && typeof p.catch === "function") {
|
||||||
p.catch(() => {
|
p.catch(() => {
|
||||||
// ignore:失败则保持 Portal 伪全屏
|
// ignore:失败则保持 Portal 伪全屏
|
||||||
@@ -457,14 +484,14 @@ const MindmapBlockView = ({
|
|||||||
if (!mm || !mindmapReadyRef.current) return;
|
if (!mm || !mindmapReadyRef.current) return;
|
||||||
|
|
||||||
const target = e.target as HTMLElement | null;
|
const target = e.target as HTMLElement | null;
|
||||||
const editClasses = (mm as any)?.editNodeClassList as string[] | undefined;
|
const editClasses = mm.editNodeClassList;
|
||||||
const wrapper = wrapperRef.current;
|
const wrapper = wrapperRef.current;
|
||||||
const isInWrapper = Boolean(wrapper && target && wrapper.contains(target));
|
const isInWrapper = Boolean(wrapper && target && wrapper.contains(target));
|
||||||
if (target && isInWrapper) {
|
if (target && isInWrapper) {
|
||||||
const tag = target.tagName;
|
const tag = target.tagName;
|
||||||
if (tag === "INPUT" || tag === "TEXTAREA") return;
|
if (tag === "INPUT" || tag === "TEXTAREA") return;
|
||||||
// contenteditable 输入中不拦截:避免破坏节点文本编辑/粘贴
|
// contenteditable 输入中不拦截:避免破坏节点文本编辑/粘贴
|
||||||
if ((target as any).isContentEditable) return;
|
if (target.isContentEditable) return;
|
||||||
const editableAncestor = target.closest?.('[contenteditable="true"]');
|
const editableAncestor = target.closest?.('[contenteditable="true"]');
|
||||||
if (editableAncestor) return;
|
if (editableAncestor) return;
|
||||||
if (editClasses && target.classList) {
|
if (editClasses && target.classList) {
|
||||||
@@ -484,11 +511,11 @@ const MindmapBlockView = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const pickTargetNode = (inst: MindMapInstance) => {
|
const pickTargetNode = (inst: MindMapInstance) => {
|
||||||
const renderer = inst.renderer as any;
|
const renderer = inst.renderer;
|
||||||
const active: any[] = renderer?.activeNodeList ?? [];
|
const active = renderer.activeNodeList ?? [];
|
||||||
const last: any[] = renderer?.lastActiveNodeList ?? [];
|
const last = renderer.lastActiveNodeList ?? [];
|
||||||
const root = renderer?.root ?? renderer?.renderTree?._node ?? null;
|
const root = renderer.root ?? renderer.renderTree?._node ?? null;
|
||||||
return (active && active[0]) || (last && last[0]) || root || null;
|
return active[0] ?? last[0] ?? root ?? null;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (key === "Enter" && !e.shiftKey && !e.altKey && !isMod) {
|
if (key === "Enter" && !e.shiftKey && !e.altKey && !isMod) {
|
||||||
@@ -517,8 +544,7 @@ const MindmapBlockView = ({
|
|||||||
stop();
|
stop();
|
||||||
const inst = ensureActiveBefore(mm);
|
const inst = ensureActiveBefore(mm);
|
||||||
if (!inst) return;
|
if (!inst) return;
|
||||||
const renderer = inst.renderer as any;
|
inst.renderer.copy?.();
|
||||||
if (typeof renderer?.copy === "function") renderer.copy();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,13 +552,13 @@ const MindmapBlockView = ({
|
|||||||
stop();
|
stop();
|
||||||
const inst = ensureActiveBefore(mm);
|
const inst = ensureActiveBefore(mm);
|
||||||
if (!inst) return;
|
if (!inst) return;
|
||||||
const renderer = inst.renderer as any;
|
const renderer = inst.renderer;
|
||||||
const copyData = renderer?.beingCopyData ?? null;
|
const copyData = renderer.beingCopyData ?? null;
|
||||||
if (copyData) {
|
if (copyData) {
|
||||||
inst.execCommand?.("PASTE_NODE", copyData);
|
inst.execCommand?.("PASTE_NODE", copyData);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (typeof renderer?.paste === "function") renderer.paste();
|
renderer.paste?.();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -614,9 +640,8 @@ const MindmapBlockView = ({
|
|||||||
if (!mindmap || activeNodes.length > 0) return;
|
if (!mindmap || activeNodes.length > 0) return;
|
||||||
const rootNode = mindmap.renderer?.root ?? mindmap.renderer?.renderTree?._node;
|
const rootNode = mindmap.renderer?.root ?? mindmap.renderer?.renderTree?._node;
|
||||||
if (rootNode) {
|
if (rootNode) {
|
||||||
setActiveNodes([rootNode]);
|
setActiveNodes([rootNode as MindMapNode]);
|
||||||
mindmap.renderer.activeNodeList = [rootNode];
|
mindmap.renderer.activeNodeList = [rootNode];
|
||||||
// @ts-expect-error 第三方库字段
|
|
||||||
mindmap.renderer.lastActiveNodeList = [rootNode];
|
mindmap.renderer.lastActiveNodeList = [rootNode];
|
||||||
mindmap.emit?.("node_active", rootNode, [rootNode]);
|
mindmap.emit?.("node_active", rootNode, [rootNode]);
|
||||||
}
|
}
|
||||||
@@ -695,33 +720,41 @@ const MindmapBlockView = ({
|
|||||||
if (destroyed) return;
|
if (destroyed) return;
|
||||||
|
|
||||||
// 兜底修补 simple-mind-map 在创建富文本节点时 host/缓存未就绪导致的空指针
|
// 兜底修补 simple-mind-map 在创建富文本节点时 host/缓存未就绪导致的空指针
|
||||||
const MindMapNodeCtor = (MindMapNodeModule as any)?.default;
|
type MindMapNodeCtorLike = {
|
||||||
|
prototype: {
|
||||||
|
__wolaiRichtextPatched?: boolean;
|
||||||
|
createRichTextNode?: (...args: unknown[]) => unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const MindMapNodeCtor = (MindMapNodeModule as unknown as { default?: MindMapNodeCtorLike })?.default;
|
||||||
if (
|
if (
|
||||||
MindMapNodeCtor &&
|
MindMapNodeCtor &&
|
||||||
!MindMapNodeCtor.prototype.__wolaiRichtextPatched
|
!MindMapNodeCtor.prototype.__wolaiRichtextPatched
|
||||||
) {
|
) {
|
||||||
const originalCreate = MindMapNodeCtor.prototype.createRichTextNode;
|
const originalCreate = MindMapNodeCtor.prototype.createRichTextNode;
|
||||||
MindMapNodeCtor.prototype.__wolaiRichtextPatched = true;
|
MindMapNodeCtor.prototype.__wolaiRichtextPatched = true;
|
||||||
// @ts-expect-error 第三方库原型扩展
|
|
||||||
MindMapNodeCtor.prototype.createRichTextNode = function patched(...args: unknown[]) {
|
MindMapNodeCtor.prototype.createRichTextNode = function patched(...args: unknown[]) {
|
||||||
|
type RichtextThis = {
|
||||||
|
mindMap?: { el?: HTMLElement | null; commonCaches?: Record<string, unknown> } | null;
|
||||||
|
};
|
||||||
|
const self = this as unknown as RichtextThis;
|
||||||
// host 兜底:优先使用实例容器,否则退回 body
|
// host 兜底:优先使用实例容器,否则退回 body
|
||||||
const host: HTMLElement =
|
const host: HTMLElement = (self?.mindMap?.el as HTMLElement | null) ?? document.body;
|
||||||
(this?.mindMap?.el as HTMLElement | null) ?? document.body;
|
if (!self.mindMap) {
|
||||||
if (!this.mindMap) {
|
self.mindMap = { el: host, commonCaches: {} };
|
||||||
this.mindMap = { el: host, commonCaches: {} } as any;
|
|
||||||
}
|
}
|
||||||
if (!this.mindMap.el || typeof (this.mindMap.el as any).appendChild !== "function") {
|
const el = self.mindMap.el;
|
||||||
this.mindMap.el = host;
|
if (!el || typeof el.appendChild !== "function") {
|
||||||
|
self.mindMap.el = host;
|
||||||
}
|
}
|
||||||
if (!this.mindMap.commonCaches) {
|
const caches = self.mindMap.commonCaches ?? (self.mindMap.commonCaches = {});
|
||||||
this.mindMap.commonCaches = {};
|
const measureKey = "measureRichtextNodeTextSizeEl";
|
||||||
}
|
if (!caches[measureKey]) {
|
||||||
if (!this.mindMap.commonCaches.measureRichtextNodeTextSizeEl) {
|
|
||||||
const measureDiv = document.createElement("div");
|
const measureDiv = document.createElement("div");
|
||||||
measureDiv.style.position = "fixed";
|
measureDiv.style.position = "fixed";
|
||||||
measureDiv.style.left = "-999999px";
|
measureDiv.style.left = "-999999px";
|
||||||
(this.mindMap.el ?? document.body).appendChild(measureDiv);
|
(self.mindMap.el ?? document.body).appendChild(measureDiv);
|
||||||
this.mindMap.commonCaches.measureRichtextNodeTextSizeEl = measureDiv;
|
caches[measureKey] = measureDiv;
|
||||||
}
|
}
|
||||||
if (typeof originalCreate === "function") {
|
if (typeof originalCreate === "function") {
|
||||||
return originalCreate.apply(this, args);
|
return originalCreate.apply(this, args);
|
||||||
@@ -759,15 +792,17 @@ const MindmapBlockView = ({
|
|||||||
console.warn(`思维导图插件加载失败:${name}`);
|
console.warn(`思维导图插件加载失败:${name}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// @ts-expect-error 第三方库缺少类型定义
|
const MindMapCtor = MindMap as unknown as {
|
||||||
if (MindMap.hasPlugin(plugin) === -1) {
|
hasPlugin?: (p: unknown) => number;
|
||||||
|
usePlugin?: (p: unknown) => void;
|
||||||
|
};
|
||||||
|
const hasPlugin = MindMapCtor.hasPlugin;
|
||||||
|
const notRegistered =
|
||||||
|
typeof hasPlugin === "function" ? hasPlugin(plugin) === -1 : true;
|
||||||
|
const registerPlugin = MindMapCtor.usePlugin;
|
||||||
|
if (notRegistered && typeof registerPlugin === "function") {
|
||||||
console.log(`注册插件: ${name}`);
|
console.log(`注册插件: ${name}`);
|
||||||
const registerPlugin =
|
registerPlugin(plugin);
|
||||||
// @ts-expect-error simple-mind-map 插件注册无类型声明
|
|
||||||
(MindMap as { usePlugin?: (p: unknown) => void }).usePlugin;
|
|
||||||
if (registerPlugin) {
|
|
||||||
registerPlugin(plugin);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -785,7 +820,22 @@ const MindmapBlockView = ({
|
|||||||
hostEl.innerHTML = "";
|
hostEl.innerHTML = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const instance = new MindMap({
|
type MindMapConstructor = new (options: {
|
||||||
|
el: HTMLElement;
|
||||||
|
data: unknown;
|
||||||
|
theme: string;
|
||||||
|
layout: string;
|
||||||
|
mousewheelAction: string;
|
||||||
|
enableFreeDrag: boolean;
|
||||||
|
enableCtrlKeyNodeSelection: boolean;
|
||||||
|
fit: boolean;
|
||||||
|
useLeftKeySelectionRightKeyDrag: boolean;
|
||||||
|
createNewNodeBehavior: string;
|
||||||
|
iconList: unknown[];
|
||||||
|
}) => MindMapInstance;
|
||||||
|
const MindMapCtor = MindMap as unknown as MindMapConstructor;
|
||||||
|
|
||||||
|
const instance = new MindMapCtor({
|
||||||
el: hostEl,
|
el: hostEl,
|
||||||
data: initialDataRef.current,
|
data: initialDataRef.current,
|
||||||
theme: "classic",
|
theme: "classic",
|
||||||
@@ -802,22 +852,25 @@ const MindmapBlockView = ({
|
|||||||
...nodeIconList,
|
...nodeIconList,
|
||||||
...(iconConfig as unknown[]),
|
...(iconConfig as unknown[]),
|
||||||
]),
|
]),
|
||||||
}) as MindMapInstance;
|
});
|
||||||
createdInstance = instance;
|
createdInstance = instance;
|
||||||
mindmapRef.current = instance;
|
mindmapRef.current = instance;
|
||||||
// 尽早标记为可用:主页面通过 `/mind` 插入后,若依赖 render_end/尺寸探测,可能导致按钮永远不可用
|
// 尽早标记为可用:主页面通过 `/mind` 插入后,若依赖 render_end/尺寸探测,可能导致按钮永远不可用
|
||||||
//(render_end 可能早于监听注册触发,或容器尺寸长时间为 0)。命令本身不需要等待 render_end。
|
//(render_end 可能早于监听注册触发,或容器尺寸长时间为 0)。命令本身不需要等待 render_end。
|
||||||
mindmapReadyRef.current = true;
|
mindmapReadyRef.current = true;
|
||||||
// 确保测量节点的缓存容器始终挂在有效 DOM 上,避免 appendChild 空指针
|
// 确保测量节点的缓存容器始终挂在有效 DOM 上,避免 appendChild 空指针
|
||||||
if (!(instance as any).commonCaches) {
|
type MindMapWithCaches = MindMapInstance & { commonCaches?: Record<string, unknown> };
|
||||||
(instance as any).commonCaches = {};
|
const instanceWithCaches = instance as MindMapWithCaches;
|
||||||
|
if (!instanceWithCaches.commonCaches) {
|
||||||
|
instanceWithCaches.commonCaches = {};
|
||||||
}
|
}
|
||||||
if (!(instance as any).commonCaches.measureRichtextNodeTextSizeEl) {
|
const measureKey = "measureRichtextNodeTextSizeEl";
|
||||||
|
if (!instanceWithCaches.commonCaches[measureKey]) {
|
||||||
const measureDiv = document.createElement("div");
|
const measureDiv = document.createElement("div");
|
||||||
measureDiv.style.position = "fixed";
|
measureDiv.style.position = "fixed";
|
||||||
measureDiv.style.left = "-999999px";
|
measureDiv.style.left = "-999999px";
|
||||||
((instance as any).commonCaches).measureRichtextNodeTextSizeEl = measureDiv;
|
instanceWithCaches.commonCaches[measureKey] = measureDiv;
|
||||||
(hostEl ?? document.body).appendChild(measureDiv);
|
hostEl.appendChild(measureDiv);
|
||||||
}
|
}
|
||||||
instance.setMode?.("edit");
|
instance.setMode?.("edit");
|
||||||
const centerAndFit = (retry = 0) => {
|
const centerAndFit = (retry = 0) => {
|
||||||
@@ -833,11 +886,7 @@ const MindmapBlockView = ({
|
|||||||
//(尤其在首次构造时同步渲染),因此不要完全依赖 render_end 来标记就绪。
|
//(尤其在首次构造时同步渲染),因此不要完全依赖 render_end 来标记就绪。
|
||||||
mindmapReadyRef.current = true;
|
mindmapReadyRef.current = true;
|
||||||
const renderer = instance.renderer;
|
const renderer = instance.renderer;
|
||||||
const rootNode =
|
const rootNode = renderer?.root ?? renderer?.renderTree?._node;
|
||||||
// @ts-expect-error 第三方库字段
|
|
||||||
renderer?.root ??
|
|
||||||
// @ts-expect-error 第三方库字段
|
|
||||||
renderer?.renderTree?._node;
|
|
||||||
if (rootNode && renderer?.setRootNodeCenter) {
|
if (rootNode && renderer?.setRootNodeCenter) {
|
||||||
renderer.setRootNodeCenter();
|
renderer.setRootNodeCenter();
|
||||||
}
|
}
|
||||||
@@ -855,7 +904,6 @@ const MindmapBlockView = ({
|
|||||||
|
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
// 便于开发阶段在控制台直接调试实例
|
// 便于开发阶段在控制台直接调试实例
|
||||||
// @ts-expect-error 调试用全局变量
|
|
||||||
window.__mindmapInstance = instance;
|
window.__mindmapInstance = instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -892,28 +940,28 @@ const MindmapBlockView = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
instance.on?.("render_end", onRenderEndOnce);
|
instance.on?.("render_end", onRenderEndOnce);
|
||||||
instance.on?.("node_active", (_node: unknown, list: unknown[]) => {
|
instance.on?.("node_active", (_node: unknown, list: unknown[]) => {
|
||||||
if (!list || list.length === 0) return;
|
if (!list || list.length === 0) return;
|
||||||
setActiveNodes(list || []);
|
setActiveNodes((list || []) as MindMapNode[]);
|
||||||
});
|
});
|
||||||
instance.on?.("node_click", (node: unknown) => {
|
instance.on?.("node_click", (node: unknown) => {
|
||||||
// 确保点击即可激活(调用节点自身的 active 逻辑,保持和官方一致)
|
// 确保点击即可激活(调用节点自身的 active 逻辑,保持和官方一致)
|
||||||
// @ts-expect-error 第三方库节点对象
|
|
||||||
const renderer = instance.renderer;
|
const renderer = instance.renderer;
|
||||||
if (typeof (node as any)?.active === "function") {
|
const nodeWithActive = node as unknown as { active?: () => void };
|
||||||
// @ts-expect-error simple-mind-map 节点对象缺少类型
|
if (typeof nodeWithActive.active === "function") {
|
||||||
node.active();
|
nodeWithActive.active();
|
||||||
} else {
|
} else {
|
||||||
// 兜底:手动维护激活列表
|
// 兜底:手动维护激活列表
|
||||||
// @ts-expect-error simple-mind-map 渲染器缺少类型
|
if (renderer?.clearActiveNodeList) renderer.clearActiveNodeList();
|
||||||
renderer?.clearActiveNodeList && renderer.clearActiveNodeList();
|
if (renderer?.addNodeToActiveList) renderer.addNodeToActiveList(node, true);
|
||||||
// @ts-expect-error simple-mind-map 渲染器缺少类型
|
if (renderer?.emitNodeActiveEvent) renderer.emitNodeActiveEvent(node);
|
||||||
renderer?.addNodeToActiveList && renderer.addNodeToActiveList(node, true);
|
|
||||||
// @ts-expect-error simple-mind-map 渲染器缺少类型
|
|
||||||
renderer?.emitNodeActiveEvent && renderer.emitNodeActiveEvent(node);
|
|
||||||
}
|
}
|
||||||
const list = renderer?.activeNodeList ?? [];
|
const list = renderer?.activeNodeList ?? [];
|
||||||
setActiveNodes(list.length === 0 && node ? [node] : (list as unknown[]));
|
setActiveNodes(
|
||||||
|
list.length === 0 && node
|
||||||
|
? [node as MindMapNode]
|
||||||
|
: (list as MindMapNode[]),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
instance.on?.("painter_start", () => setPainterMode(true));
|
instance.on?.("painter_start", () => setPainterMode(true));
|
||||||
instance.on?.("painter_end", () => setPainterMode(false));
|
instance.on?.("painter_end", () => setPainterMode(false));
|
||||||
@@ -927,19 +975,19 @@ const MindmapBlockView = ({
|
|||||||
const rootNode = getRootNode(instance);
|
const rootNode = getRootNode(instance);
|
||||||
if (rootNode) {
|
if (rootNode) {
|
||||||
// 初始化时主动标记根节点为选中,确保后续插入子节点有合法的父节点
|
// 初始化时主动标记根节点为选中,确保后续插入子节点有合法的父节点
|
||||||
renderer?.clearActiveNodeList && renderer.clearActiveNodeList();
|
if (renderer?.clearActiveNodeList) renderer.clearActiveNodeList();
|
||||||
renderer?.addNodeToActiveList && renderer.addNodeToActiveList(rootNode, true);
|
if (renderer?.addNodeToActiveList) renderer.addNodeToActiveList(rootNode, true);
|
||||||
renderer?.emitNodeActiveEvent && renderer.emitNodeActiveEvent(rootNode);
|
if (renderer?.emitNodeActiveEvent) renderer.emitNodeActiveEvent(rootNode);
|
||||||
setActiveNodes([rootNode]);
|
setActiveNodes([rootNode as MindMapNode]);
|
||||||
}
|
}
|
||||||
// 若首次渲染时 root 仍未就绪,设置兜底延迟激活
|
// 若首次渲染时 root 仍未就绪,设置兜底延迟激活
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
const root = getRootNode(instance);
|
const root = getRootNode(instance);
|
||||||
if (!root) return;
|
if (!root) return;
|
||||||
renderer?.clearActiveNodeList && renderer.clearActiveNodeList();
|
if (renderer?.clearActiveNodeList) renderer.clearActiveNodeList();
|
||||||
renderer?.addNodeToActiveList && renderer.addNodeToActiveList(root, true);
|
if (renderer?.addNodeToActiveList) renderer.addNodeToActiveList(root, true);
|
||||||
renderer?.emitNodeActiveEvent && renderer.emitNodeActiveEvent(root);
|
if (renderer?.emitNodeActiveEvent) renderer.emitNodeActiveEvent(root);
|
||||||
setActiveNodes([root]);
|
setActiveNodes([root as MindMapNode]);
|
||||||
}, 120);
|
}, 120);
|
||||||
|
|
||||||
if (destroyed) {
|
if (destroyed) {
|
||||||
@@ -981,9 +1029,7 @@ const MindmapBlockView = ({
|
|||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
// @ts-expect-error 调试用全局变量
|
|
||||||
if (window.__mindmapInstance === createdInstance) {
|
if (window.__mindmapInstance === createdInstance) {
|
||||||
// @ts-expect-error 调试用全局变量
|
|
||||||
window.__mindmapInstance = null;
|
window.__mindmapInstance = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -997,8 +1043,8 @@ const MindmapBlockView = ({
|
|||||||
const getInstanceCandidate = () =>
|
const getInstanceCandidate = () =>
|
||||||
mindmap ??
|
mindmap ??
|
||||||
(typeof window !== "undefined"
|
(typeof window !== "undefined"
|
||||||
? // @ts-expect-error 调试用全局变量
|
? ((window as unknown as { __mindmapInstance?: MindMapInstance | null })
|
||||||
(window.__mindmapInstance as MindMapInstance | undefined | null)
|
.__mindmapInstance ?? null)
|
||||||
: null);
|
: null);
|
||||||
|
|
||||||
const runWhenReady = (
|
const runWhenReady = (
|
||||||
@@ -1030,11 +1076,16 @@ const MindmapBlockView = ({
|
|||||||
|
|
||||||
const pickActiveOrRoot = (mm: MindMapInstance) => {
|
const pickActiveOrRoot = (mm: MindMapInstance) => {
|
||||||
const list = mm.renderer?.activeNodeList;
|
const list = mm.renderer?.activeNodeList;
|
||||||
const last = (mm.renderer as any)?.lastActiveNodeList;
|
const last = mm.renderer?.lastActiveNodeList;
|
||||||
// @ts-expect-error 第三方库类型缺失
|
|
||||||
return (list && list[0]) || (last && last[0]) || getRootNode(mm) || null;
|
return (list && list[0]) || (last && last[0]) || getRootNode(mm) || null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getNodeUid = (node: unknown): string | null => {
|
||||||
|
const maybeNode = node as unknown as { getData?: (key: string) => unknown; uid?: unknown };
|
||||||
|
const raw = typeof maybeNode.getData === "function" ? maybeNode.getData("uid") : maybeNode.uid;
|
||||||
|
return typeof raw === "string" && raw ? raw : null;
|
||||||
|
};
|
||||||
|
|
||||||
const handleUndo = () => runWhenReady((mm) => mm.execCommand("BACK"));
|
const handleUndo = () => runWhenReady((mm) => mm.execCommand("BACK"));
|
||||||
const handleRedo = () => runWhenReady((mm) => mm.execCommand("FORWARD"));
|
const handleRedo = () => runWhenReady((mm) => mm.execCommand("FORWARD"));
|
||||||
const handlePainter = () => {
|
const handlePainter = () => {
|
||||||
@@ -1049,11 +1100,7 @@ const MindmapBlockView = ({
|
|||||||
execWithReflow((mm) => {
|
execWithReflow((mm) => {
|
||||||
const target = pickActiveOrRoot(mm);
|
const target = pickActiveOrRoot(mm);
|
||||||
if (!target) return;
|
if (!target) return;
|
||||||
const targetUid =
|
const targetUid = getNodeUid(target);
|
||||||
// @ts-expect-error simple-mind-map 节点缺少类型
|
|
||||||
(typeof (target as any)?.getData === "function"
|
|
||||||
? (target as any).getData("uid")
|
|
||||||
: (target as any)?.uid) ?? null;
|
|
||||||
mm.execCommand?.("SET_NODE_ACTIVE", target, true);
|
mm.execCommand?.("SET_NODE_ACTIVE", target, true);
|
||||||
// openEdit=false:保持与 createNewNodeBehavior=activeOnly 的策略一致,避免强制进入编辑模式带来的时序问题
|
// openEdit=false:保持与 createNewNodeBehavior=activeOnly 的策略一致,避免强制进入编辑模式带来的时序问题
|
||||||
mm.execCommand?.("INSERT_NODE", false, [target]);
|
mm.execCommand?.("INSERT_NODE", false, [target]);
|
||||||
@@ -1062,13 +1109,15 @@ const MindmapBlockView = ({
|
|||||||
// 这里尝试在下一帧将新插入的节点设为激活(插入同级节点时,新节点位于 target 后)。
|
// 这里尝试在下一帧将新插入的节点设为激活(插入同级节点时,新节点位于 target 后)。
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
try {
|
try {
|
||||||
const renderer = mm.renderer as any;
|
const renderer = mm.renderer;
|
||||||
if ((renderer?.activeNodeList?.length ?? 0) > 0) return;
|
if ((renderer.activeNodeList?.length ?? 0) > 0) return;
|
||||||
if (!targetUid || typeof renderer?.findNodeByUid !== "function") return;
|
if (!targetUid || typeof renderer.findNodeByUid !== "function") return;
|
||||||
const currentTarget = renderer.findNodeByUid(targetUid);
|
const currentTarget = renderer.findNodeByUid(targetUid) as unknown as {
|
||||||
const parent = currentTarget?.parent;
|
parent?: { children?: unknown[] } | null;
|
||||||
const siblings: any[] = parent?.children ?? [];
|
};
|
||||||
const idx = siblings.indexOf(currentTarget);
|
const parent = currentTarget?.parent ?? null;
|
||||||
|
const siblings = (parent?.children ?? []) as unknown[];
|
||||||
|
const idx = siblings.indexOf(currentTarget as unknown);
|
||||||
const inserted = idx >= 0 ? siblings[idx + 1] : null;
|
const inserted = idx >= 0 ? siblings[idx + 1] : null;
|
||||||
if (!inserted) return;
|
if (!inserted) return;
|
||||||
renderer?.clearActiveNodeList?.();
|
renderer?.clearActiveNodeList?.();
|
||||||
@@ -1085,11 +1134,7 @@ const MindmapBlockView = ({
|
|||||||
execWithReflow((mm) => {
|
execWithReflow((mm) => {
|
||||||
const parent = pickActiveOrRoot(mm);
|
const parent = pickActiveOrRoot(mm);
|
||||||
if (!parent) return;
|
if (!parent) return;
|
||||||
const parentUid =
|
const parentUid = getNodeUid(parent);
|
||||||
// @ts-expect-error simple-mind-map 节点缺少类型
|
|
||||||
(typeof (parent as any)?.getData === "function"
|
|
||||||
? (parent as any).getData("uid")
|
|
||||||
: (parent as any)?.uid) ?? null;
|
|
||||||
mm.execCommand?.("SET_NODE_ACTIVE", parent, true);
|
mm.execCommand?.("SET_NODE_ACTIVE", parent, true);
|
||||||
// openEdit=false:同上,避免插入瞬间强制进入编辑导致异常
|
// openEdit=false:同上,避免插入瞬间强制进入编辑导致异常
|
||||||
mm.execCommand?.("INSERT_CHILD_NODE", false, [parent]);
|
mm.execCommand?.("INSERT_CHILD_NODE", false, [parent]);
|
||||||
@@ -1097,11 +1142,13 @@ const MindmapBlockView = ({
|
|||||||
// 兜底:确保新插入的子节点成为激活节点,保证随后点击“同级节点/删除节点”可用
|
// 兜底:确保新插入的子节点成为激活节点,保证随后点击“同级节点/删除节点”可用
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
try {
|
try {
|
||||||
const renderer = mm.renderer as any;
|
const renderer = mm.renderer;
|
||||||
if ((renderer?.activeNodeList?.length ?? 0) > 0) return;
|
if ((renderer.activeNodeList?.length ?? 0) > 0) return;
|
||||||
if (!parentUid || typeof renderer?.findNodeByUid !== "function") return;
|
if (!parentUid || typeof renderer.findNodeByUid !== "function") return;
|
||||||
const currentParent = renderer.findNodeByUid(parentUid);
|
const currentParent = renderer.findNodeByUid(parentUid) as unknown as {
|
||||||
const children: any[] = currentParent?.children ?? [];
|
children?: unknown[];
|
||||||
|
};
|
||||||
|
const children = (currentParent?.children ?? []) as unknown[];
|
||||||
const inserted = children.length > 0 ? children[children.length - 1] : null;
|
const inserted = children.length > 0 ? children[children.length - 1] : null;
|
||||||
if (!inserted) return;
|
if (!inserted) return;
|
||||||
renderer?.clearActiveNodeList?.();
|
renderer?.clearActiveNodeList?.();
|
||||||
@@ -1182,18 +1229,23 @@ const MindmapBlockView = ({
|
|||||||
const handler = (node: MindMapNode, e?: Event) => {
|
const handler = (node: MindMapNode, e?: Event) => {
|
||||||
e?.stopPropagation?.();
|
e?.stopPropagation?.();
|
||||||
e?.preventDefault?.();
|
e?.preventDefault?.();
|
||||||
const src =
|
const srcRaw =
|
||||||
node?.nodeData?.data?.image ||
|
node?.nodeData?.data?.image ||
|
||||||
node?.getData?.("image") ||
|
node?.getData?.("image") ||
|
||||||
node?.data?.image;
|
node?.data?.image;
|
||||||
|
const src = typeof srcRaw === "string" ? srcRaw : "";
|
||||||
if (src) {
|
if (src) {
|
||||||
setViewerSrc(src);
|
setViewerSrc(src);
|
||||||
const size = node?.getData?.("imageSize") || {};
|
const sizeRaw = node?.getData?.("imageSize");
|
||||||
|
const size =
|
||||||
|
sizeRaw && typeof sizeRaw === "object"
|
||||||
|
? (sizeRaw as { width?: unknown; height?: unknown })
|
||||||
|
: ({} as { width?: unknown; height?: unknown });
|
||||||
const title = node?.getData?.("imageTitle") || "";
|
const title = node?.getData?.("imageTitle") || "";
|
||||||
setViewerMeta({
|
setViewerMeta({
|
||||||
title: typeof title === "string" ? title : "",
|
title: typeof title === "string" ? title : "",
|
||||||
width: Number(size?.width) || undefined,
|
width: Number(size.width) || undefined,
|
||||||
height: Number(size?.height) || undefined,
|
height: Number(size.height) || undefined,
|
||||||
});
|
});
|
||||||
setShowImageViewer(true);
|
setShowImageViewer(true);
|
||||||
}
|
}
|
||||||
@@ -1377,10 +1429,10 @@ const MindmapBlockView = ({
|
|||||||
if (ext === "xmind") {
|
if (ext === "xmind") {
|
||||||
const xmindParser = await import("simple-mind-map/src/parse/xmind.js");
|
const xmindParser = await import("simple-mind-map/src/parse/xmind.js");
|
||||||
const blob = new Blob([await file.arrayBuffer()]);
|
const blob = new Blob([await file.arrayBuffer()]);
|
||||||
const data = await xmindParser.default.parseXmindFile(blob, (content: unknown[]) => {
|
const data = await xmindParser.default.parseXmindFile(blob, (content) => {
|
||||||
const list = Array.isArray(content) ? content : [];
|
const list = content;
|
||||||
if (list.length > 1) {
|
if (list.length > 1) {
|
||||||
window.alert("检测到 XMind 多画布,自动导入第一个画布。");
|
window.alert("检测到 XMind 多画布,自动导入第一个画布。");
|
||||||
}
|
}
|
||||||
return list.length > 0 ? list[0] : content;
|
return list.length > 0 ? list[0] : content;
|
||||||
});
|
});
|
||||||
@@ -1773,10 +1825,9 @@ const MindmapBlockView = ({
|
|||||||
activeSidebar={activeSidebar}
|
activeSidebar={activeSidebar}
|
||||||
onSelect={setActiveSidebar}
|
onSelect={setActiveSidebar}
|
||||||
/>
|
/>
|
||||||
{/* @ts-expect-error mindmap type */}
|
|
||||||
<MindmapSidebar
|
<MindmapSidebar
|
||||||
mindmap={mindmap}
|
mindmap={mindmap}
|
||||||
activeNodes={activeNodes} // @ts-expect-error type
|
activeNodes={activeNodes}
|
||||||
activeTab={activeSidebar}
|
activeTab={activeSidebar}
|
||||||
onClose={() => setActiveSidebar(null)}
|
onClose={() => setActiveSidebar(null)}
|
||||||
/>
|
/>
|
||||||
@@ -1795,7 +1846,6 @@ const MindmapBlockView = ({
|
|||||||
miniMapOpen={showMiniMap}
|
miniMapOpen={showMiniMap}
|
||||||
/>
|
/>
|
||||||
<MindmapCount mindmap={mindmap} />
|
<MindmapCount mindmap={mindmap} />
|
||||||
{/* @ts-expect-error mindmap type */}
|
|
||||||
<MindmapMiniMap mindmap={mindmap} show={showMiniMap} />
|
<MindmapMiniMap mindmap={mindmap} show={showMiniMap} />
|
||||||
{imgToolbar}
|
{imgToolbar}
|
||||||
{noteModal}
|
{noteModal}
|
||||||
@@ -1854,14 +1904,13 @@ const MindmapBlockView = ({
|
|||||||
contentEditable={false}
|
contentEditable={false}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MindmapSidebarTrigger
|
<MindmapSidebarTrigger
|
||||||
activeSidebar={activeSidebar}
|
activeSidebar={activeSidebar}
|
||||||
onSelect={setActiveSidebar}
|
onSelect={setActiveSidebar}
|
||||||
/>
|
/>
|
||||||
{/* @ts-expect-error mindmap type */}
|
<MindmapSidebar
|
||||||
<MindmapSidebar
|
mindmap={mindmap}
|
||||||
mindmap={mindmap}
|
activeNodes={activeNodes}
|
||||||
activeNodes={activeNodes} // @ts-expect-error type
|
|
||||||
activeTab={activeSidebar}
|
activeTab={activeSidebar}
|
||||||
onClose={() => setActiveSidebar(null)}
|
onClose={() => setActiveSidebar(null)}
|
||||||
/>
|
/>
|
||||||
@@ -1880,7 +1929,6 @@ const MindmapBlockView = ({
|
|||||||
miniMapOpen={showMiniMap}
|
miniMapOpen={showMiniMap}
|
||||||
/>
|
/>
|
||||||
<MindmapCount mindmap={mindmap} />
|
<MindmapCount mindmap={mindmap} />
|
||||||
{/* @ts-expect-error mindmap type */}
|
|
||||||
<MindmapMiniMap mindmap={mindmap} show={showMiniMap} />
|
<MindmapMiniMap mindmap={mindmap} show={showMiniMap} />
|
||||||
{imgToolbar}
|
{imgToolbar}
|
||||||
{noteModal}
|
{noteModal}
|
||||||
@@ -1893,6 +1941,8 @@ const MindmapBlockView = ({
|
|||||||
|
|
||||||
export { MindmapBlockView };
|
export { MindmapBlockView };
|
||||||
|
|
||||||
|
type MindmapBlockViewProps = Parameters<typeof MindmapBlockView>[0];
|
||||||
|
|
||||||
export const mindmapBlock = createReactBlockSpec(
|
export const mindmapBlock = createReactBlockSpec(
|
||||||
{
|
{
|
||||||
type: "mindmap",
|
type: "mindmap",
|
||||||
@@ -1901,8 +1951,10 @@ export const mindmapBlock = createReactBlockSpec(
|
|||||||
data: { default: defaultMindmapData },
|
data: { default: defaultMindmapData },
|
||||||
},
|
},
|
||||||
content: "none",
|
content: "none",
|
||||||
},
|
} as unknown as BlockConfig<"mindmap", PropSchema, "none">,
|
||||||
{
|
{
|
||||||
render: (props) => <MindmapBlockView {...props} />,
|
render: (props) => (
|
||||||
|
<MindmapBlockView {...(props as unknown as MindmapBlockViewProps)} />
|
||||||
|
),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user