2023-09-19 20:47:06 +08:00
|
|
|
import React, { useRef, useState } from "react";
|
2023-09-19 20:46:58 +08:00
|
|
|
import { useDrop } from "react-dnd";
|
2023-09-19 20:47:06 +08:00
|
|
|
import Rect from "./rect";
|
2023-09-19 20:47:11 +08:00
|
|
|
import Node from "./node";
|
2023-09-19 20:47:06 +08:00
|
|
|
|
|
|
|
export default function Canvas(props) {
|
|
|
|
const [dragging, setDragging] = useState(false);
|
|
|
|
const [offset, setOffset] = useState({ x: 0, y: 0 });
|
2023-09-19 20:47:11 +08:00
|
|
|
const [links, setLinks] = useState([]);
|
2023-09-19 20:46:43 +08:00
|
|
|
|
|
|
|
const canvas = useRef(null);
|
|
|
|
|
2023-09-19 20:47:06 +08:00
|
|
|
const handleMouseDown = (event, id) => {
|
|
|
|
const { clientX, clientY } = event;
|
|
|
|
const rectangle = props.rectangles.find((rect) => rect.id === id);
|
|
|
|
setOffset({
|
|
|
|
x: clientX - rectangle.x,
|
|
|
|
y: clientY - rectangle.y,
|
|
|
|
});
|
|
|
|
setDragging(id);
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleMouseMove = (event) => {
|
|
|
|
if (dragging === false) return;
|
|
|
|
const { clientX, clientY } = event;
|
|
|
|
const updatedRectangles = props.rectangles.map((rect) => {
|
|
|
|
if (rect.id === dragging) {
|
2023-09-19 20:47:11 +08:00
|
|
|
const updatedRect = {
|
2023-09-19 20:47:06 +08:00
|
|
|
...rect,
|
|
|
|
x: clientX - offset.x,
|
|
|
|
y: clientY - offset.y,
|
|
|
|
};
|
2023-09-19 20:47:11 +08:00
|
|
|
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;
|
2023-09-19 20:46:58 +08:00
|
|
|
}
|
2023-09-19 20:47:06 +08:00
|
|
|
return rect;
|
|
|
|
});
|
|
|
|
props.setRectangles(updatedRectangles);
|
|
|
|
};
|
2023-09-19 20:46:43 +08:00
|
|
|
|
2023-09-19 20:47:06 +08:00
|
|
|
const handleMouseUp = () => {
|
|
|
|
setDragging(false);
|
|
|
|
};
|
|
|
|
|
|
|
|
const [, drop] = useDrop(
|
|
|
|
() => ({
|
|
|
|
accept: "CARD",
|
|
|
|
drop: (item, monitor) => {
|
|
|
|
const offset = monitor.getClientOffset();
|
|
|
|
const canvasRect = canvas.current.getBoundingClientRect();
|
|
|
|
const x = offset.x - canvasRect.left - 100 * 0.5;
|
|
|
|
const y = offset.y - canvasRect.top - 100 * 0.5;
|
|
|
|
const newRectangle = {
|
|
|
|
id: props.rectangles.length + 1,
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
width: 100,
|
|
|
|
height: 100,
|
|
|
|
label: `rect ${props.rectangles.length + 1}`,
|
|
|
|
};
|
|
|
|
props.setRectangles([...props.rectangles, newRectangle]);
|
2023-09-19 20:47:10 +08:00
|
|
|
props.setCode((prev) =>
|
|
|
|
prev === ""
|
|
|
|
? `CREATE TABLE \`${newRectangle.label}\`;`
|
|
|
|
: `${prev}\n\nCREATE TABLE \`${newRectangle.label}\`;`
|
|
|
|
);
|
2023-09-19 20:46:43 +08:00
|
|
|
},
|
2023-09-19 20:47:06 +08:00
|
|
|
collect: (monitor) => ({
|
|
|
|
isOver: !!monitor.isOver(),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
[props.rectangles]
|
|
|
|
);
|
2023-09-19 20:46:43 +08:00
|
|
|
|
2023-09-19 20:46:58 +08:00
|
|
|
return (
|
2023-09-19 20:47:06 +08:00
|
|
|
<div ref={drop} className="flex-grow" id="canvas">
|
|
|
|
<div ref={canvas} className="w-full h-screen">
|
|
|
|
<svg
|
|
|
|
onMouseMove={handleMouseMove}
|
|
|
|
onMouseUp={handleMouseUp}
|
|
|
|
style={{ width: "100%", height: "100%" }}
|
|
|
|
>
|
|
|
|
{props.rectangles.map((rectangle) => (
|
|
|
|
<Rect
|
|
|
|
key={rectangle.id}
|
|
|
|
x={rectangle.x}
|
|
|
|
y={rectangle.y}
|
|
|
|
label={rectangle.label}
|
|
|
|
width={rectangle.width}
|
|
|
|
height={rectangle.height}
|
2023-09-19 20:47:11 +08:00
|
|
|
links={links}
|
|
|
|
setLinks={setLinks}
|
2023-09-19 20:47:06 +08:00
|
|
|
onMouseDown={(event) => handleMouseDown(event, rectangle.id)}
|
|
|
|
/>
|
|
|
|
))}
|
2023-09-19 20:47:11 +08:00
|
|
|
{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"
|
|
|
|
/>
|
|
|
|
)
|
|
|
|
)}
|
2023-09-19 20:47:06 +08:00
|
|
|
</svg>
|
|
|
|
</div>
|
2023-09-19 20:46:58 +08:00
|
|
|
</div>
|
|
|
|
);
|
2023-09-19 20:46:43 +08:00
|
|
|
}
|