mirror of https://github.com/ctk-hq/ctk
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
5.4 KiB
TypeScript
211 lines
5.4 KiB
TypeScript
import { FC, useState, useEffect } from "react";
|
|
import { Dictionary, values } from "lodash";
|
|
import { v4 as uuidv4 } from "uuid";
|
|
import eventBus from "../../events/eventBus";
|
|
import {
|
|
IGraphData,
|
|
CallbackFunction,
|
|
IServiceNodeItem,
|
|
IVolumeNodeItem
|
|
} from "../../types";
|
|
import { useJsPlumb } from "../useJsPlumb";
|
|
import ServiceNode from "./ServiceNode";
|
|
import VolumeNode from "./VolumeNode";
|
|
|
|
const CANVAS_ID: string = "canvas-container-" + uuidv4();
|
|
|
|
interface ICanvasProps {
|
|
nodes: Dictionary<IServiceNodeItem | IVolumeNodeItem>;
|
|
connections: any;
|
|
canvasPosition: any;
|
|
onNodeUpdate: CallbackFunction;
|
|
onGraphUpdate: CallbackFunction;
|
|
onCanvasUpdate: CallbackFunction;
|
|
onConnectionAttached: CallbackFunction;
|
|
onConnectionDetached: CallbackFunction;
|
|
setServiceToEdit: CallbackFunction;
|
|
setServiceToDelete: CallbackFunction;
|
|
setVolumeToEdit: CallbackFunction | null;
|
|
setVolumeToDelete: CallbackFunction | null;
|
|
}
|
|
|
|
export const Canvas: FC<ICanvasProps> = (props) => {
|
|
const {
|
|
nodes,
|
|
connections,
|
|
canvasPosition,
|
|
onNodeUpdate,
|
|
onGraphUpdate,
|
|
onCanvasUpdate,
|
|
onConnectionAttached,
|
|
onConnectionDetached,
|
|
setServiceToEdit,
|
|
setServiceToDelete,
|
|
setVolumeToEdit,
|
|
setVolumeToDelete
|
|
} = props;
|
|
const [dragging, setDragging] = useState(false);
|
|
const [scale, setScale] = useState(1);
|
|
const [_scale, _setScale] = useState(1);
|
|
const [_left, _setLeft] = useState(0);
|
|
const [_top, _setTop] = useState(0);
|
|
const [_initX, _setInitX] = useState(0);
|
|
const [_initY, _setInitY] = useState(0);
|
|
|
|
const translateWidth =
|
|
(document.documentElement.clientWidth * (1 - _scale)) / 2;
|
|
const translateHeight =
|
|
((document.documentElement.clientHeight - 64) * (1 - _scale)) / 2;
|
|
|
|
const [containerCallbackRef, setZoom, setStyle, removeEndpoint] = useJsPlumb(
|
|
nodes,
|
|
connections,
|
|
(graphData: IGraphData) => onGraphUpdate(graphData),
|
|
(positionData: any) => onNodeUpdate(positionData),
|
|
(connectionData: any) => onConnectionAttached(connectionData),
|
|
(connectionData: any) => onConnectionDetached(connectionData)
|
|
);
|
|
|
|
const onCanvasMousewheel = (e: any) => {
|
|
if (e.deltaY < 0) {
|
|
_setScale(_scale + _scale * 0.25);
|
|
setScale(_scale + _scale * 0.25);
|
|
}
|
|
|
|
if (e.deltaY > 0) {
|
|
_setScale(_scale - _scale * 0.25);
|
|
setScale(_scale - _scale * 0.25);
|
|
}
|
|
};
|
|
|
|
const onCanvasMouseMove = (e: any) => {
|
|
if (!dragging) {
|
|
return;
|
|
}
|
|
|
|
if (e.pageX && e.pageY) {
|
|
const styles = {
|
|
left: _left + e.pageX - _initX + "px",
|
|
top: _top + e.pageY - _initY + "px"
|
|
};
|
|
setStyle(styles);
|
|
}
|
|
};
|
|
|
|
const onCanvasMouseUpLeave = (e: any) => {
|
|
if (dragging) {
|
|
if (e.pageX && e.pageY) {
|
|
const left = _left + e.pageX - _initX;
|
|
const top = _top + e.pageY - _initY;
|
|
|
|
_setLeft(left);
|
|
_setTop(top);
|
|
setDragging(false);
|
|
onCanvasUpdate({
|
|
left: left,
|
|
top: top
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
const onCanvasMouseDown = (e: any) => {
|
|
if (e.pageX && e.pageY) {
|
|
_setInitX(e.pageX);
|
|
_setInitY(e.pageY);
|
|
setDragging(true);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
setZoom(_scale);
|
|
}, [_scale]);
|
|
|
|
useEffect(() => {
|
|
onCanvasUpdate({
|
|
scale: scale
|
|
});
|
|
}, [scale]);
|
|
|
|
useEffect(() => {
|
|
const styles = {
|
|
left: _left + "px",
|
|
top: _top + "px"
|
|
};
|
|
|
|
setStyle(styles);
|
|
}, [_left, _top, setStyle]);
|
|
|
|
useEffect(() => {
|
|
_setTop(canvasPosition.top);
|
|
_setLeft(canvasPosition.left);
|
|
_setScale(canvasPosition.scale);
|
|
}, [canvasPosition]);
|
|
|
|
useEffect(() => {
|
|
eventBus.on("NODE_DELETED", (data: any) => {
|
|
removeEndpoint(data.detail.message.node);
|
|
});
|
|
|
|
return () => {
|
|
eventBus.remove("NODE_DELETED", () => undefined);
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<>
|
|
{nodes && (
|
|
<div
|
|
key={CANVAS_ID}
|
|
className="jsplumb-box"
|
|
onWheel={onCanvasMousewheel}
|
|
onMouseMove={onCanvasMouseMove}
|
|
onMouseDown={onCanvasMouseDown}
|
|
onMouseUp={onCanvasMouseUpLeave}
|
|
onMouseLeave={onCanvasMouseUpLeave}
|
|
onContextMenu={(event) => {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
}}
|
|
>
|
|
<div
|
|
id={CANVAS_ID}
|
|
ref={containerCallbackRef}
|
|
className="canvas"
|
|
style={{
|
|
transformOrigin: "0px 0px 0px",
|
|
transform: `translate(${translateWidth}px, ${translateHeight}px) scale(${_scale})`
|
|
}}
|
|
>
|
|
{values(nodes).map((x) => {
|
|
if (x.type === "SERVICE") {
|
|
x = x as IServiceNodeItem;
|
|
return (
|
|
<ServiceNode
|
|
key={x.key}
|
|
node={x}
|
|
setServiceToEdit={setServiceToEdit}
|
|
setServiceToDelete={setServiceToDelete}
|
|
/>
|
|
);
|
|
}
|
|
|
|
if (x.type === "VOLUME" && setVolumeToEdit && setVolumeToDelete) {
|
|
x = x as IVolumeNodeItem;
|
|
return (
|
|
<VolumeNode
|
|
key={x.key}
|
|
node={x}
|
|
setVolumeToEdit={setVolumeToEdit}
|
|
setVolumeToDelete={setVolumeToDelete}
|
|
/>
|
|
);
|
|
}
|
|
})}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
};
|