|
@@ -8,10 +8,11 @@ export default function Port(props: {
|
|
|
hovered: boolean;
|
|
|
out?: boolean;
|
|
|
style?: React.CSSProperties;
|
|
|
- node?: Node,
|
|
|
- graph?: Graph
|
|
|
+ node?: Node;
|
|
|
+ graph?: Graph;
|
|
|
+ type?: "in" | "out" | "extra";
|
|
|
}) {
|
|
|
- const { hovered, style = {}, out = true, node, graph } = props;
|
|
|
+ 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);
|
|
@@ -21,102 +22,153 @@ export default function Port(props: {
|
|
|
const extraStyle = React.useMemo(() => {
|
|
|
if (out && canAdd) {
|
|
|
return {
|
|
|
- transform: "scale(2) translateY(-50%)"
|
|
|
+ transform: "scale(2) translateY(-50%)",
|
|
|
};
|
|
|
}
|
|
|
- return {};
|
|
|
+ if (!out) {
|
|
|
+ return {
|
|
|
+ width: 8,
|
|
|
+ height: 16,
|
|
|
+ borderRadius: 0,
|
|
|
+ border: "none",
|
|
|
+ left: -5,
|
|
|
+ };
|
|
|
+ } else {
|
|
|
+ return {};
|
|
|
+ }
|
|
|
}, [canAdd]);
|
|
|
|
|
|
const handleSetCanAdd = (value: boolean) => {
|
|
|
out && setCanAdd(value);
|
|
|
- node?.setData({ lock: !!value });
|
|
|
graph?.togglePanning(!value);
|
|
|
};
|
|
|
|
|
|
+ // step1: 鼠标按下拖拽开始
|
|
|
const handleMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
|
isDown.current = true;
|
|
|
+ node?.setData({ lock: true });
|
|
|
};
|
|
|
|
|
|
- const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
useEffect(() => {
|
|
|
- graph?.on("node:mouseup", (args) => {
|
|
|
- console.log("node:mouseup");
|
|
|
- isDown.current = false;
|
|
|
- isMove.current = false;
|
|
|
- // 拖拽过程中释放鼠标 检测当前是否有节点
|
|
|
- if(newEdge.current) {
|
|
|
- // 检测当前位置是否有元素
|
|
|
- const nodes = graph.getNodesInArea(args.x, args.y, 5, 5);
|
|
|
- if(!nodes.length) {
|
|
|
- console.log('展示添加菜单')
|
|
|
- graph.addNode({
|
|
|
- shape: 'menu-popover',
|
|
|
- x: args.x,
|
|
|
- y: args.y,
|
|
|
- })
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
+ // step2: 移动鼠标添加连线
|
|
|
// 检测移动 添加边 设置边目标位置
|
|
|
graph?.on("node:mousemove", (args) => {
|
|
|
- if(isDown.current) {
|
|
|
+ if (isDown.current) {
|
|
|
// 按下还没移动时创建边
|
|
|
- if(!isMove.current && node) {
|
|
|
+ if (!isMove.current && node) {
|
|
|
isMove.current = true;
|
|
|
const ports = node.getPorts();
|
|
|
- const rightPort = ports.find(item => item.group === 'right');
|
|
|
+ const rightPort = ports.find((item) => type ? item.group === 'bottom' : item.group === "right");
|
|
|
newEdge.current = graph?.addEdge({
|
|
|
- source: { cell: node.id, port: rightPort?.id},
|
|
|
- connector: { name: 'smooth' },
|
|
|
+ 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
|
|
|
+ y: args.y,
|
|
|
},
|
|
|
attrs: {
|
|
|
line: {
|
|
|
stroke: "#37d0ff",
|
|
|
strokeWidth: 2,
|
|
|
},
|
|
|
- }
|
|
|
+ },
|
|
|
});
|
|
|
} else {
|
|
|
- // 否则修改边的终点
|
|
|
- newEdge.current?.setTarget({
|
|
|
- x: args.x,
|
|
|
- y: args.y
|
|
|
+ // 判断是否进入其他节点内
|
|
|
+ // 查找到最顶层的节点 然后修改位置为节点
|
|
|
+ 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");
|
|
|
+ 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) {
|
|
|
+ if (newEdge.current && current) {
|
|
|
const addNode = current?.addNode as Node;
|
|
|
const ports = addNode.getPorts();
|
|
|
- const leftPort = ports?.find(item => item.group === 'left');
|
|
|
+ const leftPort = ports?.find((item) => item.group === "left");
|
|
|
+ const bottomPort = ports?.find((item) => item.group === "bottom");
|
|
|
newEdge.current.setTarget({
|
|
|
cell: addNode.id,
|
|
|
- port: leftPort?.id
|
|
|
+ port: type ? bottomPort?.id : leftPort?.id,
|
|
|
});
|
|
|
newEdge.current.setAttrs({
|
|
|
line: {
|
|
|
- stroke: '#1b5cdf',
|
|
|
+ stroke: "#7e8186",
|
|
|
},
|
|
|
});
|
|
|
newEdge.current = undefined;
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- // 连续添加popver关闭
|
|
|
+ // 节点菜单menu-popver关闭, 如果连线目标没有节点信息移除连线
|
|
|
graph?.on("node:change:closedPopover", () => {
|
|
|
setTimeout(() => {
|
|
|
- if(Object.hasOwn(newEdge.current?.target || {}, 'x')) {
|
|
|
+ if (Object.hasOwn(newEdge.current?.target || {}, "x")) {
|
|
|
graph?.removeCells([newEdge.current!]);
|
|
|
newEdge.current = undefined;
|
|
|
}
|
|
@@ -124,31 +176,39 @@ export default function Port(props: {
|
|
|
});
|
|
|
}, [graph]);
|
|
|
|
|
|
- const {x, y} = node?.position() || {x: 0, y: 0};
|
|
|
+ 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) {
|
|
|
-
|
|
|
+ if (addNode && node) {
|
|
|
const sourcePorts = node?.getPorts();
|
|
|
- const sourcePort = sourcePorts?.find(item => item.group === 'right');
|
|
|
+ const sourcePort = sourcePorts?.find((item) => type ? item.group === 'bottom' : item.group === "right");
|
|
|
const targetPorts = addNode?.getPorts();
|
|
|
- const targetPort = targetPorts?.find(item => item.group === 'left');
|
|
|
+ const targetPort = targetPorts?.find((item) => item.group === "left");
|
|
|
|
|
|
graph?.addEdge({
|
|
|
source: {
|
|
|
cell: node.id,
|
|
|
- port: sourcePort?.id
|
|
|
+ port: sourcePort?.id,
|
|
|
},
|
|
|
target: {
|
|
|
cell: addNode.id,
|
|
|
- port: targetPort?.id
|
|
|
+ port: targetPort?.id,
|
|
|
+ },
|
|
|
+ router: {
|
|
|
+ name: "manhattan",
|
|
|
+ args: {
|
|
|
+ padding: 20,
|
|
|
+ excludeShapes: ["notice-node"],
|
|
|
+ },
|
|
|
},
|
|
|
- connector: { name: 'smooth' },
|
|
|
+ zIndex: 0,
|
|
|
+ connector: { name: "rounded", args: {} },
|
|
|
attrs: {
|
|
|
line: {
|
|
|
- stroke: '#1b5cdf',
|
|
|
+ stroke: "#7e8186",
|
|
|
strokeWidth: 2,
|
|
|
},
|
|
|
},
|
|
@@ -158,7 +218,13 @@ export default function Port(props: {
|
|
|
|
|
|
return (
|
|
|
<Popover
|
|
|
- content={<NodeMenu graph={graph} onChange={handleAddChange} position={{ x: x1, y}} />}
|
|
|
+ content={
|
|
|
+ <NodeMenu
|
|
|
+ graph={graph}
|
|
|
+ onChange={handleAddChange}
|
|
|
+ position={{ x: x1, y }}
|
|
|
+ />
|
|
|
+ }
|
|
|
trigger={"click"}
|
|
|
placement="right"
|
|
|
arrow={false}
|
|
@@ -167,24 +233,52 @@ export default function Port(props: {
|
|
|
setOpen(open);
|
|
|
}}
|
|
|
>
|
|
|
- <div
|
|
|
- className="node-port flex items-center justify-center"
|
|
|
- style={{
|
|
|
- transform: hovered ? "scale(1.2) translateY(-50%)" : "scale(1) translateY(-50%)",
|
|
|
- opacity: hovered ? 1 : 0.5,
|
|
|
- ...style,
|
|
|
- ...extraStyle
|
|
|
- }}
|
|
|
- onMouseEnter={() => handleSetCanAdd(true)}
|
|
|
- onMouseLeave={() => handleSetCanAdd(false)}
|
|
|
- onMouseDown={handleMouseDown}
|
|
|
- onMouseMove={handleMouseMove}
|
|
|
- >
|
|
|
- { out && <PlusOutlined
|
|
|
- className="transform scale-0 transition duration-300 color-#fff text-8px"
|
|
|
- style={{ transform: canAdd ? "scale(1)" : "scale(0)" }}
|
|
|
- />}
|
|
|
- </div>
|
|
|
+ {type === "extra" ? (
|
|
|
+ <div
|
|
|
+ className="node-port flex items-center justify-center"
|
|
|
+ style={{
|
|
|
+ transform: hovered
|
|
|
+ ? "scale(1.2) translateY(-50%) rotate(45deg)"
|
|
|
+ : "scale(1) translateY(-50%) rotate(45deg)",
|
|
|
+ width: 16,
|
|
|
+ height: 16,
|
|
|
+ border: 'none',
|
|
|
+ borderRadius: 0,
|
|
|
+ ...style,
|
|
|
+ }}
|
|
|
+ onMouseEnter={() => handleSetCanAdd(true)}
|
|
|
+ onMouseLeave={() => handleSetCanAdd(false)}
|
|
|
+ onMouseDown={handleMouseDown}
|
|
|
+ >
|
|
|
+ {out && (
|
|
|
+ <PlusOutlined
|
|
|
+ className="transform scale-0 transition duration-300 color-#fff text-8px"
|
|
|
+ style={{ transform: canAdd ? "scale(1)" : "scale(0)" }}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ ) : (
|
|
|
+ <div
|
|
|
+ className="node-port flex items-center justify-center"
|
|
|
+ style={{
|
|
|
+ transform: hovered
|
|
|
+ ? "scale(1.2) translateY(-50%)"
|
|
|
+ : "scale(1) translateY(-50%)",
|
|
|
+ ...style,
|
|
|
+ ...extraStyle,
|
|
|
+ }}
|
|
|
+ onMouseEnter={() => handleSetCanAdd(true)}
|
|
|
+ onMouseLeave={() => handleSetCanAdd(false)}
|
|
|
+ onMouseDown={handleMouseDown}
|
|
|
+ >
|
|
|
+ {out && (
|
|
|
+ <PlusOutlined
|
|
|
+ className="transform scale-0 transition duration-300 color-#fff text-8px"
|
|
|
+ style={{ transform: canAdd ? "scale(1)" : "scale(0)" }}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
</Popover>
|
|
|
);
|
|
|
}
|