import React from "react"; import { createRoot, Root } from "react-dom/client"; import { Dropdown } from "antd"; import { Graph, ToolsView, EdgeView } from "@antv/x6"; import type { MenuProps } from "antd"; import { menuHander } from "./hander"; export class ContextMenuTool extends ToolsView.ToolItem< EdgeView, ContextMenuToolOptions > { private timer: number | null = null; private root: Root | null = null; private toggleContextMenu(visible: boolean, e?: MouseEvent) { this.root?.unmount(); document.removeEventListener("mousedown", this.onMouseDown); if (visible && e) { const { sx, sy } = this.graph.scale(); let offsetX = e.offsetX * sx, offsetY = e.offsetY * sy; // 非页面节点需要获取当前节点位置 + 节点本身偏移位置 if (this.cell.isNode() && !this.cell.getData()?.isPage) { const { x, y } = this.cell.getPosition(); offsetX = x * sx + e.offsetX * sx; offsetY = y * sy + e.offsetY * sy; } this.root = createRoot(this.container); const items = this.options.menu?.map((item: any) => { if (!item) return item; return { ...item, onClick: () => { setTimeout(() => { item?.onClick?.call(this, this, e); }, 200); }, }; }); this.root.render( ); document.addEventListener("mousedown", this.onMouseDown); } } private onMouseDown = () => { this.timer = window.setTimeout(() => { this.toggleContextMenu(false); }, 200); }; private onContextMenu({ e }: { e: MouseEvent }) { if (this.timer) { clearTimeout(this.timer); this.timer = 0; } console.log(e, this); this.toggleContextMenu(true, e); } delegateEvents() { this.cellView.on("cell:contextmenu", this.onContextMenu, this); this.graph.on("blank:contextmenu", this.onContextMenu, this); return super.delegateEvents(); } protected onRemove() { this.cellView.off("cell:contextmenu", this.onContextMenu, this); } } ContextMenuTool.config({ tagName: "div", isSVGElement: false, }); export interface ContextMenuToolOptions extends ToolsView.ToolItem.Options { menu: MenuProps["items"]; } Graph.registerEdgeTool("contextmenu", ContextMenuTool, true); Graph.registerNodeTool("contextmenu", ContextMenuTool, true); interface MenuItem { key?: string; label?: string; type?: "divider"; icon?: string; fastKey?: string; handler?: (tool: ContextMenuTool, e: MouseEvent) => void; } // [复制、剪切、粘贴、复用、删除、设为默认样式],[置于顶层、置于底层、上移一层、下移一层],[锁定],[全选],[导出所选图形为PNG、复制所选图形为图片] const commonMenuData: MenuItem[] = [ { key: "copy", label: "复制", fastKey: "Ctrl+C", handler: menuHander.copy }, { key: "cut", label: "剪切", fastKey: "Ctrl+X", handler: menuHander.cut }, { key: "paste", label: "粘贴", fastKey: "Ctrl+V", handler: menuHander.paste }, { key: "duplicate", label: "复用", fastKey: "Ctrl+D", handler: menuHander.duplicate, }, { key: "delete", label: "删除", fastKey: "Delete/Backspace", handler: menuHander.delete, }, { key: "setDefaultStyle", label: "设为默认样式", handler: menuHander.defaultStyle, }, { type: "divider" }, { key: "top", label: "置于顶层", fastKey: "Ctrl+]", icon: "icon-zhiding1", handler: menuHander.top, }, { key: "bottom", label: "置于底层", fastKey: "Ctrl+[", icon: "icon-zhidi1", handler: menuHander.bottom, }, { key: "up", label: "上移一层", fastKey: "Ctrl+Shift+]", icon: "icon-shangyiyiceng1", handler: menuHander.up, }, { key: "down", label: "下移一层", fastKey: "Ctrl+Shift+[", icon: "icon-xiayiyiceng1", handler: menuHander.down, }, { type: "divider" }, { key: "lock", label: "锁定", fastKey: "Ctrl+L", icon: "icon-lock", handler: menuHander.lock, }, { key: "unlock", label: "解锁", fastKey: "Ctrl+Shift+L", icon: "icon-unlock", handler: menuHander.unlock, }, { type: "divider" }, { key: "selectAll", label: "全选", fastKey: "A", handler: menuHander.selectAll, }, { type: "divider" }, { key: "export", label: "导出所选图形为PNG", icon: "icon-tupian", handler: menuHander.exportImage, }, { key: "copyAsImage", label: "复制所选图形为图片", handler: menuHander.copyAsImage, }, ]; const edgeMenuData: MenuItem[] = [ ...commonMenuData.toSpliced(6, 0, { key: "resetDefaultStyle", label: "恢复默认样式", handler: menuHander.resetStyle, }), ]; const nodeMenuData: MenuItem[] = [...commonMenuData]; const lockMenuData: MenuItem[] = [ { key: "paste", label: "粘贴", fastKey: "Ctrl+V", handler: menuHander.paste }, { type: "divider" }, { key: "unlock", label: "解锁", fastKey: "Ctrl+Shift+L", icon: "icon-lock", handler: menuHander.unlock, }, { type: "divider" }, { key: "selectAll", label: "全选", fastKey: "A", handler: menuHander.selectAll, }, ]; const pageMenuData: MenuItem[] = [ { key: "paste", label: "粘贴", fastKey: "Ctrl+V", handler: menuHander.paste, }, { key: "1", type: "divider" }, { key: "zoomIn", label: "放大", fastKey: "Ctrl+(+)", icon: "icon-fangda", handler: menuHander.zoomIn, }, { key: "zoomOut", label: "缩小", fastKey: "Ctrl+(-)", icon: "icon-suoxiao", handler: menuHander.zoomOut, }, { key: "resetView", label: "重置视图缩放", handler: menuHander.resetView, }, { key: "2", type: "divider" }, { key: "selectAll", label: "全选", fastKey: "A", handler: menuHander.selectAll, }, { key: "3", type: "divider" }, { key: "createLine", label: "创建连线", fastKey: "L", handler: menuHander.createLine, }, { key: "insertImage", label: "插入图片", fastKey: "I", handler: menuHander.insertImage, }, ]; const LabelComponent = ({ item }: { item: MenuItem }) => { return (
{item.icon && } {item.label} {item.fastKey}
); }; const getMenuData = (menuData: MenuItem[]) => { return menuData.map((item) => { if (item.type === "divider") return item; return { key: item.key, label: , onClick: item.handler, }; }); }; // 节点右键菜单 export const nodeMenu = getMenuData(nodeMenuData); // 边线右键菜单 export const edgeMenu = getMenuData(edgeMenuData); // 页面右键菜单 export const pageMenu = getMenuData(pageMenuData); // 上锁节点菜单 export const lockMenu = getMenuData(lockMenuData);