feat: Added reconnectable edges
Modified edges to support being disconnected and reconnected upon dragging their connection away from the currently connected node. ref: N25B-114
This commit is contained in:
@@ -1,14 +1,17 @@
|
|||||||
import './VisProgUI.css'
|
import './VisProgUI.css'
|
||||||
|
|
||||||
import { useState, useCallback } from 'react';
|
|
||||||
import {
|
import {
|
||||||
|
useCallback,
|
||||||
|
useRef
|
||||||
|
} from 'react';
|
||||||
|
import {
|
||||||
|
Background,
|
||||||
|
Controls,
|
||||||
ReactFlow,
|
ReactFlow,
|
||||||
applyNodeChanges,
|
useNodesState,
|
||||||
applyEdgeChanges,
|
useEdgesState,
|
||||||
addEdge,
|
reconnectEdge,
|
||||||
type NodeChange,
|
addEdge, type Edge, type Connection,
|
||||||
type EdgeChange,
|
|
||||||
type Edge, type Connection
|
|
||||||
} from '@xyflow/react';
|
} from '@xyflow/react';
|
||||||
import '@xyflow/react/dist/style.css';
|
import '@xyflow/react/dist/style.css';
|
||||||
|
|
||||||
@@ -18,39 +21,55 @@ const initialNodes = [
|
|||||||
];
|
];
|
||||||
const initialEdges = [{id: 'n1-n2', source: 'n1', target: 'n2'}];
|
const initialEdges = [{id: 'n1-n2', source: 'n1', target: 'n2'}];
|
||||||
|
|
||||||
export default function App() {
|
const VisualProgrammingUI = ()=> {
|
||||||
const [nodes, setNodes] = useState(initialNodes);
|
const edgeReconnectSuccessful = useRef(true);
|
||||||
const [edges, setEdges] = useState(initialEdges);
|
const [nodes, , onNodesChange] = useNodesState(initialNodes);
|
||||||
|
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||||
|
|
||||||
const onNodesChange = useCallback(
|
|
||||||
(changes: NodeChange<{
|
|
||||||
id: string;
|
|
||||||
position: { x: number; y: number; };
|
|
||||||
data: { label: string; };
|
|
||||||
}>[]) => setNodes((nodesSnapshot) => applyNodeChanges(changes, nodesSnapshot)),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
const onEdgesChange = useCallback(
|
|
||||||
(changes: EdgeChange<{ id: string; source: string; target: string; }>[]) => setEdges((edgesSnapshot) => applyEdgeChanges(changes, edgesSnapshot)),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
const onConnect = useCallback(
|
const onConnect = useCallback(
|
||||||
(params: Edge | Connection) => setEdges((edgesSnapshot) => addEdge(params, edgesSnapshot)),
|
(params: Edge | Connection) => setEdges((els) => addEdge(params, els)),
|
||||||
[],
|
[setEdges],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onReconnectStart = useCallback(() => {
|
||||||
|
edgeReconnectSuccessful.current = false;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onReconnect = useCallback((oldEdge: Edge, newConnection: Connection) => {
|
||||||
|
edgeReconnectSuccessful.current = true;
|
||||||
|
setEdges((els) => reconnectEdge(oldEdge, newConnection, els));
|
||||||
|
}, [setEdges]);
|
||||||
|
|
||||||
|
const onReconnectEnd = useCallback((_: unknown, edge: { id: string; }) => {
|
||||||
|
if (!edgeReconnectSuccessful.current) {
|
||||||
|
setEdges((eds) => eds.filter((e) => e.id !== edge.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
edgeReconnectSuccessful.current = true;
|
||||||
|
}, [setEdges]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ width: '100vw', height: '100vh' }}>
|
<div style={{ width: '90vw', height: '90vh' }}>
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
edges={edges}
|
edges={edges}
|
||||||
onNodesChange={onNodesChange}
|
onNodesChange={onNodesChange}
|
||||||
onEdgesChange={onEdgesChange}
|
onEdgesChange={onEdgesChange}
|
||||||
|
snapToGrid
|
||||||
|
onReconnect={onReconnect}
|
||||||
|
onReconnectStart={onReconnectStart}
|
||||||
|
onReconnectEnd={onReconnectEnd}
|
||||||
onConnect={onConnect}
|
onConnect={onConnect}
|
||||||
fitView
|
fitView
|
||||||
/>
|
attributionPosition="top-right"
|
||||||
|
>
|
||||||
|
<Controls />
|
||||||
|
<Background />
|
||||||
|
</ReactFlow>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default VisualProgrammingUI
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
import { StrictMode } from 'react'
|
import { StrictMode } from 'react'
|
||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from 'react-dom/client'
|
||||||
import './index.css'
|
import './index.css'
|
||||||
import App from './VisualProgrammingUI/VisProgUI.tsx'
|
import App from './App.tsx'
|
||||||
|
import VisualProgrammingUI from "./VisualProgrammingUI/VisProgUI.tsx";
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<App />
|
<App />
|
||||||
|
<VisualProgrammingUI />
|
||||||
</StrictMode>,
|
</StrictMode>,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user