@@ -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).
|
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 React from 'react';
|
||||||
import ReactFlow from 'react-flow-renderer';
|
import ReactFlow from 'react-flow-renderer';
|
||||||
|
|
||||||
@@ -35,21 +35,27 @@ const BasicFlow = () => <ReactFlow elements={elements} />;
|
|||||||
|
|
||||||
## Development
|
## 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:
|
If you want to contribute or develop custom features the easiest way is to start the dev server:
|
||||||
|
|
||||||
```
|
```sh
|
||||||
npm run dev
|
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
|
||||||
|
|
||||||
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:
|
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
|
npm run test
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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*
|
||||||
@@ -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
|
||||||
|
```
|
||||||
Generated
+16608
File diff suppressed because it is too large
Load Diff
@@ -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 |
@@ -2,13 +2,11 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="description" content="React Flow test flows" />
|
|
||||||
<title>React Flow - Test Flows</title>
|
<title>React Flow Examples</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|
||||||
<script src="/dist/index.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
User-agent: *
|
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 onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
||||||
const onElementClick = (event, element) => console.log('click', element);
|
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: '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: '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' },
|
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 }, className: 'light' },
|
||||||
@@ -15,11 +27,11 @@ const initialElements = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const BasicFlow = () => {
|
const BasicFlow = () => {
|
||||||
const [rfInstance, setRfInstance] = useState(null);
|
const [rfInstance, setRfInstance] = useState<OnLoadParams | null>(null);
|
||||||
const [elements, setElements] = useState(initialElements);
|
const [elements, setElements] = useState<Elements>(initialElements);
|
||||||
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
|
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
||||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
const onConnect = (params: Edge | Connection) => setElements((els) => addEdge(params, els));
|
||||||
const onLoad = (reactFlowInstance) => setRfInstance(reactFlowInstance);
|
const onLoad = (reactFlowInstance: OnLoadParams) => setRfInstance(reactFlowInstance);
|
||||||
|
|
||||||
const updatePos = () => {
|
const updatePos = () => {
|
||||||
setElements((elms) => {
|
setElements((elms) => {
|
||||||
@@ -36,8 +48,8 @@ const BasicFlow = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const logToObject = () => console.log(rfInstance.toObject());
|
const logToObject = () => console.log(rfInstance?.toObject());
|
||||||
const resetTransform = () => rfInstance.setTransform({ x: 0, y: 0, zoom: 1 });
|
const resetTransform = () => rfInstance?.setTransform({ x: 0, y: 0, zoom: 1 });
|
||||||
|
|
||||||
const toggleClassnames = () => {
|
const toggleClassnames = () => {
|
||||||
setElements((elms) => {
|
setElements((elms) => {
|
||||||
@@ -64,7 +76,7 @@ const BasicFlow = () => {
|
|||||||
minZoom={0.2}
|
minZoom={0.2}
|
||||||
maxZoom={4}
|
maxZoom={4}
|
||||||
>
|
>
|
||||||
<Background variant="lines" />
|
<Background variant={BackgroundVariant.Lines} />
|
||||||
|
|
||||||
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
|
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
|
||||||
<button onClick={resetTransform} style={{ marginRight: 5 }}>
|
<button onClick={resetTransform} style={{ marginRight: 5 }}>
|
||||||
+5
-11
@@ -1,15 +1,7 @@
|
|||||||
import React from 'react';
|
import React, { FC } from 'react';
|
||||||
|
import { ConnectionLineComponentProps } from 'react-flow-renderer';
|
||||||
|
|
||||||
export default ({
|
const ConnectionLine: FC<ConnectionLineComponentProps> = ({ sourceX, sourceY, targetX, targetY }) => {
|
||||||
sourceX,
|
|
||||||
sourceY,
|
|
||||||
sourcePosition,
|
|
||||||
targetX,
|
|
||||||
targetY,
|
|
||||||
targetPosition,
|
|
||||||
connectionLineType,
|
|
||||||
connectionLineStyle,
|
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<g>
|
<g>
|
||||||
<path
|
<path
|
||||||
@@ -23,3 +15,5 @@ export default ({
|
|||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default ConnectionLine;
|
||||||
@@ -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';
|
import ColorSelectorNode from './ColorSelectorNode';
|
||||||
|
|
||||||
const onLoad = (reactFlowInstance) => console.log('flow loaded:', reactFlowInstance);
|
const onLoad = (reactFlowInstance: OnLoadParams) => console.log('flow loaded:', reactFlowInstance);
|
||||||
const onNodeDragStop = (event, node) => console.log('drag stop', node);
|
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
||||||
const onElementClick = (event, element) => console.log('click', element);
|
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
|
||||||
|
|
||||||
const initBgColor = '#1A192B';
|
const initBgColor = '#1A192B';
|
||||||
|
|
||||||
const connectionLineStyle = { stroke: '#fff' };
|
const connectionLineStyle = { stroke: '#fff' };
|
||||||
const snapGrid = [16, 16];
|
const snapGrid: SnapGrid = [16, 16];
|
||||||
const nodeTypes = {
|
const nodeTypes = {
|
||||||
selectorNode: ColorSelectorNode,
|
selectorNode: ColorSelectorNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
const CustomNodeFlow = () => {
|
const CustomNodeFlow = () => {
|
||||||
const [elements, setElements] = useState([]);
|
const [elements, setElements] = useState<Elements>([]);
|
||||||
const [bgColor, setBgColor] = useState(initBgColor);
|
const [bgColor, setBgColor] = useState<string>(initBgColor);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onChange = (event) => {
|
const onChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
setElements((els) =>
|
setElements((els) =>
|
||||||
els.map((e) => {
|
els.map((e) => {
|
||||||
if (isEdge(e) || e.id !== '2') {
|
if (isEdge(e) || e.id !== '2') {
|
||||||
@@ -44,7 +59,13 @@ const CustomNodeFlow = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
setElements([
|
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',
|
id: '2',
|
||||||
type: 'selectorNode',
|
type: 'selectorNode',
|
||||||
@@ -52,8 +73,20 @@ const CustomNodeFlow = () => {
|
|||||||
style: { border: '1px solid #777', padding: 10 },
|
style: { border: '1px solid #777', padding: 10 },
|
||||||
position: { x: 250, y: 50 },
|
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: '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' } },
|
{ 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 onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
||||||
const onConnect = (params) =>
|
const onConnect = (params: Connection | Edge) =>
|
||||||
setElements((els) => addEdge({ ...params, animated: true, style: { stroke: '#fff' } }, els));
|
setElements((els) => addEdge({ ...params, animated: true, style: { stroke: '#fff' } }, els));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -81,13 +114,16 @@ const CustomNodeFlow = () => {
|
|||||||
defaultZoom={1.5}
|
defaultZoom={1.5}
|
||||||
>
|
>
|
||||||
<MiniMap
|
<MiniMap
|
||||||
nodeStrokeColor={(n) => {
|
nodeStrokeColor={(n: Node): string => {
|
||||||
if (n.type === 'input') return '#0041d0';
|
if (n.type === 'input') return '#0041d0';
|
||||||
if (n.type === 'selectorNode') return bgColor;
|
if (n.type === 'selectorNode') return bgColor;
|
||||||
if (n.type === 'output') return '#ff0072';
|
if (n.type === 'output') return '#ff0072';
|
||||||
|
|
||||||
|
return '#eee';
|
||||||
}}
|
}}
|
||||||
nodeColor={(n) => {
|
nodeColor={(n: Node): string => {
|
||||||
if (n.type === 'selectorNode') return bgColor;
|
if (n.type === 'selectorNode') return bgColor;
|
||||||
|
|
||||||
return '#fff';
|
return '#fff';
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -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;
|
||||||
@@ -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;
|
|
||||||
@@ -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 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';
|
import { getElements } from './utils';
|
||||||
|
|
||||||
const onLoad = (reactFlowInstance) => {
|
const onLoad = (reactFlowInstance: OnLoadParams) => {
|
||||||
reactFlowInstance.fitView();
|
reactFlowInstance.fitView();
|
||||||
console.log(reactFlowInstance.getElements());
|
console.log(reactFlowInstance.getElements());
|
||||||
};
|
};
|
||||||
@@ -14,9 +24,9 @@ const onLoad = (reactFlowInstance) => {
|
|||||||
const initialElements = getElements();
|
const initialElements = getElements();
|
||||||
|
|
||||||
const EdgeTypesFlow = () => {
|
const EdgeTypesFlow = () => {
|
||||||
const [elements, setElements] = useState(initialElements);
|
const [elements, setElements] = useState<Elements>(initialElements);
|
||||||
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
|
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
||||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
|
import { ElementId, Elements, Position } from 'react-flow-renderer';
|
||||||
|
|
||||||
const nodeWidth = 80;
|
const nodeWidth = 80;
|
||||||
const nodeGapWidth = nodeWidth * 2;
|
const nodeGapWidth = nodeWidth * 2;
|
||||||
const nodeStyle = { width: nodeWidth, fontSize: 11, color: 'white' };
|
const nodeStyle = { width: nodeWidth, fontSize: 11, color: 'white' };
|
||||||
|
|
||||||
const sourceTargetPositions = [
|
const sourceTargetPositions = [
|
||||||
{ source: 'bottom', target: 'top' },
|
{ source: Position.Bottom, target: Position.Top },
|
||||||
{ source: 'right', target: 'left' },
|
{ source: Position.Right, target: Position.Left },
|
||||||
];
|
];
|
||||||
const nodeColors = [
|
const nodeColors = [
|
||||||
['#1e9e99', '#4cb3ac', '#6ec9c0', '#8ddfd4'],
|
['#1e9e99', '#4cb3ac', '#6ec9c0', '#8ddfd4'],
|
||||||
@@ -47,9 +49,9 @@ const offsets = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
let id = 0;
|
let id = 0;
|
||||||
const getNodeId = () => (id++).toString();
|
const getNodeId = (): ElementId => (id++).toString();
|
||||||
|
|
||||||
export function getElements() {
|
export function getElements(): Elements {
|
||||||
const initialElements = [];
|
const initialElements = [];
|
||||||
|
|
||||||
for (let sourceTargetIndex = 0; sourceTargetIndex < sourceTargetPositions.length; sourceTargetIndex++) {
|
for (let sourceTargetIndex = 0; sourceTargetIndex < sourceTargetPositions.length; sourceTargetIndex++) {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React, { FC } from 'react';
|
||||||
import { getBezierPath, getMarkerEnd } from 'react-flow-renderer';
|
import { EdgeProps, getBezierPath, getMarkerEnd } from 'react-flow-renderer';
|
||||||
|
|
||||||
export default function CustomEdge({
|
const CustomEdge: FC<EdgeProps> = ({
|
||||||
id,
|
id,
|
||||||
sourceX,
|
sourceX,
|
||||||
sourceY,
|
sourceY,
|
||||||
@@ -13,7 +13,7 @@ export default function CustomEdge({
|
|||||||
data,
|
data,
|
||||||
arrowHeadType,
|
arrowHeadType,
|
||||||
markerEndId,
|
markerEndId,
|
||||||
}) {
|
}) => {
|
||||||
const edgePath = getBezierPath({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition });
|
const edgePath = getBezierPath({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition });
|
||||||
const markerEnd = getMarkerEnd(arrowHeadType, markerEndId);
|
const markerEnd = getMarkerEnd(arrowHeadType, markerEndId);
|
||||||
|
|
||||||
@@ -27,4 +27,6 @@ export default function CustomEdge({
|
|||||||
</text>
|
</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';
|
import CustomEdge from './CustomEdge';
|
||||||
|
|
||||||
const onLoad = (reactFlowInstance) => reactFlowInstance.fitView();
|
const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.fitView();
|
||||||
const onNodeDragStop = (event, node) => console.log('drag stop', node);
|
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
||||||
const onElementClick = (event, element) => console.log('click', element);
|
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: '1', type: 'input', data: { label: 'Input 1' }, position: { x: 250, y: 0 } },
|
||||||
{ id: '2', data: { label: 'Node 2' }, position: { x: 150, y: 100 } },
|
{ id: '2', data: { label: 'Node 2' }, position: { x: 150, y: 100 } },
|
||||||
{ id: '2a', data: { label: 'Node 2a' }, position: { x: 0, y: 180 } },
|
{ id: '2a', data: { label: 'Node 2a' }, position: { x: 0, y: 180 } },
|
||||||
@@ -31,7 +45,7 @@ const initialElements = [
|
|||||||
target: '6',
|
target: '6',
|
||||||
label: 'styled label',
|
label: 'styled label',
|
||||||
labelStyle: { fill: 'red', fontWeight: 700 },
|
labelStyle: { fill: 'red', fontWeight: 700 },
|
||||||
arrowHeadType: 'arrow',
|
arrowHeadType: ArrowHeadType.Arrow,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'e5-7',
|
id: 'e5-7',
|
||||||
@@ -41,20 +55,27 @@ const initialElements = [
|
|||||||
labelBgPadding: [8, 4],
|
labelBgPadding: [8, 4],
|
||||||
labelBgBorderRadius: 4,
|
labelBgBorderRadius: 4,
|
||||||
labelBgStyle: { fill: '#FFCC00', color: '#fff', fillOpacity: 0.7 },
|
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,
|
custom: CustomEdge,
|
||||||
};
|
};
|
||||||
|
|
||||||
const EdgesFlow = () => {
|
const EdgesFlow = () => {
|
||||||
const [elements, setElements] = useState(initialElements);
|
const [elements, setElements] = useState<Elements>(initialElements);
|
||||||
|
|
||||||
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
|
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
||||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
@@ -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;
|
|
||||||
@@ -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 React, { useState } from 'react';
|
||||||
|
|
||||||
import ReactFlow, { addEdge, MiniMap, Controls } from 'react-flow-renderer';
|
|
||||||
import { useEffect } from 'react';
|
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: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
|
||||||
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } },
|
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } },
|
||||||
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 } },
|
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 } },
|
||||||
@@ -14,9 +14,9 @@ const initialElements = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const HiddenFlow = () => {
|
const HiddenFlow = () => {
|
||||||
const [elements, setElements] = useState(initialElements);
|
const [elements, setElements] = useState<Elements>(initialElements);
|
||||||
const [isHidden, setIsHidden] = useState(false);
|
const [isHidden, setIsHidden] = useState<boolean>(false);
|
||||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setElements((els) =>
|
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: Elements = [
|
||||||
|
|
||||||
const initialElements = [
|
|
||||||
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
|
{ 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: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } },
|
||||||
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, 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' },
|
{ id: 'e1-3', source: '1', target: '3' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const onNodeDragStart = (event, node) => console.log('drag start', node);
|
const onNodeDragStart = (_: MouseEvent, node: Node) => console.log('drag start', node);
|
||||||
const onNodeDragStop = (event, node) => console.log('drag stop', node);
|
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
||||||
const onElementClick = (event, element) => console.log('click', element);
|
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
|
||||||
const onPaneClick = (event) => console.log('onPaneClick', event);
|
const onPaneClick = (event: MouseEvent) => console.log('onPaneClick', event);
|
||||||
const onPaneScroll = (event) => console.log('onPaneScroll', event);
|
const onPaneScroll = (event?: WheelEvent) => console.log('onPaneScroll', event);
|
||||||
const onPaneContextMenu = (event) => console.log('onPaneContextMenu', event);
|
const onPaneContextMenu = (event: MouseEvent) => console.log('onPaneContextMenu', event);
|
||||||
const onMoveEnd = (event) => console.log('onMoveEnd', event);
|
const onMoveEnd = (flowTranasform?: FlowTransform) => console.log('onMoveEnd', flowTranasform);
|
||||||
|
|
||||||
const InteractionFlow = () => {
|
const InteractionFlow = () => {
|
||||||
const [elements, setElements] = useState(initialElements);
|
const [elements, setElements] = useState<Elements>(initialElements);
|
||||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
||||||
|
|
||||||
const [isSelectable, setIsSelectable] = useState(false);
|
const [isSelectable, setIsSelectable] = useState<boolean>(false);
|
||||||
const [isDraggable, setIsDraggable] = useState(false);
|
const [isDraggable, setIsDraggable] = useState<boolean>(false);
|
||||||
const [isConnectable, setIsConnectable] = useState(false);
|
const [isConnectable, setIsConnectable] = useState<boolean>(false);
|
||||||
const [zoomOnScroll, setZoomOnScroll] = useState(false);
|
const [zoomOnScroll, setZoomOnScroll] = useState<boolean>(false);
|
||||||
const [zoomOnPinch, setZoomOnPinch] = useState(false);
|
const [zoomOnPinch, setZoomOnPinch] = useState<boolean>(false);
|
||||||
const [panOnScroll, setPanOnScroll] = useState(false);
|
const [panOnScroll, setPanOnScroll] = useState<boolean>(false);
|
||||||
const [panOnScrollMode, setPanOnScrollMode] = useState('free');
|
const [panOnScrollMode, setPanOnScrollMode] = useState<PanOnScrollMode>(PanOnScrollMode.Free);
|
||||||
const [zoomOnDoubleClick, setZoomOnDoubleClick] = useState(false);
|
const [zoomOnDoubleClick, setZoomOnDoubleClick] = useState<boolean>(false);
|
||||||
const [paneMoveable, setPaneMoveable] = useState(true);
|
const [paneMoveable, setPaneMoveable] = useState<boolean>(true);
|
||||||
const [captureZoomClick, setCaptureZoomClick] = useState(false);
|
const [captureZoomClick, setCaptureZoomClick] = useState<boolean>(false);
|
||||||
const [captureZoomScroll, setCaptureZoomScroll] = useState(false);
|
const [captureZoomScroll, setCaptureZoomScroll] = useState<boolean>(false);
|
||||||
const [captureElementClick, setCaptureElementClick] = useState(false);
|
const [captureElementClick, setCaptureElementClick] = useState<boolean>(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
@@ -139,7 +149,7 @@ const InteractionFlow = () => {
|
|||||||
<select
|
<select
|
||||||
id="panonscrollmode"
|
id="panonscrollmode"
|
||||||
value={panOnScrollMode}
|
value={panOnScrollMode}
|
||||||
onChange={(event) => setPanOnScrollMode(event.target.value)}
|
onChange={(event) => setPanOnScrollMode(event.target.value as PanOnScrollMode)}
|
||||||
className="react-flow__panonscrollmode"
|
className="react-flow__panonscrollmode"
|
||||||
>
|
>
|
||||||
<option value="free">free</option>
|
<option value="free">free</option>
|
||||||
@@ -1,5 +1,16 @@
|
|||||||
import React, { useState } from 'react';
|
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 dagre from 'dagre';
|
||||||
|
|
||||||
import initialElements from './initial-elements';
|
import initialElements from './initial-elements';
|
||||||
@@ -9,17 +20,17 @@ import './layouting.css';
|
|||||||
const dagreGraph = new dagre.graphlib.Graph();
|
const dagreGraph = new dagre.graphlib.Graph();
|
||||||
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
||||||
|
|
||||||
const nodeExtent = [
|
const nodeExtent: NodeExtent = [
|
||||||
[0, 0],
|
[0, 0],
|
||||||
[1000, 1000],
|
[1000, 1000],
|
||||||
];
|
];
|
||||||
|
|
||||||
const LayoutFlow = () => {
|
const LayoutFlow = () => {
|
||||||
const [elements, setElements] = useState(initialElements);
|
const [elements, setElements] = useState<Elements>(initialElements);
|
||||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
||||||
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
|
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
||||||
|
|
||||||
const onLayout = (direction) => {
|
const onLayout = (direction: string) => {
|
||||||
const isHorizontal = direction === 'LR';
|
const isHorizontal = direction === 'LR';
|
||||||
dagreGraph.setGraph({ rankdir: direction });
|
dagreGraph.setGraph({ rankdir: direction });
|
||||||
|
|
||||||
@@ -36,8 +47,8 @@ const LayoutFlow = () => {
|
|||||||
const layoutedElements = elements.map((el) => {
|
const layoutedElements = elements.map((el) => {
|
||||||
if (isNode(el)) {
|
if (isNode(el)) {
|
||||||
const nodeWithPosition = dagreGraph.node(el.id);
|
const nodeWithPosition = dagreGraph.node(el.id);
|
||||||
el.targetPosition = isHorizontal ? 'left' : 'top';
|
el.targetPosition = isHorizontal ? Position.Left : Position.Top;
|
||||||
el.sourcePosition = isHorizontal ? 'right' : 'bottom';
|
el.sourcePosition = isHorizontal ? Position.Right : Position.Bottom;
|
||||||
// we need to pass a slighltiy different position in order to notify react flow about the change
|
// 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?
|
// @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 };
|
el.position = { x: nodeWithPosition.x + Math.random() / 1000, y: nodeWithPosition.y };
|
||||||
+6
-2
@@ -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',
|
id: '1',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
@@ -65,3 +67,5 @@ export default [
|
|||||||
{ id: 'e56', source: '5', target: '6', type: 'smoothstep' },
|
{ id: 'e56', source: '5', target: '6', type: 'smoothstep' },
|
||||||
{ id: 'e57', source: '5', target: '7', 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',
|
id: '1',
|
||||||
sourcePosition: 'right',
|
sourcePosition: Position.Right,
|
||||||
type: 'input',
|
type: 'input',
|
||||||
data: { label: 'Input' },
|
data: { label: 'Input' },
|
||||||
position: { x: 0, y: 80 },
|
position: { x: 0, y: 80 },
|
||||||
@@ -15,17 +15,19 @@ const initialElements = [
|
|||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
type: 'output',
|
type: 'output',
|
||||||
sourcePosition: 'right',
|
sourcePosition: Position.Right,
|
||||||
targetPosition: 'left',
|
targetPosition: Position.Left,
|
||||||
data: { label: 'A Node' },
|
data: { label: 'A Node' },
|
||||||
position: { x: 250, y: 0 },
|
position: { x: 250, y: 0 },
|
||||||
},
|
},
|
||||||
{ id: 'e1-2', source: '1', type: 'smoothstep', target: '2', animated: true },
|
{ 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 NodeTypeChangeFlow = () => {
|
||||||
const [elements, setElements] = useState(initialElements);
|
const [elements, setElements] = useState<Elements>(initialElements);
|
||||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
||||||
const changeType = () => {
|
const changeType = () => {
|
||||||
setElements((elms) =>
|
setElements((elms) =>
|
||||||
elms.map((el) => {
|
elms.map((el) => {
|
||||||
@@ -43,7 +45,7 @@ const NodeTypeChangeFlow = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlow elements={elements} onConnect={onConnect} onLoad={onLoad}>
|
<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
|
change type
|
||||||
</button>
|
</button>
|
||||||
</ReactFlow>
|
</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 onNodeDragStart = (_: MouseEvent, node: Node) => console.log('drag start', node);
|
||||||
const onNodeDrag = (event, node) => console.log('drag', node);
|
const onNodeDrag = (_: MouseEvent, node: Node) => console.log('drag', node);
|
||||||
const onNodeDragStop = (event, node) => console.log('drag stop', node);
|
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
||||||
const onPaneClick = (event) => console.log('pane click', event);
|
const onPaneClick = (event: MouseEvent) => console.log('pane click', event);
|
||||||
const onPaneScroll = (event) => console.log('pane scroll', event);
|
const onPaneScroll = (event?: MouseEvent) => console.log('pane scroll', event);
|
||||||
const onPaneContextMenu = (event) => console.log('pane context menu', event);
|
const onPaneContextMenu = (event: MouseEvent) => console.log('pane context menu', event);
|
||||||
const onSelectionDrag = (event, nodes) => console.log('selection drag', nodes);
|
const onSelectionDrag = (_: MouseEvent, nodes: Node[]) => console.log('selection drag', nodes);
|
||||||
const onSelectionDragStart = (event, nodes) => console.log('selection drag start', nodes);
|
const onSelectionDragStart = (_: MouseEvent, nodes: Node[]) => console.log('selection drag start', nodes);
|
||||||
const onSelectionDragStop = (event, nodes) => console.log('selection drag stop', nodes);
|
const onSelectionDragStop = (_: MouseEvent, nodes: Node[]) => console.log('selection drag stop', nodes);
|
||||||
const onSelectionContextMenu = (event, nodes) => {
|
const onSelectionContextMenu = (event: MouseEvent, nodes: Node[]) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
console.log('selection context menu', nodes);
|
console.log('selection context menu', nodes);
|
||||||
};
|
};
|
||||||
const onElementClick = (event, element) => console.log(`${isNode(element) ? 'node' : 'edge'} click:`, element);
|
const onElementClick = (_: MouseEvent, element: FlowElement) =>
|
||||||
const onSelectionChange = (elements) => console.log('selection change', elements);
|
console.log(`${isNode(element) ? 'node' : 'edge'} click:`, element);
|
||||||
const onLoad = (reactFlowInstance) => {
|
const onSelectionChange = (elements: Elements | null) => console.log('selection change', elements);
|
||||||
|
const onLoad = (reactFlowInstance: OnLoadParams) => {
|
||||||
console.log('flow loaded:', reactFlowInstance);
|
console.log('flow loaded:', reactFlowInstance);
|
||||||
reactFlowInstance.fitView();
|
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',
|
id: '1',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
@@ -101,7 +118,7 @@ const initialElements = [
|
|||||||
{ id: 'e1-2', source: '1', target: '2', label: 'this is an edge label' },
|
{ id: 'e1-2', source: '1', target: '2', label: 'this is an edge label' },
|
||||||
{ id: 'e1-3', source: '1', target: '3' },
|
{ id: 'e1-3', source: '1', target: '3' },
|
||||||
{ id: 'e3-4', source: '3', target: '4', animated: true, label: 'animated edge' },
|
{ 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-6', source: '5', target: '6', type: 'smoothstep', label: 'smooth step edge' },
|
||||||
{
|
{
|
||||||
id: 'e5-7',
|
id: 'e5-7',
|
||||||
@@ -115,13 +132,28 @@ const initialElements = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const connectionLineStyle = { stroke: '#ddd' };
|
const connectionLineStyle: CSSProperties = { stroke: '#ddd' };
|
||||||
const snapGrid = [16, 16];
|
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 OverviewFlow = () => {
|
||||||
const [elements, setElements] = useState(initialElements);
|
const [elements, setElements] = useState(initialElements);
|
||||||
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
|
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
||||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
@@ -146,22 +178,7 @@ const OverviewFlow = () => {
|
|||||||
snapToGrid={true}
|
snapToGrid={true}
|
||||||
snapGrid={snapGrid}
|
snapGrid={snapGrid}
|
||||||
>
|
>
|
||||||
<MiniMap
|
<MiniMap nodeStrokeColor={nodeStrokeColor} nodeColor={nodeColor} nodeBorderRadius={2} />
|
||||||
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}
|
|
||||||
/>
|
|
||||||
<Controls />
|
<Controls />
|
||||||
<Background color="#aaa" gap={16} />
|
<Background color="#aaa" gap={16} />
|
||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useStoreState, useStoreActions, isNode } from 'react-flow-renderer';
|
import { useStoreState, useStoreActions } from 'react-flow-renderer';
|
||||||
|
|
||||||
const Sidebar = () => {
|
const Sidebar = () => {
|
||||||
const nodes = useStoreState((store) => store.nodes);
|
const nodes = useStoreState((store) => store.nodes);
|
||||||
@@ -1,14 +1,25 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState, MouseEvent } from 'react';
|
||||||
import ReactFlow, { ReactFlowProvider, addEdge, removeElements, Controls } from 'react-flow-renderer';
|
import ReactFlow, {
|
||||||
|
ReactFlowProvider,
|
||||||
|
addEdge,
|
||||||
|
removeElements,
|
||||||
|
Controls,
|
||||||
|
OnLoadParams,
|
||||||
|
FlowElement,
|
||||||
|
Connection,
|
||||||
|
Edge,
|
||||||
|
Elements,
|
||||||
|
ConnectionMode,
|
||||||
|
} from 'react-flow-renderer';
|
||||||
|
|
||||||
import Sidebar from './Sidebar';
|
import Sidebar from './Sidebar';
|
||||||
|
|
||||||
import './provider.css';
|
import './provider.css';
|
||||||
|
|
||||||
const onElementClick = (event, element) => console.log('click', element);
|
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
|
||||||
const onLoad = (reactFlowInstance) => console.log('flow loaded:', reactFlowInstance);
|
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: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
|
||||||
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } },
|
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } },
|
||||||
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 } },
|
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 } },
|
||||||
@@ -18,9 +29,9 @@ const initialElements = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const ProviderFlow = () => {
|
const ProviderFlow = () => {
|
||||||
const [elements, setElements] = useState(initialElements);
|
const [elements, setElements] = useState<Elements>(initialElements);
|
||||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
||||||
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
|
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="providerflow">
|
<div className="providerflow">
|
||||||
@@ -33,7 +44,7 @@ const ProviderFlow = () => {
|
|||||||
onConnect={onConnect}
|
onConnect={onConnect}
|
||||||
onElementsRemove={onElementsRemove}
|
onElementsRemove={onElementsRemove}
|
||||||
onLoad={onLoad}
|
onLoad={onLoad}
|
||||||
connectionMode="loose"
|
connectionMode={ConnectionMode.Loose}
|
||||||
>
|
>
|
||||||
<Controls />
|
<Controls />
|
||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { memo, useCallback } from 'react';
|
import React, { memo, useCallback, Dispatch, FC } from 'react';
|
||||||
import { useZoomPanHelper } from 'react-flow-renderer';
|
import { useZoomPanHelper, OnLoadParams, Elements, FlowExportObject } from 'react-flow-renderer';
|
||||||
import localforage from 'localforage';
|
import localforage from 'localforage';
|
||||||
|
|
||||||
localforage.config({
|
localforage.config({
|
||||||
@@ -11,7 +11,12 @@ const flowKey = 'example-flow';
|
|||||||
|
|
||||||
const getNodeId = () => `randomnode_${+new Date()}`;
|
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 { transform } = useZoomPanHelper();
|
||||||
|
|
||||||
const onSave = useCallback(() => {
|
const onSave = useCallback(() => {
|
||||||
@@ -23,7 +28,7 @@ const Controls = memo(({ rfInstance, setElements }) => {
|
|||||||
|
|
||||||
const onRestore = useCallback(() => {
|
const onRestore = useCallback(() => {
|
||||||
const restoreFlow = async () => {
|
const restoreFlow = async () => {
|
||||||
const flow = await localforage.getItem(flowKey);
|
const flow: FlowExportObject | null = await localforage.getItem(flowKey);
|
||||||
|
|
||||||
if (flow) {
|
if (flow) {
|
||||||
const [x = 0, y = 0] = flow.position;
|
const [x = 0, y = 0] = flow.position;
|
||||||
@@ -51,6 +56,6 @@ const Controls = memo(({ rfInstance, setElements }) => {
|
|||||||
<button onClick={onAdd}>add node</button>
|
<button onClick={onAdd}>add node</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
};
|
||||||
|
|
||||||
export default Controls;
|
export default memo(Controls);
|
||||||
@@ -1,21 +1,29 @@
|
|||||||
import React, { memo, useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import ReactFlow, { ReactFlowProvider, removeElements, addEdge } from 'react-flow-renderer';
|
import ReactFlow, {
|
||||||
|
ReactFlowProvider,
|
||||||
|
removeElements,
|
||||||
|
addEdge,
|
||||||
|
Elements,
|
||||||
|
Connection,
|
||||||
|
Edge,
|
||||||
|
OnLoadParams,
|
||||||
|
} from 'react-flow-renderer';
|
||||||
|
|
||||||
import Controls from './Controls';
|
import Controls from './Controls';
|
||||||
|
|
||||||
import './save.css';
|
import './save.css';
|
||||||
|
|
||||||
const initialElements = [
|
const initialElements: Elements = [
|
||||||
{ id: '1', data: { label: 'Node 1' }, position: { x: 100, y: 100 } },
|
{ id: '1', data: { label: 'Node 1' }, position: { x: 100, y: 100 } },
|
||||||
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 200 } },
|
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 200 } },
|
||||||
{ id: 'e1-2', source: '1', target: '2' },
|
{ id: 'e1-2', source: '1', target: '2' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const SaveRestore = () => {
|
const SaveRestore = () => {
|
||||||
const [rfInstance, setRfInstance] = useState(null);
|
const [rfInstance, setRfInstance] = useState<OnLoadParams>();
|
||||||
const [elements, setElements] = useState(initialElements);
|
const [elements, setElements] = useState<Elements>(initialElements);
|
||||||
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
|
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
||||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlowProvider>
|
<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';
|
import { getElements } from './utils';
|
||||||
|
|
||||||
const onLoad = (reactFlowInstance) => {
|
const buttonWrapperStyles: CSSProperties = { position: 'absolute', right: 10, top: 10, zIndex: 4 };
|
||||||
|
|
||||||
|
const onLoad = (reactFlowInstance: OnLoadParams) => {
|
||||||
reactFlowInstance.fitView();
|
reactFlowInstance.fitView();
|
||||||
console.log(reactFlowInstance.getElements());
|
console.log(reactFlowInstance.getElements());
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialElements = getElements(30, 30);
|
const initialElements: Elements = getElements(30, 30);
|
||||||
|
|
||||||
const StressFlow = () => {
|
const StressFlow = () => {
|
||||||
const [elements, setElements] = useState(initialElements);
|
const [elements, setElements] = useState<Elements>(initialElements);
|
||||||
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
|
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
||||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
||||||
|
|
||||||
const updatePos = () => {
|
const updatePos = () => {
|
||||||
setElements((elms) => {
|
setElements((elms) => {
|
||||||
@@ -44,7 +57,7 @@ const StressFlow = () => {
|
|||||||
<Controls />
|
<Controls />
|
||||||
<Background />
|
<Background />
|
||||||
|
|
||||||
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
|
<div style={buttonWrapperStyles}>
|
||||||
<button onClick={updatePos} style={{ marginRight: 5 }}>
|
<button onClick={updatePos} style={{ marginRight: 5 }}>
|
||||||
change pos
|
change pos
|
||||||
</button>
|
</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 = [];
|
const initialElements = [];
|
||||||
let nodeId = 1;
|
let nodeId = 1;
|
||||||
let recentNodeId = null;
|
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 onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
||||||
const onElementClick = (event, element) => console.log('click', element);
|
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: '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: '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' },
|
{ 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' },
|
{ 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: '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: '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' },
|
{ id: '2b', data: { label: 'Node 2' }, position: { x: 200, y: 100 }, className: 'light' },
|
||||||
@@ -28,9 +28,9 @@ const elementsB = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const BasicFlow = () => {
|
const BasicFlow = () => {
|
||||||
const [elements, setElements] = useState(elementsA);
|
const [elements, setElements] = useState<Elements>(elementsA);
|
||||||
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
|
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
||||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
@@ -39,7 +39,6 @@ const BasicFlow = () => {
|
|||||||
onElementsRemove={onElementsRemove}
|
onElementsRemove={onElementsRemove}
|
||||||
onConnect={onConnect}
|
onConnect={onConnect}
|
||||||
onNodeDragStop={onNodeDragStop}
|
onNodeDragStop={onNodeDragStop}
|
||||||
className="react-flow-basic-example"
|
|
||||||
>
|
>
|
||||||
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
|
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
|
||||||
<button onClick={() => setElements(elementsA)} style={{ marginRight: 5 }}>
|
<button onClick={() => setElements(elementsA)} style={{ marginRight: 5 }}>
|
||||||
@@ -1,7 +1,15 @@
|
|||||||
import React, { useState } from 'react';
|
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',
|
id: '1',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
@@ -40,12 +48,13 @@ const initialElements = [
|
|||||||
{ id: 'e1-2', source: '1', target: '2', label: 'This is a draggable edge' },
|
{ 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 UpdatableEdge = () => {
|
||||||
const [elements, setElements] = useState(initialElements);
|
const [elements, setElements] = useState<Elements>(initialElements);
|
||||||
const onEdgeUpdate = (oldEdge, newConnection) => setElements((els) => updateEdge(oldEdge, newConnection, els));
|
const onEdgeUpdate = (oldEdge: Edge, newConnection: Connection) =>
|
||||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
setElements((els) => updateEdge(oldEdge, newConnection, els));
|
||||||
|
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlow elements={elements} onLoad={onLoad} snapToGrid={true} onEdgeUpdate={onEdgeUpdate} onConnect={onConnect}>
|
<ReactFlow elements={elements} onLoad={onLoad} snapToGrid={true} onEdgeUpdate={onEdgeUpdate} onConnect={onConnect}>
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import ReactFlow from 'react-flow-renderer';
|
import ReactFlow, { Elements } from 'react-flow-renderer';
|
||||||
|
|
||||||
import './updatenode.css';
|
import './updatenode.css';
|
||||||
|
|
||||||
const initialElements = [
|
const initialElements: Elements = [
|
||||||
{ id: '1', data: { label: '-' }, position: { x: 100, y: 100 } },
|
{ id: '1', data: { label: '-' }, position: { x: 100, y: 100 } },
|
||||||
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 200 } },
|
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 200 } },
|
||||||
{ id: 'e1-2', source: '1', target: '2' },
|
{ id: 'e1-2', source: '1', target: '2' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const UpdateNode = () => {
|
const UpdateNode = () => {
|
||||||
const [elements, setElements] = useState(initialElements);
|
const [elements, setElements] = useState<Elements>(initialElements);
|
||||||
const [nodeName, setNodeName] = useState('Node 1');
|
const [nodeName, setNodeName] = useState<string>('Node 1');
|
||||||
const [nodeBg, setNodeBg] = useState('#eee');
|
const [nodeBg, setNodeBg] = useState<string>('#eee');
|
||||||
const [nodeHidden, setNodeHidden] = useState(false);
|
const [nodeHidden, setNodeHidden] = useState<boolean>(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setElements((els) =>
|
setElements((els) =>
|
||||||
@@ -7,9 +7,13 @@ import ReactFlow, {
|
|||||||
MiniMap,
|
MiniMap,
|
||||||
useZoomPanHelper,
|
useZoomPanHelper,
|
||||||
ReactFlowProvider,
|
ReactFlowProvider,
|
||||||
|
Elements,
|
||||||
|
ElementId,
|
||||||
|
Connection,
|
||||||
|
Edge,
|
||||||
} from 'react-flow-renderer';
|
} 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: '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: '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' },
|
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 }, className: 'light' },
|
||||||
@@ -19,12 +23,12 @@ const initialElements = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
let id = 5;
|
let id = 5;
|
||||||
const getId = () => `${id++}`;
|
const getId = (): ElementId => `${id++}`;
|
||||||
|
|
||||||
const UseZoomPanHelperFlow = () => {
|
const UseZoomPanHelperFlow = () => {
|
||||||
const [elements, setElements] = useState(initialElements);
|
const [elements, setElements] = useState<Elements>(initialElements);
|
||||||
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
|
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
||||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
||||||
const { project } = useZoomPanHelper();
|
const { project } = useZoomPanHelper();
|
||||||
|
|
||||||
const onPaneClick = useCallback(
|
const onPaneClick = useCallback(
|
||||||
@@ -53,8 +57,10 @@ const UseZoomPanHelperFlow = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default () => (
|
const WrappedFlow = () => (
|
||||||
<ReactFlowProvider>
|
<ReactFlowProvider>
|
||||||
<UseZoomPanHelperFlow />
|
<UseZoomPanHelperFlow />
|
||||||
</ReactFlowProvider>
|
</ReactFlowProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export default WrappedFlow;
|
||||||
@@ -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;
|
|
||||||
@@ -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 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';
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ const routes = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const Header = withRouter(({ history, location }) => {
|
const Header = withRouter(({ history, location }) => {
|
||||||
const onChange = (event) => history.push(event.target.value);
|
const onChange = (event: ChangeEvent<HTMLSelectElement>) => history.push(event.target.value);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header>
|
<header>
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
/// <reference types="react-scripts" />
|
||||||
@@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
Generated
+10
-12230
File diff suppressed because it is too large
Load Diff
+4
-8
@@ -12,11 +12,10 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rollup -c --environment NODE_ENV:production && postcss src/*.css --dir dist",
|
"build": "rollup -c --environment NODE_ENV:production && postcss src/*.css --dir dist",
|
||||||
"start": "rollup -w -c",
|
"start": "rollup -w -c",
|
||||||
"start:examples": "rollup -c rollup.examples.js -w",
|
"start:examples": "cd example && npm start",
|
||||||
"build:examples": "rollup -c rollup.examples.js --environment NODE_ENV:production",
|
"build:examples": "cd example && npm run build",
|
||||||
"dev": "npm run start:examples",
|
"start:testserver": "npm run build && 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 && cd example && npm install && npm run build",
|
||||||
"build:dev": "npm install && npm run build:examples",
|
|
||||||
"dev:wait": "start-server-and-test start:testserver http-get://localhost:3000",
|
"dev:wait": "start-server-and-test start:testserver http-get://localhost:3000",
|
||||||
"test": "npm run dev:wait test:chrome",
|
"test": "npm run dev:wait test:chrome",
|
||||||
"test:chrome": "cypress run --browser chrome --headless",
|
"test:chrome": "cypress run --browser chrome --headless",
|
||||||
@@ -57,8 +56,6 @@
|
|||||||
"autoprefixer": "^10.2.4",
|
"autoprefixer": "^10.2.4",
|
||||||
"babel-preset-react-app": "^10.0.0",
|
"babel-preset-react-app": "^10.0.0",
|
||||||
"cypress": "^6.5.0",
|
"cypress": "^6.5.0",
|
||||||
"dagre": "^0.8.5",
|
|
||||||
"localforage": "^1.9.0",
|
|
||||||
"postcss": "^8.2.6",
|
"postcss": "^8.2.6",
|
||||||
"postcss-cli": "^8.3.1",
|
"postcss-cli": "^8.3.1",
|
||||||
"postcss-nested": "^5.0.3",
|
"postcss-nested": "^5.0.3",
|
||||||
@@ -66,7 +63,6 @@
|
|||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
"react-router-dom": "^5.2.0",
|
|
||||||
"release-it": "^14.4.1",
|
"release-it": "^14.4.1",
|
||||||
"rollup": "^2.39.0",
|
"rollup": "^2.39.0",
|
||||||
"rollup-plugin-bundle-size": "^1.0.3",
|
"rollup-plugin-bundle-size": "^1.0.3",
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export const baseConfig = ({ mainFile = pkg.main, moduleFile = pkg.module, injec
|
|||||||
__REACT_FLOW_VERSION__: JSON.stringify(pkg.version),
|
__REACT_FLOW_VERSION__: JSON.stringify(pkg.version),
|
||||||
// this comes from the easy-peasy dependency
|
// this comes from the easy-peasy dependency
|
||||||
'process.env.FORCE_SIMILAR_INSTEAD_OF_MAP': false,
|
'process.env.FORCE_SIMILAR_INSTEAD_OF_MAP': false,
|
||||||
|
preventAssignment: true,
|
||||||
}),
|
}),
|
||||||
bundleSize(),
|
bundleSize(),
|
||||||
postcss({
|
postcss({
|
||||||
|
|||||||
@@ -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];
|
|
||||||
Reference in New Issue
Block a user