chore(examples): add layouting with new api
This commit is contained in:
@@ -19,7 +19,10 @@ import ReactFlow, {
|
|||||||
|
|
||||||
import ColorSelectorNode from './ColorSelectorNode';
|
import ColorSelectorNode from './ColorSelectorNode';
|
||||||
|
|
||||||
const onLoad = (reactFlowInstance: OnLoadParams) => console.log('flow loaded:', reactFlowInstance);
|
const onLoad = (reactFlowInstance: OnLoadParams) => {
|
||||||
|
console.log('flow loaded:', reactFlowInstance);
|
||||||
|
reactFlowInstance.fitView();
|
||||||
|
};
|
||||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
||||||
const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node);
|
const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import { useState, useCallback } from 'react';
|
||||||
|
import ReactFlow, {
|
||||||
|
ReactFlowProvider,
|
||||||
|
addEdge,
|
||||||
|
applyNodeChanges,
|
||||||
|
applyEdgeChanges,
|
||||||
|
Controls,
|
||||||
|
NodeChange,
|
||||||
|
EdgeChange,
|
||||||
|
Node,
|
||||||
|
Connection,
|
||||||
|
Edge,
|
||||||
|
NodeExtent,
|
||||||
|
Position,
|
||||||
|
} from 'react-flow-renderer';
|
||||||
|
import dagre from 'dagre';
|
||||||
|
|
||||||
|
import initialNodesAndEdges from './initial-elements';
|
||||||
|
|
||||||
|
import './layouting.css';
|
||||||
|
|
||||||
|
const dagreGraph = new dagre.graphlib.Graph();
|
||||||
|
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
||||||
|
|
||||||
|
const nodeExtent: NodeExtent = [
|
||||||
|
[0, 0],
|
||||||
|
[1000, 1000],
|
||||||
|
];
|
||||||
|
|
||||||
|
const LayoutFlow = () => {
|
||||||
|
const [nodes, setNodes] = useState<Node[]>(initialNodesAndEdges.nodes);
|
||||||
|
const [edges, setEdges] = useState<Edge[]>(initialNodesAndEdges.edges);
|
||||||
|
|
||||||
|
const onConnect = useCallback((params: Edge | Connection) => {
|
||||||
|
setEdges((eds) => {
|
||||||
|
return addEdge(params, eds);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onLayout = (direction: string) => {
|
||||||
|
const isHorizontal = direction === 'LR';
|
||||||
|
dagreGraph.setGraph({ rankdir: direction });
|
||||||
|
|
||||||
|
nodes.forEach((node) => {
|
||||||
|
dagreGraph.setNode(node.id, { width: 150, height: 50 });
|
||||||
|
});
|
||||||
|
|
||||||
|
edges.forEach((edge) => {
|
||||||
|
dagreGraph.setEdge(edge.source, edge.target);
|
||||||
|
});
|
||||||
|
|
||||||
|
dagre.layout(dagreGraph);
|
||||||
|
|
||||||
|
const layoutedNodes = nodes.map((node) => {
|
||||||
|
const nodeWithPosition = dagreGraph.node(node.id);
|
||||||
|
node.targetPosition = isHorizontal ? Position.Left : Position.Top;
|
||||||
|
node.sourcePosition = isHorizontal ? Position.Right : Position.Bottom;
|
||||||
|
// we need to pass a slightly different position in order to notify react flow about the change
|
||||||
|
// @TODO how can we change the position handling so that we dont need this hack?
|
||||||
|
node.position = { x: nodeWithPosition.x + Math.random() / 1000, y: nodeWithPosition.y };
|
||||||
|
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
|
||||||
|
setNodes(layoutedNodes);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onNodesChange = useCallback((changes: NodeChange[]) => setNodes((ns) => applyNodeChanges(changes, ns)), []);
|
||||||
|
|
||||||
|
const onEdgesChange = useCallback((changes: EdgeChange[]) => {
|
||||||
|
setEdges((es) => applyEdgeChanges(changes, es));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="layoutflow">
|
||||||
|
<ReactFlowProvider>
|
||||||
|
<ReactFlow
|
||||||
|
nodes={nodes}
|
||||||
|
edges={edges}
|
||||||
|
onConnect={onConnect}
|
||||||
|
nodeExtent={nodeExtent}
|
||||||
|
onLoad={() => onLayout('TB')}
|
||||||
|
onNodesChange={onNodesChange}
|
||||||
|
onEdgesChange={onEdgesChange}
|
||||||
|
>
|
||||||
|
<Controls />
|
||||||
|
</ReactFlow>
|
||||||
|
<div className="controls">
|
||||||
|
<button onClick={() => onLayout('TB')} style={{ marginRight: 10 }}>
|
||||||
|
vertical layout
|
||||||
|
</button>
|
||||||
|
<button onClick={() => onLayout('LR')}>horizontal layout</button>
|
||||||
|
</div>
|
||||||
|
</ReactFlowProvider>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LayoutFlow;
|
||||||
+8
-3
@@ -1,8 +1,8 @@
|
|||||||
import { Elements, XYPosition } from 'react-flow-renderer';
|
import { Node, Edge, XYPosition } from 'react-flow-renderer';
|
||||||
|
|
||||||
const position: XYPosition = { x: 0, y: 0 };
|
const position: XYPosition = { x: 0, y: 0 };
|
||||||
|
|
||||||
const elements: Elements = [
|
const nodes: Node[] = [
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
@@ -56,6 +56,9 @@ const elements: Elements = [
|
|||||||
position,
|
position,
|
||||||
},
|
},
|
||||||
{ id: '7', type: 'output', data: { label: 'output' }, position: { x: 400, y: 450 } },
|
{ id: '7', type: 'output', data: { label: 'output' }, position: { x: 400, y: 450 } },
|
||||||
|
];
|
||||||
|
|
||||||
|
const edges: Edge[] = [
|
||||||
{ id: 'e12', source: '1', target: '2', type: 'smoothstep' },
|
{ id: 'e12', source: '1', target: '2', type: 'smoothstep' },
|
||||||
{ id: 'e13', source: '1', target: '3', type: 'smoothstep' },
|
{ id: 'e13', source: '1', target: '3', type: 'smoothstep' },
|
||||||
{ id: 'e22a', source: '2', target: '2a', type: 'smoothstep' },
|
{ id: 'e22a', source: '2', target: '2a', type: 'smoothstep' },
|
||||||
@@ -68,4 +71,6 @@ const elements: Elements = [
|
|||||||
{ id: 'e57', source: '5', target: '7', type: 'smoothstep' },
|
{ id: 'e57', source: '5', target: '7', type: 'smoothstep' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export default elements;
|
const nodesAndEdges = { nodes, edges };
|
||||||
|
|
||||||
|
export default nodesAndEdges;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { ChangeEvent } from 'react';
|
import { ChangeEvent } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { BrowserRouter as Router, Route, Switch, withRouter } from 'react-router-dom';
|
import { BrowserRouter as Router, Route, Switch, withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
@@ -7,6 +7,7 @@ import UpdateNode from './UpdateNode';
|
|||||||
import Stress from './Stress';
|
import Stress from './Stress';
|
||||||
import CustomNode from './CustomNode';
|
import CustomNode from './CustomNode';
|
||||||
import FloatingEdges from './FloatingEdges';
|
import FloatingEdges from './FloatingEdges';
|
||||||
|
import Layouting from './Layouting';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
@@ -31,6 +32,10 @@ const routes = [
|
|||||||
path: '/floating-edges',
|
path: '/floating-edges',
|
||||||
component: FloatingEdges,
|
component: FloatingEdges,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/layouting',
|
||||||
|
component: Layouting,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const Header = withRouter(({ history, location }) => {
|
const Header = withRouter(({ history, location }) => {
|
||||||
|
|||||||
@@ -1,86 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import ReactFlow, {
|
|
||||||
ReactFlowProvider,
|
|
||||||
addEdge,
|
|
||||||
removeElements,
|
|
||||||
Controls,
|
|
||||||
isNode,
|
|
||||||
Elements,
|
|
||||||
Connection,
|
|
||||||
Edge,
|
|
||||||
NodeExtent,
|
|
||||||
Position,
|
|
||||||
} from 'react-flow-renderer';
|
|
||||||
import dagre from 'dagre';
|
|
||||||
|
|
||||||
import initialElements from './initial-elements';
|
|
||||||
|
|
||||||
import './layouting.css';
|
|
||||||
|
|
||||||
const dagreGraph = new dagre.graphlib.Graph();
|
|
||||||
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
|
||||||
|
|
||||||
const nodeExtent: NodeExtent = [
|
|
||||||
[0, 0],
|
|
||||||
[1000, 1000],
|
|
||||||
];
|
|
||||||
|
|
||||||
const LayoutFlow = () => {
|
|
||||||
const [elements, setElements] = useState<Elements>(initialElements);
|
|
||||||
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
|
||||||
|
|
||||||
const onLayout = (direction: string) => {
|
|
||||||
const isHorizontal = direction === 'LR';
|
|
||||||
dagreGraph.setGraph({ rankdir: direction });
|
|
||||||
|
|
||||||
elements.forEach((el) => {
|
|
||||||
if (isNode(el)) {
|
|
||||||
dagreGraph.setNode(el.id, { width: 150, height: 50 });
|
|
||||||
} else {
|
|
||||||
dagreGraph.setEdge(el.source, el.target);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
dagre.layout(dagreGraph);
|
|
||||||
|
|
||||||
const layoutedElements = elements.map((el) => {
|
|
||||||
if (isNode(el)) {
|
|
||||||
const nodeWithPosition = dagreGraph.node(el.id);
|
|
||||||
el.targetPosition = isHorizontal ? Position.Left : Position.Top;
|
|
||||||
el.sourcePosition = isHorizontal ? Position.Right : Position.Bottom;
|
|
||||||
// we need to pass a slightly different position in order to notify react flow about the change
|
|
||||||
// @TODO how can we change the position handling so that we dont need this hack?
|
|
||||||
el.position = { x: nodeWithPosition.x + Math.random() / 1000, y: nodeWithPosition.y };
|
|
||||||
}
|
|
||||||
|
|
||||||
return el;
|
|
||||||
});
|
|
||||||
|
|
||||||
setElements(layoutedElements);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="layoutflow">
|
|
||||||
<ReactFlowProvider>
|
|
||||||
<ReactFlow
|
|
||||||
elements={elements}
|
|
||||||
onConnect={onConnect}
|
|
||||||
onElementsRemove={onElementsRemove}
|
|
||||||
nodeExtent={nodeExtent}
|
|
||||||
onLoad={() => onLayout('TB')}
|
|
||||||
>
|
|
||||||
<Controls />
|
|
||||||
</ReactFlow>
|
|
||||||
<div className="controls">
|
|
||||||
<button onClick={() => onLayout('TB')} style={{ marginRight: 10 }}>
|
|
||||||
vertical layout
|
|
||||||
</button>
|
|
||||||
<button onClick={() => onLayout('LR')}>horizontal layout</button>
|
|
||||||
</div>
|
|
||||||
</ReactFlowProvider>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LayoutFlow;
|
|
||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "react-flow-renderer",
|
"name": "react-flow-renderer",
|
||||||
"version": "10.0.0-next.6",
|
"version": "10.0.0-next.7",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "react-flow-renderer",
|
"name": "react-flow-renderer",
|
||||||
"version": "10.0.0-next.6",
|
"version": "10.0.0-next.7",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.15.4",
|
"@babel/runtime": "^7.15.4",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "react-flow-renderer",
|
"name": "react-flow-renderer",
|
||||||
"version": "10.0.0-next.6",
|
"version": "10.0.0-next.7",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user