import { PlusOutlined } from "@ant-design/icons"; import { Edge, EventArgs, Graph, Node } from "@antv/x6"; import React, { useEffect, useRef } from "react"; import { Dropdown, Popover } from "antd"; import NodeMenu from "../NodeMenu"; export default function Port(props: { hovered: boolean; out?: boolean; style?: React.CSSProperties; node?: Node; graph?: Graph; type?: "in" | "out" | "extra"; }) { const { hovered, style = {}, out = true, node, graph, type } = props; const [canAdd, setCanAdd] = React.useState(false); const [open, setOpen] = React.useState(false); const isDown = useRef(false); const isMove = useRef(false); const newEdge = React.useRef(); const extraStyle = React.useMemo(() => { if (out && canAdd) { return { transform: "scale(2) translateY(-50%)", }; } if (!out) { return { width: 8, height: 16, borderRadius: 0, border: "none", left: -5, }; } else { return {}; } }, [canAdd]); const handleSetCanAdd = (value: boolean) => { out && setCanAdd(value); graph?.togglePanning(!value); }; // step1: 鼠标按下拖拽开始 const handleMouseDown = (e: React.MouseEvent) => { isDown.current = true; node?.setData({ lock: true }); }; useEffect(() => { // step2: 移动鼠标添加连线 // 检测移动 添加边 设置边目标位置 graph?.on("node:mousemove", (args) => { if (isDown.current) { // 按下还没移动时创建边 if (!isMove.current && node) { isMove.current = true; const ports = node.getPorts(); const rightPort = ports.find((item) => type ? item.group === 'bottom' : item.group === "right"); newEdge.current = graph?.addEdge({ source: { cell: node.id, port: rightPort?.id }, router: { name: "manhattan", args: { padding: 20, excludeShapes: ["notice-node"], }, }, connector: { name: "rounded" }, target: { x: args.x, y: args.y, }, attrs: { line: { stroke: "#37d0ff", strokeWidth: 2, }, }, }); } else { // 判断是否进入其他节点内 // 查找到最顶层的节点 然后修改位置为节点 const nodes = graph.getNodesInArea(args.x, args.y, 5, 5); const targetNode = nodes.length ? (nodes || [])?.reduce((prev, curr) => { const prevZ = prev.zIndex || -1; const currZ = curr?.zIndex || -1; return prevZ >= currZ ? prev : curr; }) : null; // 进入节点 不存在已连接边 则添加一条边 if (targetNode && targetNode.shape !== "notice-node") { const ports = targetNode.getPorts(); const targetPort = ports.find((item) => item.group === "left"); newEdge.current?.setTarget({ cell: targetNode.id, port: targetPort?.id, }); } else { // 否则修改边的终点 newEdge.current?.setTarget({ x: args.x, y: args.y, }); } } } }); // step3: 鼠标抬起,展示节点菜单或者连线连接到目标节点 graph?.on("node:mouseup", (args) => { console.log("node:mouseup", newEdge.current); node?.setData({ lock: false }); isDown.current = false; isMove.current = false; // 拖拽过程中释放鼠标 检测当前是否有节点 if (newEdge.current) { // 判断是否连接到了节点 if (!Object.hasOwn(newEdge.current.target, "x")) { newEdge.current.setAttrs({ line: { stroke: "#7e8186", }, }); newEdge.current.setZIndex(0); newEdge.current = undefined; return; } // 判断是否存在menu-popover const els = document.querySelectorAll("[data-shape='menu-popover']"); if (els.length) { return; } graph.addNode({ shape: "menu-popover", x: args.x, y: args.y, }); } }); // 添加节点完成设置连线目标节点 graph?.on("node:change:addedNode", (args: EventArgs["cell:change:*"]) => { const { current } = args; if (newEdge.current && current) { const addNode = current?.addNode as Node; const ports = addNode.getPorts(); const leftPort = ports?.find((item) => item.group === "left"); const bottomPort = ports?.find((item) => item.group === "bottom"); newEdge.current.setTarget({ cell: addNode.id, port: type ? bottomPort?.id : leftPort?.id, }); newEdge.current.setAttrs({ line: { stroke: "#7e8186", }, }); newEdge.current = undefined; } }); // 节点菜单menu-popver关闭, 如果连线目标没有节点信息移除连线 graph?.on("node:change:closedPopover", () => { setTimeout(() => { if (Object.hasOwn(newEdge.current?.target || {}, "x")) { graph?.removeCells([newEdge.current!]); newEdge.current = undefined; } }, 300); }); }, []); const { x, y } = node?.position() || { x: 0, y: 0 }; const x1 = (node?.getBBox()?.width || 0) + x + 50; // 点击添加节点成功后,设置连线 const handleAddChange = (addNode?: Node) => { setOpen(false); if (addNode && node) { const sourcePorts = node?.getPorts(); const sourcePort = sourcePorts?.find((item) => type ? item.group === 'bottom' : item.group === "right"); const targetPorts = addNode?.getPorts(); const targetPort = targetPorts?.find((item) => item.group === "left"); graph?.addEdge({ source: { cell: node.id, port: sourcePort?.id, }, target: { cell: addNode.id, port: targetPort?.id, }, router: { name: "manhattan", args: { padding: 20, excludeShapes: ["notice-node"], }, }, zIndex: 0, connector: { name: "rounded", args: {} }, attrs: { line: { stroke: "#7e8186", strokeWidth: 2, }, }, }); } }; return ( } trigger={"click"} placement="right" arrow={false} open={open} onOpenChange={(open) => { setOpen(open); }} > {type === "extra" ? (
handleSetCanAdd(true)} onMouseLeave={() => handleSetCanAdd(false)} onMouseDown={handleMouseDown} > {out && ( )}
) : (
handleSetCanAdd(true)} onMouseLeave={() => handleSetCanAdd(false)} onMouseDown={handleMouseDown} > {out && ( )}
)}
); }