Portal/rsconcept/frontend/src/pages/OssPage/EditorOssGraph/OssFlow.tsx

188 lines
5.4 KiB
TypeScript
Raw Normal View History

2024-06-27 14:43:06 +03:00
'use client';
2024-07-23 23:03:58 +03:00
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
2024-07-20 18:26:32 +03:00
import {
2024-07-23 23:03:58 +03:00
Node,
2024-07-20 18:26:32 +03:00
NodeChange,
NodeTypes,
ProOptions,
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-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-23 23:03:58 +03:00
import { PARAMETER } from '@/utils/constants';
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';
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>>;
}
function OssFlow({ isModified, setIsModified }: OssFlowProps) {
2024-06-27 14:43:06 +03:00
const { calculateHeight } = useConceptOptions();
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-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);
const onSelectionChange = useCallback(
({ nodes }: { nodes: Node[] }) => {
controller.setSelected(nodes.map(node => Number(node.id)));
console.log(nodes);
},
[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),
data: { label: operation.alias },
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),
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);
}, [model.schema, setNodes, setEdges, setIsModified, toggleReset]);
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
const handleCreateOperation = useCallback(() => {
2024-07-23 23:03:58 +03:00
const center = flow.project({ x: window.innerWidth / 2, y: window.innerHeight / 2 });
console.log(center);
controller.promptCreateOperation(center.x, center.y, getPositions());
}, [controller, getPositions, flow]);
const handleDeleteOperation = useCallback(() => {
if (controller.selected.length !== 1) {
return;
}
controller.deleteOperation(controller.selected[0], getPositions());
}, [controller, getPositions]);
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
// Hotkeys implementation
if (controller.isProcessing) {
return;
}
if (!controller.isMutable) {
return;
}
if (event.key === 'Delete') {
event.preventDefault();
event.stopPropagation();
handleDeleteOperation();
return;
}
}
2024-06-27 14:43:06 +03:00
2024-07-21 15:17:36 +03:00
const proOptions: ProOptions = useMemo(() => ({ hideAttribution: true }), []);
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-07-21 15:17:36 +03:00
fitView
proOptions={proOptions}
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-21 15:17:36 +03:00
/>
),
2024-07-23 23:03:58 +03:00
[nodes, edges, proOptions, handleNodesChange, onEdgesChange, OssNodeTypes]
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}
onFitView={() => flow.fitView({ duration: PARAMETER.zoomDuration })}
onCreate={handleCreateOperation}
onDelete={handleDeleteOperation}
onResetPositions={() => setToggleReset(prev => !prev)}
onSavePositions={handleSavePositions}
/>
2024-07-20 18:26:32 +03:00
</Overlay>
<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;