link rects with lines
This commit is contained in:
parent
65a33e1fc3
commit
7dd588378b
@ -1,10 +1,12 @@
|
|||||||
import React, { useRef, useState } from "react";
|
import React, { useRef, useState } from "react";
|
||||||
import { useDrop } from "react-dnd";
|
import { useDrop } from "react-dnd";
|
||||||
import Rect from "./rect";
|
import Rect from "./rect";
|
||||||
|
import Node from "./node";
|
||||||
|
|
||||||
export default function Canvas(props) {
|
export default function Canvas(props) {
|
||||||
const [dragging, setDragging] = useState(false);
|
const [dragging, setDragging] = useState(false);
|
||||||
const [offset, setOffset] = useState({ x: 0, y: 0 });
|
const [offset, setOffset] = useState({ x: 0, y: 0 });
|
||||||
|
const [links, setLinks] = useState([]);
|
||||||
|
|
||||||
const canvas = useRef(null);
|
const canvas = useRef(null);
|
||||||
|
|
||||||
@ -23,11 +25,52 @@ export default function Canvas(props) {
|
|||||||
const { clientX, clientY } = event;
|
const { clientX, clientY } = event;
|
||||||
const updatedRectangles = props.rectangles.map((rect) => {
|
const updatedRectangles = props.rectangles.map((rect) => {
|
||||||
if (rect.id === dragging) {
|
if (rect.id === dragging) {
|
||||||
return {
|
const updatedRect = {
|
||||||
...rect,
|
...rect,
|
||||||
x: clientX - offset.x,
|
x: clientX - offset.x,
|
||||||
y: clientY - offset.y,
|
y: clientY - offset.y,
|
||||||
};
|
};
|
||||||
|
const updatedLinks = links.map((link) => {
|
||||||
|
let updatedLink = link;
|
||||||
|
if (link.rect === updatedRect.id) {
|
||||||
|
switch (link.node) {
|
||||||
|
case Node.TOP:
|
||||||
|
updatedLink = {
|
||||||
|
...link,
|
||||||
|
x: updatedRect.x + updatedRect.width / 2,
|
||||||
|
y: updatedRect.y,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case Node.BOTTOM:
|
||||||
|
updatedLink = {
|
||||||
|
...link,
|
||||||
|
x: updatedRect.x + updatedRect.width / 2,
|
||||||
|
y: updatedRect.y + updatedRect.height,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case Node.LEFT:
|
||||||
|
updatedLink = {
|
||||||
|
...link,
|
||||||
|
x: updatedRect.x,
|
||||||
|
y: updatedRect.y + updatedRect.height / 2,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case Node.RIGHT:
|
||||||
|
updatedLink = {
|
||||||
|
...link,
|
||||||
|
x: updatedRect.x + updatedRect.width,
|
||||||
|
y: updatedRect.y + updatedRect.height / 2,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return updatedLink;
|
||||||
|
});
|
||||||
|
|
||||||
|
setLinks(updatedLinks);
|
||||||
|
return updatedRect;
|
||||||
}
|
}
|
||||||
return rect;
|
return rect;
|
||||||
});
|
});
|
||||||
@ -84,9 +127,27 @@ export default function Canvas(props) {
|
|||||||
label={rectangle.label}
|
label={rectangle.label}
|
||||||
width={rectangle.width}
|
width={rectangle.width}
|
||||||
height={rectangle.height}
|
height={rectangle.height}
|
||||||
|
links={links}
|
||||||
|
setLinks={setLinks}
|
||||||
onMouseDown={(event) => handleMouseDown(event, rectangle.id)}
|
onMouseDown={(event) => handleMouseDown(event, rectangle.id)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
{links.map(
|
||||||
|
(link, index) =>
|
||||||
|
links.length >= 2 &&
|
||||||
|
index % 2 === 0 &&
|
||||||
|
index + 1 < links.length && (
|
||||||
|
<line
|
||||||
|
key={index}
|
||||||
|
x1={links[index].x}
|
||||||
|
y1={links[index].y}
|
||||||
|
x2={links[index + 1].x}
|
||||||
|
y2={links[index + 1].y}
|
||||||
|
stroke="red"
|
||||||
|
strokeDasharray="5,5"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
9
src/components/node.js
Normal file
9
src/components/node.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
const Node = {
|
||||||
|
NONE: "NONE",
|
||||||
|
TOP: "TOP",
|
||||||
|
LEFT: "LEFT",
|
||||||
|
RIGHT: "RIGHT",
|
||||||
|
BOTTOM: "BOTTOM",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Node;
|
@ -1,7 +1,11 @@
|
|||||||
import React from "react";
|
import { React, useState } from "react";
|
||||||
|
import Node from "./node";
|
||||||
import { Button } from "@arco-design/web-react";
|
import { Button } from "@arco-design/web-react";
|
||||||
|
|
||||||
const Rect = (props) => {
|
const Rect = (props) => {
|
||||||
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
|
const [node, setNode] = useState(Node.NONE);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<g>
|
<g>
|
||||||
<foreignObject
|
<foreignObject
|
||||||
@ -10,10 +14,19 @@ const Rect = (props) => {
|
|||||||
y={props.y}
|
y={props.y}
|
||||||
width={props.width}
|
width={props.width}
|
||||||
height={props.height}
|
height={props.height}
|
||||||
style={{ fill: "blue", cursor: "move" }}
|
style={{ cursor: "move" }}
|
||||||
onMouseDown={props.onMouseDown}
|
onMouseDown={props.onMouseDown}
|
||||||
|
onMouseEnter={() => {
|
||||||
|
setIsHovered(true);
|
||||||
|
}}
|
||||||
|
onMouseLeave={() => {
|
||||||
|
setIsHovered(false);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div xmlns="http://www.w3.org/1999/xhtml" className="bg-blue p-3">
|
<div
|
||||||
|
xmlns="http://www.w3.org/1999/xhtml"
|
||||||
|
className={`${isHovered ? "bg-red-500" : "bg-blue"} p-3`}
|
||||||
|
>
|
||||||
<div className="text-white">{props.label}</div>
|
<div className="text-white">{props.label}</div>
|
||||||
<form onSubmit={(e) => e.preventDefault()}>
|
<form onSubmit={(e) => e.preventDefault()}>
|
||||||
<input type="text" className="w-full" />
|
<input type="text" className="w-full" />
|
||||||
@ -23,6 +36,82 @@ const Rect = (props) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</foreignObject>
|
</foreignObject>
|
||||||
|
<circle
|
||||||
|
id="TOP"
|
||||||
|
cx={props.x + props.width / 2}
|
||||||
|
cy={props.y}
|
||||||
|
r={5}
|
||||||
|
onClick={(e) => {
|
||||||
|
setNode(Node.TOP);
|
||||||
|
props.setLinks([
|
||||||
|
...props.links,
|
||||||
|
{
|
||||||
|
rect: props.id,
|
||||||
|
node: Node.TOP,
|
||||||
|
x: props.x + props.width / 2,
|
||||||
|
y: props.y,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}}
|
||||||
|
style={{ fill: node === Node.TOP ? "green" : "black" }}
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
id="LEFT"
|
||||||
|
cx={props.x}
|
||||||
|
cy={props.y + props.height / 2}
|
||||||
|
r={5}
|
||||||
|
onClick={(e) => {
|
||||||
|
setNode(Node.LEFT);
|
||||||
|
props.setLinks([
|
||||||
|
...props.links,
|
||||||
|
{
|
||||||
|
rect: `${props.id}`,
|
||||||
|
node: Node.LEFT,
|
||||||
|
x: props.x,
|
||||||
|
y: props.y + props.height / 2,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}}
|
||||||
|
style={{ fill: node === Node.LEFT ? "green" : "black" }}
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
id="RIGHT"
|
||||||
|
cx={props.x + props.width}
|
||||||
|
cy={props.y + props.height / 2}
|
||||||
|
r={5}
|
||||||
|
onClick={(e) => {
|
||||||
|
setNode(Node.RIGHT);
|
||||||
|
props.setLinks([
|
||||||
|
...props.links,
|
||||||
|
{
|
||||||
|
rect: props.id,
|
||||||
|
node: Node.RIGHT,
|
||||||
|
x: props.x + props.width,
|
||||||
|
y: props.y + props.height / 2,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}}
|
||||||
|
style={{ fill: node === Node.RIGHT ? "green" : "black" }}
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
id="BOTTOM"
|
||||||
|
cx={props.x + props.width / 2}
|
||||||
|
cy={props.y + props.height}
|
||||||
|
r={5}
|
||||||
|
onClick={(e) => {
|
||||||
|
setNode(Node.BOTTOM);
|
||||||
|
props.setLinks([
|
||||||
|
...props.links,
|
||||||
|
{
|
||||||
|
rect: props.id,
|
||||||
|
node: Node.BOTTOM,
|
||||||
|
x: props.x + props.width / 2,
|
||||||
|
y: props.y + props.height,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}}
|
||||||
|
style={{ fill: node === Node.BOTTOM ? "green" : "black" }}
|
||||||
|
/>
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user