Merge pull request #1771 from wbkd/feat/mobile-support
Feat/mobile support
This commit is contained in:
@@ -1,20 +1,18 @@
|
|||||||
import React, { useState, MouseEvent, CSSProperties } from 'react';
|
import { MouseEvent, CSSProperties } from 'react';
|
||||||
|
|
||||||
import ReactFlow, {
|
import ReactFlow, {
|
||||||
removeElements,
|
|
||||||
addEdge,
|
addEdge,
|
||||||
MiniMap,
|
MiniMap,
|
||||||
Controls,
|
Controls,
|
||||||
Background,
|
Background,
|
||||||
isNode,
|
|
||||||
Node,
|
Node,
|
||||||
Elements,
|
|
||||||
FlowElement,
|
|
||||||
OnLoadParams,
|
|
||||||
FlowTransform,
|
FlowTransform,
|
||||||
SnapGrid,
|
SnapGrid,
|
||||||
Connection,
|
Connection,
|
||||||
Edge,
|
Edge,
|
||||||
|
ReactFlowInstance,
|
||||||
|
useNodesState,
|
||||||
|
useEdgesState,
|
||||||
|
OnSelectionChangeParams,
|
||||||
} from 'react-flow-renderer';
|
} from 'react-flow-renderer';
|
||||||
|
|
||||||
const onNodeDragStart = (_: MouseEvent, node: Node) => console.log('drag start', node);
|
const onNodeDragStart = (_: MouseEvent, node: Node) => console.log('drag start', node);
|
||||||
@@ -31,10 +29,10 @@ const onSelectionContextMenu = (event: MouseEvent, nodes: Node[]) => {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
console.log('selection context menu', nodes);
|
console.log('selection context menu', nodes);
|
||||||
};
|
};
|
||||||
const onElementClick = (_: MouseEvent, element: FlowElement) =>
|
const onNodeClick = (_: MouseEvent, node: Node) => console.log('node click:', node);
|
||||||
console.log(`${isNode(element) ? 'node' : 'edge'} click:`, element);
|
|
||||||
const onSelectionChange = (elements: Elements | null) => console.log('selection change', elements);
|
const onSelectionChange = ({ nodes, edges }: OnSelectionChangeParams) => console.log('selection change', nodes, edges);
|
||||||
const onLoad = (reactFlowInstance: OnLoadParams) => {
|
const onPaneReady = (reactFlowInstance: ReactFlowInstance) => {
|
||||||
console.log('flow loaded:', reactFlowInstance);
|
console.log('flow loaded:', reactFlowInstance);
|
||||||
reactFlowInstance.fitView();
|
reactFlowInstance.fitView();
|
||||||
};
|
};
|
||||||
@@ -47,7 +45,7 @@ const onEdgeMouseMove = (_: MouseEvent, edge: Edge) => console.log('edge mouse m
|
|||||||
const onEdgeMouseLeave = (_: MouseEvent, edge: Edge) => console.log('edge mouse leave', edge);
|
const onEdgeMouseLeave = (_: MouseEvent, edge: Edge) => console.log('edge mouse leave', edge);
|
||||||
const onEdgeDoubleClick = (_: MouseEvent, edge: Edge) => console.log('edge double click', edge);
|
const onEdgeDoubleClick = (_: MouseEvent, edge: Edge) => console.log('edge double click', edge);
|
||||||
|
|
||||||
const initialElements: Elements = [
|
const initialNodes: Node[] = [
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
@@ -121,6 +119,9 @@ const initialElements: Elements = [
|
|||||||
position: { x: 100, y: 480 },
|
position: { x: 100, y: 480 },
|
||||||
},
|
},
|
||||||
{ id: '7', type: 'output', data: { label: 'Another output node' }, position: { x: 400, y: 450 } },
|
{ id: '7', type: 'output', data: { label: 'Another output node' }, position: { x: 400, y: 450 } },
|
||||||
|
];
|
||||||
|
|
||||||
|
const initialEdges: Edge[] = [
|
||||||
{ id: 'e1-2', source: '1', target: '2', label: 'this is an edge label' },
|
{ id: 'e1-2', source: '1', target: '2', label: 'this is an edge label' },
|
||||||
{ id: 'e1-3', source: '1', target: '3' },
|
{ id: 'e1-3', source: '1', target: '3' },
|
||||||
{ id: 'e3-4', source: '3', target: '4', animated: true, label: 'animated edge' },
|
{ id: 'e3-4', source: '3', target: '4', animated: true, label: 'animated edge' },
|
||||||
@@ -157,15 +158,17 @@ const nodeColor = (n: Node): string => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const OverviewFlow = () => {
|
const OverviewFlow = () => {
|
||||||
const [elements, setElements] = useState(initialElements);
|
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||||
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
const onConnect = (params: Connection | Edge) => setEdges((eds) => addEdge(params, eds));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
elements={elements}
|
nodes={nodes}
|
||||||
onElementClick={onElementClick}
|
edges={edges}
|
||||||
onElementsRemove={onElementsRemove}
|
onNodesChange={onNodesChange}
|
||||||
|
onEdgesChange={onEdgesChange}
|
||||||
|
onNodeClick={onNodeClick}
|
||||||
onConnect={onConnect}
|
onConnect={onConnect}
|
||||||
onPaneClick={onPaneClick}
|
onPaneClick={onPaneClick}
|
||||||
onPaneScroll={onPaneScroll}
|
onPaneScroll={onPaneScroll}
|
||||||
@@ -181,7 +184,7 @@ const OverviewFlow = () => {
|
|||||||
onSelectionChange={onSelectionChange}
|
onSelectionChange={onSelectionChange}
|
||||||
onMoveStart={onMoveStart}
|
onMoveStart={onMoveStart}
|
||||||
onMoveEnd={onMoveEnd}
|
onMoveEnd={onMoveEnd}
|
||||||
onLoad={onLoad}
|
onPaneReady={onPaneReady}
|
||||||
connectionLineStyle={connectionLineStyle}
|
connectionLineStyle={connectionLineStyle}
|
||||||
snapToGrid={true}
|
snapToGrid={true}
|
||||||
snapGrid={snapGrid}
|
snapGrid={snapGrid}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
|
import ReactFlow, {
|
||||||
|
Node,
|
||||||
|
Edge,
|
||||||
|
useNodesState,
|
||||||
|
useEdgesState,
|
||||||
|
Position,
|
||||||
|
Connection,
|
||||||
|
addEdge,
|
||||||
|
} from 'react-flow-renderer';
|
||||||
|
|
||||||
|
import './touch-device.css';
|
||||||
|
|
||||||
|
const initialNodes: Node[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
data: { label: 'Node 1' },
|
||||||
|
position: { x: 100, y: 100 },
|
||||||
|
sourcePosition: Position.Right,
|
||||||
|
targetPosition: Position.Left,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
data: { label: 'Node 2' },
|
||||||
|
position: { x: 300, y: 100 },
|
||||||
|
sourcePosition: Position.Right,
|
||||||
|
targetPosition: Position.Left,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const initialEdges: Edge[] = [];
|
||||||
|
|
||||||
|
const TouchDeviceFlow = () => {
|
||||||
|
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
||||||
|
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||||
|
const onConnect = useCallback((connection: Connection) => setEdges((eds) => addEdge(connection, eds)), []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ReactFlow
|
||||||
|
nodes={nodes}
|
||||||
|
edges={edges}
|
||||||
|
onConnect={onConnect}
|
||||||
|
onNodesChange={onNodesChange}
|
||||||
|
onEdgesChange={onEdgesChange}
|
||||||
|
className="touchdevice-flow"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TouchDeviceFlow;
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
.touchdevice-flow .react-flow__handle {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: #9f7aea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.touchdevice-flow .react-flow__handle.connecting {
|
||||||
|
animation: bounce 1600ms infinite ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bounce {
|
||||||
|
0% {
|
||||||
|
transform: translate(0, -50%) scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translate(0, -50%) scale(1.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import { ChangeEvent } from 'react';
|
|||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { BrowserRouter as Router, Route, Switch, withRouter } from 'react-router-dom';
|
import { BrowserRouter as Router, Route, Switch, withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
|
import Overview from './Overview';
|
||||||
import Basic from './Basic';
|
import Basic from './Basic';
|
||||||
import UpdateNode from './UpdateNode';
|
import UpdateNode from './UpdateNode';
|
||||||
import Stress from './Stress';
|
import Stress from './Stress';
|
||||||
@@ -11,12 +12,17 @@ import Layouting from './Layouting';
|
|||||||
import NestedNodes from './NestedNodes';
|
import NestedNodes from './NestedNodes';
|
||||||
import Hidden from './Hidden';
|
import Hidden from './Hidden';
|
||||||
import UpdatableEdge from './UpdatableEdge';
|
import UpdatableEdge from './UpdatableEdge';
|
||||||
|
import TouchDevice from './TouchDevice';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
|
component: Overview,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/basic',
|
||||||
component: Basic,
|
component: Basic,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -51,6 +57,10 @@ const routes = [
|
|||||||
path: '/updatable-edge',
|
path: '/updatable-edge',
|
||||||
component: UpdatableEdge,
|
component: UpdatableEdge,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/touch-device',
|
||||||
|
component: TouchDevice,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const Header = withRouter(({ history, location }) => {
|
const Header = withRouter(({ history, location }) => {
|
||||||
|
|||||||
@@ -1,98 +0,0 @@
|
|||||||
import React, { useState, MouseEvent } from 'react';
|
|
||||||
|
|
||||||
import ReactFlow, {
|
|
||||||
removeElements,
|
|
||||||
addEdge,
|
|
||||||
isNode,
|
|
||||||
Background,
|
|
||||||
Elements,
|
|
||||||
BackgroundVariant,
|
|
||||||
FlowElement,
|
|
||||||
Node,
|
|
||||||
Edge,
|
|
||||||
Connection,
|
|
||||||
OnLoadParams,
|
|
||||||
} from 'react-flow-renderer';
|
|
||||||
|
|
||||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
|
||||||
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
|
|
||||||
|
|
||||||
const initialNodes: Node[] = [
|
|
||||||
{ 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' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const initialEdges: Edge[] = [
|
|
||||||
{ id: 'e1-2', source: '1', target: '2', animated: true },
|
|
||||||
{ id: 'e1-3', source: '1', target: '3' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const BasicFlow = () => {
|
|
||||||
const [rfInstance, setRfInstance] = useState<OnLoadParams | null>(null);
|
|
||||||
const [nodes, setNodes] = useState<Node[]>(initialNodes);
|
|
||||||
const [edges, setEdges] = useState<Edge[]>(initialEdges);
|
|
||||||
// const onElementsRemove = (elementsToRemove: Elements) => setNodes((els) => removeElements(elementsToRemove, els));
|
|
||||||
// const onConnect = (params: Edge | Connection) => setNodes((els) => addEdge(params, els));
|
|
||||||
const onLoad = (reactFlowInstance: OnLoadParams) => setRfInstance(reactFlowInstance);
|
|
||||||
|
|
||||||
const updatePos = () => {
|
|
||||||
setNodes((nds) => {
|
|
||||||
return nds.map((n) => {
|
|
||||||
n.position = {
|
|
||||||
x: Math.random() * 400,
|
|
||||||
y: Math.random() * 400,
|
|
||||||
};
|
|
||||||
|
|
||||||
return n;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const logToObject = () => console.log(rfInstance?.toObject());
|
|
||||||
const resetTransform = () => rfInstance?.setTransform({ x: 0, y: 0, zoom: 1 });
|
|
||||||
|
|
||||||
const toggleClassnames = () => {
|
|
||||||
setNodes((nds) => {
|
|
||||||
return nds.map((n) => {
|
|
||||||
n.className = n.className === 'light' ? 'dark' : 'light';
|
|
||||||
|
|
||||||
return n;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ReactFlow
|
|
||||||
nodes={nodes}
|
|
||||||
edges={edges}
|
|
||||||
onLoad={onLoad}
|
|
||||||
onElementClick={onElementClick}
|
|
||||||
// onElementsRemove={onElementsRemove}
|
|
||||||
// onConnect={onConnect}
|
|
||||||
onNodeDragStop={onNodeDragStop}
|
|
||||||
className="react-flow-basic-example"
|
|
||||||
defaultZoom={1.5}
|
|
||||||
minZoom={0.2}
|
|
||||||
maxZoom={4}
|
|
||||||
>
|
|
||||||
<Background variant={BackgroundVariant.Lines} />
|
|
||||||
|
|
||||||
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
|
|
||||||
<button onClick={resetTransform} style={{ marginRight: 5 }}>
|
|
||||||
reset transform
|
|
||||||
</button>
|
|
||||||
<button onClick={updatePos} style={{ marginRight: 5 }}>
|
|
||||||
change pos
|
|
||||||
</button>
|
|
||||||
<button onClick={toggleClassnames} style={{ marginRight: 5 }}>
|
|
||||||
toggle classnames
|
|
||||||
</button>
|
|
||||||
<button onClick={logToObject}>toObject</button>
|
|
||||||
</div>
|
|
||||||
</ReactFlow>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BasicFlow;
|
|
||||||
@@ -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,70 +0,0 @@
|
|||||||
import React, { useState, CSSProperties } from 'react';
|
|
||||||
import ReactFlow, {
|
|
||||||
removeElements,
|
|
||||||
addEdge,
|
|
||||||
MiniMap,
|
|
||||||
isNode,
|
|
||||||
Controls,
|
|
||||||
Background,
|
|
||||||
OnLoadParams,
|
|
||||||
Elements,
|
|
||||||
Connection,
|
|
||||||
Edge,
|
|
||||||
} from 'react-flow-renderer';
|
|
||||||
|
|
||||||
import { getElements } from './utils';
|
|
||||||
|
|
||||||
const buttonWrapperStyles: CSSProperties = { position: 'absolute', right: 10, top: 10, zIndex: 4 };
|
|
||||||
|
|
||||||
const onLoad = (reactFlowInstance: OnLoadParams) => {
|
|
||||||
reactFlowInstance.fitView();
|
|
||||||
console.log(reactFlowInstance.getElements());
|
|
||||||
};
|
|
||||||
|
|
||||||
const initialElements: Elements = getElements(30, 30);
|
|
||||||
|
|
||||||
const StressFlow = () => {
|
|
||||||
const [elements, setElements] = useState<Elements>(initialElements);
|
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
|
|
||||||
const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els));
|
|
||||||
|
|
||||||
const updatePos = () => {
|
|
||||||
setElements((elms) => {
|
|
||||||
return elms.map((el) => {
|
|
||||||
if (isNode(el)) {
|
|
||||||
return {
|
|
||||||
...el,
|
|
||||||
position: {
|
|
||||||
x: Math.random() * window.innerWidth,
|
|
||||||
y: Math.random() * window.innerHeight,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return el;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateElements = () => {
|
|
||||||
const grid = Math.ceil(Math.random() * 10);
|
|
||||||
setElements(getElements(grid, grid));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ReactFlow elements={elements} onLoad={onLoad} onElementsRemove={onElementsRemove} onConnect={onConnect}>
|
|
||||||
<MiniMap />
|
|
||||||
<Controls />
|
|
||||||
<Background />
|
|
||||||
|
|
||||||
<div style={buttonWrapperStyles}>
|
|
||||||
<button onClick={updatePos} style={{ marginRight: 5 }}>
|
|
||||||
change pos
|
|
||||||
</button>
|
|
||||||
<button onClick={updateElements}>update elements</button>
|
|
||||||
</div>
|
|
||||||
</ReactFlow>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default StressFlow;
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import { Elements } from 'react-flow-renderer';
|
|
||||||
|
|
||||||
export function getElements(xElements: number = 10, yElements: number = 10): Elements {
|
|
||||||
const initialElements = [];
|
|
||||||
let nodeId = 1;
|
|
||||||
let recentNodeId = null;
|
|
||||||
|
|
||||||
for (let y = 0; y < yElements; y++) {
|
|
||||||
for (let x = 0; x < xElements; x++) {
|
|
||||||
const position = { x: x * 100, y: y * 50 };
|
|
||||||
const data = { label: `Node ${nodeId}` };
|
|
||||||
const node = {
|
|
||||||
id: nodeId.toString(),
|
|
||||||
style: { width: 50, fontSize: 11 },
|
|
||||||
data,
|
|
||||||
position,
|
|
||||||
};
|
|
||||||
initialElements.push(node);
|
|
||||||
|
|
||||||
if (recentNodeId && nodeId <= xElements * yElements) {
|
|
||||||
initialElements.push({ id: `${x}-${y}`, source: recentNodeId.toString(), target: nodeId.toString() });
|
|
||||||
}
|
|
||||||
|
|
||||||
recentNodeId = nodeId;
|
|
||||||
nodeId++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return initialElements;
|
|
||||||
}
|
|
||||||
Generated
+25
@@ -23,6 +23,7 @@
|
|||||||
"@babel/preset-env": "^7.16.4",
|
"@babel/preset-env": "^7.16.4",
|
||||||
"@babel/preset-react": "^7.16.0",
|
"@babel/preset-react": "^7.16.0",
|
||||||
"@babel/preset-typescript": "^7.16.0",
|
"@babel/preset-typescript": "^7.16.0",
|
||||||
|
"@rollup/plugin-alias": "^3.1.8",
|
||||||
"@rollup/plugin-babel": "^5.3.0",
|
"@rollup/plugin-babel": "^5.3.0",
|
||||||
"@rollup/plugin-commonjs": "^21.0.1",
|
"@rollup/plugin-commonjs": "^21.0.1",
|
||||||
"@rollup/plugin-node-resolve": "^13.0.6",
|
"@rollup/plugin-node-resolve": "^13.0.6",
|
||||||
@@ -2103,6 +2104,21 @@
|
|||||||
"@octokit/openapi-types": "^11.2.0"
|
"@octokit/openapi-types": "^11.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@rollup/plugin-alias": {
|
||||||
|
"version": "3.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-3.1.8.tgz",
|
||||||
|
"integrity": "sha512-tf7HeSs/06wO2LPqKNY3Ckbvy0JRe7Jyn98bXnt/gfrxbe+AJucoNJlsEVi9sdgbQtXemjbakCpO/76JVgnHpA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"slash": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"rollup": "^1.20.0||^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/plugin-babel": {
|
"node_modules/@rollup/plugin-babel": {
|
||||||
"version": "5.3.0",
|
"version": "5.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz",
|
||||||
@@ -12374,6 +12390,15 @@
|
|||||||
"@octokit/openapi-types": "^11.2.0"
|
"@octokit/openapi-types": "^11.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@rollup/plugin-alias": {
|
||||||
|
"version": "3.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-3.1.8.tgz",
|
||||||
|
"integrity": "sha512-tf7HeSs/06wO2LPqKNY3Ckbvy0JRe7Jyn98bXnt/gfrxbe+AJucoNJlsEVi9sdgbQtXemjbakCpO/76JVgnHpA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"slash": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@rollup/plugin-babel": {
|
"@rollup/plugin-babel": {
|
||||||
"version": "5.3.0",
|
"version": "5.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz",
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
"@babel/preset-env": "^7.16.4",
|
"@babel/preset-env": "^7.16.4",
|
||||||
"@babel/preset-react": "^7.16.0",
|
"@babel/preset-react": "^7.16.0",
|
||||||
"@babel/preset-typescript": "^7.16.0",
|
"@babel/preset-typescript": "^7.16.0",
|
||||||
|
"@rollup/plugin-alias": "^3.1.8",
|
||||||
"@rollup/plugin-babel": "^5.3.0",
|
"@rollup/plugin-babel": "^5.3.0",
|
||||||
"@rollup/plugin-commonjs": "^21.0.1",
|
"@rollup/plugin-commonjs": "^21.0.1",
|
||||||
"@rollup/plugin-node-resolve": "^13.0.6",
|
"@rollup/plugin-node-resolve": "^13.0.6",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import babel from '@rollup/plugin-babel';
|
|||||||
import postcss from 'rollup-plugin-postcss';
|
import postcss from 'rollup-plugin-postcss';
|
||||||
import bundleSize from 'rollup-plugin-bundle-size';
|
import bundleSize from 'rollup-plugin-bundle-size';
|
||||||
import replace from '@rollup/plugin-replace';
|
import replace from '@rollup/plugin-replace';
|
||||||
|
import alias from '@rollup/plugin-alias';
|
||||||
import svgr from '@svgr/rollup';
|
import svgr from '@svgr/rollup';
|
||||||
import typescript from 'rollup-plugin-typescript2';
|
import typescript from 'rollup-plugin-typescript2';
|
||||||
import { DEFAULT_EXTENSIONS as DEFAULT_BABEL_EXTENSIONS } from '@babel/core';
|
import { DEFAULT_EXTENSIONS as DEFAULT_BABEL_EXTENSIONS } from '@babel/core';
|
||||||
@@ -38,6 +39,9 @@ export const baseConfig = ({ mainFile = pkg.main, moduleFile = pkg.module, injec
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
|
alias({
|
||||||
|
entries: [{ find: 'd3-color', replacement: __dirname + '/src/d3-color-alias' }],
|
||||||
|
}),
|
||||||
replace({
|
replace({
|
||||||
__ENV__: JSON.stringify(processEnv),
|
__ENV__: JSON.stringify(processEnv),
|
||||||
__REACT_FLOW_VERSION__: JSON.stringify(pkg.version),
|
__REACT_FLOW_VERSION__: JSON.stringify(pkg.version),
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ type Result = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// checks if element below mouse is a handle and returns connection in form of an object { source: 123, target: 312 }
|
// checks if element below mouse is a handle and returns connection in form of an object { source: 123, target: 312 }
|
||||||
function checkElementBelowIsValid(
|
export function checkElementBelowIsValid(
|
||||||
event: MouseEvent,
|
event: MouseEvent,
|
||||||
connectionMode: ConnectionMode,
|
connectionMode: ConnectionMode,
|
||||||
isTarget: boolean,
|
isTarget: boolean,
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import shallow from 'zustand/shallow';
|
|||||||
import { useStore, useStoreApi } from '../../store';
|
import { useStore, useStoreApi } from '../../store';
|
||||||
import NodeIdContext from '../../contexts/NodeIdContext';
|
import NodeIdContext from '../../contexts/NodeIdContext';
|
||||||
import { HandleProps, Connection, ReactFlowState, Position } from '../../types';
|
import { HandleProps, Connection, ReactFlowState, Position } from '../../types';
|
||||||
import { onMouseDown } from './handler';
|
import { checkElementBelowIsValid, onMouseDown } from './handler';
|
||||||
|
import { getHostForElement } from '../../utils';
|
||||||
|
|
||||||
const alwaysValid = () => true;
|
const alwaysValid = () => true;
|
||||||
|
|
||||||
@@ -17,6 +18,8 @@ const selector = (s: ReactFlowState) => ({
|
|||||||
onConnectStop: s.onConnectStop,
|
onConnectStop: s.onConnectStop,
|
||||||
onConnectEnd: s.onConnectEnd,
|
onConnectEnd: s.onConnectEnd,
|
||||||
connectionMode: s.connectionMode,
|
connectionMode: s.connectionMode,
|
||||||
|
connectionStartHandle: s.connectionStartHandle,
|
||||||
|
connectOnClick: s.connectOnClick,
|
||||||
});
|
});
|
||||||
|
|
||||||
const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||||
@@ -36,10 +39,15 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
|||||||
) => {
|
) => {
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
const nodeId = useContext(NodeIdContext) as string;
|
const nodeId = useContext(NodeIdContext) as string;
|
||||||
const { onConnectAction, onConnectStart, onConnectStop, onConnectEnd, connectionMode } = useStore(
|
const {
|
||||||
selector,
|
onConnectAction,
|
||||||
shallow
|
onConnectStart,
|
||||||
);
|
onConnectStop,
|
||||||
|
onConnectEnd,
|
||||||
|
connectionMode,
|
||||||
|
connectionStartHandle,
|
||||||
|
connectOnClick,
|
||||||
|
} = useStore(selector, shallow);
|
||||||
|
|
||||||
const handleId = id || null;
|
const handleId = id || null;
|
||||||
const isTarget = type === 'target';
|
const isTarget = type === 'target';
|
||||||
@@ -83,6 +91,47 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onClick = useCallback(
|
||||||
|
(event: React.MouseEvent) => {
|
||||||
|
if (!connectionStartHandle) {
|
||||||
|
onConnectStart?.(event, { nodeId, handleId, handleType: type });
|
||||||
|
store.setState({ connectionStartHandle: { nodeId, type, handleId } });
|
||||||
|
} else {
|
||||||
|
const doc = getHostForElement(event.target as HTMLElement);
|
||||||
|
const { connection, isValid } = checkElementBelowIsValid(
|
||||||
|
event as unknown as MouseEvent,
|
||||||
|
connectionMode,
|
||||||
|
connectionStartHandle.type === 'target',
|
||||||
|
connectionStartHandle.nodeId,
|
||||||
|
connectionStartHandle.handleId || null,
|
||||||
|
isValidConnection,
|
||||||
|
doc
|
||||||
|
);
|
||||||
|
|
||||||
|
onConnectStop?.(event as unknown as MouseEvent);
|
||||||
|
|
||||||
|
if (isValid) {
|
||||||
|
onConnectExtended(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
onConnectEnd?.(event as unknown as MouseEvent);
|
||||||
|
|
||||||
|
store.setState({ connectionStartHandle: null });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
connectionStartHandle,
|
||||||
|
onConnectStart,
|
||||||
|
onConnectExtended,
|
||||||
|
onConnectStop,
|
||||||
|
onConnectEnd,
|
||||||
|
isTarget,
|
||||||
|
nodeId,
|
||||||
|
handleId,
|
||||||
|
type,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
const handleClasses = cc([
|
const handleClasses = cc([
|
||||||
'react-flow__handle',
|
'react-flow__handle',
|
||||||
`react-flow__handle-${position}`,
|
`react-flow__handle-${position}`,
|
||||||
@@ -92,6 +141,10 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
|||||||
source: !isTarget,
|
source: !isTarget,
|
||||||
target: isTarget,
|
target: isTarget,
|
||||||
connectable: isConnectable,
|
connectable: isConnectable,
|
||||||
|
connecting:
|
||||||
|
connectionStartHandle?.nodeId === nodeId &&
|
||||||
|
connectionStartHandle?.handleId === handleId &&
|
||||||
|
connectionStartHandle?.type === type,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -102,6 +155,7 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
|||||||
data-handlepos={position}
|
data-handlepos={position}
|
||||||
className={handleClasses}
|
className={handleClasses}
|
||||||
onMouseDown={onMouseDownHandler}
|
onMouseDown={onMouseDownHandler}
|
||||||
|
onClick={connectOnClick ? onClick : undefined}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ interface StoreUpdaterProps {
|
|||||||
snapGrid?: SnapGrid;
|
snapGrid?: SnapGrid;
|
||||||
translateExtent?: CoordinateExtent;
|
translateExtent?: CoordinateExtent;
|
||||||
fitViewOnInit: boolean;
|
fitViewOnInit: boolean;
|
||||||
|
connectOnClick: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({
|
const selector = (s: ReactFlowState) => ({
|
||||||
@@ -87,6 +88,7 @@ const StoreUpdater = ({
|
|||||||
snapToGrid,
|
snapToGrid,
|
||||||
translateExtent,
|
translateExtent,
|
||||||
fitViewOnInit,
|
fitViewOnInit,
|
||||||
|
connectOnClick,
|
||||||
}: StoreUpdaterProps) => {
|
}: StoreUpdaterProps) => {
|
||||||
const { setNodes, setEdges, setMinZoom, setMaxZoom, setTranslateExtent, setNodeExtent, reset } = useStore(
|
const { setNodes, setEdges, setMinZoom, setMaxZoom, setTranslateExtent, setNodeExtent, reset } = useStore(
|
||||||
selector,
|
selector,
|
||||||
@@ -120,6 +122,7 @@ const StoreUpdater = ({
|
|||||||
useDirectStoreUpdater('snapGrid', snapGrid, store.setState);
|
useDirectStoreUpdater('snapGrid', snapGrid, store.setState);
|
||||||
useDirectStoreUpdater('onNodesChange', onNodesChange, store.setState);
|
useDirectStoreUpdater('onNodesChange', onNodesChange, store.setState);
|
||||||
useDirectStoreUpdater('onEdgesChange', onEdgesChange, store.setState);
|
useDirectStoreUpdater('onEdgesChange', onEdgesChange, store.setState);
|
||||||
|
useDirectStoreUpdater('connectOnClick', connectOnClick, store.setState);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ export interface ReactFlowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'on
|
|||||||
noWheelClassName?: string;
|
noWheelClassName?: string;
|
||||||
noPanClassName?: string;
|
noPanClassName?: string;
|
||||||
fitViewOnInit?: boolean;
|
fitViewOnInit?: boolean;
|
||||||
|
connectOnClick?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ReactFlowRefType = HTMLDivElement;
|
export type ReactFlowRefType = HTMLDivElement;
|
||||||
@@ -221,6 +222,7 @@ const ReactFlow: FunctionComponent<ReactFlowProps> = forwardRef<ReactFlowRefType
|
|||||||
noWheelClassName = 'nowheel',
|
noWheelClassName = 'nowheel',
|
||||||
noPanClassName = 'nopan',
|
noPanClassName = 'nopan',
|
||||||
fitViewOnInit = false,
|
fitViewOnInit = false,
|
||||||
|
connectOnClick = true,
|
||||||
...rest
|
...rest
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
@@ -309,6 +311,7 @@ const ReactFlow: FunctionComponent<ReactFlowProps> = forwardRef<ReactFlowRefType
|
|||||||
connectionMode={connectionMode}
|
connectionMode={connectionMode}
|
||||||
translateExtent={translateExtent}
|
translateExtent={translateExtent}
|
||||||
fitViewOnInit={fitViewOnInit}
|
fitViewOnInit={fitViewOnInit}
|
||||||
|
connectOnClick={connectOnClick}
|
||||||
/>
|
/>
|
||||||
{onSelectionChange && <SelectionListener onSelectionChange={onSelectionChange} />}
|
{onSelectionChange && <SelectionListener onSelectionChange={onSelectionChange} />}
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
Vendored
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// this helps us to reduce bundle size by ~10kb. d3-color is not used but can't be treeshaked in this case.
|
||||||
|
|
||||||
|
const obj = {};
|
||||||
|
export const rgb = {};
|
||||||
|
export const color = {};
|
||||||
|
export const hsl = {};
|
||||||
|
export const lab = {};
|
||||||
|
export const hcl = {};
|
||||||
|
export const cubehelix = {};
|
||||||
|
export default obj;
|
||||||
@@ -42,6 +42,9 @@ const initialState: ReactFlowStore = {
|
|||||||
|
|
||||||
fitViewOnInit: false,
|
fitViewOnInit: false,
|
||||||
fitViewOnInitDone: false,
|
fitViewOnInitDone: false,
|
||||||
|
|
||||||
|
connectionStartHandle: null,
|
||||||
|
connectOnClick: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default initialState;
|
export default initialState;
|
||||||
|
|||||||
+11
-2
@@ -5,7 +5,7 @@ import { XYPosition, Rect, Transform, CoordinateExtent } from './utils';
|
|||||||
import { NodeChange, EdgeChange } from './changes';
|
import { NodeChange, EdgeChange } from './changes';
|
||||||
import { Node, NodeInternals, NodeDimensionUpdate, NodeDiffUpdate } from './nodes';
|
import { Node, NodeInternals, NodeDimensionUpdate, NodeDiffUpdate } from './nodes';
|
||||||
import { Edge } from './edges';
|
import { Edge } from './edges';
|
||||||
import { HandleType } from './handles';
|
import { HandleType, StartHandle } from './handles';
|
||||||
|
|
||||||
export type FlowElement<T = any> = Node<T> | Edge<T>;
|
export type FlowElement<T = any> = Node<T> | Edge<T>;
|
||||||
|
|
||||||
@@ -178,10 +178,14 @@ export type ReactFlowStore = {
|
|||||||
fitViewOnInit: boolean;
|
fitViewOnInit: boolean;
|
||||||
fitViewOnInitDone: boolean;
|
fitViewOnInitDone: boolean;
|
||||||
|
|
||||||
|
connectionStartHandle: StartHandle | null;
|
||||||
|
|
||||||
onConnect?: OnConnect;
|
onConnect?: OnConnect;
|
||||||
onConnectStart?: OnConnectStart;
|
onConnectStart?: OnConnectStart;
|
||||||
onConnectStop?: OnConnectStop;
|
onConnectStop?: OnConnectStop;
|
||||||
onConnectEnd?: OnConnectEnd;
|
onConnectEnd?: OnConnectEnd;
|
||||||
|
|
||||||
|
connectOnClick: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ReactFlowActions = {
|
export type ReactFlowActions = {
|
||||||
@@ -204,4 +208,9 @@ export type ReactFlowState = ReactFlowStore & ReactFlowActions;
|
|||||||
|
|
||||||
export type UpdateNodeInternals = (nodeId: string) => void;
|
export type UpdateNodeInternals = (nodeId: string) => void;
|
||||||
|
|
||||||
export type OnSelectionChangeFunc = (params: { nodes: Node[]; edges: Edge[] }) => void;
|
export type OnSelectionChangeParams = {
|
||||||
|
nodes: Node[];
|
||||||
|
edges: Edge[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type OnSelectionChangeFunc = (params: OnSelectionChangeParams) => void;
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ export interface HandleElement extends XYPosition, Dimensions {
|
|||||||
position: Position;
|
position: Position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface StartHandle {
|
||||||
|
nodeId: string;
|
||||||
|
type: HandleType;
|
||||||
|
handleId?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface HandleProps {
|
export interface HandleProps {
|
||||||
type: HandleType;
|
type: HandleType;
|
||||||
position: Position;
|
position: Position;
|
||||||
|
|||||||
Reference in New Issue
Block a user