2024-06-27 14:43:06 +03:00
|
|
|
'use client';
|
|
|
|
|
2024-07-24 18:11:28 +03:00
|
|
|
import { toSvg } from 'html-to-image';
|
2024-07-23 23:03:58 +03:00
|
|
|
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
2024-07-24 18:11:28 +03:00
|
|
|
import { toast } from 'react-toastify';
|
2024-07-20 18:26:32 +03:00
|
|
|
import {
|
2024-07-24 18:11:28 +03:00
|
|
|
Background,
|
|
|
|
getNodesBounds,
|
|
|
|
getViewportForBounds,
|
2024-07-23 23:03:58 +03:00
|
|
|
Node,
|
2024-07-20 18:26:32 +03:00
|
|
|
NodeChange,
|
|
|
|
NodeTypes,
|
|
|
|
ReactFlow,
|
|
|
|
useEdgesState,
|
|
|
|
useNodesState,
|
2024-07-23 23:03:58 +03:00
|
|
|
useOnSelectionChange,
|
|
|
|
useReactFlow
|
2024-07-20 18:26:32 +03:00
|
|
|
} from 'reactflow';
|
2024-06-27 14:43:06 +03:00
|
|
|
|
2024-07-26 17:30:37 +03:00
|
|
|
import { CProps } from '@/components/props';
|
2024-07-20 18:26:32 +03:00
|
|
|
import Overlay from '@/components/ui/Overlay';
|
|
|
|
import AnimateFade from '@/components/wrap/AnimateFade';
|
2024-06-27 14:43:06 +03:00
|
|
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
|
|
|
import { useOSS } from '@/context/OssContext';
|
2024-07-26 21:08:31 +03:00
|
|
|
import useLocalStorage from '@/hooks/useLocalStorage';
|
2024-07-26 17:30:37 +03:00
|
|
|
import { OssNode } from '@/models/miscellaneous';
|
2024-08-02 11:17:27 +03:00
|
|
|
import { OperationID, OperationType } from '@/models/oss';
|
2024-07-26 21:08:31 +03:00
|
|
|
import { PARAMETER, storage } from '@/utils/constants';
|
2024-07-24 18:11:28 +03:00
|
|
|
import { errors } from '@/utils/labels';
|
2024-06-27 14:43:06 +03:00
|
|
|
|
2024-07-20 18:26:32 +03:00
|
|
|
import { useOssEdit } from '../OssEditContext';
|
2024-06-27 14:43:06 +03:00
|
|
|
import InputNode from './InputNode';
|
2024-07-26 17:30:37 +03:00
|
|
|
import NodeContextMenu, { ContextMenuData } from './NodeContextMenu';
|
2024-06-27 14:43:06 +03:00
|
|
|
import OperationNode from './OperationNode';
|
2024-07-20 18:26:32 +03:00
|
|
|
import ToolbarOssGraph from './ToolbarOssGraph';
|
2024-06-27 14:43:06 +03:00
|
|
|
|
2024-07-23 23:03:58 +03:00
|
|
|
interface OssFlowProps {
|
|
|
|
isModified: boolean;
|
|
|
|
setIsModified: React.Dispatch<React.SetStateAction<boolean>>;
|
|
|
|
}
|
|
|
|
|
2024-07-26 21:08:31 +03:00
|
|
|
function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
2024-07-24 18:11:28 +03:00
|
|
|
const { calculateHeight, colors } = useConceptOptions();
|
2024-06-27 14:43:06 +03:00
|
|
|
const model = useOSS();
|
2024-07-20 18:26:32 +03:00
|
|
|
const controller = useOssEdit();
|
2024-07-23 23:03:58 +03:00
|
|
|
const flow = useReactFlow();
|
2024-06-27 14:43:06 +03:00
|
|
|
|
2024-07-26 21:08:31 +03:00
|
|
|
const [showGrid, setShowGrid] = useLocalStorage<boolean>(storage.ossShowGrid, false);
|
|
|
|
const [edgeAnimate, setEdgeAnimate] = useLocalStorage<boolean>(storage.ossEdgeAnimate, false);
|
|
|
|
const [edgeStraight, setEdgeStraight] = useLocalStorage<boolean>(storage.ossEdgeStraight, false);
|
|
|
|
|
2024-07-21 22:50:43 +03:00
|
|
|
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
|
|
|
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
2024-07-23 23:03:58 +03:00
|
|
|
const [toggleReset, setToggleReset] = useState(false);
|
2024-07-26 17:30:37 +03:00
|
|
|
const [menuProps, setMenuProps] = useState<ContextMenuData | undefined>(undefined);
|
2024-07-23 23:03:58 +03:00
|
|
|
|
|
|
|
const onSelectionChange = useCallback(
|
|
|
|
({ nodes }: { nodes: Node[] }) => {
|
|
|
|
controller.setSelected(nodes.map(node => Number(node.id)));
|
|
|
|
},
|
|
|
|
[controller]
|
|
|
|
);
|
|
|
|
|
|
|
|
useOnSelectionChange({
|
|
|
|
onChange: onSelectionChange
|
|
|
|
});
|
2024-07-21 22:50:43 +03:00
|
|
|
|
|
|
|
useLayoutEffect(() => {
|
|
|
|
if (!model.schema) {
|
|
|
|
setNodes([]);
|
|
|
|
setEdges([]);
|
2024-07-23 23:03:58 +03:00
|
|
|
} else {
|
|
|
|
setNodes(
|
|
|
|
model.schema.items.map(operation => ({
|
|
|
|
id: String(operation.id),
|
2024-07-26 00:33:22 +03:00
|
|
|
data: { label: operation.alias, operation: operation },
|
2024-07-23 23:03:58 +03:00
|
|
|
position: { x: operation.position_x, y: operation.position_y },
|
|
|
|
type: operation.operation_type.toString()
|
|
|
|
}))
|
|
|
|
);
|
|
|
|
setEdges(
|
|
|
|
model.schema.arguments.map((argument, index) => ({
|
|
|
|
id: String(index),
|
|
|
|
source: String(argument.argument),
|
|
|
|
target: String(argument.operation),
|
2024-07-27 22:49:20 +03:00
|
|
|
type: edgeStraight ? 'straight' : 'simplebezier',
|
2024-07-26 21:08:31 +03:00
|
|
|
animated: edgeAnimate,
|
2024-07-23 23:03:58 +03:00
|
|
|
targetHandle:
|
|
|
|
model.schema!.operationByID.get(argument.argument)!.position_x >
|
|
|
|
model.schema!.operationByID.get(argument.operation)!.position_x
|
|
|
|
? 'right'
|
|
|
|
: 'left'
|
|
|
|
}))
|
|
|
|
);
|
2024-07-21 22:50:43 +03:00
|
|
|
}
|
2024-07-23 23:03:58 +03:00
|
|
|
setTimeout(() => {
|
|
|
|
setIsModified(false);
|
|
|
|
}, PARAMETER.graphRefreshDelay);
|
2024-07-26 21:08:31 +03:00
|
|
|
}, [model.schema, setNodes, setEdges, setIsModified, toggleReset, edgeStraight, edgeAnimate]);
|
2024-06-27 14:43:06 +03:00
|
|
|
|
2024-07-21 15:17:36 +03:00
|
|
|
const getPositions = useCallback(
|
|
|
|
() =>
|
|
|
|
nodes.map(node => ({
|
|
|
|
id: Number(node.id),
|
|
|
|
position_x: node.position.x,
|
|
|
|
position_y: node.position.y
|
|
|
|
})),
|
|
|
|
[nodes]
|
|
|
|
);
|
|
|
|
|
2024-07-20 18:26:32 +03:00
|
|
|
const handleNodesChange = useCallback(
|
|
|
|
(changes: NodeChange[]) => {
|
2024-07-23 23:03:58 +03:00
|
|
|
if (changes.some(change => change.type === 'position' && change.position)) {
|
|
|
|
setIsModified(true);
|
|
|
|
}
|
2024-07-20 18:26:32 +03:00
|
|
|
onNodesChange(changes);
|
|
|
|
},
|
2024-07-23 23:03:58 +03:00
|
|
|
[onNodesChange, setIsModified]
|
2024-07-20 18:26:32 +03:00
|
|
|
);
|
|
|
|
|
2024-07-23 23:03:58 +03:00
|
|
|
const handleSavePositions = useCallback(() => {
|
|
|
|
controller.savePositions(getPositions(), () => setIsModified(false));
|
|
|
|
}, [controller, getPositions, setIsModified]);
|
2024-07-20 18:26:32 +03:00
|
|
|
|
2024-08-01 20:10:58 +03:00
|
|
|
const handleCreateOperation = useCallback(
|
|
|
|
(inputs: OperationID[]) => () => {
|
|
|
|
if (!controller.schema) {
|
|
|
|
return;
|
|
|
|
}
|
2024-08-02 11:17:27 +03:00
|
|
|
|
2024-08-01 20:10:58 +03:00
|
|
|
let target = { x: 0, y: 0 };
|
|
|
|
const positions = getPositions();
|
2024-08-02 11:17:27 +03:00
|
|
|
if (positions.length == 0) {
|
2024-08-01 20:10:58 +03:00
|
|
|
target = flow.project({ x: window.innerWidth / 2, y: window.innerHeight / 2 });
|
2024-08-14 23:36:52 +03:00
|
|
|
} else if (inputs.length <= 1) {
|
2024-08-02 11:17:27 +03:00
|
|
|
let inputsNodes = positions.filter(pos =>
|
|
|
|
controller.schema!.items.find(
|
|
|
|
operation => operation.operation_type === OperationType.INPUT && operation.id === pos.id
|
|
|
|
)
|
|
|
|
);
|
|
|
|
if (inputsNodes.length > 0) {
|
|
|
|
inputsNodes = positions;
|
|
|
|
}
|
|
|
|
const maxX = Math.max(...inputsNodes.map(node => node.position_x));
|
|
|
|
const minY = Math.min(...inputsNodes.map(node => node.position_y));
|
|
|
|
target.x = maxX + 180;
|
|
|
|
target.y = minY;
|
2024-08-01 20:10:58 +03:00
|
|
|
} else {
|
|
|
|
const inputsNodes = positions.filter(pos => inputs.includes(pos.id));
|
|
|
|
const maxY = Math.max(...inputsNodes.map(node => node.position_y));
|
|
|
|
const minX = Math.min(...inputsNodes.map(node => node.position_x));
|
|
|
|
const maxX = Math.max(...inputsNodes.map(node => node.position_x));
|
|
|
|
target.x = Math.ceil((maxX + minX) / 2 / PARAMETER.ossGridSize) * PARAMETER.ossGridSize;
|
2024-08-02 11:17:27 +03:00
|
|
|
target.y = maxY + 100;
|
2024-08-01 20:10:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
let flagIntersect = false;
|
|
|
|
do {
|
|
|
|
flagIntersect = positions.some(
|
|
|
|
position =>
|
|
|
|
Math.abs(position.position_x - target.x) < PARAMETER.ossMinDistance &&
|
|
|
|
Math.abs(position.position_y - target.y) < PARAMETER.ossMinDistance
|
|
|
|
);
|
|
|
|
if (flagIntersect) {
|
|
|
|
target.x += PARAMETER.ossMinDistance;
|
|
|
|
target.y += PARAMETER.ossMinDistance;
|
|
|
|
}
|
|
|
|
} while (flagIntersect);
|
2024-08-14 23:36:52 +03:00
|
|
|
|
2024-08-02 11:17:27 +03:00
|
|
|
controller.promptCreateOperation({
|
|
|
|
x: target.x,
|
|
|
|
y: target.y,
|
|
|
|
inputs: inputs,
|
|
|
|
positions: positions,
|
|
|
|
callback: () => flow.fitView({ duration: PARAMETER.zoomDuration })
|
|
|
|
});
|
2024-08-01 20:10:58 +03:00
|
|
|
},
|
|
|
|
[controller, getPositions, flow]
|
|
|
|
);
|
2024-07-23 23:03:58 +03:00
|
|
|
|
2024-07-26 17:30:37 +03:00
|
|
|
const handleDeleteSelected = useCallback(() => {
|
2024-07-23 23:03:58 +03:00
|
|
|
if (controller.selected.length !== 1) {
|
|
|
|
return;
|
|
|
|
}
|
2024-08-15 23:23:10 +03:00
|
|
|
handleDeleteOperation(controller.selected[0]);
|
2024-07-23 23:03:58 +03:00
|
|
|
}, [controller, getPositions]);
|
|
|
|
|
2024-07-26 17:30:37 +03:00
|
|
|
const handleDeleteOperation = useCallback(
|
|
|
|
(target: OperationID) => {
|
2024-08-15 23:23:10 +03:00
|
|
|
if (!controller.canDelete(target)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
controller.promptDeleteOperation(target, getPositions());
|
2024-07-26 17:30:37 +03:00
|
|
|
},
|
|
|
|
[controller, getPositions]
|
|
|
|
);
|
|
|
|
|
2024-07-28 00:37:33 +03:00
|
|
|
const handleCreateInput = useCallback(
|
|
|
|
(target: OperationID) => {
|
|
|
|
controller.createInput(target, getPositions());
|
|
|
|
},
|
|
|
|
[controller, getPositions]
|
|
|
|
);
|
|
|
|
|
2024-07-28 21:29:46 +03:00
|
|
|
const handleEditSchema = useCallback(
|
|
|
|
(target: OperationID) => {
|
|
|
|
controller.promptEditInput(target, getPositions());
|
|
|
|
},
|
|
|
|
[controller, getPositions]
|
|
|
|
);
|
|
|
|
|
2024-07-29 16:55:48 +03:00
|
|
|
const handleEditOperation = useCallback(
|
|
|
|
(target: OperationID) => {
|
|
|
|
controller.promptEditOperation(target, getPositions());
|
|
|
|
},
|
|
|
|
[controller, getPositions]
|
|
|
|
);
|
|
|
|
|
2024-07-29 23:14:45 +03:00
|
|
|
const handleExecuteOperation = useCallback(
|
2024-07-29 22:30:24 +03:00
|
|
|
(target: OperationID) => {
|
2024-07-29 23:14:45 +03:00
|
|
|
controller.executeOperation(target, getPositions());
|
2024-07-29 22:30:24 +03:00
|
|
|
},
|
|
|
|
[controller, getPositions]
|
|
|
|
);
|
|
|
|
|
2024-07-29 23:14:45 +03:00
|
|
|
const handleExecuteSelected = useCallback(() => {
|
2024-07-31 18:09:08 +03:00
|
|
|
if (controller.selected.length !== 1) {
|
|
|
|
return;
|
2024-07-29 23:14:45 +03:00
|
|
|
}
|
2024-07-31 18:09:08 +03:00
|
|
|
handleExecuteOperation(controller.selected[0]);
|
|
|
|
}, [controller, handleExecuteOperation]);
|
2024-07-29 23:14:45 +03:00
|
|
|
|
2024-07-24 18:11:28 +03:00
|
|
|
const handleFitView = useCallback(() => {
|
|
|
|
flow.fitView({ duration: PARAMETER.zoomDuration });
|
|
|
|
}, [flow]);
|
|
|
|
|
|
|
|
const handleResetPositions = useCallback(() => {
|
|
|
|
setToggleReset(prev => !prev);
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
const handleSaveImage = useCallback(() => {
|
|
|
|
const canvas: HTMLElement | null = document.querySelector('.react-flow__viewport');
|
|
|
|
if (canvas === null) {
|
|
|
|
toast.error(errors.imageFailed);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const imageWidth = PARAMETER.ossImageWidth;
|
|
|
|
const imageHeight = PARAMETER.ossImageHeight;
|
|
|
|
const nodesBounds = getNodesBounds(nodes);
|
|
|
|
const viewport = getViewportForBounds(nodesBounds, imageWidth, imageHeight, 0.5, 2);
|
|
|
|
toSvg(canvas, {
|
|
|
|
backgroundColor: colors.bgDefault,
|
|
|
|
width: imageWidth,
|
|
|
|
height: imageHeight,
|
|
|
|
style: {
|
|
|
|
width: String(imageWidth),
|
|
|
|
height: String(imageHeight),
|
|
|
|
transform: `translate(${viewport.x}px, ${viewport.y}px) scale(${viewport.zoom})`
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.then(dataURL => {
|
|
|
|
const a = document.createElement('a');
|
|
|
|
a.setAttribute('download', 'reactflow.svg');
|
|
|
|
a.setAttribute('href', dataURL);
|
|
|
|
a.click();
|
|
|
|
})
|
|
|
|
.catch(error => {
|
|
|
|
console.error(error);
|
|
|
|
toast.error(errors.imageFailed);
|
|
|
|
});
|
|
|
|
}, [colors, nodes]);
|
|
|
|
|
2024-07-26 00:33:22 +03:00
|
|
|
const handleContextMenu = useCallback(
|
2024-07-26 17:30:37 +03:00
|
|
|
(event: CProps.EventMouse, node: OssNode) => {
|
2024-07-26 00:33:22 +03:00
|
|
|
event.preventDefault();
|
|
|
|
event.stopPropagation();
|
2024-07-26 17:30:37 +03:00
|
|
|
|
|
|
|
setMenuProps({
|
|
|
|
operation: node.data.operation,
|
|
|
|
cursorX: event.clientX,
|
|
|
|
cursorY: event.clientY
|
|
|
|
});
|
|
|
|
controller.setShowTooltip(false);
|
2024-07-26 00:33:22 +03:00
|
|
|
},
|
|
|
|
[controller]
|
|
|
|
);
|
|
|
|
|
2024-07-26 17:30:37 +03:00
|
|
|
const handleContextMenuHide = useCallback(() => {
|
|
|
|
controller.setShowTooltip(true);
|
|
|
|
setMenuProps(undefined);
|
|
|
|
}, [controller]);
|
|
|
|
|
|
|
|
const handleClickCanvas = useCallback(() => {
|
|
|
|
handleContextMenuHide();
|
|
|
|
}, [handleContextMenuHide]);
|
|
|
|
|
2024-08-01 20:10:58 +03:00
|
|
|
const handleNodeDoubleClick = useCallback(
|
2024-07-29 22:30:24 +03:00
|
|
|
(event: CProps.EventMouse, node: OssNode) => {
|
2024-08-01 20:10:58 +03:00
|
|
|
event.preventDefault();
|
|
|
|
event.stopPropagation();
|
|
|
|
handleEditOperation(Number(node.id));
|
2024-07-29 22:30:24 +03:00
|
|
|
},
|
|
|
|
[handleEditOperation]
|
|
|
|
);
|
|
|
|
|
2024-07-23 23:03:58 +03:00
|
|
|
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
|
|
|
|
if (controller.isProcessing) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!controller.isMutable) {
|
|
|
|
return;
|
|
|
|
}
|
2024-07-24 18:11:28 +03:00
|
|
|
if ((event.ctrlKey || event.metaKey) && event.key === 's') {
|
|
|
|
event.preventDefault();
|
|
|
|
event.stopPropagation();
|
|
|
|
handleSavePositions();
|
|
|
|
return;
|
|
|
|
}
|
2024-07-30 15:59:37 +03:00
|
|
|
if ((event.ctrlKey || event.metaKey) && event.key === 'q') {
|
|
|
|
event.preventDefault();
|
|
|
|
event.stopPropagation();
|
2024-08-01 20:10:58 +03:00
|
|
|
handleCreateOperation(controller.selected);
|
2024-07-30 15:59:37 +03:00
|
|
|
return;
|
|
|
|
}
|
2024-07-23 23:03:58 +03:00
|
|
|
if (event.key === 'Delete') {
|
|
|
|
event.preventDefault();
|
|
|
|
event.stopPropagation();
|
2024-07-26 17:30:37 +03:00
|
|
|
handleDeleteSelected();
|
2024-07-23 23:03:58 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2024-06-27 14:43:06 +03:00
|
|
|
|
2024-07-20 18:26:32 +03:00
|
|
|
const canvasWidth = useMemo(() => 'calc(100vw - 1rem)', []);
|
2024-06-27 14:43:06 +03:00
|
|
|
const canvasHeight = useMemo(() => calculateHeight('1.75rem + 4px'), [calculateHeight]);
|
|
|
|
|
2024-07-20 18:26:32 +03:00
|
|
|
const OssNodeTypes: NodeTypes = useMemo(
|
|
|
|
() => ({
|
|
|
|
synthesis: OperationNode,
|
|
|
|
input: InputNode
|
|
|
|
}),
|
|
|
|
[]
|
|
|
|
);
|
|
|
|
|
2024-07-21 15:17:36 +03:00
|
|
|
const graph = useMemo(
|
|
|
|
() => (
|
|
|
|
<ReactFlow
|
|
|
|
nodes={nodes}
|
|
|
|
edges={edges}
|
|
|
|
onNodesChange={handleNodesChange}
|
2024-07-23 23:03:58 +03:00
|
|
|
onEdgesChange={onEdgesChange}
|
2024-08-01 20:10:58 +03:00
|
|
|
onNodeDoubleClick={handleNodeDoubleClick}
|
2024-07-26 21:08:31 +03:00
|
|
|
proOptions={{ hideAttribution: true }}
|
2024-07-29 23:14:45 +03:00
|
|
|
fitView
|
2024-07-21 15:17:36 +03:00
|
|
|
nodeTypes={OssNodeTypes}
|
2024-07-21 22:50:43 +03:00
|
|
|
maxZoom={2}
|
|
|
|
minZoom={0.75}
|
2024-07-23 23:03:58 +03:00
|
|
|
nodesConnectable={false}
|
2024-07-24 18:11:28 +03:00
|
|
|
snapToGrid={true}
|
2024-08-01 20:10:58 +03:00
|
|
|
snapGrid={[PARAMETER.ossGridSize, PARAMETER.ossGridSize]}
|
2024-07-26 17:30:37 +03:00
|
|
|
onNodeContextMenu={handleContextMenu}
|
|
|
|
onClick={handleClickCanvas}
|
2024-07-24 18:11:28 +03:00
|
|
|
>
|
2024-08-01 20:10:58 +03:00
|
|
|
{showGrid ? <Background gap={PARAMETER.ossGridSize} /> : null}
|
2024-07-24 18:11:28 +03:00
|
|
|
</ReactFlow>
|
2024-07-21 15:17:36 +03:00
|
|
|
),
|
2024-07-29 23:14:45 +03:00
|
|
|
[
|
|
|
|
nodes,
|
|
|
|
edges,
|
|
|
|
handleNodesChange,
|
|
|
|
handleContextMenu,
|
|
|
|
handleClickCanvas,
|
|
|
|
onEdgesChange,
|
2024-08-01 20:10:58 +03:00
|
|
|
handleNodeDoubleClick,
|
2024-07-29 23:14:45 +03:00
|
|
|
OssNodeTypes,
|
|
|
|
showGrid
|
|
|
|
]
|
2024-07-21 15:17:36 +03:00
|
|
|
);
|
|
|
|
|
2024-06-27 14:43:06 +03:00
|
|
|
return (
|
2024-07-23 23:03:58 +03:00
|
|
|
<AnimateFade tabIndex={-1} onKeyDown={handleKeyDown}>
|
2024-07-20 18:26:32 +03:00
|
|
|
<Overlay position='top-0 pt-1 right-1/2 translate-x-1/2' className='rounded-b-2xl cc-blur'>
|
2024-07-23 23:03:58 +03:00
|
|
|
<ToolbarOssGraph
|
|
|
|
isModified={isModified}
|
2024-07-24 18:11:28 +03:00
|
|
|
showGrid={showGrid}
|
2024-07-26 21:08:31 +03:00
|
|
|
edgeAnimate={edgeAnimate}
|
|
|
|
edgeStraight={edgeStraight}
|
2024-07-24 18:11:28 +03:00
|
|
|
onFitView={handleFitView}
|
2024-08-01 20:10:58 +03:00
|
|
|
onCreate={handleCreateOperation(controller.selected)}
|
2024-07-26 17:30:37 +03:00
|
|
|
onDelete={handleDeleteSelected}
|
2024-07-29 23:14:45 +03:00
|
|
|
onEdit={() => handleEditOperation(controller.selected[0])}
|
|
|
|
onExecute={handleExecuteSelected}
|
2024-07-24 18:11:28 +03:00
|
|
|
onResetPositions={handleResetPositions}
|
2024-07-23 23:03:58 +03:00
|
|
|
onSavePositions={handleSavePositions}
|
2024-07-24 18:11:28 +03:00
|
|
|
onSaveImage={handleSaveImage}
|
|
|
|
toggleShowGrid={() => setShowGrid(prev => !prev)}
|
2024-07-26 21:08:31 +03:00
|
|
|
toggleEdgeAnimate={() => setEdgeAnimate(prev => !prev)}
|
|
|
|
toggleEdgeStraight={() => setEdgeStraight(prev => !prev)}
|
2024-07-23 23:03:58 +03:00
|
|
|
/>
|
2024-07-20 18:26:32 +03:00
|
|
|
</Overlay>
|
2024-07-26 17:30:37 +03:00
|
|
|
{menuProps ? (
|
2024-07-28 00:37:33 +03:00
|
|
|
<NodeContextMenu
|
|
|
|
onHide={handleContextMenuHide}
|
|
|
|
onDelete={handleDeleteOperation}
|
|
|
|
onCreateInput={handleCreateInput}
|
2024-07-28 21:29:46 +03:00
|
|
|
onEditSchema={handleEditSchema}
|
2024-07-29 16:55:48 +03:00
|
|
|
onEditOperation={handleEditOperation}
|
2024-07-29 23:14:45 +03:00
|
|
|
onExecuteOperation={handleExecuteOperation}
|
2024-07-28 00:37:33 +03:00
|
|
|
{...menuProps}
|
|
|
|
/>
|
2024-07-26 17:30:37 +03:00
|
|
|
) : null}
|
2024-07-20 18:26:32 +03:00
|
|
|
<div className='relative' style={{ height: canvasHeight, width: canvasWidth }}>
|
2024-07-21 15:17:36 +03:00
|
|
|
{graph}
|
2024-07-20 18:26:32 +03:00
|
|
|
</div>
|
|
|
|
</AnimateFade>
|
2024-06-27 14:43:06 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
export default OssFlow;
|