refactor(examples): hide old api examples
This commit is contained in:
@@ -21,3 +21,5 @@
|
|||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
|
||||||
|
src_oldapi
|
||||||
+43
-28
@@ -1,49 +1,54 @@
|
|||||||
import React, { useState, MouseEvent } from 'react';
|
import { useState, MouseEvent, useCallback } from 'react';
|
||||||
|
|
||||||
import ReactFlow, {
|
import ReactFlow, {
|
||||||
removeElements,
|
|
||||||
addEdge,
|
addEdge,
|
||||||
isNode,
|
|
||||||
Background,
|
Background,
|
||||||
Elements,
|
applyNodeChanges,
|
||||||
BackgroundVariant,
|
applyEdgeChanges,
|
||||||
|
MiniMap,
|
||||||
|
Controls,
|
||||||
FlowElement,
|
FlowElement,
|
||||||
Node,
|
Node,
|
||||||
Edge,
|
Edge,
|
||||||
Connection,
|
ElementChange,
|
||||||
OnLoadParams,
|
OnLoadParams,
|
||||||
|
Connection,
|
||||||
} from 'react-flow-renderer';
|
} from 'react-flow-renderer';
|
||||||
|
|
||||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
||||||
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
|
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
|
||||||
|
|
||||||
const initialElements: Elements = [
|
const initialNodes: Node[] = [
|
||||||
{ 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' },
|
||||||
{ id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 }, className: 'light' },
|
{ id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 }, className: 'light' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const initialEdges: Edge[] = [
|
||||||
{ id: 'e1-2', source: '1', target: '2', animated: true },
|
{ id: 'e1-2', source: '1', target: '2', animated: true },
|
||||||
{ id: 'e1-3', source: '1', target: '3' },
|
{ id: 'e1-3', source: '1', target: '3' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const BasicFlow = () => {
|
const BasicFlow = () => {
|
||||||
const [rfInstance, setRfInstance] = useState<OnLoadParams | null>(null);
|
const [rfInstance, setRfInstance] = useState<OnLoadParams | null>(null);
|
||||||
const [elements, setElements] = useState<Elements>(initialElements);
|
const [nodes, setNodes] = useState<Node[]>(initialNodes);
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
const [edges, setEdges] = useState<Edge[]>(initialEdges);
|
||||||
const onConnect = (params: Edge | Connection) => setElements((els) => addEdge(params, els));
|
|
||||||
|
const onConnect = useCallback((params: Edge | Connection, nds: Node[]) => {
|
||||||
|
setEdges((eds) => addEdge(params, nds, eds));
|
||||||
|
}, []);
|
||||||
const onLoad = (reactFlowInstance: OnLoadParams) => setRfInstance(reactFlowInstance);
|
const onLoad = (reactFlowInstance: OnLoadParams) => setRfInstance(reactFlowInstance);
|
||||||
|
|
||||||
const updatePos = () => {
|
const updatePos = () => {
|
||||||
setElements((elms) => {
|
setNodes((nds) => {
|
||||||
return elms.map((el) => {
|
return nds.map((n) => {
|
||||||
if (isNode(el)) {
|
n.position = {
|
||||||
el.position = {
|
x: Math.random() * 400,
|
||||||
x: Math.random() * 400,
|
y: Math.random() * 400,
|
||||||
y: Math.random() * 400,
|
};
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return el;
|
return n;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -52,23 +57,31 @@ const BasicFlow = () => {
|
|||||||
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) => {
|
setNodes((nds) => {
|
||||||
return elms.map((el) => {
|
return nds.map((n) => {
|
||||||
if (isNode(el)) {
|
n.className = n.className === 'light' ? 'dark' : 'light';
|
||||||
el.className = el.className === 'light' ? 'dark' : 'light';
|
|
||||||
}
|
|
||||||
|
|
||||||
return el;
|
return n;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onNodesChange = useCallback((changes: ElementChange[]) => {
|
||||||
|
setNodes((ns) => applyNodeChanges(changes, ns));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onEdgesChange = useCallback((changes: ElementChange[]) => {
|
||||||
|
setEdges((es) => applyEdgeChanges(changes, es));
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
elements={elements}
|
nodes={nodes}
|
||||||
|
edges={edges}
|
||||||
onLoad={onLoad}
|
onLoad={onLoad}
|
||||||
|
onNodesChange={onNodesChange}
|
||||||
|
onEdgesChange={onEdgesChange}
|
||||||
onElementClick={onElementClick}
|
onElementClick={onElementClick}
|
||||||
onElementsRemove={onElementsRemove}
|
|
||||||
onConnect={onConnect}
|
onConnect={onConnect}
|
||||||
onNodeDragStop={onNodeDragStop}
|
onNodeDragStop={onNodeDragStop}
|
||||||
className="react-flow-basic-example"
|
className="react-flow-basic-example"
|
||||||
@@ -76,7 +89,9 @@ const BasicFlow = () => {
|
|||||||
minZoom={0.2}
|
minZoom={0.2}
|
||||||
maxZoom={4}
|
maxZoom={4}
|
||||||
>
|
>
|
||||||
<Background variant={BackgroundVariant.Lines} />
|
<MiniMap />
|
||||||
|
<Controls />
|
||||||
|
<Background />
|
||||||
|
|
||||||
<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 }}>
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
import React, { FC } from 'react';
|
|
||||||
import { ConnectionLineComponentProps } from 'react-flow-renderer';
|
|
||||||
|
|
||||||
const ConnectionLine: FC<ConnectionLineComponentProps> = ({ sourceX, sourceY, targetX, targetY }) => {
|
|
||||||
return (
|
|
||||||
<g>
|
|
||||||
<path
|
|
||||||
fill="none"
|
|
||||||
stroke="#222"
|
|
||||||
strokeWidth={1.5}
|
|
||||||
className="animated"
|
|
||||||
d={`M${sourceX},${sourceY} C ${sourceX} ${targetY} ${sourceX} ${targetY} ${targetX},${targetY}`}
|
|
||||||
/>
|
|
||||||
<circle cx={targetX} cy={targetY} fill="#fff" r={3} stroke="#222" strokeWidth={1.5} />
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ConnectionLine;
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
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,25 +0,0 @@
|
|||||||
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, isConnectable }) => {
|
|
||||||
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} isConnectable={isConnectable} />
|
|
||||||
<Handle type="source" position={Position.Right} id="b" style={sourceHandleStyleB} isConnectable={isConnectable} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(ColorSelectorNode);
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
import React, { useState, useEffect, MouseEvent } from 'react';
|
|
||||||
import { ChangeEvent } from 'react';
|
|
||||||
|
|
||||||
import ReactFlow, {
|
|
||||||
isEdge,
|
|
||||||
removeElements,
|
|
||||||
addEdge,
|
|
||||||
MiniMap,
|
|
||||||
Controls,
|
|
||||||
Node,
|
|
||||||
FlowElement,
|
|
||||||
OnLoadParams,
|
|
||||||
Elements,
|
|
||||||
Position,
|
|
||||||
SnapGrid,
|
|
||||||
Connection,
|
|
||||||
Edge,
|
|
||||||
} from 'react-flow-renderer';
|
|
||||||
|
|
||||||
import ColorSelectorNode from './ColorSelectorNode';
|
|
||||||
|
|
||||||
const onLoad = (reactFlowInstance: OnLoadParams) => console.log('flow loaded:', reactFlowInstance);
|
|
||||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
|
||||||
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
|
|
||||||
|
|
||||||
const initBgColor = '#1A192B';
|
|
||||||
|
|
||||||
const connectionLineStyle = { stroke: '#fff' };
|
|
||||||
const snapGrid: SnapGrid = [16, 16];
|
|
||||||
const nodeTypes = {
|
|
||||||
selectorNode: ColorSelectorNode,
|
|
||||||
};
|
|
||||||
|
|
||||||
const CustomNodeFlow = () => {
|
|
||||||
const [elements, setElements] = useState<Elements>([]);
|
|
||||||
const [bgColor, setBgColor] = useState<string>(initBgColor);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const onChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
setElements((els) =>
|
|
||||||
els.map((e) => {
|
|
||||||
if (isEdge(e) || e.id !== '2') {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
const color = event.target.value;
|
|
||||||
|
|
||||||
setBgColor(color);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...e,
|
|
||||||
data: {
|
|
||||||
...e.data,
|
|
||||||
color,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
setElements([
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
type: 'input',
|
|
||||||
data: { label: 'An input node' },
|
|
||||||
position: { x: 0, y: 50 },
|
|
||||||
sourcePosition: Position.Right,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
type: 'selectorNode',
|
|
||||||
data: { onChange: onChange, color: initBgColor },
|
|
||||||
style: { border: '1px solid #777', padding: 10 },
|
|
||||||
position: { x: 250, y: 50 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
type: 'output',
|
|
||||||
data: { label: 'Output A' },
|
|
||||||
position: { x: 550, y: 25 },
|
|
||||||
targetPosition: Position.Left,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '4',
|
|
||||||
type: 'output',
|
|
||||||
data: { label: 'Output B' },
|
|
||||||
position: { x: 550, y: 100 },
|
|
||||||
targetPosition: Position.Left,
|
|
||||||
},
|
|
||||||
|
|
||||||
{ id: 'e1-2', source: '1', target: '2', animated: true, style: { stroke: '#fff' } },
|
|
||||||
{ id: 'e2a-3', source: '2', sourceHandle: 'a', target: '3', animated: true, style: { stroke: '#fff' } },
|
|
||||||
{ id: 'e2b-4', source: '2', sourceHandle: 'b', target: '4', animated: true, style: { stroke: '#fff' } },
|
|
||||||
]);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
|
||||||
const onConnect = (params: Connection | Edge) =>
|
|
||||||
setElements((els) => addEdge({ ...params, animated: true, style: { stroke: '#fff' } }, els));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ReactFlow
|
|
||||||
elements={elements}
|
|
||||||
onElementClick={onElementClick}
|
|
||||||
onElementsRemove={onElementsRemove}
|
|
||||||
onConnect={onConnect}
|
|
||||||
onNodeDragStop={onNodeDragStop}
|
|
||||||
style={{ background: bgColor }}
|
|
||||||
onLoad={onLoad}
|
|
||||||
nodeTypes={nodeTypes}
|
|
||||||
connectionLineStyle={connectionLineStyle}
|
|
||||||
snapToGrid={true}
|
|
||||||
snapGrid={snapGrid}
|
|
||||||
defaultZoom={1.5}
|
|
||||||
>
|
|
||||||
<MiniMap
|
|
||||||
nodeStrokeColor={(n: Node): string => {
|
|
||||||
if (n.type === 'input') return '#0041d0';
|
|
||||||
if (n.type === 'selectorNode') return bgColor;
|
|
||||||
if (n.type === 'output') return '#ff0072';
|
|
||||||
|
|
||||||
return '#eee';
|
|
||||||
}}
|
|
||||||
nodeColor={(n: Node): string => {
|
|
||||||
if (n.type === 'selectorNode') return bgColor;
|
|
||||||
|
|
||||||
return '#fff';
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Controls />
|
|
||||||
</ReactFlow>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CustomNodeFlow;
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import { memo, FC } from 'react';
|
|
||||||
|
|
||||||
import { Handle, Position, NodeProps, Connection, Edge } from 'react-flow-renderer';
|
|
||||||
|
|
||||||
const onConnect = (params: Connection | Edge) => console.log('handle onConnect', params);
|
|
||||||
|
|
||||||
const labelStyle = {
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
};
|
|
||||||
|
|
||||||
const dragHandleStyle = {
|
|
||||||
display: 'inline-block',
|
|
||||||
width: 25,
|
|
||||||
height: 25,
|
|
||||||
backgroundColor: 'teal',
|
|
||||||
marginLeft: 5,
|
|
||||||
borderRadius: '50%',
|
|
||||||
};
|
|
||||||
|
|
||||||
const ColorSelectorNode: FC<NodeProps> = () => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Handle type="target" position={Position.Left} onConnect={onConnect} />
|
|
||||||
<div style={labelStyle}>
|
|
||||||
Only draggable here → <span className="custom-drag-handle" style={dragHandleStyle} />
|
|
||||||
</div>
|
|
||||||
<Handle type="source" position={Position.Right} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(ColorSelectorNode);
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import ReactFlow from 'react-flow-renderer';
|
|
||||||
|
|
||||||
import DragHandleNode from './DragHandleNode';
|
|
||||||
|
|
||||||
const nodeTypes = {
|
|
||||||
dragHandleNode: DragHandleNode,
|
|
||||||
};
|
|
||||||
|
|
||||||
const elements = [
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
type: 'dragHandleNode',
|
|
||||||
dragHandle: '.custom-drag-handle',
|
|
||||||
style: { border: '1px solid #ddd', padding: '20px 40px' },
|
|
||||||
position: { x: 200, y: 200 },
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const DragHandleFlow = () => <ReactFlow elements={elements} nodeTypes={nodeTypes} />;
|
|
||||||
|
|
||||||
export default DragHandleFlow;
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
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 left.</div>
|
|
||||||
<div className="react-flow__node-input" onDragStart={(event: DragEvent) => onDragStart(event, 'input')} draggable>
|
|
||||||
Input Node
|
|
||||||
</div>
|
|
||||||
<div className="react-flow__node-default" onDragStart={(event: DragEvent) => onDragStart(event, 'default')} draggable>
|
|
||||||
Default Node
|
|
||||||
</div>
|
|
||||||
<div className="react-flow__node-output" onDragStart={(event: DragEvent) => onDragStart(event, 'output')} draggable>
|
|
||||||
Output Node
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Sidebar;
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
.dndflow {
|
|
||||||
flex-direction: column;
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dndflow aside {
|
|
||||||
border-right: 1px solid #eee;
|
|
||||||
padding: 15px 10px;
|
|
||||||
font-size: 12px;
|
|
||||||
background: #fcfcfc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dndflow aside > * {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
cursor: grab;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dndflow aside .description {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dndflow .reactflow-wrapper {
|
|
||||||
flex-grow: 1;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
|
||||||
.dndflow {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dndflow aside {
|
|
||||||
width: 20%;
|
|
||||||
max-width: 180px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
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;
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
/**
|
|
||||||
* Example for checking the different edge types and source and target positions
|
|
||||||
*/
|
|
||||||
import React, { useState } from 'react';
|
|
||||||
|
|
||||||
import ReactFlow, {
|
|
||||||
removeElements,
|
|
||||||
addEdge,
|
|
||||||
MiniMap,
|
|
||||||
Controls,
|
|
||||||
Background,
|
|
||||||
OnLoadParams,
|
|
||||||
Connection,
|
|
||||||
Edge,
|
|
||||||
Elements,
|
|
||||||
} from 'react-flow-renderer';
|
|
||||||
import { getElements } from './utils';
|
|
||||||
|
|
||||||
const onLoad = (reactFlowInstance: OnLoadParams) => {
|
|
||||||
reactFlowInstance.fitView();
|
|
||||||
console.log(reactFlowInstance.getElements());
|
|
||||||
};
|
|
||||||
|
|
||||||
const initialElements = getElements();
|
|
||||||
|
|
||||||
const multiSelectionKeyCode = ['ShiftLeft', 'ShiftRight'];
|
|
||||||
const deleteKeyCode = ['AltLeft+KeyD', 'Backspace'];
|
|
||||||
|
|
||||||
const EdgeTypesFlow = () => {
|
|
||||||
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}
|
|
||||||
onLoad={onLoad}
|
|
||||||
onElementsRemove={onElementsRemove}
|
|
||||||
onConnect={onConnect}
|
|
||||||
minZoom={0.2}
|
|
||||||
zoomOnScroll={false}
|
|
||||||
selectionKeyCode="a+s"
|
|
||||||
multiSelectionKeyCode={multiSelectionKeyCode}
|
|
||||||
deleteKeyCode={deleteKeyCode}
|
|
||||||
zoomActivationKeyCode="z"
|
|
||||||
>
|
|
||||||
<MiniMap />
|
|
||||||
<Controls />
|
|
||||||
<Background />
|
|
||||||
</ReactFlow>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EdgeTypesFlow;
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
import { ElementId, Elements, Position } from 'react-flow-renderer';
|
|
||||||
|
|
||||||
const nodeWidth = 80;
|
|
||||||
const nodeGapWidth = nodeWidth * 2;
|
|
||||||
const nodeStyle = { width: nodeWidth, fontSize: 11, color: 'white' };
|
|
||||||
|
|
||||||
const sourceTargetPositions = [
|
|
||||||
{ source: Position.Bottom, target: Position.Top },
|
|
||||||
{ source: Position.Right, target: Position.Left },
|
|
||||||
];
|
|
||||||
const nodeColors = [
|
|
||||||
['#1e9e99', '#4cb3ac', '#6ec9c0', '#8ddfd4'],
|
|
||||||
['#0f4c75', '#1b5d8b', '#276fa1', '#3282b8'],
|
|
||||||
];
|
|
||||||
const edgeTypes = ['default', 'step', 'smoothstep', 'straight'];
|
|
||||||
const offsets = [
|
|
||||||
{
|
|
||||||
x: 0,
|
|
||||||
y: -nodeGapWidth,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: nodeGapWidth,
|
|
||||||
y: -nodeGapWidth,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: nodeGapWidth,
|
|
||||||
y: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: nodeGapWidth,
|
|
||||||
y: nodeGapWidth,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: 0,
|
|
||||||
y: nodeGapWidth,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: -nodeGapWidth,
|
|
||||||
y: nodeGapWidth,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: -nodeGapWidth,
|
|
||||||
y: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: -nodeGapWidth,
|
|
||||||
y: -nodeGapWidth,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
let id = 0;
|
|
||||||
const getNodeId = (): ElementId => (id++).toString();
|
|
||||||
|
|
||||||
export function getElements(): Elements {
|
|
||||||
const initialElements = [];
|
|
||||||
|
|
||||||
for (let sourceTargetIndex = 0; sourceTargetIndex < sourceTargetPositions.length; sourceTargetIndex++) {
|
|
||||||
const currSourceTargetPos = sourceTargetPositions[sourceTargetIndex];
|
|
||||||
|
|
||||||
for (let edgeTypeIndex = 0; edgeTypeIndex < edgeTypes.length; edgeTypeIndex++) {
|
|
||||||
const currEdgeType = edgeTypes[edgeTypeIndex];
|
|
||||||
|
|
||||||
for (let offsetIndex = 0; offsetIndex < offsets.length; offsetIndex++) {
|
|
||||||
const currOffset = offsets[offsetIndex];
|
|
||||||
|
|
||||||
const style = { ...nodeStyle, background: nodeColors[sourceTargetIndex][edgeTypeIndex] };
|
|
||||||
const sourcePosition = {
|
|
||||||
x: offsetIndex * nodeWidth * 4,
|
|
||||||
y: edgeTypeIndex * 300 + sourceTargetIndex * edgeTypes.length * 300,
|
|
||||||
};
|
|
||||||
const sourceId = getNodeId();
|
|
||||||
const sourceData = { label: `Source ${sourceId}` };
|
|
||||||
const sourceNode = {
|
|
||||||
id: sourceId,
|
|
||||||
style,
|
|
||||||
data: sourceData,
|
|
||||||
position: sourcePosition,
|
|
||||||
sourcePosition: currSourceTargetPos.source,
|
|
||||||
targetPosition: currSourceTargetPos.target,
|
|
||||||
};
|
|
||||||
|
|
||||||
const targetId = getNodeId();
|
|
||||||
const targetData = { label: `Target ${targetId}` };
|
|
||||||
const targetPosition = {
|
|
||||||
x: sourcePosition.x + currOffset.x,
|
|
||||||
y: sourcePosition.y + currOffset.y,
|
|
||||||
};
|
|
||||||
const targetNode = {
|
|
||||||
id: targetId,
|
|
||||||
style,
|
|
||||||
data: targetData,
|
|
||||||
position: targetPosition,
|
|
||||||
sourcePosition: currSourceTargetPos.source,
|
|
||||||
targetPosition: currSourceTargetPos.target,
|
|
||||||
};
|
|
||||||
|
|
||||||
initialElements.push(sourceNode);
|
|
||||||
initialElements.push(targetNode);
|
|
||||||
|
|
||||||
initialElements.push({ id: `${sourceId}-${targetId}`, source: sourceId, target: targetId, type: currEdgeType });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return initialElements;
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import { FC } from 'react';
|
|
||||||
import { EdgeProps, getBezierPath, getMarkerEnd } from 'react-flow-renderer';
|
|
||||||
|
|
||||||
const CustomEdge: FC<EdgeProps> = ({
|
|
||||||
id,
|
|
||||||
sourceX,
|
|
||||||
sourceY,
|
|
||||||
targetX,
|
|
||||||
targetY,
|
|
||||||
sourcePosition,
|
|
||||||
targetPosition,
|
|
||||||
data,
|
|
||||||
arrowHeadType,
|
|
||||||
markerEndId,
|
|
||||||
}) => {
|
|
||||||
const edgePath = getBezierPath({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition });
|
|
||||||
const markerEnd = getMarkerEnd(arrowHeadType, markerEndId);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<path id={id} className="react-flow__edge-path" d={edgePath} markerEnd={markerEnd} />
|
|
||||||
<text>
|
|
||||||
<textPath href={`#${id}`} style={{ fontSize: '12px' }} startOffset="50%" textAnchor="middle">
|
|
||||||
{data.text}
|
|
||||||
</textPath>
|
|
||||||
</text>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CustomEdge;
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
import { FC } from 'react';
|
|
||||||
import { EdgeProps, getBezierPath, getMarkerEnd, EdgeText, getEdgeCenter } from 'react-flow-renderer';
|
|
||||||
|
|
||||||
const CustomEdge: FC<EdgeProps> = ({
|
|
||||||
id,
|
|
||||||
sourceX,
|
|
||||||
sourceY,
|
|
||||||
targetX,
|
|
||||||
targetY,
|
|
||||||
sourcePosition,
|
|
||||||
targetPosition,
|
|
||||||
data,
|
|
||||||
arrowHeadType,
|
|
||||||
markerEndId,
|
|
||||||
}) => {
|
|
||||||
const edgePath = getBezierPath({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition });
|
|
||||||
const markerEnd = getMarkerEnd(arrowHeadType, markerEndId);
|
|
||||||
const [centerX, centerY] = getEdgeCenter({
|
|
||||||
sourceX,
|
|
||||||
sourceY,
|
|
||||||
targetX,
|
|
||||||
targetY,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<path id={id} className="react-flow__edge-path" d={edgePath} markerEnd={markerEnd} />
|
|
||||||
<EdgeText
|
|
||||||
x={centerX}
|
|
||||||
y={centerY}
|
|
||||||
label={data.text}
|
|
||||||
labelStyle={{ fill: 'white' }}
|
|
||||||
labelShowBg
|
|
||||||
labelBgStyle={{ fill: 'red' }}
|
|
||||||
labelBgPadding={[2, 4]}
|
|
||||||
labelBgBorderRadius={2}
|
|
||||||
onClick={() => console.log(data)}
|
|
||||||
/>
|
|
||||||
;
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CustomEdge;
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
import React, { useState, MouseEvent } from 'react';
|
|
||||||
|
|
||||||
import ReactFlow, {
|
|
||||||
removeElements,
|
|
||||||
addEdge,
|
|
||||||
MiniMap,
|
|
||||||
Controls,
|
|
||||||
Background,
|
|
||||||
OnLoadParams,
|
|
||||||
FlowElement,
|
|
||||||
EdgeTypesType,
|
|
||||||
Elements,
|
|
||||||
Connection,
|
|
||||||
Edge,
|
|
||||||
ArrowHeadType,
|
|
||||||
Node,
|
|
||||||
} from 'react-flow-renderer';
|
|
||||||
|
|
||||||
import CustomEdge from './CustomEdge';
|
|
||||||
import CustomEdge2 from './CustomEdge2';
|
|
||||||
|
|
||||||
const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.fitView();
|
|
||||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
|
||||||
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
|
|
||||||
|
|
||||||
const initialElements: Elements = [
|
|
||||||
{ id: '1', type: 'input', data: { label: 'Input 1' }, position: { x: 250, y: 0 } },
|
|
||||||
{ id: '2', data: { label: 'Node 2' }, position: { x: 150, y: 100 } },
|
|
||||||
{ id: '2a', data: { label: 'Node 2a' }, position: { x: 0, y: 180 } },
|
|
||||||
{ id: '3', data: { label: 'Node 3' }, position: { x: 250, y: 200 } },
|
|
||||||
{ id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 300 } },
|
|
||||||
{ id: '3a', data: { label: 'Node 3a' }, position: { x: 150, y: 300 } },
|
|
||||||
{ id: '5', data: { label: 'Node 5' }, position: { x: 250, y: 400 } },
|
|
||||||
{ id: '6', type: 'output', data: { label: 'Output 6' }, position: { x: 50, y: 550 } },
|
|
||||||
{ id: '7', type: 'output', data: { label: 'Output 7' }, position: { x: 250, y: 550 } },
|
|
||||||
{ id: '8', type: 'output', data: { label: 'Output 8' }, position: { x: 525, y: 600 } },
|
|
||||||
{ id: '9', type: 'output', data: { label: 'Output 9' }, position: { x: 675, y: 500 } },
|
|
||||||
{ id: 'e1-2', source: '1', target: '2', label: 'bezier edge (default)', className: 'normal-edge' },
|
|
||||||
{ id: 'e2-2a', source: '2', target: '2a', type: 'smoothstep', label: 'smoothstep edge' },
|
|
||||||
{ id: 'e2-3', source: '2', target: '3', type: 'step', label: 'step edge' },
|
|
||||||
{ id: 'e3-4', source: '3', target: '4', type: 'straight', label: 'straight edge' },
|
|
||||||
{ id: 'e3-3a', source: '3', target: '3a', type: 'straight', label: 'label only edge', style: { stroke: 'none' } },
|
|
||||||
{ id: 'e3-5', source: '4', target: '5', animated: true, label: 'animated styled edge', style: { stroke: 'red' } },
|
|
||||||
{
|
|
||||||
id: 'e5-6',
|
|
||||||
source: '5',
|
|
||||||
target: '6',
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
<tspan>i am using</tspan>
|
|
||||||
<tspan dy={10} x={0}>
|
|
||||||
{'<tspan>'}
|
|
||||||
</tspan>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
labelStyle: { fill: 'red', fontWeight: 700 },
|
|
||||||
arrowHeadType: ArrowHeadType.Arrow,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'e5-7',
|
|
||||||
source: '5',
|
|
||||||
target: '7',
|
|
||||||
label: 'label with styled bg',
|
|
||||||
labelBgPadding: [8, 4],
|
|
||||||
labelBgBorderRadius: 4,
|
|
||||||
labelBgStyle: { fill: '#FFCC00', color: '#fff', fillOpacity: 0.7 },
|
|
||||||
arrowHeadType: ArrowHeadType.ArrowClosed,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'e5-8',
|
|
||||||
source: '5',
|
|
||||||
target: '8',
|
|
||||||
type: 'custom',
|
|
||||||
data: { text: 'custom edge' },
|
|
||||||
arrowHeadType: ArrowHeadType.ArrowClosed,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'e5-9',
|
|
||||||
source: '5',
|
|
||||||
target: '9',
|
|
||||||
type: 'custom2',
|
|
||||||
data: { text: 'custom edge 2' },
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const edgeTypes: EdgeTypesType = {
|
|
||||||
custom: CustomEdge,
|
|
||||||
custom2: CustomEdge2,
|
|
||||||
};
|
|
||||||
|
|
||||||
const EdgesFlow = () => {
|
|
||||||
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}
|
|
||||||
onElementClick={onElementClick}
|
|
||||||
onElementsRemove={onElementsRemove}
|
|
||||||
onConnect={onConnect}
|
|
||||||
onNodeDragStop={onNodeDragStop}
|
|
||||||
onLoad={onLoad}
|
|
||||||
snapToGrid={true}
|
|
||||||
edgeTypes={edgeTypes}
|
|
||||||
>
|
|
||||||
<MiniMap />
|
|
||||||
<Controls />
|
|
||||||
<Background />
|
|
||||||
</ReactFlow>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EdgesFlow;
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
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,53 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
import ReactFlow, { addEdge, MiniMap, Controls, Connection, Edge, Elements } from 'react-flow-renderer';
|
|
||||||
|
|
||||||
const initialElements: Elements = [
|
|
||||||
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
|
|
||||||
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } },
|
|
||||||
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 } },
|
|
||||||
{ id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 } },
|
|
||||||
{ id: 'e1-2', source: '1', target: '2' },
|
|
||||||
{ id: 'e1-3', source: '1', target: '3' },
|
|
||||||
{ id: 'e3-4', source: '3', target: '4' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const HiddenFlow = () => {
|
|
||||||
const [elements, setElements] = useState<Elements>(initialElements);
|
|
||||||
const [isHidden, setIsHidden] = useState<boolean>(false);
|
|
||||||
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setElements((els) =>
|
|
||||||
els.map((e) => {
|
|
||||||
e.isHidden = isHidden;
|
|
||||||
return e;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}, [isHidden]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ReactFlow elements={elements} onConnect={onConnect}>
|
|
||||||
<MiniMap />
|
|
||||||
<Controls />
|
|
||||||
|
|
||||||
<div style={{ position: 'absolute', left: 10, top: 10, zIndex: 4 }}>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="ishidden">
|
|
||||||
isHidden
|
|
||||||
<input
|
|
||||||
id="ishidden"
|
|
||||||
type="checkbox"
|
|
||||||
checked={isHidden}
|
|
||||||
onChange={(event) => setIsHidden(event.target.checked)}
|
|
||||||
className="react-flow__ishidden"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ReactFlow>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default HiddenFlow;
|
|
||||||
@@ -1,226 +0,0 @@
|
|||||||
import React, { useState, MouseEvent, WheelEvent } from 'react';
|
|
||||||
import ReactFlow, {
|
|
||||||
addEdge,
|
|
||||||
MiniMap,
|
|
||||||
Controls,
|
|
||||||
Elements,
|
|
||||||
Node,
|
|
||||||
FlowElement,
|
|
||||||
Connection,
|
|
||||||
Edge,
|
|
||||||
PanOnScrollMode,
|
|
||||||
FlowTransform,
|
|
||||||
} from 'react-flow-renderer';
|
|
||||||
|
|
||||||
const initialElements: Elements = [
|
|
||||||
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
|
|
||||||
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } },
|
|
||||||
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 } },
|
|
||||||
{ id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 } },
|
|
||||||
{ id: 'e1-2', source: '1', target: '2', animated: true },
|
|
||||||
{ id: 'e1-3', source: '1', target: '3' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const onNodeDragStart = (_: MouseEvent, node: Node) => console.log('drag start', node);
|
|
||||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
|
||||||
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
|
|
||||||
const onPaneClick = (event: MouseEvent) => console.log('onPaneClick', event);
|
|
||||||
const onPaneScroll = (event?: WheelEvent) => console.log('onPaneScroll', event);
|
|
||||||
const onPaneContextMenu = (event: MouseEvent) => console.log('onPaneContextMenu', event);
|
|
||||||
const onMoveEnd = (flowTranasform?: FlowTransform) => console.log('onMoveEnd', flowTranasform);
|
|
||||||
|
|
||||||
const InteractionFlow = () => {
|
|
||||||
const [elements, setElements] = useState<Elements>(initialElements);
|
|
||||||
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
|
||||||
|
|
||||||
const [isSelectable, setIsSelectable] = useState<boolean>(false);
|
|
||||||
const [isDraggable, setIsDraggable] = useState<boolean>(false);
|
|
||||||
const [isConnectable, setIsConnectable] = useState<boolean>(false);
|
|
||||||
const [zoomOnScroll, setZoomOnScroll] = useState<boolean>(false);
|
|
||||||
const [zoomOnPinch, setZoomOnPinch] = useState<boolean>(false);
|
|
||||||
const [panOnScroll, setPanOnScroll] = useState<boolean>(false);
|
|
||||||
const [panOnScrollMode, setPanOnScrollMode] = useState<PanOnScrollMode>(PanOnScrollMode.Free);
|
|
||||||
const [zoomOnDoubleClick, setZoomOnDoubleClick] = useState<boolean>(false);
|
|
||||||
const [paneMoveable, setPaneMoveable] = useState<boolean>(true);
|
|
||||||
const [captureZoomClick, setCaptureZoomClick] = useState<boolean>(false);
|
|
||||||
const [captureZoomScroll, setCaptureZoomScroll] = useState<boolean>(false);
|
|
||||||
const [captureElementClick, setCaptureElementClick] = useState<boolean>(false);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ReactFlow
|
|
||||||
elements={elements}
|
|
||||||
elementsSelectable={isSelectable}
|
|
||||||
nodesConnectable={isConnectable}
|
|
||||||
nodesDraggable={isDraggable}
|
|
||||||
zoomOnScroll={zoomOnScroll}
|
|
||||||
zoomOnPinch={zoomOnPinch}
|
|
||||||
panOnScroll={panOnScroll}
|
|
||||||
panOnScrollMode={panOnScrollMode}
|
|
||||||
zoomOnDoubleClick={zoomOnDoubleClick}
|
|
||||||
onConnect={onConnect}
|
|
||||||
onElementClick={captureElementClick ? onElementClick : undefined}
|
|
||||||
onNodeDragStart={onNodeDragStart}
|
|
||||||
onNodeDragStop={onNodeDragStop}
|
|
||||||
paneMoveable={paneMoveable}
|
|
||||||
onPaneClick={captureZoomClick ? onPaneClick : undefined}
|
|
||||||
onPaneScroll={captureZoomScroll ? onPaneScroll : undefined}
|
|
||||||
onPaneContextMenu={captureZoomClick ? onPaneContextMenu : undefined}
|
|
||||||
onMoveEnd={onMoveEnd}
|
|
||||||
>
|
|
||||||
<MiniMap />
|
|
||||||
<Controls />
|
|
||||||
|
|
||||||
<div style={{ position: 'absolute', left: 10, top: 10, zIndex: 4 }}>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="draggable">
|
|
||||||
nodesDraggable
|
|
||||||
<input
|
|
||||||
id="draggable"
|
|
||||||
type="checkbox"
|
|
||||||
checked={isDraggable}
|
|
||||||
onChange={(event) => setIsDraggable(event.target.checked)}
|
|
||||||
className="react-flow__draggable"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="connectable">
|
|
||||||
nodesConnectable
|
|
||||||
<input
|
|
||||||
id="connectable"
|
|
||||||
type="checkbox"
|
|
||||||
checked={isConnectable}
|
|
||||||
onChange={(event) => setIsConnectable(event.target.checked)}
|
|
||||||
className="react-flow__connectable"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="selectable">
|
|
||||||
elementsSelectable
|
|
||||||
<input
|
|
||||||
id="selectable"
|
|
||||||
type="checkbox"
|
|
||||||
checked={isSelectable}
|
|
||||||
onChange={(event) => setIsSelectable(event.target.checked)}
|
|
||||||
className="react-flow__selectable"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="zoomonscroll">
|
|
||||||
zoomOnScroll
|
|
||||||
<input
|
|
||||||
id="zoomonscroll"
|
|
||||||
type="checkbox"
|
|
||||||
checked={zoomOnScroll}
|
|
||||||
onChange={(event) => setZoomOnScroll(event.target.checked)}
|
|
||||||
className="react-flow__zoomonscroll"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="zoomonpinch">
|
|
||||||
zoomOnPinch
|
|
||||||
<input
|
|
||||||
id="zoomonpinch"
|
|
||||||
type="checkbox"
|
|
||||||
checked={zoomOnPinch}
|
|
||||||
onChange={(event) => setZoomOnPinch(event.target.checked)}
|
|
||||||
className="react-flow__zoomonpinch"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="panonscroll">
|
|
||||||
panOnScroll
|
|
||||||
<input
|
|
||||||
id="panonscroll"
|
|
||||||
type="checkbox"
|
|
||||||
checked={panOnScroll}
|
|
||||||
onChange={(event) => setPanOnScroll(event.target.checked)}
|
|
||||||
className="react-flow__panonscroll"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="panonscrollmode">
|
|
||||||
panOnScrollMode
|
|
||||||
<select
|
|
||||||
id="panonscrollmode"
|
|
||||||
value={panOnScrollMode}
|
|
||||||
onChange={(event) => setPanOnScrollMode(event.target.value as PanOnScrollMode)}
|
|
||||||
className="react-flow__panonscrollmode"
|
|
||||||
>
|
|
||||||
<option value="free">free</option>
|
|
||||||
<option value="horizontal">horizontal</option>
|
|
||||||
<option value="vertical">vertical</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="zoomondbl">
|
|
||||||
zoomOnDoubleClick
|
|
||||||
<input
|
|
||||||
id="zoomondbl"
|
|
||||||
type="checkbox"
|
|
||||||
checked={zoomOnDoubleClick}
|
|
||||||
onChange={(event) => setZoomOnDoubleClick(event.target.checked)}
|
|
||||||
className="react-flow__zoomondbl"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="panemoveable">
|
|
||||||
paneMoveable
|
|
||||||
<input
|
|
||||||
id="panemoveable"
|
|
||||||
type="checkbox"
|
|
||||||
checked={paneMoveable}
|
|
||||||
onChange={(event) => setPaneMoveable(event.target.checked)}
|
|
||||||
className="react-flow__panemoveable"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="capturezoompaneclick">
|
|
||||||
capture onPaneClick
|
|
||||||
<input
|
|
||||||
id="capturezoompaneclick"
|
|
||||||
type="checkbox"
|
|
||||||
checked={captureZoomClick}
|
|
||||||
onChange={(event) => setCaptureZoomClick(event.target.checked)}
|
|
||||||
className="react-flow__capturezoompaneclick"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="capturezoompanescroll">
|
|
||||||
capture onPaneScroll
|
|
||||||
<input
|
|
||||||
id="capturezoompanescroll"
|
|
||||||
type="checkbox"
|
|
||||||
checked={captureZoomScroll}
|
|
||||||
onChange={(event) => setCaptureZoomScroll(event.target.checked)}
|
|
||||||
className="react-flow__capturezoompanescroll"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="captureelementclick">
|
|
||||||
capture onElementClick
|
|
||||||
<input
|
|
||||||
id="captureelementclick"
|
|
||||||
type="checkbox"
|
|
||||||
checked={captureElementClick}
|
|
||||||
onChange={(event) => setCaptureElementClick(event.target.checked)}
|
|
||||||
className="react-flow__captureelementclick"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ReactFlow>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default InteractionFlow;
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import ReactFlow, {
|
|
||||||
ReactFlowProvider,
|
|
||||||
addEdge,
|
|
||||||
removeElements,
|
|
||||||
Controls,
|
|
||||||
isNode,
|
|
||||||
Elements,
|
|
||||||
Connection,
|
|
||||||
Edge,
|
|
||||||
NodeExtent,
|
|
||||||
Position,
|
|
||||||
} from 'react-flow-renderer';
|
|
||||||
import dagre from 'dagre';
|
|
||||||
|
|
||||||
import initialElements from './initial-elements';
|
|
||||||
|
|
||||||
import './layouting.css';
|
|
||||||
|
|
||||||
const dagreGraph = new dagre.graphlib.Graph();
|
|
||||||
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
|
||||||
|
|
||||||
const nodeExtent: NodeExtent = [
|
|
||||||
[0, 0],
|
|
||||||
[1000, 1000],
|
|
||||||
];
|
|
||||||
|
|
||||||
const LayoutFlow = () => {
|
|
||||||
const [elements, setElements] = useState<Elements>(initialElements);
|
|
||||||
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
|
||||||
|
|
||||||
const onLayout = (direction: string) => {
|
|
||||||
const isHorizontal = direction === 'LR';
|
|
||||||
dagreGraph.setGraph({ rankdir: direction });
|
|
||||||
|
|
||||||
elements.forEach((el) => {
|
|
||||||
if (isNode(el)) {
|
|
||||||
dagreGraph.setNode(el.id, { width: 150, height: 50 });
|
|
||||||
} else {
|
|
||||||
dagreGraph.setEdge(el.source, el.target);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
dagre.layout(dagreGraph);
|
|
||||||
|
|
||||||
const layoutedElements = elements.map((el) => {
|
|
||||||
if (isNode(el)) {
|
|
||||||
const nodeWithPosition = dagreGraph.node(el.id);
|
|
||||||
el.targetPosition = isHorizontal ? Position.Left : Position.Top;
|
|
||||||
el.sourcePosition = isHorizontal ? Position.Right : Position.Bottom;
|
|
||||||
// we need to pass a slightly different position in order to notify react flow about the change
|
|
||||||
// @TODO how can we change the position handling so that we dont need this hack?
|
|
||||||
el.position = { x: nodeWithPosition.x + Math.random() / 1000, y: nodeWithPosition.y };
|
|
||||||
}
|
|
||||||
|
|
||||||
return el;
|
|
||||||
});
|
|
||||||
|
|
||||||
setElements(layoutedElements);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="layoutflow">
|
|
||||||
<ReactFlowProvider>
|
|
||||||
<ReactFlow
|
|
||||||
elements={elements}
|
|
||||||
onConnect={onConnect}
|
|
||||||
onElementsRemove={onElementsRemove}
|
|
||||||
nodeExtent={nodeExtent}
|
|
||||||
onLoad={() => onLayout('TB')}
|
|
||||||
>
|
|
||||||
<Controls />
|
|
||||||
</ReactFlow>
|
|
||||||
<div className="controls">
|
|
||||||
<button onClick={() => onLayout('TB')} style={{ marginRight: 10 }}>
|
|
||||||
vertical layout
|
|
||||||
</button>
|
|
||||||
<button onClick={() => onLayout('LR')}>horizontal layout</button>
|
|
||||||
</div>
|
|
||||||
</ReactFlowProvider>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LayoutFlow;
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
import { Elements, XYPosition } from 'react-flow-renderer';
|
|
||||||
|
|
||||||
const position: XYPosition = { x: 0, y: 0 };
|
|
||||||
|
|
||||||
const elements: Elements = [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
type: 'input',
|
|
||||||
data: { label: 'input' },
|
|
||||||
position,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
data: { label: 'node 2' },
|
|
||||||
position,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2a',
|
|
||||||
data: { label: 'node 2a' },
|
|
||||||
position,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2b',
|
|
||||||
data: { label: 'node 2b' },
|
|
||||||
position,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2c',
|
|
||||||
data: { label: 'node 2c' },
|
|
||||||
position,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2d',
|
|
||||||
data: { label: 'node 2d' },
|
|
||||||
position,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
data: { label: 'node 3' },
|
|
||||||
position,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '4',
|
|
||||||
data: { label: 'node 4' },
|
|
||||||
position,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '5',
|
|
||||||
data: { label: 'node 5' },
|
|
||||||
position,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '6',
|
|
||||||
type: 'output',
|
|
||||||
data: { label: 'output' },
|
|
||||||
position,
|
|
||||||
},
|
|
||||||
{ id: '7', type: 'output', data: { label: 'output' }, position: { x: 400, y: 450 } },
|
|
||||||
{ id: 'e12', source: '1', target: '2', type: 'smoothstep' },
|
|
||||||
{ id: 'e13', source: '1', target: '3', type: 'smoothstep' },
|
|
||||||
{ id: 'e22a', source: '2', target: '2a', type: 'smoothstep' },
|
|
||||||
{ id: 'e22b', source: '2', target: '2b', type: 'smoothstep' },
|
|
||||||
{ id: 'e22c', source: '2', target: '2c', type: 'smoothstep' },
|
|
||||||
{ id: 'e2c2d', source: '2c', target: '2d', type: 'smoothstep' },
|
|
||||||
|
|
||||||
{ id: 'e45', source: '4', target: '5', type: 'smoothstep' },
|
|
||||||
{ id: 'e56', source: '5', target: '6', type: 'smoothstep' },
|
|
||||||
{ id: 'e57', source: '5', target: '7', type: 'smoothstep' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export default elements;
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
.layoutflow {
|
|
||||||
flex-grow: 1;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.layoutflow .controls {
|
|
||||||
position: absolute;
|
|
||||||
right: 10px;
|
|
||||||
top: 10px;
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
import React, { useState, FC } from 'react';
|
|
||||||
|
|
||||||
import ReactFlow, {
|
|
||||||
removeElements,
|
|
||||||
addEdge,
|
|
||||||
Background,
|
|
||||||
Elements,
|
|
||||||
Edge,
|
|
||||||
Connection,
|
|
||||||
ReactFlowProvider,
|
|
||||||
} from 'react-flow-renderer';
|
|
||||||
|
|
||||||
import './multiflows.css';
|
|
||||||
|
|
||||||
const initialElements: Elements = [
|
|
||||||
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 }, className: 'light' },
|
|
||||||
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 }, className: 'light' },
|
|
||||||
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 }, className: 'light' },
|
|
||||||
{ id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 }, className: 'light' },
|
|
||||||
{ id: 'e1-2', source: '1', target: '2', animated: true },
|
|
||||||
{ id: 'e1-3', source: '1', target: '3' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const Flow: FC = () => {
|
|
||||||
const [elements, setElements] = useState<Elements>(initialElements);
|
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
|
||||||
const onConnect = (params: Edge | Connection) => setElements((els) => addEdge(params, els));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ReactFlowProvider>
|
|
||||||
<ReactFlow elements={elements} onElementsRemove={onElementsRemove} onConnect={onConnect}>
|
|
||||||
<Background />
|
|
||||||
</ReactFlow>
|
|
||||||
</ReactFlowProvider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const MultiFlows: FC = () => (
|
|
||||||
<div className="react-flow__example-multiflows">
|
|
||||||
<Flow />
|
|
||||||
<Flow />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default MultiFlows;
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
.react-flow__example-multiflows {
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-flow__example-multiflows .react-flow {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-flow__example-multiflows .react-flow:first-child {
|
|
||||||
border-right: 2px solid #333;
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
import React, { useState, CSSProperties } from 'react';
|
|
||||||
|
|
||||||
import ReactFlow, { addEdge, isEdge, OnLoadParams, Elements, Position, Connection, Edge } from 'react-flow-renderer';
|
|
||||||
|
|
||||||
const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.fitView();
|
|
||||||
|
|
||||||
const initialElements: Elements = [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
sourcePosition: Position.Right,
|
|
||||||
type: 'input',
|
|
||||||
data: { label: 'Input' },
|
|
||||||
position: { x: 0, y: 80 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
type: 'output',
|
|
||||||
sourcePosition: Position.Right,
|
|
||||||
targetPosition: Position.Left,
|
|
||||||
data: { label: 'A Node' },
|
|
||||||
position: { x: 250, y: 0 },
|
|
||||||
},
|
|
||||||
{ id: 'e1-2', source: '1', type: 'smoothstep', target: '2', animated: true },
|
|
||||||
];
|
|
||||||
|
|
||||||
const buttonStyle: CSSProperties = { position: 'absolute', right: 10, top: 30, zIndex: 4 };
|
|
||||||
|
|
||||||
const NodeTypeChangeFlow = () => {
|
|
||||||
const [elements, setElements] = useState<Elements>(initialElements);
|
|
||||||
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
|
||||||
const changeType = () => {
|
|
||||||
setElements((elms) =>
|
|
||||||
elms.map((el) => {
|
|
||||||
if (isEdge(el) || el.type === 'input') {
|
|
||||||
return el;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...el,
|
|
||||||
type: el.type === 'default' ? 'output' : 'default',
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ReactFlow elements={elements} onConnect={onConnect} onLoad={onLoad}>
|
|
||||||
<button onClick={changeType} style={buttonStyle}>
|
|
||||||
change type
|
|
||||||
</button>
|
|
||||||
</ReactFlow>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default NodeTypeChangeFlow;
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
import React, { useState, CSSProperties, FC } from 'react';
|
|
||||||
|
|
||||||
import ReactFlow, {
|
|
||||||
addEdge,
|
|
||||||
Elements,
|
|
||||||
Position,
|
|
||||||
Connection,
|
|
||||||
Edge,
|
|
||||||
NodeProps,
|
|
||||||
NodeTypesType,
|
|
||||||
} from 'react-flow-renderer';
|
|
||||||
|
|
||||||
const initialElements: Elements = [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
sourcePosition: Position.Right,
|
|
||||||
type: 'input',
|
|
||||||
data: { label: 'Input' },
|
|
||||||
position: { x: 0, y: 80 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
type: 'a',
|
|
||||||
sourcePosition: Position.Right,
|
|
||||||
targetPosition: Position.Left,
|
|
||||||
data: { label: 'A Node' },
|
|
||||||
position: { x: 250, y: 0 },
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const buttonStyle: CSSProperties = { position: 'absolute', right: 10, top: 30, zIndex: 4 };
|
|
||||||
|
|
||||||
const nodeStyles: CSSProperties = { padding: '10px 15px', border: '1px solid #ddd' };
|
|
||||||
|
|
||||||
const NodeA: FC<NodeProps> = () => {
|
|
||||||
return <div style={nodeStyles}>A</div>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const NodeB: FC<NodeProps> = () => {
|
|
||||||
return <div style={nodeStyles}>B</div>;
|
|
||||||
};
|
|
||||||
|
|
||||||
type NodeTypesObject = {
|
|
||||||
[key: string]: NodeTypesType;
|
|
||||||
};
|
|
||||||
|
|
||||||
const nodeTypesObjects: NodeTypesObject = {
|
|
||||||
a: {
|
|
||||||
a: NodeA,
|
|
||||||
},
|
|
||||||
b: {
|
|
||||||
b: NodeB,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const NodeTypeChangeFlow = () => {
|
|
||||||
const [nodeTypesId, setNodeTypesId] = useState<string>('a');
|
|
||||||
const [elements, setElements] = useState<Elements>(initialElements);
|
|
||||||
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
|
||||||
const changeType = () => setNodeTypesId((nt) => (nt === 'a' ? 'b' : 'a'));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ReactFlow
|
|
||||||
elements={elements}
|
|
||||||
onConnect={onConnect}
|
|
||||||
nodeTypes={nodeTypesObjects[nodeTypesId]}
|
|
||||||
nodeTypesId={nodeTypesId}
|
|
||||||
>
|
|
||||||
<button onClick={changeType} style={buttonStyle}>
|
|
||||||
change type
|
|
||||||
</button>
|
|
||||||
</ReactFlow>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default NodeTypeChangeFlow;
|
|
||||||
@@ -1,202 +0,0 @@
|
|||||||
import React, { useState, MouseEvent, CSSProperties } from 'react';
|
|
||||||
|
|
||||||
import ReactFlow, {
|
|
||||||
removeElements,
|
|
||||||
addEdge,
|
|
||||||
MiniMap,
|
|
||||||
Controls,
|
|
||||||
Background,
|
|
||||||
isNode,
|
|
||||||
Node,
|
|
||||||
Elements,
|
|
||||||
FlowElement,
|
|
||||||
OnLoadParams,
|
|
||||||
FlowTransform,
|
|
||||||
SnapGrid,
|
|
||||||
ArrowHeadType,
|
|
||||||
Connection,
|
|
||||||
Edge,
|
|
||||||
} from 'react-flow-renderer';
|
|
||||||
|
|
||||||
const onNodeDragStart = (_: MouseEvent, node: Node) => console.log('drag start', node);
|
|
||||||
const onNodeDrag = (_: MouseEvent, node: Node) => console.log('drag', node);
|
|
||||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
|
||||||
const onNodeDoubleClick = (_: MouseEvent, node: Node) => console.log('node double click', node);
|
|
||||||
const onPaneClick = (event: MouseEvent) => console.log('pane click', event);
|
|
||||||
const onPaneScroll = (event?: MouseEvent) => console.log('pane scroll', event);
|
|
||||||
const onPaneContextMenu = (event: MouseEvent) => console.log('pane context menu', event);
|
|
||||||
const onSelectionDrag = (_: MouseEvent, nodes: Node[]) => console.log('selection drag', nodes);
|
|
||||||
const onSelectionDragStart = (_: MouseEvent, nodes: Node[]) => console.log('selection drag start', nodes);
|
|
||||||
const onSelectionDragStop = (_: MouseEvent, nodes: Node[]) => console.log('selection drag stop', nodes);
|
|
||||||
const onSelectionContextMenu = (event: MouseEvent, nodes: Node[]) => {
|
|
||||||
event.preventDefault();
|
|
||||||
console.log('selection context menu', nodes);
|
|
||||||
};
|
|
||||||
const onElementClick = (_: MouseEvent, element: FlowElement) =>
|
|
||||||
console.log(`${isNode(element) ? 'node' : 'edge'} click:`, element);
|
|
||||||
const onSelectionChange = (elements: Elements | null) => console.log('selection change', elements);
|
|
||||||
const onLoad = (reactFlowInstance: OnLoadParams) => {
|
|
||||||
console.log('flow loaded:', reactFlowInstance);
|
|
||||||
reactFlowInstance.fitView();
|
|
||||||
};
|
|
||||||
|
|
||||||
const onMoveStart = (transform?: FlowTransform) => console.log('zoom/move start', transform);
|
|
||||||
const onMoveEnd = (transform?: FlowTransform) => console.log('zoom/move end', transform);
|
|
||||||
const onEdgeContextMenu = (_: MouseEvent, edge: Edge) => console.log('edge context menu', edge);
|
|
||||||
const onEdgeMouseEnter = (_: MouseEvent, edge: Edge) => console.log('edge mouse enter', edge);
|
|
||||||
const onEdgeMouseMove = (_: MouseEvent, edge: Edge) => console.log('edge mouse move', edge);
|
|
||||||
const onEdgeMouseLeave = (_: MouseEvent, edge: Edge) => console.log('edge mouse leave', edge);
|
|
||||||
const onEdgeDoubleClick = (_: MouseEvent, edge: Edge) => console.log('edge double click', edge);
|
|
||||||
|
|
||||||
const initialElements: Elements = [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
type: 'input',
|
|
||||||
data: {
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
Welcome to <strong>React Flow!</strong>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
position: { x: 250, y: 0 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
data: {
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
This is a <strong>default node</strong>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
position: { x: 100, y: 100 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
data: {
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
This one has a <strong>custom style</strong>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
position: { x: 400, y: 100 },
|
|
||||||
style: { background: '#D6D5E6', color: '#333', border: '1px solid #222138', width: 180 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '4',
|
|
||||||
position: { x: 250, y: 200 },
|
|
||||||
data: {
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
You can find the docs on{' '}
|
|
||||||
<a href="https://github.com/wbkd/react-flow" target="_blank" rel="noopener noreferrer">
|
|
||||||
Github
|
|
||||||
</a>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '5',
|
|
||||||
data: {
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
Or check out the other <strong>examples</strong>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
position: { x: 250, y: 325 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '6',
|
|
||||||
type: 'output',
|
|
||||||
data: {
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
An <strong>output node</strong>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
position: { x: 100, y: 480 },
|
|
||||||
},
|
|
||||||
{ id: '7', type: 'output', data: { label: 'Another output node' }, position: { x: 400, y: 450 } },
|
|
||||||
{ id: 'e1-2', source: '1', target: '2', label: 'this is an edge label' },
|
|
||||||
{ id: 'e1-3', source: '1', target: '3' },
|
|
||||||
{ id: 'e3-4', source: '3', target: '4', animated: true, label: 'animated edge' },
|
|
||||||
{ id: 'e4-5', source: '4', target: '5', arrowHeadType: ArrowHeadType.Arrow, label: 'edge with arrow head' },
|
|
||||||
{ id: 'e5-6', source: '5', target: '6', type: 'smoothstep', label: 'smooth step edge' },
|
|
||||||
{
|
|
||||||
id: 'e5-7',
|
|
||||||
source: '5',
|
|
||||||
target: '7',
|
|
||||||
type: 'step',
|
|
||||||
style: { stroke: '#f6ab6c' },
|
|
||||||
label: 'a step edge',
|
|
||||||
animated: true,
|
|
||||||
labelStyle: { fill: '#f6ab6c', fontWeight: 700 },
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const connectionLineStyle: CSSProperties = { stroke: '#ddd' };
|
|
||||||
const snapGrid: SnapGrid = [16, 16];
|
|
||||||
|
|
||||||
const nodeStrokeColor = (n: Node): string => {
|
|
||||||
if (n.style?.background) return n.style.background as string;
|
|
||||||
if (n.type === 'input') return '#0041d0';
|
|
||||||
if (n.type === 'output') return '#ff0072';
|
|
||||||
if (n.type === 'default') return '#1a192b';
|
|
||||||
|
|
||||||
return '#eee';
|
|
||||||
};
|
|
||||||
|
|
||||||
const nodeColor = (n: Node): string => {
|
|
||||||
if (n.style?.background) return n.style.background as string;
|
|
||||||
|
|
||||||
return '#fff';
|
|
||||||
};
|
|
||||||
|
|
||||||
const OverviewFlow = () => {
|
|
||||||
const [elements, setElements] = useState(initialElements);
|
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
|
||||||
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ReactFlow
|
|
||||||
elements={elements}
|
|
||||||
onElementClick={onElementClick}
|
|
||||||
onElementsRemove={onElementsRemove}
|
|
||||||
onConnect={onConnect}
|
|
||||||
onPaneClick={onPaneClick}
|
|
||||||
onPaneScroll={onPaneScroll}
|
|
||||||
onPaneContextMenu={onPaneContextMenu}
|
|
||||||
onNodeDragStart={onNodeDragStart}
|
|
||||||
onNodeDrag={onNodeDrag}
|
|
||||||
onNodeDragStop={onNodeDragStop}
|
|
||||||
onNodeDoubleClick={onNodeDoubleClick}
|
|
||||||
onSelectionDragStart={onSelectionDragStart}
|
|
||||||
onSelectionDrag={onSelectionDrag}
|
|
||||||
onSelectionDragStop={onSelectionDragStop}
|
|
||||||
onSelectionContextMenu={onSelectionContextMenu}
|
|
||||||
onSelectionChange={onSelectionChange}
|
|
||||||
onMoveStart={onMoveStart}
|
|
||||||
onMoveEnd={onMoveEnd}
|
|
||||||
onLoad={onLoad}
|
|
||||||
connectionLineStyle={connectionLineStyle}
|
|
||||||
snapToGrid={true}
|
|
||||||
snapGrid={snapGrid}
|
|
||||||
onEdgeContextMenu={onEdgeContextMenu}
|
|
||||||
onEdgeMouseEnter={onEdgeMouseEnter}
|
|
||||||
onEdgeMouseMove={onEdgeMouseMove}
|
|
||||||
onEdgeMouseLeave={onEdgeMouseLeave}
|
|
||||||
onEdgeDoubleClick={onEdgeDoubleClick}
|
|
||||||
>
|
|
||||||
<MiniMap nodeStrokeColor={nodeStrokeColor} nodeColor={nodeColor} nodeBorderRadius={2} />
|
|
||||||
<Controls />
|
|
||||||
<Background color="#aaa" gap={20} />
|
|
||||||
</ReactFlow>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default OverviewFlow;
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useStoreState, useStoreActions } from 'react-flow-renderer';
|
|
||||||
|
|
||||||
const Sidebar = () => {
|
|
||||||
const nodes = useStoreState((store) => store.nodes);
|
|
||||||
const transform = useStoreState((store) => store.transform);
|
|
||||||
const setSelectedElements = useStoreActions((actions) => actions.setSelectedElements);
|
|
||||||
|
|
||||||
const selectAll = () => {
|
|
||||||
setSelectedElements(nodes.map((node) => ({ id: node.id, type: node.type })));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<aside>
|
|
||||||
<div className="description">
|
|
||||||
This is an example of how you can access the internal state outside of the ReactFlow component.
|
|
||||||
</div>
|
|
||||||
<div className="title">Zoom & pan transform</div>
|
|
||||||
<div className="transform">
|
|
||||||
[{transform[0].toFixed(2)}, {transform[1].toFixed(2)}, {transform[2].toFixed(2)}]
|
|
||||||
</div>
|
|
||||||
<div className="title">Nodes</div>
|
|
||||||
{nodes.map((node) => (
|
|
||||||
<div key={node.id}>
|
|
||||||
Node {node.id} - x: {node.__rf.position.x.toFixed(2)}, y: {node.__rf.position.y.toFixed(2)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<div className="selectall">
|
|
||||||
<button onClick={selectAll}>select all nodes</button>
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Sidebar;
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import React, { useState, MouseEvent } from 'react';
|
|
||||||
import ReactFlow, {
|
|
||||||
ReactFlowProvider,
|
|
||||||
addEdge,
|
|
||||||
removeElements,
|
|
||||||
Controls,
|
|
||||||
OnLoadParams,
|
|
||||||
FlowElement,
|
|
||||||
Connection,
|
|
||||||
Edge,
|
|
||||||
Elements,
|
|
||||||
ConnectionMode,
|
|
||||||
} from 'react-flow-renderer';
|
|
||||||
|
|
||||||
import Sidebar from './Sidebar';
|
|
||||||
|
|
||||||
import './provider.css';
|
|
||||||
|
|
||||||
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
|
|
||||||
const onLoad = (reactFlowInstance: OnLoadParams) => console.log('flow loaded:', reactFlowInstance);
|
|
||||||
|
|
||||||
const initialElements: Elements = [
|
|
||||||
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
|
|
||||||
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } },
|
|
||||||
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 } },
|
|
||||||
{ id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 } },
|
|
||||||
{ id: 'e1-2', source: '1', target: '2', animated: true },
|
|
||||||
{ id: 'e1-3', source: '1', target: '3' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const ProviderFlow = () => {
|
|
||||||
const [elements, setElements] = useState<Elements>(initialElements);
|
|
||||||
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="providerflow">
|
|
||||||
<ReactFlowProvider>
|
|
||||||
<Sidebar />
|
|
||||||
<div className="reactflow-wrapper">
|
|
||||||
<ReactFlow
|
|
||||||
elements={elements}
|
|
||||||
onElementClick={onElementClick}
|
|
||||||
onConnect={onConnect}
|
|
||||||
onElementsRemove={onElementsRemove}
|
|
||||||
onLoad={onLoad}
|
|
||||||
connectionMode={ConnectionMode.Loose}
|
|
||||||
>
|
|
||||||
<Controls />
|
|
||||||
</ReactFlow>
|
|
||||||
</div>
|
|
||||||
</ReactFlowProvider>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ProviderFlow;
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
.providerflow {
|
|
||||||
flex-direction: column;
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.providerflow aside {
|
|
||||||
border-right: 1px solid #eee;
|
|
||||||
padding: 15px 10px;
|
|
||||||
font-size: 12px;
|
|
||||||
background: #fcfcfc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.providerflow aside .description {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.providerflow aside .title {
|
|
||||||
font-weight: 700;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.providerflow aside .transform {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.providerflow .reactflow-wrapper {
|
|
||||||
flex-grow: 1;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.providerflow .selectall {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
|
||||||
.providerflow {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
.providerflow aside {
|
|
||||||
width: 20%;
|
|
||||||
max-width: 250px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
import React, { memo, useCallback, Dispatch, FC } from 'react';
|
|
||||||
import { useZoomPanHelper, OnLoadParams, Elements, FlowExportObject } from 'react-flow-renderer';
|
|
||||||
import localforage from 'localforage';
|
|
||||||
|
|
||||||
localforage.config({
|
|
||||||
name: 'react-flow',
|
|
||||||
storeName: 'flows',
|
|
||||||
});
|
|
||||||
|
|
||||||
const flowKey = 'example-flow';
|
|
||||||
|
|
||||||
const getNodeId = () => `randomnode_${+new Date()}`;
|
|
||||||
|
|
||||||
type ControlsProps = {
|
|
||||||
rfInstance?: OnLoadParams;
|
|
||||||
setElements: Dispatch<React.SetStateAction<Elements<any>>>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Controls: FC<ControlsProps> = ({ rfInstance, setElements }) => {
|
|
||||||
const { transform } = useZoomPanHelper();
|
|
||||||
|
|
||||||
const onSave = useCallback(() => {
|
|
||||||
if (rfInstance) {
|
|
||||||
const flow = rfInstance.toObject();
|
|
||||||
localforage.setItem(flowKey, flow);
|
|
||||||
}
|
|
||||||
}, [rfInstance]);
|
|
||||||
|
|
||||||
const onRestore = useCallback(() => {
|
|
||||||
const restoreFlow = async () => {
|
|
||||||
const flow: FlowExportObject | null = await localforage.getItem(flowKey);
|
|
||||||
|
|
||||||
if (flow) {
|
|
||||||
const [x = 0, y = 0] = flow.position;
|
|
||||||
setElements(flow.elements || []);
|
|
||||||
transform({ x, y, zoom: flow.zoom || 0 });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
restoreFlow();
|
|
||||||
}, [setElements, transform]);
|
|
||||||
|
|
||||||
const onAdd = useCallback(() => {
|
|
||||||
const newNode = {
|
|
||||||
id: `random_node-${getNodeId()}`,
|
|
||||||
data: { label: 'Added node' },
|
|
||||||
position: { x: Math.random() * window.innerWidth - 100, y: Math.random() * window.innerHeight },
|
|
||||||
};
|
|
||||||
setElements((els) => els.concat(newNode));
|
|
||||||
}, [setElements]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="save__controls">
|
|
||||||
<button onClick={onSave}>save</button>
|
|
||||||
<button onClick={onRestore}>restore</button>
|
|
||||||
<button onClick={onAdd}>add node</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(Controls);
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import ReactFlow, {
|
|
||||||
ReactFlowProvider,
|
|
||||||
removeElements,
|
|
||||||
addEdge,
|
|
||||||
Elements,
|
|
||||||
Connection,
|
|
||||||
Edge,
|
|
||||||
OnLoadParams,
|
|
||||||
} from 'react-flow-renderer';
|
|
||||||
|
|
||||||
import Controls from './Controls';
|
|
||||||
|
|
||||||
import './save.css';
|
|
||||||
|
|
||||||
const initialElements: Elements = [
|
|
||||||
{ id: '1', data: { label: 'Node 1' }, position: { x: 100, y: 100 } },
|
|
||||||
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 200 } },
|
|
||||||
{ id: 'e1-2', source: '1', target: '2' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const SaveRestore = () => {
|
|
||||||
const [rfInstance, setRfInstance] = useState<OnLoadParams>();
|
|
||||||
const [elements, setElements] = useState<Elements>(initialElements);
|
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
|
||||||
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ReactFlowProvider>
|
|
||||||
<ReactFlow elements={elements} onElementsRemove={onElementsRemove} onConnect={onConnect} onLoad={setRfInstance}>
|
|
||||||
<Controls rfInstance={rfInstance} setElements={setElements} />
|
|
||||||
</ReactFlow>
|
|
||||||
</ReactFlowProvider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SaveRestore;
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
.save__controls {
|
|
||||||
position: absolute;
|
|
||||||
right: 10px;
|
|
||||||
top: 10px;
|
|
||||||
z-index: 4;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.save__controls button {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,13 @@
|
|||||||
import React, { useState, CSSProperties } from 'react';
|
import { useState, CSSProperties, useCallback } from 'react';
|
||||||
import ReactFlow, {
|
import ReactFlow, {
|
||||||
removeElements,
|
|
||||||
addEdge,
|
|
||||||
MiniMap,
|
MiniMap,
|
||||||
isNode,
|
|
||||||
Controls,
|
Controls,
|
||||||
Background,
|
Background,
|
||||||
OnLoadParams,
|
OnLoadParams,
|
||||||
Elements,
|
|
||||||
Connection,
|
|
||||||
Edge,
|
Edge,
|
||||||
|
Node,
|
||||||
|
ElementChange,
|
||||||
|
applyNodeChanges,
|
||||||
} from 'react-flow-renderer';
|
} from 'react-flow-renderer';
|
||||||
|
|
||||||
import { getElements } from './utils';
|
import { getElements } from './utils';
|
||||||
@@ -21,38 +19,41 @@ const onLoad = (reactFlowInstance: OnLoadParams) => {
|
|||||||
console.log(reactFlowInstance.getElements());
|
console.log(reactFlowInstance.getElements());
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialElements: Elements = getElements(30, 30);
|
const initialElements = getElements(30, 30);
|
||||||
|
|
||||||
const StressFlow = () => {
|
const StressFlow = () => {
|
||||||
const [elements, setElements] = useState<Elements>(initialElements);
|
const [nodes, setNodes] = useState<Node[]>(initialElements.nodes);
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
const [edges, setEdges] = useState<Edge[]>(initialElements.edges);
|
||||||
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
// const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
||||||
|
// const onConnect = (params: Connection | Edge, nds: Node[]) => setElements((els) => addEdge(params, els));
|
||||||
|
|
||||||
const updatePos = () => {
|
const updatePos = () => {
|
||||||
setElements((elms) => {
|
setNodes((nds) => {
|
||||||
return elms.map((el) => {
|
return nds.map((n) => {
|
||||||
if (isNode(el)) {
|
return {
|
||||||
return {
|
...n,
|
||||||
...el,
|
position: {
|
||||||
position: {
|
x: Math.random() * window.innerWidth,
|
||||||
x: Math.random() * window.innerWidth,
|
y: Math.random() * window.innerHeight,
|
||||||
y: Math.random() * window.innerHeight,
|
},
|
||||||
},
|
};
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return el;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateElements = () => {
|
const updateElements = () => {
|
||||||
const grid = Math.ceil(Math.random() * 10);
|
const grid = Math.ceil(Math.random() * 10);
|
||||||
setElements(getElements(grid, grid));
|
const initialElements = getElements(grid, grid);
|
||||||
|
setNodes(initialElements.nodes);
|
||||||
|
setEdges(initialElements.edges);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onNodesChange = useCallback((changes: ElementChange[]) => {
|
||||||
|
setNodes((ns) => applyNodeChanges(changes, ns));
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlow elements={elements} onLoad={onLoad} onElementsRemove={onElementsRemove} onConnect={onConnect}>
|
<ReactFlow nodes={nodes} edges={edges} onLoad={onLoad} onNodesChange={onNodesChange}>
|
||||||
<MiniMap />
|
<MiniMap />
|
||||||
<Controls />
|
<Controls />
|
||||||
<Background />
|
<Background />
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
import { Elements } from 'react-flow-renderer';
|
import { Node, Edge } from 'react-flow-renderer';
|
||||||
|
|
||||||
export function getElements(xElements: number = 10, yElements: number = 10): Elements {
|
type ElementsCollection = {
|
||||||
const initialElements = [];
|
nodes: Node[];
|
||||||
|
edges: Edge[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getElements(xElements: number = 10, yElements: number = 10): ElementsCollection {
|
||||||
|
const initialNodes = [];
|
||||||
|
const initialEdges = [];
|
||||||
let nodeId = 1;
|
let nodeId = 1;
|
||||||
let recentNodeId = null;
|
let recentNodeId = null;
|
||||||
|
|
||||||
@@ -15,10 +21,10 @@ export function getElements(xElements: number = 10, yElements: number = 10): Ele
|
|||||||
data,
|
data,
|
||||||
position,
|
position,
|
||||||
};
|
};
|
||||||
initialElements.push(node);
|
initialNodes.push(node);
|
||||||
|
|
||||||
if (recentNodeId && nodeId <= xElements * yElements) {
|
if (recentNodeId && nodeId <= xElements * yElements) {
|
||||||
initialElements.push({ id: `${x}-${y}`, source: recentNodeId.toString(), target: nodeId.toString() });
|
initialEdges.push({ id: `${x}-${y}`, source: recentNodeId.toString(), target: nodeId.toString() });
|
||||||
}
|
}
|
||||||
|
|
||||||
recentNodeId = nodeId;
|
recentNodeId = nodeId;
|
||||||
@@ -26,5 +32,8 @@ export function getElements(xElements: number = 10, yElements: number = 10): Ele
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return initialElements;
|
return {
|
||||||
|
nodes: initialNodes,
|
||||||
|
edges: initialEdges,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
import React, { useState, MouseEvent } from 'react';
|
|
||||||
|
|
||||||
import ReactFlow, { removeElements, addEdge, Node, FlowElement, Elements, Connection, Edge } from 'react-flow-renderer';
|
|
||||||
|
|
||||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
|
||||||
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
|
|
||||||
|
|
||||||
const elementsA: Elements = [
|
|
||||||
{ id: '1a', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 }, className: 'light' },
|
|
||||||
{ id: '2a', data: { label: 'Node 2' }, position: { x: 100, y: 100 }, className: 'light' },
|
|
||||||
{ id: '3a', data: { label: 'Node 3' }, position: { x: 400, y: 100 }, className: 'light' },
|
|
||||||
{ id: '4a', data: { label: 'Node 4' }, position: { x: 400, y: 200 }, className: 'light' },
|
|
||||||
{ id: 'e1-2', source: '1a', target: '2a' },
|
|
||||||
{ id: 'e1-3', source: '1a', target: '3a' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const elementsB: Elements = [
|
|
||||||
{ id: 'inputb', type: 'input', data: { label: 'Input' }, position: { x: 300, y: 5 }, className: 'light' },
|
|
||||||
{ id: '1b', data: { label: 'Node 1' }, position: { x: 0, y: 100 }, className: 'light' },
|
|
||||||
{ id: '2b', data: { label: 'Node 2' }, position: { x: 200, y: 100 }, className: 'light' },
|
|
||||||
{ id: '3b', data: { label: 'Node 3' }, position: { x: 400, y: 100 }, className: 'light' },
|
|
||||||
{ id: '4b', data: { label: 'Node 4' }, position: { x: 600, y: 100 }, className: 'light' },
|
|
||||||
|
|
||||||
{ id: 'e1b', source: 'inputb', target: '1b' },
|
|
||||||
{ id: 'e2b', source: 'inputb', target: '2b' },
|
|
||||||
{ id: 'e3b', source: 'inputb', target: '3b' },
|
|
||||||
{ id: 'e4b', source: 'inputb', target: '4b' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const BasicFlow = () => {
|
|
||||||
const [elements, setElements] = useState<Elements>(elementsA);
|
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
|
||||||
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ReactFlow
|
|
||||||
elements={elements}
|
|
||||||
onElementClick={onElementClick}
|
|
||||||
onElementsRemove={onElementsRemove}
|
|
||||||
onConnect={onConnect}
|
|
||||||
onNodeDragStop={onNodeDragStop}
|
|
||||||
>
|
|
||||||
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
|
|
||||||
<button onClick={() => setElements(elementsA)} style={{ marginRight: 5 }}>
|
|
||||||
flow a
|
|
||||||
</button>
|
|
||||||
<button onClick={() => setElements(elementsB)}>flow b</button>
|
|
||||||
</div>
|
|
||||||
</ReactFlow>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BasicFlow;
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import React, { memo, FC, CSSProperties } from 'react';
|
|
||||||
|
|
||||||
import { Handle, Position, NodeProps } from 'react-flow-renderer';
|
|
||||||
|
|
||||||
const nodeStyles: CSSProperties = { padding: '10px 15px', border: '1px solid #ddd' };
|
|
||||||
|
|
||||||
const CustomNode: FC<NodeProps> = ({ id }) => {
|
|
||||||
return (
|
|
||||||
<div style={nodeStyles}>
|
|
||||||
<div>node {id}</div>
|
|
||||||
<Handle type="source" id="left" position={Position.Left} />
|
|
||||||
<Handle type="source" id="right" position={Position.Right} />
|
|
||||||
<Handle type="source" id="top" position={Position.Top} />
|
|
||||||
<Handle type="source" id="bottom" position={Position.Bottom} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(CustomNode);
|
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
import React, { useState, useCallback } from 'react';
|
|
||||||
|
|
||||||
import ReactFlow, {
|
|
||||||
NodeTypesType,
|
|
||||||
addEdge,
|
|
||||||
useZoomPanHelper,
|
|
||||||
ReactFlowProvider,
|
|
||||||
Elements,
|
|
||||||
Connection,
|
|
||||||
Edge,
|
|
||||||
ElementId,
|
|
||||||
ConnectionLineType,
|
|
||||||
ConnectionMode,
|
|
||||||
updateEdge,
|
|
||||||
ArrowHeadType,
|
|
||||||
} from 'react-flow-renderer';
|
|
||||||
import CustomNode from './CustomNode';
|
|
||||||
|
|
||||||
const initialElements: Elements = [
|
|
||||||
{
|
|
||||||
id: '00',
|
|
||||||
type: 'custom',
|
|
||||||
position: { x: 300, y: 250 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '01',
|
|
||||||
type: 'custom',
|
|
||||||
position: { x: 100, y: 50 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '02',
|
|
||||||
type: 'custom',
|
|
||||||
position: { x: 500, y: 50 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '03',
|
|
||||||
type: 'custom',
|
|
||||||
position: { x: 500, y: 500 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '04',
|
|
||||||
type: 'custom',
|
|
||||||
position: { x: 100, y: 500 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '10',
|
|
||||||
type: 'custom',
|
|
||||||
position: { x: 300, y: 5 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '20',
|
|
||||||
type: 'custom',
|
|
||||||
position: { x: 600, y: 250 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '30',
|
|
||||||
type: 'custom',
|
|
||||||
position: { x: 300, y: 600 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '40',
|
|
||||||
type: 'custom',
|
|
||||||
position: { x: 5, y: 250 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'e0-1a',
|
|
||||||
source: '00',
|
|
||||||
target: '01',
|
|
||||||
sourceHandle: 'left',
|
|
||||||
targetHandle: 'bottom',
|
|
||||||
type: 'smoothstep',
|
|
||||||
arrowHeadType: ArrowHeadType.Arrow,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'e0-1b',
|
|
||||||
source: '00',
|
|
||||||
target: '01',
|
|
||||||
sourceHandle: 'top',
|
|
||||||
targetHandle: 'right',
|
|
||||||
type: 'smoothstep',
|
|
||||||
arrowHeadType: ArrowHeadType.Arrow,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'e0-2a',
|
|
||||||
source: '00',
|
|
||||||
target: '02',
|
|
||||||
sourceHandle: 'top',
|
|
||||||
targetHandle: 'left',
|
|
||||||
type: 'smoothstep',
|
|
||||||
arrowHeadType: ArrowHeadType.Arrow,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'e0-2b',
|
|
||||||
source: '00',
|
|
||||||
target: '02',
|
|
||||||
sourceHandle: 'right',
|
|
||||||
targetHandle: 'bottom',
|
|
||||||
type: 'smoothstep',
|
|
||||||
arrowHeadType: ArrowHeadType.Arrow,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'e0-3a',
|
|
||||||
source: '00',
|
|
||||||
target: '03',
|
|
||||||
sourceHandle: 'right',
|
|
||||||
targetHandle: 'top',
|
|
||||||
type: 'smoothstep',
|
|
||||||
arrowHeadType: ArrowHeadType.Arrow,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'e0-3b',
|
|
||||||
source: '00',
|
|
||||||
target: '03',
|
|
||||||
sourceHandle: 'bottom',
|
|
||||||
targetHandle: 'left',
|
|
||||||
type: 'smoothstep',
|
|
||||||
arrowHeadType: ArrowHeadType.Arrow,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'e0-4a',
|
|
||||||
source: '00',
|
|
||||||
target: '04',
|
|
||||||
sourceHandle: 'bottom',
|
|
||||||
targetHandle: 'right',
|
|
||||||
type: 'smoothstep',
|
|
||||||
arrowHeadType: ArrowHeadType.Arrow,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'e0-4b',
|
|
||||||
source: '00',
|
|
||||||
target: '04',
|
|
||||||
sourceHandle: 'left',
|
|
||||||
targetHandle: 'top',
|
|
||||||
type: 'smoothstep',
|
|
||||||
arrowHeadType: ArrowHeadType.Arrow,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'e0-10',
|
|
||||||
source: '00',
|
|
||||||
target: '10',
|
|
||||||
sourceHandle: 'top',
|
|
||||||
targetHandle: 'bottom',
|
|
||||||
type: 'smoothstep',
|
|
||||||
arrowHeadType: ArrowHeadType.Arrow,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'e0-20',
|
|
||||||
source: '00',
|
|
||||||
target: '20',
|
|
||||||
sourceHandle: 'right',
|
|
||||||
targetHandle: 'left',
|
|
||||||
type: 'smoothstep',
|
|
||||||
arrowHeadType: ArrowHeadType.Arrow,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'e0-30',
|
|
||||||
source: '00',
|
|
||||||
target: '30',
|
|
||||||
sourceHandle: 'bottom',
|
|
||||||
targetHandle: 'top',
|
|
||||||
type: 'smoothstep',
|
|
||||||
arrowHeadType: ArrowHeadType.Arrow,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'e0-40',
|
|
||||||
source: '00',
|
|
||||||
target: '40',
|
|
||||||
sourceHandle: 'left',
|
|
||||||
targetHandle: 'right',
|
|
||||||
type: 'smoothstep',
|
|
||||||
arrowHeadType: ArrowHeadType.Arrow,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const nodeTypes: NodeTypesType = {
|
|
||||||
custom: CustomNode,
|
|
||||||
};
|
|
||||||
|
|
||||||
let id = 4;
|
|
||||||
const getId = (): ElementId => `${id++}`;
|
|
||||||
|
|
||||||
const UpdateNodeInternalsFlow = () => {
|
|
||||||
const [elements, setElements] = useState<Elements>(initialElements);
|
|
||||||
const onConnect = (params: Connection | Edge) =>
|
|
||||||
setElements((els) => addEdge({ ...params, type: 'smoothstep' }, els));
|
|
||||||
const { project } = useZoomPanHelper();
|
|
||||||
const onEdgeUpdate = (oldEdge: Edge, newConnection: Connection) =>
|
|
||||||
setElements((els) => updateEdge(oldEdge, newConnection, els));
|
|
||||||
|
|
||||||
const onPaneClick = useCallback(
|
|
||||||
(evt) =>
|
|
||||||
setElements((els) =>
|
|
||||||
els.concat({
|
|
||||||
id: getId(),
|
|
||||||
position: project({ x: evt.clientX, y: evt.clientY - 40 }),
|
|
||||||
type: 'custom',
|
|
||||||
})
|
|
||||||
),
|
|
||||||
[project]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ReactFlow
|
|
||||||
elements={elements}
|
|
||||||
nodeTypes={nodeTypes}
|
|
||||||
onConnect={onConnect}
|
|
||||||
onPaneClick={onPaneClick}
|
|
||||||
connectionLineType={ConnectionLineType.SmoothStep}
|
|
||||||
connectionMode={ConnectionMode.Loose}
|
|
||||||
onEdgeUpdate={onEdgeUpdate}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const WrappedFlow = () => (
|
|
||||||
<ReactFlowProvider>
|
|
||||||
<UpdateNodeInternalsFlow />
|
|
||||||
</ReactFlowProvider>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default WrappedFlow;
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import ReactFlow, {
|
|
||||||
Controls,
|
|
||||||
updateEdge,
|
|
||||||
addEdge,
|
|
||||||
Elements,
|
|
||||||
OnLoadParams,
|
|
||||||
Connection,
|
|
||||||
Edge,
|
|
||||||
removeElements,
|
|
||||||
} from 'react-flow-renderer';
|
|
||||||
|
|
||||||
const initialElements: Elements = [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
type: 'input',
|
|
||||||
data: {
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
Node <strong>A</strong>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
position: { x: 250, y: 0 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
data: {
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
Node <strong>B</strong>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
position: { x: 100, y: 100 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
data: {
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
Node <strong>C</strong>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
position: { x: 400, y: 100 },
|
|
||||||
style: { background: '#D6D5E6', color: '#333', border: '1px solid #222138', width: 180 },
|
|
||||||
},
|
|
||||||
{ id: 'e1-2', source: '1', target: '2', label: 'This is a draggable edge' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.fitView();
|
|
||||||
const onEdgeUpdateStart = (_: React.MouseEvent, edge: Edge) => console.log('start update', edge);
|
|
||||||
const onEdgeUpdateEnd = (_: MouseEvent, edge: Edge) => console.log('end update', edge);
|
|
||||||
|
|
||||||
const UpdatableEdge = () => {
|
|
||||||
const [elements, setElements] = useState<Elements>(initialElements);
|
|
||||||
const onEdgeUpdate = (oldEdge: Edge, newConnection: Connection) =>
|
|
||||||
setElements((els) => updateEdge(oldEdge, newConnection, els));
|
|
||||||
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ReactFlow
|
|
||||||
elements={elements}
|
|
||||||
onLoad={onLoad}
|
|
||||||
snapToGrid={true}
|
|
||||||
onEdgeUpdate={onEdgeUpdate}
|
|
||||||
onConnect={onConnect}
|
|
||||||
onEdgeUpdateStart={onEdgeUpdateStart}
|
|
||||||
onElementsRemove={onElementsRemove}
|
|
||||||
onEdgeUpdateEnd={onEdgeUpdateEnd}
|
|
||||||
>
|
|
||||||
<Controls />
|
|
||||||
</ReactFlow>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default UpdatableEdge;
|
|
||||||
@@ -1,64 +1,71 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import { useEffect, useState, useCallback } from 'react';
|
||||||
import ReactFlow, { Elements } from 'react-flow-renderer';
|
import ReactFlow, { Node, Edge, applyNodeChanges, ElementChange } from 'react-flow-renderer';
|
||||||
|
|
||||||
import './updatenode.css';
|
import './updatenode.css';
|
||||||
|
|
||||||
const initialElements: Elements = [
|
const initialNodes: Node[] = [
|
||||||
{ 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' },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const initialEdges: Edge[] = [{ id: 'e1-2', source: '1', target: '2' }];
|
||||||
|
|
||||||
const UpdateNode = () => {
|
const UpdateNode = () => {
|
||||||
const [elements, setElements] = useState<Elements>(initialElements);
|
const [nodes, setNodes] = useState<Node[]>(initialNodes);
|
||||||
|
const [edges, setEdges] = useState<Edge[]>(initialEdges);
|
||||||
|
|
||||||
const [nodeName, setNodeName] = useState<string>('Node 1');
|
const [nodeName, setNodeName] = useState<string>('Node 1');
|
||||||
const [nodeBg, setNodeBg] = useState<string>('#eee');
|
const [nodeBg, setNodeBg] = useState<string>('#eee');
|
||||||
const [nodeHidden, setNodeHidden] = useState<boolean>(false);
|
const [nodeHidden, setNodeHidden] = useState<boolean>(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setElements((els) =>
|
setNodes((nds) =>
|
||||||
els.map((el) => {
|
nds.map((n) => {
|
||||||
if (el.id === '1') {
|
if (n.id === '1') {
|
||||||
// it's important that you create a new object here in order to notify react flow about the change
|
// it's important that you create a new object here in order to notify react flow about the change
|
||||||
el.data = {
|
n.data = {
|
||||||
...el.data,
|
...n.data,
|
||||||
label: nodeName,
|
label: nodeName,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return el;
|
return n;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}, [nodeName, setElements]);
|
}, [nodeName]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setElements((els) =>
|
setNodes((nds) =>
|
||||||
els.map((el) => {
|
nds.map((n) => {
|
||||||
if (el.id === '1') {
|
if (n.id === '1') {
|
||||||
// it's important that you create a new object here in order to notify react flow about the change
|
// it's important that you create a new object here in order to notify react flow about the change
|
||||||
el.style = { ...el.style, backgroundColor: nodeBg };
|
n.style = { ...n.style, backgroundColor: nodeBg };
|
||||||
}
|
}
|
||||||
|
|
||||||
return el;
|
return n;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}, [nodeBg, setElements]);
|
}, [nodeBg]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setElements((els) =>
|
setNodes((nds) =>
|
||||||
els.map((el) => {
|
nds.map((n) => {
|
||||||
if (el.id === '1' || el.id === 'e1-2') {
|
if (n.id === '1' || n.id === 'e1-2') {
|
||||||
// when you update a simple type you can just update the value
|
// when you update a simple type you can just update the value
|
||||||
el.isHidden = nodeHidden;
|
n.isHidden = nodeHidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
return el;
|
return n;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}, [nodeHidden, setElements]);
|
}, [nodeHidden]);
|
||||||
|
|
||||||
|
const onNodesChange = useCallback((changes: ElementChange[]) => {
|
||||||
|
setNodes((ns) => applyNodeChanges(changes, ns));
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlow elements={elements} defaultZoom={1.5} minZoom={0.2} maxZoom={4}>
|
<ReactFlow nodes={nodes} edges={edges} defaultZoom={1.5} minZoom={0.2} maxZoom={4} onNodesChange={onNodesChange}>
|
||||||
<div className="updatenode__controls">
|
<div className="updatenode__controls">
|
||||||
<label>label:</label>
|
<label>label:</label>
|
||||||
<input value={nodeName} onChange={(evt) => setNodeName(evt.target.value)} />
|
<input value={nodeName} onChange={(evt) => setNodeName(evt.target.value)} />
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
import React, { memo, FC, useMemo, CSSProperties } from 'react';
|
|
||||||
|
|
||||||
import { Handle, Position, NodeProps } from 'react-flow-renderer';
|
|
||||||
|
|
||||||
const nodeStyles: CSSProperties = { padding: 10, border: '1px solid #ddd' };
|
|
||||||
|
|
||||||
const CustomNode: FC<NodeProps> = ({ data }) => {
|
|
||||||
const handles = useMemo(
|
|
||||||
() =>
|
|
||||||
Array.from({ length: data.handleCount }, (x, i) => {
|
|
||||||
const handleId = `handle-${i}`;
|
|
||||||
return (
|
|
||||||
<Handle
|
|
||||||
key={handleId}
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
id={handleId}
|
|
||||||
style={{ top: 10 * i + data.handlePosition * 10 }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
[data.handleCount, data.handlePosition]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={nodeStyles}>
|
|
||||||
<Handle type="target" position={Position.Left} />
|
|
||||||
<div>output handle count: {data.handleCount}</div>
|
|
||||||
{handles}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(CustomNode);
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
import React, { useState, useCallback, CSSProperties } from 'react';
|
|
||||||
|
|
||||||
import ReactFlow, {
|
|
||||||
NodeTypesType,
|
|
||||||
addEdge,
|
|
||||||
useZoomPanHelper,
|
|
||||||
ReactFlowProvider,
|
|
||||||
Elements,
|
|
||||||
Connection,
|
|
||||||
Edge,
|
|
||||||
ElementId,
|
|
||||||
useUpdateNodeInternals,
|
|
||||||
Position,
|
|
||||||
isEdge,
|
|
||||||
} from 'react-flow-renderer';
|
|
||||||
import CustomNode from './CustomNode';
|
|
||||||
|
|
||||||
const initialHandleCount = 1;
|
|
||||||
|
|
||||||
const initialElements: Elements = [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
type: 'custom',
|
|
||||||
data: { label: 'Node 1', handleCount: initialHandleCount, handlePosition: 0 },
|
|
||||||
position: { x: 250, y: 5 },
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const buttonWrapperStyles: CSSProperties = { position: 'absolute', right: 10, top: 10, zIndex: 10 };
|
|
||||||
|
|
||||||
const nodeTypes: NodeTypesType = {
|
|
||||||
custom: CustomNode,
|
|
||||||
};
|
|
||||||
|
|
||||||
let id = 5;
|
|
||||||
const getId = (): ElementId => `${id++}`;
|
|
||||||
|
|
||||||
const UpdateNodeInternalsFlow = () => {
|
|
||||||
const [elements, setElements] = useState<Elements>(initialElements);
|
|
||||||
const updateNodeInternals = useUpdateNodeInternals();
|
|
||||||
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
|
||||||
const { project } = useZoomPanHelper();
|
|
||||||
|
|
||||||
const onPaneClick = useCallback(
|
|
||||||
(evt) =>
|
|
||||||
setElements((els) =>
|
|
||||||
els.concat({
|
|
||||||
id: getId(),
|
|
||||||
position: project({ x: evt.clientX, y: evt.clientY - 40 }),
|
|
||||||
data: { label: 'new node' },
|
|
||||||
targetPosition: Position.Left,
|
|
||||||
sourcePosition: Position.Right,
|
|
||||||
})
|
|
||||||
),
|
|
||||||
[project]
|
|
||||||
);
|
|
||||||
|
|
||||||
const toggleHandleCount = useCallback(() => {
|
|
||||||
setElements((els) =>
|
|
||||||
els.map((el) => {
|
|
||||||
if (isEdge(el)) {
|
|
||||||
return el;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { ...el, data: { ...el.data, handleCount: el.data?.handleCount === 1 ? 2 : 1 } };
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const toggleHandlePosition = useCallback(() => {
|
|
||||||
setElements((els) =>
|
|
||||||
els.map((el) => {
|
|
||||||
if (isEdge(el)) {
|
|
||||||
return el;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { ...el, data: { ...el.data, handlePosition: el.data?.handlePosition === 0 ? 1 : 0 } };
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const updateNode = useCallback(() => updateNodeInternals('1'), [updateNodeInternals]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ReactFlow elements={elements} nodeTypes={nodeTypes} onConnect={onConnect} onPaneClick={onPaneClick}>
|
|
||||||
<div style={buttonWrapperStyles}>
|
|
||||||
<button onClick={toggleHandleCount}>toggle handle count</button>
|
|
||||||
<button onClick={toggleHandlePosition}>toggle handle position</button>
|
|
||||||
<button onClick={updateNode}>update node internals</button>
|
|
||||||
</div>
|
|
||||||
</ReactFlow>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const WrappedFlow = () => (
|
|
||||||
<ReactFlowProvider>
|
|
||||||
<UpdateNodeInternalsFlow />
|
|
||||||
</ReactFlowProvider>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default WrappedFlow;
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import React, { useState, useCallback } from 'react';
|
|
||||||
|
|
||||||
import ReactFlow, {
|
|
||||||
removeElements,
|
|
||||||
addEdge,
|
|
||||||
Background,
|
|
||||||
MiniMap,
|
|
||||||
useZoomPanHelper,
|
|
||||||
ReactFlowProvider,
|
|
||||||
Elements,
|
|
||||||
ElementId,
|
|
||||||
Connection,
|
|
||||||
Edge,
|
|
||||||
} from 'react-flow-renderer';
|
|
||||||
|
|
||||||
const initialElements: Elements = [
|
|
||||||
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 }, className: 'light' },
|
|
||||||
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 }, className: 'light' },
|
|
||||||
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 }, className: 'light' },
|
|
||||||
{ id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 }, className: 'light' },
|
|
||||||
{ id: 'e1-2', source: '1', target: '2', animated: true },
|
|
||||||
{ id: 'e1-3', source: '1', target: '3' },
|
|
||||||
];
|
|
||||||
|
|
||||||
let id = 5;
|
|
||||||
const getId = (): ElementId => `${id++}`;
|
|
||||||
|
|
||||||
const UseZoomPanHelperFlow = () => {
|
|
||||||
const [elements, setElements] = useState<Elements>(initialElements);
|
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
|
||||||
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
|
||||||
const { project } = useZoomPanHelper();
|
|
||||||
|
|
||||||
const onPaneClick = useCallback(
|
|
||||||
(evt) => {
|
|
||||||
const projectedPosition = project({ x: evt.clientX, y: evt.clientY - 40 });
|
|
||||||
|
|
||||||
setElements((els) =>
|
|
||||||
els.concat({
|
|
||||||
id: getId(),
|
|
||||||
position: projectedPosition,
|
|
||||||
data: {
|
|
||||||
label: `${projectedPosition.x}-${projectedPosition.y}`,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[project]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ReactFlow elements={elements} onElementsRemove={onElementsRemove} onConnect={onConnect} onPaneClick={onPaneClick}>
|
|
||||||
<Background />
|
|
||||||
<MiniMap />
|
|
||||||
</ReactFlow>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const WrappedFlow = () => (
|
|
||||||
<ReactFlowProvider>
|
|
||||||
<UseZoomPanHelperFlow />
|
|
||||||
</ReactFlowProvider>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default WrappedFlow;
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
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,31 +0,0 @@
|
|||||||
.validationflow .react-flow__node {
|
|
||||||
width: 150px;
|
|
||||||
border-radius: 5px;
|
|
||||||
padding: 10px;
|
|
||||||
color: #555;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.validationflow .react-flow__node-customnode {
|
|
||||||
background: #e6e6e9;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-flow__node-custominput .react-flow__handle {
|
|
||||||
background: #e6e6e9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.validationflow .react-flow__node-custominput {
|
|
||||||
background: #fff;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.validationflow .react-flow__handle-connecting {
|
|
||||||
background: #ff6060;
|
|
||||||
}
|
|
||||||
|
|
||||||
.validationflow .react-flow__handle-valid {
|
|
||||||
background: #55dd99;
|
|
||||||
}
|
|
||||||
+3
-113
@@ -2,134 +2,24 @@ 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';
|
||||||
|
|
||||||
import Overview from './Overview';
|
|
||||||
import Basic from './Basic';
|
import Basic from './Basic';
|
||||||
import CustomNode from './CustomNode';
|
|
||||||
import Stress from './Stress';
|
|
||||||
import Interaction from './Interaction';
|
|
||||||
import Empty from './Empty';
|
|
||||||
import Edges from './Edges';
|
|
||||||
import Validation from './Validation';
|
|
||||||
import Provider from './Provider';
|
|
||||||
import Hidden from './Hidden';
|
|
||||||
import EdgeTypes from './EdgeTypes';
|
|
||||||
import CustomConnectionLine from './CustomConnectionLine';
|
|
||||||
import NodeTypeChange from './NodeTypeChange';
|
|
||||||
import NodeTypesObjectChange from './NodeTypesObjectChange';
|
|
||||||
import UpdatableEdge from './UpdatableEdge';
|
|
||||||
import UpdateNode from './UpdateNode';
|
import UpdateNode from './UpdateNode';
|
||||||
import SaveRestore from './SaveRestore';
|
import Stress from './Stress';
|
||||||
import DragNDrop from './DragNDrop';
|
|
||||||
import Layout from './Layouting';
|
|
||||||
import SwitchFlows from './Switch';
|
|
||||||
import UseZoomPanHelper from './UseZoomPanHelper';
|
|
||||||
import UseUpdateNodeInternals from './UseUpdateNodeInternals';
|
|
||||||
import Undirectional from './Undirectional';
|
|
||||||
import MultiFlows from './MultiFlows';
|
|
||||||
import DragHandle from './DragHandle';
|
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
|
||||||
path: '/',
|
|
||||||
component: Overview,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/edges',
|
|
||||||
component: Edges,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/custom-node',
|
|
||||||
component: CustomNode,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/validation',
|
|
||||||
component: Validation,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/provider',
|
|
||||||
component: Provider,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/stress',
|
|
||||||
component: Stress,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/interaction',
|
|
||||||
component: Interaction,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/basic',
|
path: '/basic',
|
||||||
component: Basic,
|
component: Basic,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/empty',
|
|
||||||
component: Empty,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/hidden',
|
|
||||||
component: Hidden,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/edge-types',
|
|
||||||
component: EdgeTypes,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/custom-connectionline',
|
|
||||||
component: CustomConnectionLine,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/nodetype-change',
|
|
||||||
component: NodeTypeChange,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/nodetypesobject-change',
|
|
||||||
component: NodeTypesObjectChange,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/updatable-edge',
|
|
||||||
component: UpdatableEdge,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/update-node',
|
path: '/update-node',
|
||||||
component: UpdateNode,
|
component: UpdateNode,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/save-restore',
|
path: '/stress',
|
||||||
component: SaveRestore,
|
component: Stress,
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/drag-and-drop',
|
|
||||||
component: DragNDrop,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/layouting',
|
|
||||||
component: Layout,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/switch',
|
|
||||||
component: SwitchFlows,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/usezoompanhelper',
|
|
||||||
component: UseZoomPanHelper,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/useupdatenodeinternals',
|
|
||||||
component: UseUpdateNodeInternals,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/undirectional',
|
|
||||||
component: Undirectional,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/multiflows',
|
|
||||||
component: MultiFlows,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/draghandle',
|
|
||||||
component: DragHandle,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
Generated
+37
-1
@@ -18,7 +18,8 @@
|
|||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"react-draggable": "^4.4.4",
|
"react-draggable": "^4.4.4",
|
||||||
"react-redux": "^7.2.5",
|
"react-redux": "^7.2.5",
|
||||||
"redux": "^4.1.1"
|
"redux": "^4.1.1",
|
||||||
|
"redux-thunk": "^2.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.15.5",
|
"@babel/core": "^7.15.5",
|
||||||
@@ -32,6 +33,7 @@
|
|||||||
"@svgr/rollup": "^5.5.0",
|
"@svgr/rollup": "^5.5.0",
|
||||||
"@types/redux": "^3.6.31",
|
"@types/redux": "^3.6.31",
|
||||||
"@types/resize-observer-browser": "^0.1.6",
|
"@types/resize-observer-browser": "^0.1.6",
|
||||||
|
"@welldone-software/why-did-you-render": "^6.2.1",
|
||||||
"autoprefixer": "^10.3.6",
|
"autoprefixer": "^10.3.6",
|
||||||
"babel-preset-react-app": "^10.0.0",
|
"babel-preset-react-app": "^10.0.0",
|
||||||
"cypress": "^8.5.0",
|
"cypress": "^8.5.0",
|
||||||
@@ -52,6 +54,9 @@
|
|||||||
"start-server-and-test": "^1.14.0",
|
"start-server-and-test": "^1.14.0",
|
||||||
"typescript": "^4.4.3"
|
"typescript": "^4.4.3"
|
||||||
},
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "16 || 17",
|
"react": "16 || 17",
|
||||||
"react-dom": "16 || 17"
|
"react-dom": "16 || 17"
|
||||||
@@ -2833,6 +2838,18 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@welldone-software/why-did-you-render": {
|
||||||
|
"version": "6.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@welldone-software/why-did-you-render/-/why-did-you-render-6.2.1.tgz",
|
||||||
|
"integrity": "sha512-eIVKeK6ueS3tuzCqMVTaaNrPYvb9cA8NHiNgLA7Op8SD4TiT31zqNjxmhzLEK+y3sBxcwr6YhsiQGX9EThrvaw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": "^4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16 || ^17"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/aggregate-error": {
|
"node_modules/aggregate-error": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
|
||||||
@@ -9261,6 +9278,11 @@
|
|||||||
"@babel/runtime": "^7.9.2"
|
"@babel/runtime": "^7.9.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/redux-thunk": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw=="
|
||||||
|
},
|
||||||
"node_modules/regenerate": {
|
"node_modules/regenerate": {
|
||||||
"version": "1.4.2",
|
"version": "1.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
||||||
@@ -13296,6 +13318,15 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@welldone-software/why-did-you-render": {
|
||||||
|
"version": "6.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@welldone-software/why-did-you-render/-/why-did-you-render-6.2.1.tgz",
|
||||||
|
"integrity": "sha512-eIVKeK6ueS3tuzCqMVTaaNrPYvb9cA8NHiNgLA7Op8SD4TiT31zqNjxmhzLEK+y3sBxcwr6YhsiQGX9EThrvaw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"lodash": "^4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"aggregate-error": {
|
"aggregate-error": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
|
||||||
@@ -18047,6 +18078,11 @@
|
|||||||
"@babel/runtime": "^7.9.2"
|
"@babel/runtime": "^7.9.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"redux-thunk": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw=="
|
||||||
|
},
|
||||||
"regenerate": {
|
"regenerate": {
|
||||||
"version": "1.4.2",
|
"version": "1.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
||||||
|
|||||||
+3
-1
@@ -40,7 +40,8 @@
|
|||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"react-draggable": "^4.4.4",
|
"react-draggable": "^4.4.4",
|
||||||
"react-redux": "^7.2.5",
|
"react-redux": "^7.2.5",
|
||||||
"redux": "^4.1.1"
|
"redux": "^4.1.1",
|
||||||
|
"redux-thunk": "^2.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.15.5",
|
"@babel/core": "^7.15.5",
|
||||||
@@ -54,6 +55,7 @@
|
|||||||
"@svgr/rollup": "^5.5.0",
|
"@svgr/rollup": "^5.5.0",
|
||||||
"@types/redux": "^3.6.31",
|
"@types/redux": "^3.6.31",
|
||||||
"@types/resize-observer-browser": "^0.1.6",
|
"@types/resize-observer-browser": "^0.1.6",
|
||||||
|
"@welldone-software/why-did-you-render": "^6.2.1",
|
||||||
"autoprefixer": "^10.3.6",
|
"autoprefixer": "^10.3.6",
|
||||||
"babel-preset-react-app": "^10.0.0",
|
"babel-preset-react-app": "^10.0.0",
|
||||||
"cypress": "^8.5.0",
|
"cypress": "^8.5.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user