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);