feat: added ReactFlow-based node graph #11
@@ -15,7 +15,7 @@ import {
|
|||||||
addEdge,
|
addEdge,
|
||||||
MarkerType,
|
MarkerType,
|
||||||
type Edge,
|
type Edge,
|
||||||
type Connection,
|
type Connection, Panel,
|
||||||
} from '@xyflow/react';
|
} from '@xyflow/react';
|
||||||
import '@xyflow/react/dist/style.css';
|
import '@xyflow/react/dist/style.css';
|
||||||
import {
|
import {
|
||||||
@@ -27,6 +27,9 @@ import {
|
|||||||
|
|
||||||
import { Sidebar } from './components/DragDropSidebar.tsx';
|
import { Sidebar } from './components/DragDropSidebar.tsx';
|
||||||
|
|
||||||
|
// --| config starting params for flow |--
|
||||||
|
|
||||||
|
|
||||||
const nodeTypes = {
|
const nodeTypes = {
|
||||||
start: StartNode,
|
start: StartNode,
|
||||||
end: EndNode,
|
end: EndNode,
|
||||||
@@ -34,6 +37,8 @@ const nodeTypes = {
|
|||||||
norm: NormNode
|
norm: NormNode
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const initialNodes = [
|
const initialNodes = [
|
||||||
{
|
{
|
||||||
id: 'start',
|
id: 'start',
|
||||||
@@ -54,6 +59,7 @@ const initialNodes = [
|
|||||||
data: {label: 'End'}
|
data: {label: 'End'}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const initialEdges = [{id: 'start-end', source: 'start', target: 'end'}];
|
const initialEdges = [{id: 'start-end', source: 'start', target: 'end'}];
|
||||||
|
|
||||||
const defaultEdgeOptions = {
|
const defaultEdgeOptions = {
|
||||||
@@ -64,30 +70,36 @@ const defaultEdgeOptions = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// --| define ReactFlow editor |--
|
||||||
|
|
||||||
const VisProgUI = ()=> {
|
const VisProgUI = ()=> {
|
||||||
const edgeReconnectSuccessful = useRef(true);
|
const edgeReconnectSuccessful = useRef(true);
|
||||||
const [nodes, , onNodesChange] = useNodesState(initialNodes);
|
const [nodes, , onNodesChange] = useNodesState(initialNodes);
|
||||||
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||||
|
|
||||||
|
// handles connection of newly created edges
|
||||||
const onConnect = useCallback(
|
const onConnect = useCallback(
|
||||||
(params: Edge | Connection) => setEdges((els) => addEdge(params, els)),
|
(params: Edge | Connection) => setEdges((els) => addEdge(params, els)),
|
||||||
[setEdges],
|
[setEdges],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// Handles initiation of reconnection of edges that are manually disconnected from a node
|
||||||
const onReconnectStart = useCallback(() => {
|
const onReconnectStart = useCallback(() => {
|
||||||
edgeReconnectSuccessful.current = false;
|
edgeReconnectSuccessful.current = false;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// handles attempted reconnections of a previously disconnected edge
|
||||||
const onReconnect = useCallback((oldEdge: Edge, newConnection: Connection) => {
|
const onReconnect = useCallback((oldEdge: Edge, newConnection: Connection) => {
|
||||||
edgeReconnectSuccessful.current = true;
|
edgeReconnectSuccessful.current = true;
|
||||||
setEdges((els) => reconnectEdge(oldEdge, newConnection, els));
|
setEdges((els) => reconnectEdge(oldEdge, newConnection, els));
|
||||||
}, [setEdges]);
|
}, [setEdges]);
|
||||||
|
|
||||||
|
// Drops the edge from the set of edges, removing it from the flow, if no successful reconnection occurred
|
||||||
const onReconnectEnd = useCallback((_: unknown, edge: { id: string; }) => {
|
const onReconnectEnd = useCallback((_: unknown, edge: { id: string; }) => {
|
||||||
if (!edgeReconnectSuccessful.current) {
|
if (!edgeReconnectSuccessful.current) {
|
||||||
setEdges((eds) => eds.filter((e) => e.id !== edge.id));
|
setEdges((eds) => eds.filter((e) => e.id !== edge.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
edgeReconnectSuccessful.current = true;
|
edgeReconnectSuccessful.current = true;
|
||||||
}, [setEdges]);
|
}, [setEdges]);
|
||||||
|
|
||||||
@@ -109,18 +121,25 @@ const VisProgUI = ()=> {
|
|||||||
fitView
|
fitView
|
||||||
proOptions={{hideAttribution: true }}
|
proOptions={{hideAttribution: true }}
|
||||||
>
|
>
|
||||||
|
<Panel position="center-right" style={{marginInlineStart: 'auto', marginInlineEnd:'0.5em',backgroundColor: 'canvas' ,marginBottom: 'auto', marginTop:'auto', width: '20%'}}>
|
||||||
|
<Sidebar />
|
||||||
|
</Panel>
|
||||||
<Controls />
|
<Controls />
|
||||||
<Background />
|
<Background />
|
||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
</div>
|
</div>
|
||||||
<div style={{width: '20%', height: '100%'}}>
|
|
||||||
<Sidebar />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* --| Places the VisProgUI component inside a ReactFlowProvider
|
||||||
|
*
|
||||||
|
* Wrapping the editor component inside a ReactFlowProvider
|
||||||
|
* allows us to access and interact with the components inside the editor,
|
||||||
|
* thus facilitating the addition of node specific functions inside their node definitions
|
||||||
|
*/
|
||||||
function VisualProgrammingUI(){
|
function VisualProgrammingUI(){
|
||||||
return (
|
return (
|
||||||
<ReactFlowProvider>
|
<ReactFlowProvider>
|
||||||
|
|||||||
Reference in New Issue
Block a user