Merge pull request #933 from wbkd/refactor/examples

Refactor/examples
This commit is contained in:
Moritz Klack
2021-02-23 19:16:55 +01:00
committed by GitHub
50 changed files with 17439 additions and 12763 deletions
+12 -6
View File
@@ -19,7 +19,7 @@ npm install react-flow-renderer
This is a very basic example of how to use React Flow. You can find more advanced examples on the [website](https://reactflow.dev/examples).
```javascript
```js
import React from 'react';
import ReactFlow from 'react-flow-renderer';
@@ -35,21 +35,27 @@ const BasicFlow = () => <ReactFlow elements={elements} />;
## Development
Before you start you need to install the React Flow dependencies via `npm install`.
Before you start you need to install the React Flow dependencies via `npm install` and the ones of the examples `cd example && npm install`.
If you want to contribute or develop custom features the easiest way is to start the dev server:
```
npm run dev
```sh
npm start
```
This serves the content of the `example` folder and watches changes inside the `src` folder. The examples are using the source of the `src` folder.
and the example app via:
```sh
cd example && npm start
```
The example app serves the content of the `example` folder and watches changes inside the `src` folder. The examples are using the source of the `src` folder.
## Testing
Testing is done with cypress. You can find the tests in the [`integration/flow`](/cypress/integration/flow) folder. In order to run the tests do:
```
```sh
npm run test
```
+23
View File
@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
+23
View File
@@ -0,0 +1,23 @@
# React Flow Examples
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Installation
```sh
npm install
```
## Start
Starts a dev server at http://localhost:3000
```sh
npm start
```
## Build
```sh
npm run build
```
+16608
View File
File diff suppressed because it is too large Load Diff
+43
View File
@@ -0,0 +1,43 @@
{
"name": "react-flow-examples",
"version": "0.1.0",
"private": true,
"dependencies": {
"dagre": "^0.8.5",
"localforage": "^1.9.0",
"react": "file:../node_modules/react",
"react-dom": "file:../node_modules/react-dom",
"react-flow-renderer": "file:../",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"typescript": "^4.1.5"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build"
},
"eslintConfig": {
"extends": [
"react-app"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/react-router-dom": "^5.1.7",
"@types/dagre": "^0.7.44",
"@types/localforage": "0.0.34",
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.1"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

+3 -5
View File
@@ -2,13 +2,11 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
<meta name="description" content="React Flow test flows" />
<title>React Flow - Test Flows</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>React Flow Examples</title>
</head>
<body>
<div id="root"></div>
<script src="/dist/index.js"></script>
</body>
</html>
+2
View File
@@ -1 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
@@ -1,11 +1,23 @@
import React, { useState } from 'react';
import React, { useState, MouseEvent } from 'react';
import ReactFlow, { removeElements, addEdge, isNode, Background } from 'react-flow-renderer';
import ReactFlow, {
removeElements,
addEdge,
isNode,
Background,
Elements,
BackgroundVariant,
FlowElement,
Node,
Edge,
Connection,
OnLoadParams,
} from 'react-flow-renderer';
const onNodeDragStop = (event, node) => console.log('drag stop', node);
const onElementClick = (event, element) => console.log('click', element);
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
const initialElements = [
const initialElements: Elements = [
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 }, className: 'light' },
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 }, className: 'light' },
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 }, className: 'light' },
@@ -15,11 +27,11 @@ const initialElements = [
];
const BasicFlow = () => {
const [rfInstance, setRfInstance] = useState(null);
const [elements, setElements] = useState(initialElements);
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params) => setElements((els) => addEdge(params, els));
const onLoad = (reactFlowInstance) => setRfInstance(reactFlowInstance);
const [rfInstance, setRfInstance] = useState<OnLoadParams | null>(null);
const [elements, setElements] = useState<Elements>(initialElements);
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params: Edge | Connection) => setElements((els) => addEdge(params, els));
const onLoad = (reactFlowInstance: OnLoadParams) => setRfInstance(reactFlowInstance);
const updatePos = () => {
setElements((elms) => {
@@ -36,8 +48,8 @@ const BasicFlow = () => {
});
};
const logToObject = () => console.log(rfInstance.toObject());
const resetTransform = () => rfInstance.setTransform({ x: 0, y: 0, zoom: 1 });
const logToObject = () => console.log(rfInstance?.toObject());
const resetTransform = () => rfInstance?.setTransform({ x: 0, y: 0, zoom: 1 });
const toggleClassnames = () => {
setElements((elms) => {
@@ -64,7 +76,7 @@ const BasicFlow = () => {
minZoom={0.2}
maxZoom={4}
>
<Background variant="lines" />
<Background variant={BackgroundVariant.Lines} />
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
<button onClick={resetTransform} style={{ marginRight: 5 }}>
@@ -1,15 +1,7 @@
import React from 'react';
import React, { FC } from 'react';
import { ConnectionLineComponentProps } from 'react-flow-renderer';
export default ({
sourceX,
sourceY,
sourcePosition,
targetX,
targetY,
targetPosition,
connectionLineType,
connectionLineStyle,
}) => {
const ConnectionLine: FC<ConnectionLineComponentProps> = ({ sourceX, sourceY, targetX, targetY }) => {
return (
<g>
<path
@@ -23,3 +15,5 @@ export default ({
</g>
);
};
export default ConnectionLine;
-25
View File
@@ -1,25 +0,0 @@
import React, { useState } from 'react';
import ReactFlow, { removeElements, addEdge, Background } from 'react-flow-renderer';
import ConnectionLine from './ConnectionLine';
const initialElements = [{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } }];
const ConnectionLineFlow = () => {
const [elements, setElements] = useState(initialElements);
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params) => setElements((els) => addEdge(params, els));
return (
<ReactFlow
elements={elements}
connectionLineComponent={ConnectionLine}
onElementsRemove={onElementsRemove}
onConnect={onConnect}
>
<Background variant="lines" />
</ReactFlow>
);
};
export default ConnectionLineFlow;
@@ -0,0 +1,33 @@
import React, { useState } from 'react';
import ReactFlow, {
removeElements,
addEdge,
Background,
BackgroundVariant,
Elements,
Connection,
Edge,
} from 'react-flow-renderer';
import ConnectionLine from './ConnectionLine';
const initialElements: Elements = [{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } }];
const ConnectionLineFlow = () => {
const [elements, setElements] = useState<Elements>(initialElements);
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
return (
<ReactFlow
elements={elements}
connectionLineComponent={ConnectionLine}
onElementsRemove={onElementsRemove}
onConnect={onConnect}
>
<Background variant={BackgroundVariant.Lines} />
</ReactFlow>
);
};
export default ConnectionLineFlow;
@@ -1,22 +0,0 @@
import React, { memo } from 'react';
import { Handle } from 'react-flow-renderer';
export default memo(({ data }) => {
return (
<>
<Handle
type="target"
position="left"
style={{ background: '#555' }}
onConnect={(params) => console.log('handle onConnect', params)}
/>
<div>
Custom Color Picker Node: <strong>{data.color}</strong>
</div>
<input className="nodrag" type="color" onChange={data.onChange} defaultValue={data.color} />
<Handle type="source" position="right" id="a" style={{ top: 10, background: '#555' }} />
<Handle type="source" position="right" id="b" style={{ bottom: 10, top: 'auto', background: '#555' }} />
</>
);
});
@@ -0,0 +1,25 @@
import React, { memo, FC, CSSProperties } from 'react';
import { Handle, Position, NodeProps, Connection, Edge } from 'react-flow-renderer';
const targetHandleStyle: CSSProperties = { background: '#555' };
const sourceHandleStyleA: CSSProperties = { ...targetHandleStyle, top: 10 };
const sourceHandleStyleB: CSSProperties = { ...targetHandleStyle, bottom: 10, top: 'auto' };
const onConnect = (params: Connection | Edge) => console.log('handle onConnect', params);
const ColorSelectorNode: FC<NodeProps> = ({ data }) => {
return (
<>
<Handle type="target" position={Position.Left} style={targetHandleStyle} onConnect={onConnect} />
<div>
Custom Color Picker Node: <strong>{data.color}</strong>
</div>
<input className="nodrag" type="color" onChange={data.onChange} defaultValue={data.color} />
<Handle type="source" position={Position.Right} id="a" style={sourceHandleStyleA} />
<Handle type="source" position={Position.Right} id="b" style={sourceHandleStyleB} />
</>
);
};
export default memo(ColorSelectorNode);
@@ -1,27 +1,42 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, MouseEvent } from 'react';
import { ChangeEvent } from 'react';
import ReactFlow, { isEdge, removeElements, addEdge, MiniMap, Controls } from 'react-flow-renderer';
import ReactFlow, {
isEdge,
removeElements,
addEdge,
MiniMap,
Controls,
Node,
FlowElement,
OnLoadParams,
Elements,
Position,
SnapGrid,
Connection,
Edge,
} from 'react-flow-renderer';
import ColorSelectorNode from './ColorSelectorNode';
const onLoad = (reactFlowInstance) => console.log('flow loaded:', reactFlowInstance);
const onNodeDragStop = (event, node) => console.log('drag stop', node);
const onElementClick = (event, element) => console.log('click', element);
const onLoad = (reactFlowInstance: OnLoadParams) => console.log('flow loaded:', reactFlowInstance);
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
const initBgColor = '#1A192B';
const connectionLineStyle = { stroke: '#fff' };
const snapGrid = [16, 16];
const snapGrid: SnapGrid = [16, 16];
const nodeTypes = {
selectorNode: ColorSelectorNode,
};
const CustomNodeFlow = () => {
const [elements, setElements] = useState([]);
const [bgColor, setBgColor] = useState(initBgColor);
const [elements, setElements] = useState<Elements>([]);
const [bgColor, setBgColor] = useState<string>(initBgColor);
useEffect(() => {
const onChange = (event) => {
const onChange = (event: ChangeEvent<HTMLInputElement>) => {
setElements((els) =>
els.map((e) => {
if (isEdge(e) || e.id !== '2') {
@@ -44,7 +59,13 @@ const CustomNodeFlow = () => {
};
setElements([
{ id: '1', type: 'input', data: { label: 'An input node' }, position: { x: 0, y: 50 }, sourcePosition: 'right' },
{
id: '1',
type: 'input',
data: { label: 'An input node' },
position: { x: 0, y: 50 },
sourcePosition: Position.Right,
},
{
id: '2',
type: 'selectorNode',
@@ -52,8 +73,20 @@ const CustomNodeFlow = () => {
style: { border: '1px solid #777', padding: 10 },
position: { x: 250, y: 50 },
},
{ id: '3', type: 'output', data: { label: 'Output A' }, position: { x: 550, y: 25 }, targetPosition: 'left' },
{ id: '4', type: 'output', data: { label: 'Output B' }, position: { x: 550, y: 100 }, targetPosition: 'left' },
{
id: '3',
type: 'output',
data: { label: 'Output A' },
position: { x: 550, y: 25 },
targetPosition: Position.Left,
},
{
id: '4',
type: 'output',
data: { label: 'Output B' },
position: { x: 550, y: 100 },
targetPosition: Position.Left,
},
{ id: 'e1-2', source: '1', target: '2', animated: true, style: { stroke: '#fff' } },
{ id: 'e2a-3', source: '2', sourceHandle: 'a', target: '3', animated: true, style: { stroke: '#fff' } },
@@ -61,8 +94,8 @@ const CustomNodeFlow = () => {
]);
}, []);
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params) =>
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params: Connection | Edge) =>
setElements((els) => addEdge({ ...params, animated: true, style: { stroke: '#fff' } }, els));
return (
@@ -81,13 +114,16 @@ const CustomNodeFlow = () => {
defaultZoom={1.5}
>
<MiniMap
nodeStrokeColor={(n) => {
nodeStrokeColor={(n: Node): string => {
if (n.type === 'input') return '#0041d0';
if (n.type === 'selectorNode') return bgColor;
if (n.type === 'output') return '#ff0072';
return '#eee';
}}
nodeColor={(n) => {
nodeColor={(n: Node): string => {
if (n.type === 'selectorNode') return bgColor;
return '#fff';
}}
/>
-23
View File
@@ -1,23 +0,0 @@
import React from 'react';
export default () => {
const onDragStart = (event, nodeType) => {
event.dataTransfer.setData('application/reactflow', nodeType);
event.dataTransfer.effectAllowed = 'move';
};
return (
<aside>
<div className="description">You can drag these nodes to the pane on the right.</div>
<div className="dndnode input" onDragStart={(event) => onDragStart(event, 'input')} draggable>
Input Node
</div>
<div className="dndnode" onDragStart={(event) => onDragStart(event, 'default')} draggable>
Default Node
</div>
<div className="dndnode output" onDragStart={(event) => onDragStart(event, 'output')} draggable>
Output Node
</div>
</aside>
);
};
+25
View File
@@ -0,0 +1,25 @@
import React, { DragEvent } from 'react';
const onDragStart = (event: DragEvent, nodeType: string) => {
event.dataTransfer.setData('application/reactflow', nodeType);
event.dataTransfer.effectAllowed = 'move';
};
const Sidebar = () => {
return (
<aside>
<div className="description">You can drag these nodes to the pane on the right.</div>
<div className="dndnode input" onDragStart={(event: DragEvent) => onDragStart(event, 'input')} draggable>
Input Node
</div>
<div className="dndnode" onDragStart={(event: DragEvent) => onDragStart(event, 'default')} draggable>
Default Node
</div>
<div className="dndnode output" onDragStart={(event: DragEvent) => onDragStart(event, 'output')} draggable>
Output Node
</div>
</aside>
);
};
export default Sidebar;
-62
View File
@@ -1,62 +0,0 @@
import React, { useState } from 'react';
import ReactFlow, { ReactFlowProvider, addEdge, removeElements, Controls } from 'react-flow-renderer';
import Sidebar from './Sidebar';
import './dnd.css';
const initialElements = [{ id: '1', type: 'input', data: { label: 'input node' }, position: { x: 250, y: 5 } }];
let id = 0;
const getId = () => `dndnode_${id++}`;
const DnDFlow = () => {
const [reactFlowInstance, setReactFlowInstance] = useState(null);
const [elements, setElements] = useState(initialElements);
const onConnect = (params) => setElements((els) => addEdge(params, els));
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
const onLoad = (_reactFlowInstance) => setReactFlowInstance(_reactFlowInstance);
const onDragOver = (event) => {
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
};
const onDrop = (event) => {
event.preventDefault();
const type = event.dataTransfer.getData('application/reactflow');
const position = reactFlowInstance.project({ x: event.clientX, y: event.clientY - 40 });
const newNode = {
id: getId(),
type,
position,
data: { label: `${type} node` },
};
setElements((es) => es.concat(newNode));
};
return (
<div className="dndflow">
<ReactFlowProvider>
<div className="reactflow-wrapper">
<ReactFlow
elements={elements}
onConnect={onConnect}
onElementsRemove={onElementsRemove}
onLoad={onLoad}
onDrop={onDrop}
onDragOver={onDragOver}
>
<Controls />
</ReactFlow>
</div>
<Sidebar />
</ReactFlowProvider>
</div>
);
};
export default DnDFlow;
+75
View File
@@ -0,0 +1,75 @@
import React, { useState, DragEvent } from 'react';
import ReactFlow, {
ReactFlowProvider,
addEdge,
removeElements,
Controls,
OnLoadParams,
Elements,
Connection,
Edge,
ElementId,
Node,
} from 'react-flow-renderer';
import Sidebar from './Sidebar';
import './dnd.css';
const initialElements = [{ id: '1', type: 'input', data: { label: 'input node' }, position: { x: 250, y: 5 } }];
const onDragOver = (event: DragEvent) => {
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
};
let id = 0;
const getId = (): ElementId => `dndnode_${id++}`;
const DnDFlow = () => {
const [reactFlowInstance, setReactFlowInstance] = useState<OnLoadParams>();
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 onLoad = (_reactFlowInstance: OnLoadParams) => setReactFlowInstance(_reactFlowInstance);
const onDrop = (event: DragEvent) => {
event.preventDefault();
if (reactFlowInstance) {
const type = event.dataTransfer.getData('application/reactflow');
const position = reactFlowInstance.project({ x: event.clientX, y: event.clientY - 40 });
const newNode: Node = {
id: getId(),
type,
position,
data: { label: `${type} node` },
};
setElements((es) => es.concat(newNode));
}
};
return (
<div className="dndflow">
<ReactFlowProvider>
<div className="reactflow-wrapper">
<ReactFlow
elements={elements}
onConnect={onConnect}
onElementsRemove={onElementsRemove}
onLoad={onLoad}
onDrop={onDrop}
onDragOver={onDragOver}
>
<Controls />
</ReactFlow>
</div>
<Sidebar />
</ReactFlowProvider>
</div>
);
};
export default DnDFlow;
@@ -3,10 +3,20 @@
*/
import React, { useState } from 'react';
import ReactFlow, { removeElements, addEdge, MiniMap, Controls, Background } from 'react-flow-renderer';
import ReactFlow, {
removeElements,
addEdge,
MiniMap,
Controls,
Background,
OnLoadParams,
Connection,
Edge,
Elements,
} from 'react-flow-renderer';
import { getElements } from './utils';
const onLoad = (reactFlowInstance) => {
const onLoad = (reactFlowInstance: OnLoadParams) => {
reactFlowInstance.fitView();
console.log(reactFlowInstance.getElements());
};
@@ -14,9 +24,9 @@ const onLoad = (reactFlowInstance) => {
const initialElements = getElements();
const EdgeTypesFlow = () => {
const [elements, setElements] = useState(initialElements);
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params) => setElements((els) => addEdge(params, els));
const [elements, setElements] = useState<Elements>(initialElements);
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
return (
<ReactFlow
@@ -1,10 +1,12 @@
import { ElementId, Elements, Position } from 'react-flow-renderer';
const nodeWidth = 80;
const nodeGapWidth = nodeWidth * 2;
const nodeStyle = { width: nodeWidth, fontSize: 11, color: 'white' };
const sourceTargetPositions = [
{ source: 'bottom', target: 'top' },
{ source: 'right', target: 'left' },
{ source: Position.Bottom, target: Position.Top },
{ source: Position.Right, target: Position.Left },
];
const nodeColors = [
['#1e9e99', '#4cb3ac', '#6ec9c0', '#8ddfd4'],
@@ -47,9 +49,9 @@ const offsets = [
];
let id = 0;
const getNodeId = () => (id++).toString();
const getNodeId = (): ElementId => (id++).toString();
export function getElements() {
export function getElements(): Elements {
const initialElements = [];
for (let sourceTargetIndex = 0; sourceTargetIndex < sourceTargetPositions.length; sourceTargetIndex++) {
@@ -1,7 +1,7 @@
import React from 'react';
import { getBezierPath, getMarkerEnd } from 'react-flow-renderer';
import React, { FC } from 'react';
import { EdgeProps, getBezierPath, getMarkerEnd } from 'react-flow-renderer';
export default function CustomEdge({
const CustomEdge: FC<EdgeProps> = ({
id,
sourceX,
sourceY,
@@ -13,7 +13,7 @@ export default function CustomEdge({
data,
arrowHeadType,
markerEndId,
}) {
}) => {
const edgePath = getBezierPath({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition });
const markerEnd = getMarkerEnd(arrowHeadType, markerEndId);
@@ -27,4 +27,6 @@ export default function CustomEdge({
</text>
</>
);
}
};
export default CustomEdge;
@@ -1,14 +1,28 @@
import React, { useState } from 'react';
import React, { useState, MouseEvent } from 'react';
import ReactFlow, { removeElements, addEdge, MiniMap, Controls, Background } from 'react-flow-renderer';
import ReactFlow, {
removeElements,
addEdge,
MiniMap,
Controls,
Background,
OnLoadParams,
FlowElement,
EdgeTypesType,
Elements,
Connection,
Edge,
ArrowHeadType,
Node,
} from 'react-flow-renderer';
import CustomEdge from './CustomEdge';
const onLoad = (reactFlowInstance) => reactFlowInstance.fitView();
const onNodeDragStop = (event, node) => console.log('drag stop', node);
const onElementClick = (event, element) => console.log('click', element);
const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.fitView();
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
const initialElements = [
const initialElements: Elements = [
{ id: '1', type: 'input', data: { label: 'Input 1' }, position: { x: 250, y: 0 } },
{ id: '2', data: { label: 'Node 2' }, position: { x: 150, y: 100 } },
{ id: '2a', data: { label: 'Node 2a' }, position: { x: 0, y: 180 } },
@@ -31,7 +45,7 @@ const initialElements = [
target: '6',
label: 'styled label',
labelStyle: { fill: 'red', fontWeight: 700 },
arrowHeadType: 'arrow',
arrowHeadType: ArrowHeadType.Arrow,
},
{
id: 'e5-7',
@@ -41,20 +55,27 @@ const initialElements = [
labelBgPadding: [8, 4],
labelBgBorderRadius: 4,
labelBgStyle: { fill: '#FFCC00', color: '#fff', fillOpacity: 0.7 },
arrowHeadType: 'arrowclosed',
arrowHeadType: ArrowHeadType.ArrowClosed,
},
{
id: 'e5-8',
source: '5',
target: '8',
type: 'custom',
data: { text: 'custom edge' },
arrowHeadType: ArrowHeadType.ArrowClosed,
},
{ id: 'e5-8', source: '5', target: '8', type: 'custom', data: { text: 'custom edge' }, arrowHeadType: 'arrowclosed' },
];
const edgeTypes = {
const edgeTypes: EdgeTypesType = {
custom: CustomEdge,
};
const EdgesFlow = () => {
const [elements, setElements] = useState(initialElements);
const [elements, setElements] = useState<Elements>(initialElements);
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params) => setElements((els) => addEdge(params, els));
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
return (
<ReactFlow
-44
View File
@@ -1,44 +0,0 @@
import React, { useState } from 'react';
import ReactFlow, { removeElements, addEdge, MiniMap, Controls, Background } from 'react-flow-renderer';
const onLoad = (reactFlowInstance) => console.log('flow loaded:', reactFlowInstance);
const onElementClick = (event, element) => console.log('click', element);
const onNodeDragStop = (event, node) => console.log('drag stop', node);
const EmptyFlow = () => {
const [elements, setElements] = useState([]);
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params) => setElements((els) => addEdge(params, els));
const addRandomNode = () => {
const nodeId = (elements.length + 1).toString();
const newNode = {
id: nodeId,
data: { label: `Node: ${nodeId}` },
position: { x: Math.random() * window.innerWidth, y: Math.random() * window.innerHeight },
};
setElements((els) => els.concat(newNode));
};
return (
<ReactFlow
elements={elements}
onLoad={onLoad}
onElementClick={onElementClick}
onElementsRemove={onElementsRemove}
onConnect={(p) => onConnect(p)}
onNodeDragStop={onNodeDragStop}
onlyRenderVisibleElements={false}
>
<MiniMap />
<Controls />
<Background variant="lines" />
<button type="button" onClick={addRandomNode} style={{ position: 'absolute', left: 10, top: 10, zIndex: 4 }}>
add node
</button>
</ReactFlow>
);
};
export default EmptyFlow;
+60
View File
@@ -0,0 +1,60 @@
import React, { useState, MouseEvent, CSSProperties } from 'react';
import ReactFlow, {
removeElements,
addEdge,
MiniMap,
Controls,
Background,
OnLoadParams,
Elements,
ElementId,
Node,
FlowElement,
BackgroundVariant,
Connection,
Edge,
} from 'react-flow-renderer';
const onLoad = (reactFlowInstance: OnLoadParams) => console.log('flow loaded:', reactFlowInstance);
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
const buttonStyle: CSSProperties = { position: 'absolute', left: 10, top: 10, zIndex: 4 };
const EmptyFlow = () => {
const [elements, setElements] = useState<Elements>([]);
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
const addRandomNode = () => {
const nodeId: ElementId = (elements.length + 1).toString();
const newNode: Node = {
id: nodeId,
data: { label: `Node: ${nodeId}` },
position: { x: Math.random() * window.innerWidth, y: Math.random() * window.innerHeight },
};
setElements((els) => els.concat(newNode));
};
return (
<ReactFlow
elements={elements}
onLoad={onLoad}
onElementClick={onElementClick}
onElementsRemove={onElementsRemove}
onConnect={(p) => onConnect(p)}
onNodeDragStop={onNodeDragStop}
onlyRenderVisibleElements={false}
>
<MiniMap />
<Controls />
<Background variant={BackgroundVariant.Lines} />
<button type="button" onClick={addRandomNode} style={buttonStyle}>
add node
</button>
</ReactFlow>
);
};
export default EmptyFlow;
@@ -1,9 +1,9 @@
import React, { useState } from 'react';
import ReactFlow, { addEdge, MiniMap, Controls } from 'react-flow-renderer';
import { useEffect } from 'react';
import ReactFlow, { addEdge, MiniMap, Controls, Connection, Edge, Elements } from 'react-flow-renderer';
const initialElements = [
const initialElements: Elements = [
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } },
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 } },
@@ -14,9 +14,9 @@ const initialElements = [
];
const HiddenFlow = () => {
const [elements, setElements] = useState(initialElements);
const [isHidden, setIsHidden] = useState(false);
const onConnect = (params) => setElements((els) => addEdge(params, els));
const [elements, setElements] = useState<Elements>(initialElements);
const [isHidden, setIsHidden] = useState<boolean>(false);
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
useEffect(() => {
setElements((els) =>
@@ -1,8 +1,18 @@
import React, { useState } from 'react';
import React, { useState, MouseEvent, WheelEvent } from 'react';
import ReactFlow, {
addEdge,
MiniMap,
Controls,
Elements,
Node,
FlowElement,
Connection,
Edge,
PanOnScrollMode,
FlowTransform,
} from 'react-flow-renderer';
import ReactFlow, { addEdge, MiniMap, Controls } from 'react-flow-renderer';
const initialElements = [
const initialElements: Elements = [
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } },
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 } },
@@ -11,30 +21,30 @@ const initialElements = [
{ id: 'e1-3', source: '1', target: '3' },
];
const onNodeDragStart = (event, node) => console.log('drag start', node);
const onNodeDragStop = (event, node) => console.log('drag stop', node);
const onElementClick = (event, element) => console.log('click', element);
const onPaneClick = (event) => console.log('onPaneClick', event);
const onPaneScroll = (event) => console.log('onPaneScroll', event);
const onPaneContextMenu = (event) => console.log('onPaneContextMenu', event);
const onMoveEnd = (event) => console.log('onMoveEnd', event);
const onNodeDragStart = (_: MouseEvent, node: Node) => console.log('drag start', node);
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
const onPaneClick = (event: MouseEvent) => console.log('onPaneClick', event);
const onPaneScroll = (event?: WheelEvent) => console.log('onPaneScroll', event);
const onPaneContextMenu = (event: MouseEvent) => console.log('onPaneContextMenu', event);
const onMoveEnd = (flowTranasform?: FlowTransform) => console.log('onMoveEnd', flowTranasform);
const InteractionFlow = () => {
const [elements, setElements] = useState(initialElements);
const onConnect = (params) => setElements((els) => addEdge(params, els));
const [elements, setElements] = useState<Elements>(initialElements);
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
const [isSelectable, setIsSelectable] = useState(false);
const [isDraggable, setIsDraggable] = useState(false);
const [isConnectable, setIsConnectable] = useState(false);
const [zoomOnScroll, setZoomOnScroll] = useState(false);
const [zoomOnPinch, setZoomOnPinch] = useState(false);
const [panOnScroll, setPanOnScroll] = useState(false);
const [panOnScrollMode, setPanOnScrollMode] = useState('free');
const [zoomOnDoubleClick, setZoomOnDoubleClick] = useState(false);
const [paneMoveable, setPaneMoveable] = useState(true);
const [captureZoomClick, setCaptureZoomClick] = useState(false);
const [captureZoomScroll, setCaptureZoomScroll] = useState(false);
const [captureElementClick, setCaptureElementClick] = useState(false);
const [isSelectable, setIsSelectable] = useState<boolean>(false);
const [isDraggable, setIsDraggable] = useState<boolean>(false);
const [isConnectable, setIsConnectable] = useState<boolean>(false);
const [zoomOnScroll, setZoomOnScroll] = useState<boolean>(false);
const [zoomOnPinch, setZoomOnPinch] = useState<boolean>(false);
const [panOnScroll, setPanOnScroll] = useState<boolean>(false);
const [panOnScrollMode, setPanOnScrollMode] = useState<PanOnScrollMode>(PanOnScrollMode.Free);
const [zoomOnDoubleClick, setZoomOnDoubleClick] = useState<boolean>(false);
const [paneMoveable, setPaneMoveable] = useState<boolean>(true);
const [captureZoomClick, setCaptureZoomClick] = useState<boolean>(false);
const [captureZoomScroll, setCaptureZoomScroll] = useState<boolean>(false);
const [captureElementClick, setCaptureElementClick] = useState<boolean>(false);
return (
<ReactFlow
@@ -139,7 +149,7 @@ const InteractionFlow = () => {
<select
id="panonscrollmode"
value={panOnScrollMode}
onChange={(event) => setPanOnScrollMode(event.target.value)}
onChange={(event) => setPanOnScrollMode(event.target.value as PanOnScrollMode)}
className="react-flow__panonscrollmode"
>
<option value="free">free</option>
@@ -1,5 +1,16 @@
import React, { useState } from 'react';
import ReactFlow, { ReactFlowProvider, addEdge, removeElements, Controls, isNode } from 'react-flow-renderer';
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';
@@ -9,17 +20,17 @@ import './layouting.css';
const dagreGraph = new dagre.graphlib.Graph();
dagreGraph.setDefaultEdgeLabel(() => ({}));
const nodeExtent = [
const nodeExtent: NodeExtent = [
[0, 0],
[1000, 1000],
];
const LayoutFlow = () => {
const [elements, setElements] = useState(initialElements);
const onConnect = (params) => setElements((els) => addEdge(params, els));
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
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) => {
const onLayout = (direction: string) => {
const isHorizontal = direction === 'LR';
dagreGraph.setGraph({ rankdir: direction });
@@ -36,8 +47,8 @@ const LayoutFlow = () => {
const layoutedElements = elements.map((el) => {
if (isNode(el)) {
const nodeWithPosition = dagreGraph.node(el.id);
el.targetPosition = isHorizontal ? 'left' : 'top';
el.sourcePosition = isHorizontal ? 'right' : 'bottom';
el.targetPosition = isHorizontal ? Position.Left : Position.Top;
el.sourcePosition = isHorizontal ? Position.Right : Position.Bottom;
// we need to pass a slighltiy 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 };
@@ -1,6 +1,8 @@
const position = { x: 0, y: 0 };
import { Elements, XYPosition } from 'react-flow-renderer';
export default [
const position: XYPosition = { x: 0, y: 0 };
const elements: Elements = [
{
id: '1',
type: 'input',
@@ -65,3 +67,5 @@ export default [
{ id: 'e56', source: '5', target: '6', type: 'smoothstep' },
{ id: 'e57', source: '5', target: '7', type: 'smoothstep' },
];
export default elements;
@@ -1,13 +1,13 @@
import React, { useState } from 'react';
import React, { useState, CSSProperties } from 'react';
import ReactFlow, { addEdge, isEdge } from 'react-flow-renderer';
import ReactFlow, { addEdge, isEdge, OnLoadParams, Elements, Position, Connection, Edge } from 'react-flow-renderer';
const onLoad = (reactFlowInstance) => reactFlowInstance.fitView();
const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.fitView();
const initialElements = [
const initialElements: Elements = [
{
id: '1',
sourcePosition: 'right',
sourcePosition: Position.Right,
type: 'input',
data: { label: 'Input' },
position: { x: 0, y: 80 },
@@ -15,17 +15,19 @@ const initialElements = [
{
id: '2',
type: 'output',
sourcePosition: 'right',
targetPosition: 'left',
sourcePosition: Position.Right,
targetPosition: Position.Left,
data: { label: 'A Node' },
position: { x: 250, y: 0 },
},
{ id: 'e1-2', source: '1', type: 'smoothstep', target: '2', animated: true },
];
const buttonStyle: CSSProperties = { position: 'absolute', right: 10, top: 30, zIndex: 4 };
const NodeTypeChangeFlow = () => {
const [elements, setElements] = useState(initialElements);
const onConnect = (params) => setElements((els) => addEdge(params, els));
const [elements, setElements] = useState<Elements>(initialElements);
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
const changeType = () => {
setElements((elms) =>
elms.map((el) => {
@@ -43,7 +45,7 @@ const NodeTypeChangeFlow = () => {
return (
<ReactFlow elements={elements} onConnect={onConnect} onLoad={onLoad}>
<button onClick={changeType} style={{ position: 'absolute', right: 10, top: 30, zIndex: 4 }}>
<button onClick={changeType} style={buttonStyle}>
change type
</button>
</ReactFlow>
@@ -1,30 +1,47 @@
import React, { useState } from 'react';
import React, { useState, MouseEvent, CSSProperties } from 'react';
import ReactFlow, { removeElements, addEdge, MiniMap, Controls, Background, isNode } from 'react-flow-renderer';
import ReactFlow, {
removeElements,
addEdge,
MiniMap,
Controls,
Background,
isNode,
Node,
Elements,
FlowElement,
OnLoadParams,
FlowTransform,
SnapGrid,
ArrowHeadType,
Connection,
Edge,
} from 'react-flow-renderer';
const onNodeDragStart = (event, node) => console.log('drag start', node);
const onNodeDrag = (event, node) => console.log('drag', node);
const onNodeDragStop = (event, node) => console.log('drag stop', node);
const onPaneClick = (event) => console.log('pane click', event);
const onPaneScroll = (event) => console.log('pane scroll', event);
const onPaneContextMenu = (event) => console.log('pane context menu', event);
const onSelectionDrag = (event, nodes) => console.log('selection drag', nodes);
const onSelectionDragStart = (event, nodes) => console.log('selection drag start', nodes);
const onSelectionDragStop = (event, nodes) => console.log('selection drag stop', nodes);
const onSelectionContextMenu = (event, nodes) => {
const onNodeDragStart = (_: MouseEvent, node: Node) => console.log('drag start', node);
const onNodeDrag = (_: MouseEvent, node: Node) => console.log('drag', node);
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
const onPaneClick = (event: MouseEvent) => console.log('pane click', event);
const onPaneScroll = (event?: MouseEvent) => console.log('pane scroll', event);
const onPaneContextMenu = (event: MouseEvent) => console.log('pane context menu', event);
const onSelectionDrag = (_: MouseEvent, nodes: Node[]) => console.log('selection drag', nodes);
const onSelectionDragStart = (_: MouseEvent, nodes: Node[]) => console.log('selection drag start', nodes);
const onSelectionDragStop = (_: MouseEvent, nodes: Node[]) => console.log('selection drag stop', nodes);
const onSelectionContextMenu = (event: MouseEvent, nodes: Node[]) => {
event.preventDefault();
console.log('selection context menu', nodes);
};
const onElementClick = (event, element) => console.log(`${isNode(element) ? 'node' : 'edge'} click:`, element);
const onSelectionChange = (elements) => console.log('selection change', elements);
const onLoad = (reactFlowInstance) => {
const onElementClick = (_: MouseEvent, element: FlowElement) =>
console.log(`${isNode(element) ? 'node' : 'edge'} click:`, element);
const onSelectionChange = (elements: Elements | null) => console.log('selection change', elements);
const onLoad = (reactFlowInstance: OnLoadParams) => {
console.log('flow loaded:', reactFlowInstance);
reactFlowInstance.fitView();
};
const onMoveEnd = (transform) => console.log('zoom/move end', transform);
const onMoveEnd = (transform?: FlowTransform) => console.log('zoom/move end', transform);
const initialElements = [
const initialElements: Elements = [
{
id: '1',
type: 'input',
@@ -101,7 +118,7 @@ const initialElements = [
{ id: 'e1-2', source: '1', target: '2', label: 'this is an edge label' },
{ id: 'e1-3', source: '1', target: '3' },
{ id: 'e3-4', source: '3', target: '4', animated: true, label: 'animated edge' },
{ id: 'e4-5', source: '4', target: '5', arrowHeadType: 'arrowclosed', label: 'edge with arrow head' },
{ id: 'e4-5', source: '4', target: '5', arrowHeadType: ArrowHeadType.Arrow, label: 'edge with arrow head' },
{ id: 'e5-6', source: '5', target: '6', type: 'smoothstep', label: 'smooth step edge' },
{
id: 'e5-7',
@@ -115,13 +132,28 @@ const initialElements = [
},
];
const connectionLineStyle = { stroke: '#ddd' };
const snapGrid = [16, 16];
const connectionLineStyle: CSSProperties = { stroke: '#ddd' };
const snapGrid: SnapGrid = [16, 16];
const nodeStrokeColor = (n: Node): string => {
if (n.style?.background) return n.style.background as string;
if (n.type === 'input') return '#0041d0';
if (n.type === 'output') return '#ff0072';
if (n.type === 'default') return '#1a192b';
return '#eee';
};
const nodeColor = (n: Node): string => {
if (n.style?.background) return n.style.background as string;
return '#fff';
};
const OverviewFlow = () => {
const [elements, setElements] = useState(initialElements);
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params) => setElements((els) => addEdge(params, els));
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
return (
<ReactFlow
@@ -146,22 +178,7 @@ const OverviewFlow = () => {
snapToGrid={true}
snapGrid={snapGrid}
>
<MiniMap
nodeStrokeColor={(n) => {
if (n.style?.background) return n.style.background;
if (n.type === 'input') return '#0041d0';
if (n.type === 'output') return '#ff0072';
if (n.type === 'default') return '#1a192b';
return '#eee';
}}
nodeColor={(n) => {
if (n.style?.background) return n.style.background;
return '#fff';
}}
nodeBorderRadius={2}
/>
<MiniMap nodeStrokeColor={nodeStrokeColor} nodeColor={nodeColor} nodeBorderRadius={2} />
<Controls />
<Background color="#aaa" gap={16} />
</ReactFlow>
@@ -1,5 +1,5 @@
import React from 'react';
import { useStoreState, useStoreActions, isNode } from 'react-flow-renderer';
import { useStoreState, useStoreActions } from 'react-flow-renderer';
const Sidebar = () => {
const nodes = useStoreState((store) => store.nodes);
@@ -1,14 +1,25 @@
import React, { useState } from 'react';
import ReactFlow, { ReactFlowProvider, addEdge, removeElements, Controls } from 'react-flow-renderer';
import React, { useState, MouseEvent } from 'react';
import ReactFlow, {
ReactFlowProvider,
addEdge,
removeElements,
Controls,
OnLoadParams,
FlowElement,
Connection,
Edge,
Elements,
ConnectionMode,
} from 'react-flow-renderer';
import Sidebar from './Sidebar';
import './provider.css';
const onElementClick = (event, element) => console.log('click', element);
const onLoad = (reactFlowInstance) => console.log('flow loaded:', reactFlowInstance);
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
const onLoad = (reactFlowInstance: OnLoadParams) => console.log('flow loaded:', reactFlowInstance);
const initialElements = [
const initialElements: Elements = [
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } },
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 } },
@@ -18,9 +29,9 @@ const initialElements = [
];
const ProviderFlow = () => {
const [elements, setElements] = useState(initialElements);
const onConnect = (params) => setElements((els) => addEdge(params, els));
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
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));
return (
<div className="providerflow">
@@ -33,7 +44,7 @@ const ProviderFlow = () => {
onConnect={onConnect}
onElementsRemove={onElementsRemove}
onLoad={onLoad}
connectionMode="loose"
connectionMode={ConnectionMode.Loose}
>
<Controls />
</ReactFlow>
@@ -1,5 +1,5 @@
import React, { memo, useCallback } from 'react';
import { useZoomPanHelper } from 'react-flow-renderer';
import React, { memo, useCallback, Dispatch, FC } from 'react';
import { useZoomPanHelper, OnLoadParams, Elements, FlowExportObject } from 'react-flow-renderer';
import localforage from 'localforage';
localforage.config({
@@ -11,7 +11,12 @@ const flowKey = 'example-flow';
const getNodeId = () => `randomnode_${+new Date()}`;
const Controls = memo(({ rfInstance, setElements }) => {
type ControlsProps = {
rfInstance?: OnLoadParams;
setElements: Dispatch<React.SetStateAction<Elements<any>>>;
};
const Controls: FC<ControlsProps> = ({ rfInstance, setElements }) => {
const { transform } = useZoomPanHelper();
const onSave = useCallback(() => {
@@ -23,7 +28,7 @@ const Controls = memo(({ rfInstance, setElements }) => {
const onRestore = useCallback(() => {
const restoreFlow = async () => {
const flow = await localforage.getItem(flowKey);
const flow: FlowExportObject | null = await localforage.getItem(flowKey);
if (flow) {
const [x = 0, y = 0] = flow.position;
@@ -51,6 +56,6 @@ const Controls = memo(({ rfInstance, setElements }) => {
<button onClick={onAdd}>add node</button>
</div>
);
});
};
export default Controls;
export default memo(Controls);
@@ -1,21 +1,29 @@
import React, { memo, useState } from 'react';
import ReactFlow, { ReactFlowProvider, removeElements, addEdge } from 'react-flow-renderer';
import React, { useState } from 'react';
import ReactFlow, {
ReactFlowProvider,
removeElements,
addEdge,
Elements,
Connection,
Edge,
OnLoadParams,
} from 'react-flow-renderer';
import Controls from './Controls';
import './save.css';
const initialElements = [
const initialElements: Elements = [
{ id: '1', data: { label: 'Node 1' }, position: { x: 100, y: 100 } },
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 200 } },
{ id: 'e1-2', source: '1', target: '2' },
];
const SaveRestore = () => {
const [rfInstance, setRfInstance] = useState(null);
const [elements, setElements] = useState(initialElements);
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params) => setElements((els) => addEdge(params, els));
const [rfInstance, setRfInstance] = useState<OnLoadParams>();
const [elements, setElements] = useState<Elements>(initialElements);
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
return (
<ReactFlowProvider>
@@ -1,19 +1,32 @@
import React, { useState } from 'react';
import React, { useState, CSSProperties } from 'react';
import ReactFlow, {
removeElements,
addEdge,
MiniMap,
isNode,
Controls,
Background,
OnLoadParams,
Elements,
Connection,
Edge,
} from 'react-flow-renderer';
import ReactFlow, { removeElements, addEdge, MiniMap, isNode, Controls, Background } from 'react-flow-renderer';
import { getElements } from './utils';
const onLoad = (reactFlowInstance) => {
const buttonWrapperStyles: CSSProperties = { position: 'absolute', right: 10, top: 10, zIndex: 4 };
const onLoad = (reactFlowInstance: OnLoadParams) => {
reactFlowInstance.fitView();
console.log(reactFlowInstance.getElements());
};
const initialElements = getElements(30, 30);
const initialElements: Elements = getElements(30, 30);
const StressFlow = () => {
const [elements, setElements] = useState(initialElements);
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params) => setElements((els) => addEdge(params, els));
const [elements, setElements] = useState<Elements>(initialElements);
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
const updatePos = () => {
setElements((elms) => {
@@ -44,7 +57,7 @@ const StressFlow = () => {
<Controls />
<Background />
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
<div style={buttonWrapperStyles}>
<button onClick={updatePos} style={{ marginRight: 5 }}>
change pos
</button>
@@ -1,4 +1,6 @@
export function getElements(xElements = 10, yElements = 10) {
import { Elements } from 'react-flow-renderer';
export function getElements(xElements: number = 10, yElements: number = 10): Elements {
const initialElements = [];
let nodeId = 1;
let recentNodeId = null;
@@ -1,11 +1,11 @@
import React, { useState } from 'react';
import React, { useState, MouseEvent } from 'react';
import ReactFlow, { removeElements, addEdge } from 'react-flow-renderer';
import ReactFlow, { removeElements, addEdge, Node, FlowElement, Elements, Connection, Edge } from 'react-flow-renderer';
const onNodeDragStop = (event, node) => console.log('drag stop', node);
const onElementClick = (event, element) => console.log('click', element);
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
const elementsA = [
const elementsA: Elements = [
{ id: '1a', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 }, className: 'light' },
{ id: '2a', data: { label: 'Node 2' }, position: { x: 100, y: 100 }, className: 'light' },
{ id: '3a', data: { label: 'Node 3' }, position: { x: 400, y: 100 }, className: 'light' },
@@ -14,7 +14,7 @@ const elementsA = [
{ id: 'e1-3', source: '1a', target: '3a' },
];
const elementsB = [
const elementsB: Elements = [
{ id: 'inputb', type: 'input', data: { label: 'Input' }, position: { x: 300, y: 5 }, className: 'light' },
{ id: '1b', data: { label: 'Node 1' }, position: { x: 0, y: 100 }, className: 'light' },
{ id: '2b', data: { label: 'Node 2' }, position: { x: 200, y: 100 }, className: 'light' },
@@ -28,9 +28,9 @@ const elementsB = [
];
const BasicFlow = () => {
const [elements, setElements] = useState(elementsA);
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params) => setElements((els) => addEdge(params, els));
const [elements, setElements] = useState<Elements>(elementsA);
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
return (
<ReactFlow
@@ -39,7 +39,6 @@ const BasicFlow = () => {
onElementsRemove={onElementsRemove}
onConnect={onConnect}
onNodeDragStop={onNodeDragStop}
className="react-flow-basic-example"
>
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
<button onClick={() => setElements(elementsA)} style={{ marginRight: 5 }}>
@@ -1,7 +1,15 @@
import React, { useState } from 'react';
import ReactFlow, { Controls, updateEdge, addEdge } from 'react-flow-renderer';
import ReactFlow, {
Controls,
updateEdge,
addEdge,
Elements,
OnLoadParams,
Connection,
Edge,
} from 'react-flow-renderer';
const initialElements = [
const initialElements: Elements = [
{
id: '1',
type: 'input',
@@ -40,12 +48,13 @@ const initialElements = [
{ id: 'e1-2', source: '1', target: '2', label: 'This is a draggable edge' },
];
const onLoad = (reactFlowInstance) => reactFlowInstance.fitView();
const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.fitView();
const UpdatableEdge = () => {
const [elements, setElements] = useState(initialElements);
const onEdgeUpdate = (oldEdge, newConnection) => setElements((els) => updateEdge(oldEdge, newConnection, els));
const onConnect = (params) => setElements((els) => addEdge(params, els));
const [elements, setElements] = useState<Elements>(initialElements);
const onEdgeUpdate = (oldEdge: Edge, newConnection: Connection) =>
setElements((els) => updateEdge(oldEdge, newConnection, els));
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
return (
<ReactFlow elements={elements} onLoad={onLoad} snapToGrid={true} onEdgeUpdate={onEdgeUpdate} onConnect={onConnect}>
@@ -1,19 +1,19 @@
import React, { useEffect, useState } from 'react';
import ReactFlow from 'react-flow-renderer';
import ReactFlow, { Elements } from 'react-flow-renderer';
import './updatenode.css';
const initialElements = [
const initialElements: Elements = [
{ id: '1', data: { label: '-' }, position: { x: 100, y: 100 } },
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 200 } },
{ id: 'e1-2', source: '1', target: '2' },
];
const UpdateNode = () => {
const [elements, setElements] = useState(initialElements);
const [nodeName, setNodeName] = useState('Node 1');
const [nodeBg, setNodeBg] = useState('#eee');
const [nodeHidden, setNodeHidden] = useState(false);
const [elements, setElements] = useState<Elements>(initialElements);
const [nodeName, setNodeName] = useState<string>('Node 1');
const [nodeBg, setNodeBg] = useState<string>('#eee');
const [nodeHidden, setNodeHidden] = useState<boolean>(false);
useEffect(() => {
setElements((els) =>
@@ -7,9 +7,13 @@ import ReactFlow, {
MiniMap,
useZoomPanHelper,
ReactFlowProvider,
Elements,
ElementId,
Connection,
Edge,
} from 'react-flow-renderer';
const initialElements = [
const initialElements: Elements = [
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 }, className: 'light' },
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 }, className: 'light' },
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 }, className: 'light' },
@@ -19,12 +23,12 @@ const initialElements = [
];
let id = 5;
const getId = () => `${id++}`;
const getId = (): ElementId => `${id++}`;
const UseZoomPanHelperFlow = () => {
const [elements, setElements] = useState(initialElements);
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params) => setElements((els) => addEdge(params, els));
const [elements, setElements] = useState<Elements>(initialElements);
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
const { project } = useZoomPanHelper();
const onPaneClick = useCallback(
@@ -53,8 +57,10 @@ const UseZoomPanHelperFlow = () => {
);
};
export default () => (
const WrappedFlow = () => (
<ReactFlowProvider>
<UseZoomPanHelperFlow />
</ReactFlowProvider>
);
export default WrappedFlow;
-62
View File
@@ -1,62 +0,0 @@
import React, { useState } from 'react';
import ReactFlow, { addEdge, Handle } from 'react-flow-renderer';
import './validation.css';
const initialElements = [
{ id: '0', type: 'custominput', position: { x: 0, y: 150 } },
{ id: 'A', type: 'customnode', position: { x: 250, y: 0 } },
{ id: 'B', type: 'customnode', position: { x: 250, y: 150 } },
{ id: 'C', type: 'customnode', position: { x: 250, y: 300 } },
];
const onLoad = (reactFlowInstance) => reactFlowInstance.fitView();
const isValidConnection = (connection) => connection.target === 'B';
const onConnectStart = (event, { nodeId, handleType }) => console.log('on connect start', { nodeId, handleType });
const onConnectStop = (event) => console.log('on connect stop', event);
const onConnectEnd = (event) => console.log('on connect end', event);
const CustomInput = () => (
<>
<div>Only connectable with B</div>
<Handle type="source" position="right" isValidConnection={isValidConnection} />
</>
);
const CustomNode = ({ id }) => (
<>
<Handle type="target" position="left" isValidConnection={isValidConnection} />
<div>{id}</div>
<Handle type="source" position="right" isValidConnection={isValidConnection} />
</>
);
const nodeTypes = {
custominput: CustomInput,
customnode: CustomNode,
};
const HorizontalFlow = () => {
const [elements, setElements] = useState(initialElements);
const onConnect = (params) => {
console.log('on connect', params);
setElements((els) => addEdge(params, els));
};
return (
<ReactFlow
elements={elements}
onConnect={onConnect}
selectNodesOnDrag={false}
onLoad={onLoad}
className="validationflow"
nodeTypes={nodeTypes}
onConnectStart={onConnectStart}
onConnectStop={onConnectStop}
onConnectEnd={onConnectEnd}
/>
);
};
export default HorizontalFlow;
+73
View File
@@ -0,0 +1,73 @@
import React, { useState, MouseEvent as ReactMouseEvent, FC } from 'react';
import ReactFlow, {
addEdge,
Handle,
OnLoadParams,
Connection,
Position,
Elements,
Edge,
OnConnectStartParams,
NodeProps,
NodeTypesType,
} from 'react-flow-renderer';
import './validation.css';
const initialElements: Elements = [
{ id: '0', type: 'custominput', position: { x: 0, y: 150 } },
{ id: 'A', type: 'customnode', position: { x: 250, y: 0 } },
{ id: 'B', type: 'customnode', position: { x: 250, y: 150 } },
{ id: 'C', type: 'customnode', position: { x: 250, y: 300 } },
];
const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.fitView();
const isValidConnection = (connection: Connection) => connection.target === 'B';
const onConnectStart = (_: ReactMouseEvent, { nodeId, handleType }: OnConnectStartParams) =>
console.log('on connect start', { nodeId, handleType });
const onConnectStop = (event: MouseEvent) => console.log('on connect stop', event);
const onConnectEnd = (event: MouseEvent) => console.log('on connect end', event);
const CustomInput: FC<NodeProps> = () => (
<>
<div>Only connectable with B</div>
<Handle type="source" position={Position.Right} isValidConnection={isValidConnection} />
</>
);
const CustomNode: FC<NodeProps> = ({ id }) => (
<>
<Handle type="target" position={Position.Left} isValidConnection={isValidConnection} />
<div>{id}</div>
<Handle type="source" position={Position.Right} isValidConnection={isValidConnection} />
</>
);
const nodeTypes: NodeTypesType = {
custominput: CustomInput,
customnode: CustomNode,
};
const HorizontalFlow = () => {
const [elements, setElements] = useState<Elements>(initialElements);
const onConnect = (params: Connection | Edge) => {
console.log('on connect', params);
setElements((els) => addEdge(params, els));
};
return (
<ReactFlow
elements={elements}
onConnect={onConnect}
selectNodesOnDrag={false}
onLoad={onLoad}
className="validationflow"
nodeTypes={nodeTypes}
onConnectStart={onConnectStart}
onConnectStop={onConnectStop}
onConnectEnd={onConnectEnd}
/>
);
};
export default HorizontalFlow;
@@ -1,4 +1,4 @@
import React from 'react';
import React, { ChangeEvent } from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route, Switch, withRouter } from 'react-router-dom';
@@ -109,7 +109,7 @@ const routes = [
];
const Header = withRouter(({ history, location }) => {
const onChange = (event) => history.push(event.target.value);
const onChange = (event: ChangeEvent<HTMLSelectElement>) => history.push(event.target.value);
return (
<header>
+1
View File
@@ -0,0 +1 @@
/// <reference types="react-scripts" />
+26
View File
@@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"src"
]
}
+10 -12230
View File
File diff suppressed because it is too large Load Diff
+4 -8
View File
@@ -12,11 +12,10 @@
"scripts": {
"build": "rollup -c --environment NODE_ENV:production && postcss src/*.css --dir dist",
"start": "rollup -w -c",
"start:examples": "rollup -c rollup.examples.js -w",
"build:examples": "rollup -c rollup.examples.js --environment NODE_ENV:production",
"dev": "npm run start:examples",
"start:testserver": "rollup -c --environment NODE_ENV:testing && rollup -c rollup.examples.js -w --environment NODE_ENV:testing",
"build:dev": "npm install && npm run build:examples",
"start:examples": "cd example && npm start",
"build:examples": "cd example && npm run build",
"start:testserver": "npm run build && npm run start:examples",
"build:dev": "npm install && npm run build && cd example && npm install && npm run build",
"dev:wait": "start-server-and-test start:testserver http-get://localhost:3000",
"test": "npm run dev:wait test:chrome",
"test:chrome": "cypress run --browser chrome --headless",
@@ -57,8 +56,6 @@
"autoprefixer": "^10.2.4",
"babel-preset-react-app": "^10.0.0",
"cypress": "^6.5.0",
"dagre": "^0.8.5",
"localforage": "^1.9.0",
"postcss": "^8.2.6",
"postcss-cli": "^8.3.1",
"postcss-nested": "^5.0.3",
@@ -66,7 +63,6 @@
"prop-types": "^15.7.2",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-router-dom": "^5.2.0",
"release-it": "^14.4.1",
"rollup": "^2.39.0",
"rollup-plugin-bundle-size": "^1.0.3",
+1
View File
@@ -41,6 +41,7 @@ export const baseConfig = ({ mainFile = pkg.main, moduleFile = pkg.module, injec
__REACT_FLOW_VERSION__: JSON.stringify(pkg.version),
// this comes from the easy-peasy dependency
'process.env.FORCE_SIMILAR_INSTEAD_OF_MAP': false,
preventAssignment: true,
}),
bundleSize(),
postcss({
-58
View File
@@ -1,58 +0,0 @@
import path from 'path';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import babel from '@rollup/plugin-babel';
import alias from '@rollup/plugin-alias';
import replace from '@rollup/plugin-replace';
import postcss from 'rollup-plugin-postcss';
import livereload from 'rollup-plugin-livereload';
import serve from 'rollup-plugin-serve';
import { baseConfig } from './rollup.config.js';
const isProd = process.env.NODE_ENV === 'production';
const isTesting = process.env.NODE_ENV === 'testing';
const serveFiles = !isProd || isTesting;
const doLiveReload = !isProd && !isTesting;
const rollupExamplesConfig = {
input: 'example/src/index.js',
output: [
{
dir: 'example/public/dist',
format: 'iife',
sourcemap: !isProd,
},
],
plugins: [
replace({
'process.env.NODE_ENV': isProd ? JSON.stringify('production') : JSON.stringify('development'),
'process.env.FORCE_SIMILAR_INSTEAD_OF_MAP': false,
}),
postcss({
modules: false,
}),
alias({
entries: [{ find: 'react-flow-renderer', replacement: path.resolve(__dirname, 'dist', 'ReactFlow.esm.js') }],
}),
babel({
exclude: 'node_modules/**',
babelHelpers: 'runtime',
}),
resolve(),
commonjs({
include: 'node_modules/**',
}),
serveFiles &&
serve({
open: true,
port: 3000,
contentBase: 'example/public/',
historyApiFallback: true,
}),
doLiveReload && livereload(),
],
};
export default isTesting ? rollupExamplesConfig : [baseConfig(), rollupExamplesConfig];