refactor(state): separate nodes and edges
This commit is contained in:
@@ -7,16 +7,17 @@ import ReactFlow, {
|
|||||||
applyEdgeChanges,
|
applyEdgeChanges,
|
||||||
MiniMap,
|
MiniMap,
|
||||||
Controls,
|
Controls,
|
||||||
FlowElement,
|
|
||||||
Node,
|
Node,
|
||||||
Edge,
|
Edge,
|
||||||
ElementChange,
|
NodeChange,
|
||||||
|
EdgeChange,
|
||||||
OnLoadParams,
|
OnLoadParams,
|
||||||
Connection,
|
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 onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node);
|
||||||
|
const onEdgeClick = (_: MouseEvent, edge: Edge) => console.log('click', edge);
|
||||||
|
|
||||||
const initialNodes: Node[] = [
|
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' },
|
||||||
@@ -66,11 +67,11 @@ const BasicFlow = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onNodesChange = useCallback((changes: ElementChange[]) => {
|
const onNodesChange = useCallback((changes: NodeChange[]) => {
|
||||||
setNodes((ns) => applyNodeChanges(changes, ns));
|
setNodes((ns) => applyNodeChanges(changes, ns));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onEdgesChange = useCallback((changes: ElementChange[]) => {
|
const onEdgesChange = useCallback((changes: EdgeChange[]) => {
|
||||||
setEdges((es) => applyEdgeChanges(changes, es));
|
setEdges((es) => applyEdgeChanges(changes, es));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -81,7 +82,8 @@ const BasicFlow = () => {
|
|||||||
onLoad={onLoad}
|
onLoad={onLoad}
|
||||||
onNodesChange={onNodesChange}
|
onNodesChange={onNodesChange}
|
||||||
onEdgesChange={onEdgesChange}
|
onEdgesChange={onEdgesChange}
|
||||||
onElementClick={onElementClick}
|
onNodeClick={onNodeClick}
|
||||||
|
onEdgeClick={onEdgeClick}
|
||||||
onConnect={onConnect}
|
onConnect={onConnect}
|
||||||
onNodeDragStop={onNodeDragStop}
|
onNodeDragStop={onNodeDragStop}
|
||||||
className="react-flow-basic-example"
|
className="react-flow-basic-example"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import ReactFlow, {
|
|||||||
OnLoadParams,
|
OnLoadParams,
|
||||||
Edge,
|
Edge,
|
||||||
Node,
|
Node,
|
||||||
ElementChange,
|
NodeChange,
|
||||||
applyNodeChanges,
|
applyNodeChanges,
|
||||||
} from 'react-flow-renderer';
|
} from 'react-flow-renderer';
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ const StressFlow = () => {
|
|||||||
setEdges(initialElements.edges);
|
setEdges(initialElements.edges);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onNodesChange = useCallback((changes: ElementChange[]) => {
|
const onNodesChange = useCallback((changes: NodeChange[]) => {
|
||||||
setNodes((ns) => applyNodeChanges(changes, ns));
|
setNodes((ns) => applyNodeChanges(changes, ns));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState, useCallback } from 'react';
|
import { useEffect, useState, useCallback } from 'react';
|
||||||
import ReactFlow, { Node, Edge, applyNodeChanges, ElementChange } from 'react-flow-renderer';
|
import ReactFlow, { Node, Edge, applyNodeChanges, NodeChange } from 'react-flow-renderer';
|
||||||
|
|
||||||
import './updatenode.css';
|
import './updatenode.css';
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ const UpdateNode = () => {
|
|||||||
);
|
);
|
||||||
}, [nodeHidden]);
|
}, [nodeHidden]);
|
||||||
|
|
||||||
const onNodesChange = useCallback((changes: ElementChange[]) => {
|
const onNodesChange = useCallback((changes: NodeChange[]) => {
|
||||||
setNodes((ns) => applyNodeChanges(changes, ns));
|
setNodes((ns) => applyNodeChanges(changes, ns));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -52,15 +52,15 @@ export default ({
|
|||||||
setSourceNode(nextSourceNode);
|
setSourceNode(nextSourceNode);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!sourceNode || !isConnectable) {
|
if (!sourceNode || !isConnectable || !sourceNode.handleBounds?.[connectionHandleType]) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sourceHandle = handleId
|
const sourceHandle = handleId
|
||||||
? sourceNode.handleBounds[connectionHandleType].find((d: HandleElement) => d.id === handleId)
|
? sourceNode.handleBounds[connectionHandleType]!.find((d: HandleElement) => d.id === handleId)
|
||||||
: sourceNode.handleBounds[connectionHandleType][0];
|
: sourceNode.handleBounds[connectionHandleType]![0];
|
||||||
const sourceHandleX = sourceHandle ? sourceHandle.x + sourceHandle.width / 2 : sourceNode.width! / 2;
|
const sourceHandleX = sourceHandle ? sourceHandle.x + sourceHandle.width / 2 : sourceNode.width! / 2;
|
||||||
const sourceHandleY = sourceHandle ? sourceHandle.y + sourceHandle.height / 2 : sourceNode.height;
|
const sourceHandleY = sourceHandle ? sourceHandle.y + sourceHandle.height / 2 : sourceNode.height!;
|
||||||
const sourceX = sourceNode.position.x + sourceHandleX;
|
const sourceX = sourceNode.position.x + sourceHandleX;
|
||||||
const sourceY = sourceNode.position.y + sourceHandleY;
|
const sourceY = sourceNode.position.y + sourceHandleY;
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
|||||||
data,
|
data,
|
||||||
onClick,
|
onClick,
|
||||||
onEdgeDoubleClick,
|
onEdgeDoubleClick,
|
||||||
selected,
|
isSelected,
|
||||||
animated,
|
animated,
|
||||||
label,
|
label,
|
||||||
labelStyle,
|
labelStyle,
|
||||||
@@ -69,7 +69,7 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
|||||||
'react-flow__edge',
|
'react-flow__edge',
|
||||||
`react-flow__edge-${type}`,
|
`react-flow__edge-${type}`,
|
||||||
className,
|
className,
|
||||||
{ selected, animated, inactive, updating },
|
{ selected: isSelected, animated, inactive, updating },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const edgeElement = useMemo<Edge>(() => {
|
const edgeElement = useMemo<Edge>(() => {
|
||||||
@@ -219,7 +219,7 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
|||||||
id={id}
|
id={id}
|
||||||
source={source}
|
source={source}
|
||||||
target={target}
|
target={target}
|
||||||
selected={selected}
|
isSelected={isSelected}
|
||||||
animated={animated}
|
animated={animated}
|
||||||
label={label}
|
label={label}
|
||||||
labelStyle={labelStyle}
|
labelStyle={labelStyle}
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
import { useEffect } from 'react';
|
|
||||||
import shallow from 'zustand/shallow';
|
|
||||||
|
|
||||||
import { useStore } from '../../store';
|
|
||||||
import { Node, Edge, ReactFlowState } from '../../types';
|
|
||||||
|
|
||||||
interface ElementUpdaterProps {
|
|
||||||
nodes: Node[];
|
|
||||||
edges: Edge[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({
|
|
||||||
setNodes: s.setNodes,
|
|
||||||
setEdges: s.setEdges,
|
|
||||||
});
|
|
||||||
|
|
||||||
const ElementUpdater = ({ nodes, edges }: ElementUpdaterProps) => {
|
|
||||||
const { setNodes, setEdges } = useStore(selector, shallow);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setNodes(nodes);
|
|
||||||
}, [nodes]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setEdges(edges);
|
|
||||||
}, [edges]);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ElementUpdater;
|
|
||||||
@@ -9,9 +9,9 @@ import { NodeComponentProps, WrapNodeProps, ReactFlowState } from '../../types';
|
|||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({
|
const selector = (s: ReactFlowState) => ({
|
||||||
addSelectedElements: s.addSelectedElements,
|
addSelectedElements: s.addSelectedElements,
|
||||||
onNodesChange: s.onNodesChange,
|
|
||||||
unsetNodesSelection: s.unsetNodesSelection,
|
unsetNodesSelection: s.unsetNodesSelection,
|
||||||
updateNodePosDiff: s.updateNodePosDiff,
|
updateNodePosition: s.updateNodePosition,
|
||||||
|
updateNodeDimensions: s.updateNodeDimensions,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
||||||
@@ -22,7 +22,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
|||||||
scale,
|
scale,
|
||||||
xPos,
|
xPos,
|
||||||
yPos,
|
yPos,
|
||||||
selected,
|
isSelected,
|
||||||
onClick,
|
onClick,
|
||||||
onMouseEnter,
|
onMouseEnter,
|
||||||
onMouseMove,
|
onMouseMove,
|
||||||
@@ -48,8 +48,10 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
|||||||
resizeObserver,
|
resizeObserver,
|
||||||
dragHandle,
|
dragHandle,
|
||||||
}: WrapNodeProps) => {
|
}: WrapNodeProps) => {
|
||||||
// const updateNodeDimensions = useStoreActions((actions) => actions.updateNodeDimensions);
|
const { addSelectedElements, unsetNodesSelection, updateNodePosition, updateNodeDimensions } = useStore(
|
||||||
const { addSelectedElements, onNodesChange, unsetNodesSelection, updateNodePosDiff } = useStore(selector, shallow);
|
selector,
|
||||||
|
shallow
|
||||||
|
);
|
||||||
const nodeElement = useRef<HTMLDivElement>(null);
|
const nodeElement = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const node = useMemo(() => ({ id, type, position: { x: xPos, y: yPos }, data }), [id, type, xPos, yPos, data]);
|
const node = useMemo(() => ({ id, type, position: { x: xPos, y: yPos }, data }), [id, type, xPos, yPos, data]);
|
||||||
@@ -57,7 +59,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
|||||||
|
|
||||||
const nodeStyle: CSSProperties = useMemo(
|
const nodeStyle: CSSProperties = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
zIndex: selected ? 10 : 3,
|
zIndex: isSelected ? 10 : 3,
|
||||||
transform: `translate(${xPos}px,${yPos}px)`,
|
transform: `translate(${xPos}px,${yPos}px)`,
|
||||||
pointerEvents:
|
pointerEvents:
|
||||||
isSelectable || isDraggable || onClick || onMouseEnter || onMouseMove || onMouseLeave ? 'all' : 'none',
|
isSelectable || isDraggable || onClick || onMouseEnter || onMouseMove || onMouseLeave ? 'all' : 'none',
|
||||||
@@ -66,7 +68,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
|||||||
...style,
|
...style,
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
selected,
|
isSelected,
|
||||||
xPos,
|
xPos,
|
||||||
yPos,
|
yPos,
|
||||||
isSelectable,
|
isSelectable,
|
||||||
@@ -118,7 +120,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
|||||||
if (isSelectable) {
|
if (isSelectable) {
|
||||||
unsetNodesSelection();
|
unsetNodesSelection();
|
||||||
|
|
||||||
if (!selected) {
|
if (!isSelected) {
|
||||||
addSelectedElements([node]);
|
addSelectedElements([node]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,7 +128,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
|||||||
onClick?.(event, node);
|
onClick?.(event, node);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[isSelectable, selected, isDraggable, onClick, node]
|
[isSelectable, isSelected, isDraggable, onClick, node]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onDragStart = useCallback(
|
const onDragStart = useCallback(
|
||||||
@@ -136,15 +138,15 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
|||||||
if (selectNodesOnDrag && isSelectable) {
|
if (selectNodesOnDrag && isSelectable) {
|
||||||
unsetNodesSelection();
|
unsetNodesSelection();
|
||||||
|
|
||||||
if (!selected) {
|
if (!isSelected) {
|
||||||
addSelectedElements([node]);
|
addSelectedElements([node]);
|
||||||
}
|
}
|
||||||
} else if (!selectNodesOnDrag && !selected && isSelectable) {
|
} else if (!selectNodesOnDrag && !isSelected && isSelectable) {
|
||||||
unsetNodesSelection();
|
unsetNodesSelection();
|
||||||
addSelectedElements([]);
|
addSelectedElements([]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[node, selected, selectNodesOnDrag, isSelectable, onNodeDragStart]
|
[node, isSelected, selectNodesOnDrag, isSelectable, onNodeDragStart]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onDrag = useCallback(
|
const onDrag = useCallback(
|
||||||
@@ -156,7 +158,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
|||||||
onNodeDrag(event as MouseEvent, node);
|
onNodeDrag(event as MouseEvent, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateNodePosDiff({ id, isDragging: true, diff: { x: draggableData.deltaX, y: draggableData.deltaY } });
|
updateNodePosition({ id, isDragging: true, diff: { x: draggableData.deltaX, y: draggableData.deltaY } });
|
||||||
},
|
},
|
||||||
[id, node, onNodeDrag]
|
[id, node, onNodeDrag]
|
||||||
);
|
);
|
||||||
@@ -166,7 +168,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
|||||||
// onDragStop also gets called when user just clicks on a node.
|
// onDragStop also gets called when user just clicks on a node.
|
||||||
// Because of that we set dragging to true inside the onDrag handler and handle the click here
|
// Because of that we set dragging to true inside the onDrag handler and handle the click here
|
||||||
if (!isDragging) {
|
if (!isDragging) {
|
||||||
if (isSelectable && !selectNodesOnDrag && !selected) {
|
if (isSelectable && !selectNodesOnDrag && !isSelected) {
|
||||||
addSelectedElements([node]);
|
addSelectedElements([node]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,18 +177,14 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
onNodesChange?.([
|
updateNodePosition({
|
||||||
{
|
id: node.id,
|
||||||
id: node.id,
|
isDragging: false,
|
||||||
change: {
|
});
|
||||||
isDragging: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
onNodeDragStop?.(event as MouseEvent, node);
|
onNodeDragStop?.(event as MouseEvent, node);
|
||||||
},
|
},
|
||||||
[node, isSelectable, selectNodesOnDrag, onClick, onNodeDragStop, isDragging, selected]
|
[node, isSelectable, selectNodesOnDrag, onClick, onNodeDragStop, isDragging, isSelected]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onNodeDoubleClickHandler = useCallback(
|
const onNodeDoubleClickHandler = useCallback(
|
||||||
@@ -196,11 +194,11 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
|||||||
[node, onNodeDoubleClick]
|
[node, onNodeDoubleClick]
|
||||||
);
|
);
|
||||||
|
|
||||||
// useEffect(() => {
|
useEffect(() => {
|
||||||
// if (nodeElement.current && !isHidden) {
|
if (nodeElement.current && !isHidden && !isInitialized) {
|
||||||
// updateNodeDimensions([{ id, nodeElement: nodeElement.current, forceUpdate: true }]);
|
updateNodeDimensions([{ id, nodeElement: nodeElement.current, forceUpdate: true }]);
|
||||||
// }
|
}
|
||||||
// }, [id, isHidden, sourcePosition, targetPosition]);
|
}, [id, isHidden, sourcePosition, targetPosition, isInitialized]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (nodeElement.current) {
|
if (nodeElement.current) {
|
||||||
@@ -220,7 +218,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
|||||||
`react-flow__node-${type}`,
|
`react-flow__node-${type}`,
|
||||||
className,
|
className,
|
||||||
{
|
{
|
||||||
selected,
|
selected: isSelected,
|
||||||
selectable: isSelectable,
|
selectable: isSelectable,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
@@ -257,7 +255,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
|||||||
type={type}
|
type={type}
|
||||||
xPos={xPos}
|
xPos={xPos}
|
||||||
yPos={yPos}
|
yPos={yPos}
|
||||||
selected={selected}
|
isSelected={isSelected}
|
||||||
isConnectable={isConnectable}
|
isConnectable={isConnectable}
|
||||||
sourcePosition={sourcePosition}
|
sourcePosition={sourcePosition}
|
||||||
targetPosition={targetPosition}
|
targetPosition={targetPosition}
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ const selector = (s: ReactFlowState) => ({
|
|||||||
transform: s.transform,
|
transform: s.transform,
|
||||||
selectedNodesBbox: s.selectedNodesBbox,
|
selectedNodesBbox: s.selectedNodesBbox,
|
||||||
selectionActive: s.selectionActive,
|
selectionActive: s.selectionActive,
|
||||||
selectedNodes: s.nodes.filter((n) => n.selected),
|
selectedNodes: s.nodes.filter((n) => n.isSelected),
|
||||||
snapToGrid: s.snapToGrid,
|
snapToGrid: s.snapToGrid,
|
||||||
snapGrid: s.snapGrid,
|
snapGrid: s.snapGrid,
|
||||||
updateNodePosDiff: s.updateNodePosDiff,
|
updateNodePosition: s.updateNodePosition,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default ({
|
export default ({
|
||||||
@@ -32,7 +32,7 @@ export default ({
|
|||||||
onSelectionDragStop,
|
onSelectionDragStop,
|
||||||
onSelectionContextMenu,
|
onSelectionContextMenu,
|
||||||
}: NodesSelectionProps) => {
|
}: NodesSelectionProps) => {
|
||||||
const { transform, selectedNodesBbox, selectionActive, selectedNodes, snapToGrid, snapGrid, updateNodePosDiff } =
|
const { transform, selectedNodesBbox, selectionActive, selectedNodes, snapToGrid, snapGrid, updateNodePosition } =
|
||||||
useStore(selector);
|
useStore(selector);
|
||||||
const [tX, tY, tScale] = transform;
|
const [tX, tY, tScale] = transform;
|
||||||
const nodeRef = useRef(null);
|
const nodeRef = useRef(null);
|
||||||
@@ -69,7 +69,7 @@ export default ({
|
|||||||
onSelectionDrag(event, selectedNodes);
|
onSelectionDrag(event, selectedNodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateNodePosDiff({
|
updateNodePosition({
|
||||||
diff: {
|
diff: {
|
||||||
x: data.deltaX,
|
x: data.deltaX,
|
||||||
y: data.deltaY,
|
y: data.deltaY,
|
||||||
@@ -77,12 +77,12 @@ export default ({
|
|||||||
isDragging: true,
|
isDragging: true,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[onSelectionDrag, selectedNodes, updateNodePosDiff]
|
[onSelectionDrag, selectedNodes, updateNodePosition]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onStop = useCallback(
|
const onStop = useCallback(
|
||||||
(event: MouseEvent) => {
|
(event: MouseEvent) => {
|
||||||
updateNodePosDiff({
|
updateNodePosition({
|
||||||
isDragging: false,
|
isDragging: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ interface SelectionListenerProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const selectedElementsSelector = (s: ReactFlowState) => [
|
const selectedElementsSelector = (s: ReactFlowState) => [
|
||||||
...s.nodes.filter((n) => n.selected),
|
...s.nodes.filter((n) => n.isSelected),
|
||||||
...s.edges.filter((e) => e.selected),
|
...s.edges.filter((e) => e.isSelected),
|
||||||
];
|
];
|
||||||
|
|
||||||
// This is just a helper component for calling the onSelectionChange listener.
|
// This is just a helper component for calling the onSelectionChange listener.
|
||||||
|
|||||||
@@ -0,0 +1,211 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import shallow from 'zustand/shallow';
|
||||||
|
|
||||||
|
import { useStore } from '../../store';
|
||||||
|
import {
|
||||||
|
Node,
|
||||||
|
Edge,
|
||||||
|
ReactFlowState,
|
||||||
|
OnConnectFunc,
|
||||||
|
OnConnectStartFunc,
|
||||||
|
OnConnectStopFunc,
|
||||||
|
OnConnectEndFunc,
|
||||||
|
NodeExtent,
|
||||||
|
OnNodesChange,
|
||||||
|
OnEdgesChange,
|
||||||
|
ConnectionMode,
|
||||||
|
SnapGrid,
|
||||||
|
TranslateExtent,
|
||||||
|
} from '../../types';
|
||||||
|
|
||||||
|
interface StoreUpdaterProps {
|
||||||
|
nodes: Node[];
|
||||||
|
edges: Edge[];
|
||||||
|
onConnect?: OnConnectFunc;
|
||||||
|
onConnectStart?: OnConnectStartFunc;
|
||||||
|
onConnectStop?: OnConnectStopFunc;
|
||||||
|
onConnectEnd?: OnConnectEndFunc;
|
||||||
|
nodesDraggable?: boolean;
|
||||||
|
nodesConnectable?: boolean;
|
||||||
|
minZoom?: number;
|
||||||
|
maxZoom?: number;
|
||||||
|
nodeExtent?: NodeExtent;
|
||||||
|
onNodesChange?: OnNodesChange;
|
||||||
|
onEdgesChange?: OnEdgesChange;
|
||||||
|
elementsSelectable?: boolean;
|
||||||
|
connectionMode?: ConnectionMode;
|
||||||
|
snapToGrid?: boolean;
|
||||||
|
snapGrid?: SnapGrid;
|
||||||
|
translateExtent?: TranslateExtent;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selector = (s: ReactFlowState) => ({
|
||||||
|
setNodes: s.setNodes,
|
||||||
|
setEdges: s.setEdges,
|
||||||
|
setOnConnect: s.setOnConnect,
|
||||||
|
setOnConnectStart: s.setOnConnectStart,
|
||||||
|
setOnConnectStop: s.setOnConnectStop,
|
||||||
|
setOnConnectEnd: s.setOnConnectEnd,
|
||||||
|
setSnapGrid: s.setSnapGrid,
|
||||||
|
setSnapToGrid: s.setSnapToGrid,
|
||||||
|
setNodesDraggable: s.setNodesDraggable,
|
||||||
|
setNodesConnectable: s.setNodesConnectable,
|
||||||
|
setElementsSelectable: s.setElementsSelectable,
|
||||||
|
setMinZoom: s.setMinZoom,
|
||||||
|
setMaxZoom: s.setMaxZoom,
|
||||||
|
setTranslateExtent: s.setTranslateExtent,
|
||||||
|
setNodeExtent: s.setNodeExtent,
|
||||||
|
setConnectionMode: s.setConnectionMode,
|
||||||
|
setOnNodesChange: s.setOnNodesChange,
|
||||||
|
setOnEdgesChange: s.setOnEdgesChange,
|
||||||
|
});
|
||||||
|
|
||||||
|
const StoreUpdater = ({
|
||||||
|
nodes,
|
||||||
|
edges,
|
||||||
|
onConnect,
|
||||||
|
onConnectStart,
|
||||||
|
onConnectStop,
|
||||||
|
onConnectEnd,
|
||||||
|
nodesDraggable,
|
||||||
|
nodesConnectable,
|
||||||
|
minZoom,
|
||||||
|
maxZoom,
|
||||||
|
nodeExtent,
|
||||||
|
onNodesChange,
|
||||||
|
onEdgesChange,
|
||||||
|
elementsSelectable,
|
||||||
|
connectionMode,
|
||||||
|
snapGrid,
|
||||||
|
snapToGrid,
|
||||||
|
translateExtent,
|
||||||
|
}: StoreUpdaterProps) => {
|
||||||
|
const {
|
||||||
|
setNodes,
|
||||||
|
setEdges,
|
||||||
|
setOnConnect,
|
||||||
|
setOnConnectStart,
|
||||||
|
setOnConnectStop,
|
||||||
|
setOnConnectEnd,
|
||||||
|
setNodesDraggable,
|
||||||
|
setSnapGrid,
|
||||||
|
setSnapToGrid,
|
||||||
|
setNodesConnectable,
|
||||||
|
setElementsSelectable,
|
||||||
|
setMinZoom,
|
||||||
|
setMaxZoom,
|
||||||
|
setTranslateExtent,
|
||||||
|
setNodeExtent,
|
||||||
|
setOnNodesChange,
|
||||||
|
setOnEdgesChange,
|
||||||
|
setConnectionMode,
|
||||||
|
} = useStore(selector, shallow);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setNodes(nodes);
|
||||||
|
}, [nodes]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setEdges(edges, nodes);
|
||||||
|
}, [edges, nodes]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (onConnect) {
|
||||||
|
setOnConnect(onConnect);
|
||||||
|
}
|
||||||
|
}, [onConnect]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (onConnectStart) {
|
||||||
|
setOnConnectStart(onConnectStart);
|
||||||
|
}
|
||||||
|
}, [onConnectStart]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (onConnectStop) {
|
||||||
|
setOnConnectStop(onConnectStop);
|
||||||
|
}
|
||||||
|
}, [onConnectStop]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (onConnectEnd) {
|
||||||
|
setOnConnectEnd(onConnectEnd);
|
||||||
|
}
|
||||||
|
}, [onConnectEnd]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof snapToGrid !== 'undefined') {
|
||||||
|
setSnapToGrid(snapToGrid);
|
||||||
|
}
|
||||||
|
}, [snapToGrid]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof snapGrid !== 'undefined') {
|
||||||
|
setSnapGrid(snapGrid);
|
||||||
|
}
|
||||||
|
}, [snapGrid]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof nodesDraggable !== 'undefined') {
|
||||||
|
setNodesDraggable(nodesDraggable);
|
||||||
|
}
|
||||||
|
}, [nodesDraggable]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof nodesConnectable !== 'undefined') {
|
||||||
|
setNodesConnectable(nodesConnectable);
|
||||||
|
}
|
||||||
|
}, [nodesConnectable]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof elementsSelectable !== 'undefined') {
|
||||||
|
setElementsSelectable(elementsSelectable);
|
||||||
|
}
|
||||||
|
}, [elementsSelectable]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof minZoom !== 'undefined') {
|
||||||
|
setMinZoom(minZoom);
|
||||||
|
}
|
||||||
|
}, [minZoom]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof maxZoom !== 'undefined') {
|
||||||
|
setMaxZoom(maxZoom);
|
||||||
|
}
|
||||||
|
}, [maxZoom]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof translateExtent !== 'undefined') {
|
||||||
|
setTranslateExtent(translateExtent);
|
||||||
|
}
|
||||||
|
}, [translateExtent]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof nodeExtent !== 'undefined') {
|
||||||
|
setNodeExtent(nodeExtent);
|
||||||
|
}
|
||||||
|
}, [nodeExtent]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof connectionMode !== 'undefined') {
|
||||||
|
setConnectionMode(connectionMode);
|
||||||
|
}
|
||||||
|
}, [connectionMode]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof onNodesChange !== 'undefined') {
|
||||||
|
setOnNodesChange(onNodesChange);
|
||||||
|
}
|
||||||
|
}, [onNodesChange]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof onEdgesChange !== 'undefined') {
|
||||||
|
setOnEdgesChange(onEdgesChange);
|
||||||
|
}
|
||||||
|
}, [onEdgesChange]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StoreUpdater;
|
||||||
@@ -4,7 +4,7 @@ import shallow from 'zustand/shallow';
|
|||||||
import { useStore } from '../../store';
|
import { useStore } from '../../store';
|
||||||
import ConnectionLine from '../../components/ConnectionLine/index';
|
import ConnectionLine from '../../components/ConnectionLine/index';
|
||||||
import MarkerDefinitions from './MarkerDefinitions';
|
import MarkerDefinitions from './MarkerDefinitions';
|
||||||
import { getEdgePositions, getHandle } from './utils';
|
import { getEdgePositions, getHandle, getSourceTargetNodes } from './utils';
|
||||||
import {
|
import {
|
||||||
Position,
|
Position,
|
||||||
Edge,
|
Edge,
|
||||||
@@ -15,15 +15,17 @@ import {
|
|||||||
ConnectionMode,
|
ConnectionMode,
|
||||||
OnEdgeUpdateFunc,
|
OnEdgeUpdateFunc,
|
||||||
ReactFlowState,
|
ReactFlowState,
|
||||||
|
NodeHandleBounds,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
interface EdgeRendererProps {
|
interface EdgeRendererProps {
|
||||||
|
nodes: Node[];
|
||||||
|
edges: Edge[];
|
||||||
edgeTypes: any;
|
edgeTypes: any;
|
||||||
connectionLineType: ConnectionLineType;
|
connectionLineType: ConnectionLineType;
|
||||||
connectionLineStyle?: CSSProperties;
|
connectionLineStyle?: CSSProperties;
|
||||||
connectionLineComponent?: ConnectionLineComponent;
|
connectionLineComponent?: ConnectionLineComponent;
|
||||||
connectionMode?: ConnectionMode;
|
onEdgeClick?: (event: React.MouseEvent, node: Edge) => void;
|
||||||
onElementClick?: (event: React.MouseEvent, element: Node | Edge) => void;
|
|
||||||
onEdgeDoubleClick?: (event: React.MouseEvent, edge: Edge) => void;
|
onEdgeDoubleClick?: (event: React.MouseEvent, edge: Edge) => void;
|
||||||
arrowHeadColor: string;
|
arrowHeadColor: string;
|
||||||
markerEndId?: string;
|
markerEndId?: string;
|
||||||
@@ -42,7 +44,7 @@ interface EdgeWrapperProps {
|
|||||||
edge: Edge;
|
edge: Edge;
|
||||||
edgeTypes: any;
|
edgeTypes: any;
|
||||||
markerEndId?: string;
|
markerEndId?: string;
|
||||||
onElementClick?: (event: React.MouseEvent, element: Node | Edge) => void;
|
onEdgeClick?: (event: React.MouseEvent, node: Edge) => void;
|
||||||
onEdgeContextMenu?: (event: React.MouseEvent, edge: Edge) => void;
|
onEdgeContextMenu?: (event: React.MouseEvent, edge: Edge) => void;
|
||||||
onEdgeMouseEnter?: (event: React.MouseEvent, edge: Edge) => void;
|
onEdgeMouseEnter?: (event: React.MouseEvent, edge: Edge) => void;
|
||||||
onEdgeMouseMove?: (event: React.MouseEvent, edge: Edge) => void;
|
onEdgeMouseMove?: (event: React.MouseEvent, edge: Edge) => void;
|
||||||
@@ -52,10 +54,18 @@ interface EdgeWrapperProps {
|
|||||||
onEdgeUpdateStart?: (event: React.MouseEvent, edge: Edge) => void;
|
onEdgeUpdateStart?: (event: React.MouseEvent, edge: Edge) => void;
|
||||||
onEdgeUpdateEnd?: (event: MouseEvent, edge: Edge) => void;
|
onEdgeUpdateEnd?: (event: MouseEvent, edge: Edge) => void;
|
||||||
onEdgeUpdate?: OnEdgeUpdateFunc;
|
onEdgeUpdate?: OnEdgeUpdateFunc;
|
||||||
targetNode?: Node;
|
|
||||||
sourceNode?: Node;
|
|
||||||
elementsSelectable: boolean;
|
elementsSelectable: boolean;
|
||||||
connectionMode?: ConnectionMode;
|
connectionMode?: ConnectionMode;
|
||||||
|
sourceNodeWidth?: number | null;
|
||||||
|
sourceNodeHeight?: number | null;
|
||||||
|
sourceNodeX?: number;
|
||||||
|
sourceNodeY?: number;
|
||||||
|
sourceNodeHandleBounds?: NodeHandleBounds;
|
||||||
|
targetNodeWidth?: number | null;
|
||||||
|
targetNodeHeight?: number | null;
|
||||||
|
targetNodeX?: number;
|
||||||
|
targetNodeY?: number;
|
||||||
|
targetNodeHandleBounds?: NodeHandleBounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Edge = memo(
|
const Edge = memo(
|
||||||
@@ -63,7 +73,7 @@ const Edge = memo(
|
|||||||
edge,
|
edge,
|
||||||
edgeTypes,
|
edgeTypes,
|
||||||
markerEndId,
|
markerEndId,
|
||||||
onElementClick,
|
onEdgeClick,
|
||||||
onEdgeContextMenu,
|
onEdgeContextMenu,
|
||||||
onEdgeMouseEnter,
|
onEdgeMouseEnter,
|
||||||
onEdgeMouseMove,
|
onEdgeMouseMove,
|
||||||
@@ -73,10 +83,18 @@ const Edge = memo(
|
|||||||
onEdgeUpdateStart,
|
onEdgeUpdateStart,
|
||||||
onEdgeUpdateEnd,
|
onEdgeUpdateEnd,
|
||||||
onEdgeUpdate,
|
onEdgeUpdate,
|
||||||
targetNode,
|
|
||||||
sourceNode,
|
|
||||||
elementsSelectable,
|
|
||||||
connectionMode,
|
connectionMode,
|
||||||
|
elementsSelectable,
|
||||||
|
sourceNodeWidth,
|
||||||
|
sourceNodeHeight,
|
||||||
|
sourceNodeX,
|
||||||
|
sourceNodeY,
|
||||||
|
sourceNodeHandleBounds,
|
||||||
|
targetNodeWidth,
|
||||||
|
targetNodeHeight,
|
||||||
|
targetNodeX,
|
||||||
|
targetNodeY,
|
||||||
|
targetNodeHandleBounds,
|
||||||
}: EdgeWrapperProps) => {
|
}: EdgeWrapperProps) => {
|
||||||
const sourceHandleId = edge.sourceHandle || null;
|
const sourceHandleId = edge.sourceHandle || null;
|
||||||
const targetHandleId = edge.targetHandle || null;
|
const targetHandleId = edge.targetHandle || null;
|
||||||
@@ -88,31 +106,40 @@ const Edge = memo(
|
|||||||
[edge, onEdgeUpdate]
|
[edge, onEdgeUpdate]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!sourceNode) {
|
// source and target node need to be initialized
|
||||||
|
if (!sourceNodeHandleBounds || !targetNodeHandleBounds) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!sourceNodeWidth ||
|
||||||
|
!sourceNodeHeight ||
|
||||||
|
typeof sourceNodeX === 'undefined' ||
|
||||||
|
typeof sourceNodeY === 'undefined'
|
||||||
|
) {
|
||||||
console.warn(`couldn't create edge for source id: ${edge.source}; edge id: ${edge.id}`);
|
console.warn(`couldn't create edge for source id: ${edge.source}; edge id: ${edge.id}`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!targetNode) {
|
if (
|
||||||
|
!targetNodeWidth ||
|
||||||
|
!targetNodeHeight ||
|
||||||
|
typeof targetNodeX === 'undefined' ||
|
||||||
|
typeof targetNodeY === 'undefined'
|
||||||
|
) {
|
||||||
console.warn(`couldn't create edge for target id: ${edge.target}; edge id: ${edge.id}`);
|
console.warn(`couldn't create edge for target id: ${edge.target}; edge id: ${edge.id}`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// source and target node need to be initialized
|
|
||||||
if (!sourceNode.width || !targetNode.width) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const edgeType = edge.type || 'default';
|
const edgeType = edge.type || 'default';
|
||||||
const EdgeComponent = edgeTypes[edgeType] || edgeTypes.default;
|
const EdgeComponent = edgeTypes[edgeType] || edgeTypes.default;
|
||||||
const targetNodeBounds = targetNode.handleBounds;
|
|
||||||
// when connection type is loose we can define all handles as sources
|
// when connection type is loose we can define all handles as sources
|
||||||
const targetNodeHandles =
|
const targetNodeHandles =
|
||||||
connectionMode === ConnectionMode.Strict
|
connectionMode === ConnectionMode.Strict
|
||||||
? targetNodeBounds.target
|
? targetNodeHandleBounds.target
|
||||||
: targetNodeBounds.target || targetNodeBounds.source;
|
: targetNodeHandleBounds.target || targetNodeHandleBounds.source;
|
||||||
const sourceHandle = getHandle(sourceNode.handleBounds.source, sourceHandleId);
|
const sourceHandle = getHandle(sourceNodeHandleBounds.source!, sourceHandleId);
|
||||||
const targetHandle = getHandle(targetNodeHandles, targetHandleId);
|
const targetHandle = getHandle(targetNodeHandles!, targetHandleId);
|
||||||
const sourcePosition = sourceHandle ? sourceHandle.position : Position.Bottom;
|
const sourcePosition = sourceHandle ? sourceHandle.position : Position.Bottom;
|
||||||
const targetPosition = targetHandle ? targetHandle.position : Position.Top;
|
const targetPosition = targetHandle ? targetHandle.position : Position.Top;
|
||||||
|
|
||||||
@@ -127,10 +154,10 @@ const Edge = memo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { sourceX, sourceY, targetX, targetY } = getEdgePositions(
|
const { sourceX, sourceY, targetX, targetY } = getEdgePositions(
|
||||||
sourceNode,
|
{ x: sourceNodeX, y: sourceNodeY, width: sourceNodeWidth, height: sourceNodeHeight },
|
||||||
sourceHandle,
|
sourceHandle,
|
||||||
sourcePosition,
|
sourcePosition,
|
||||||
targetNode,
|
{ x: targetNodeX, y: targetNodeY, width: targetNodeWidth, height: targetNodeHeight },
|
||||||
targetHandle,
|
targetHandle,
|
||||||
targetPosition
|
targetPosition
|
||||||
);
|
);
|
||||||
@@ -156,8 +183,8 @@ const Edge = memo(
|
|||||||
className={edge.className}
|
className={edge.className}
|
||||||
type={edge.type}
|
type={edge.type}
|
||||||
data={edge.data}
|
data={edge.data}
|
||||||
onClick={onElementClick}
|
onClick={onEdgeClick}
|
||||||
selected={!!edge.selected}
|
isSelected={!!edge.isSelected}
|
||||||
animated={edge.animated}
|
animated={edge.animated}
|
||||||
label={edge.label}
|
label={edge.label}
|
||||||
labelStyle={edge.labelStyle}
|
labelStyle={edge.labelStyle}
|
||||||
@@ -197,7 +224,6 @@ const Edge = memo(
|
|||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({
|
const selector = (s: ReactFlowState) => ({
|
||||||
transform: s.transform,
|
transform: s.transform,
|
||||||
edges: s.edges,
|
|
||||||
connectionNodeId: s.connectionNodeId,
|
connectionNodeId: s.connectionNodeId,
|
||||||
connectionHandleId: s.connectionHandleId,
|
connectionHandleId: s.connectionHandleId,
|
||||||
connectionHandleType: s.connectionHandleType,
|
connectionHandleType: s.connectionHandleType,
|
||||||
@@ -206,12 +232,12 @@ const selector = (s: ReactFlowState) => ({
|
|||||||
elementsSelectable: s.elementsSelectable,
|
elementsSelectable: s.elementsSelectable,
|
||||||
width: s.width,
|
width: s.width,
|
||||||
height: s.height,
|
height: s.height,
|
||||||
|
connectionMode: s.connectionMode,
|
||||||
});
|
});
|
||||||
|
|
||||||
const EdgeRenderer = (props: EdgeRendererProps) => {
|
const EdgeRenderer = (props: EdgeRendererProps) => {
|
||||||
const {
|
const {
|
||||||
transform,
|
transform,
|
||||||
edges,
|
|
||||||
connectionNodeId,
|
connectionNodeId,
|
||||||
connectionHandleId,
|
connectionHandleId,
|
||||||
connectionHandleType,
|
connectionHandleType,
|
||||||
@@ -220,6 +246,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
|
|||||||
elementsSelectable,
|
elementsSelectable,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
connectionMode,
|
||||||
} = useStore(selector, shallow);
|
} = useStore(selector, shallow);
|
||||||
|
|
||||||
if (!width) {
|
if (!width) {
|
||||||
@@ -227,33 +254,44 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { connectionLineType, arrowHeadColor, connectionLineStyle, connectionLineComponent } = props;
|
const { connectionLineType, arrowHeadColor, connectionLineStyle, connectionLineComponent } = props;
|
||||||
const transformStyle = `translate(${transform[0]},${transform[1]}) scale(${transform[2]})`;
|
|
||||||
const renderConnectionLine = connectionNodeId && connectionHandleType;
|
const renderConnectionLine = connectionNodeId && connectionHandleType;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<svg width={width} height={height} className="react-flow__edges">
|
<svg width={width} height={height} className="react-flow__edges">
|
||||||
<MarkerDefinitions color={arrowHeadColor} />
|
<MarkerDefinitions color={arrowHeadColor} />
|
||||||
<g transform={transformStyle}>
|
<g transform={`translate(${transform[0]},${transform[1]}) scale(${transform[2]})`}>
|
||||||
{edges.map((edge: Edge) => (
|
{props.edges.map((edge: Edge) => {
|
||||||
<Edge
|
const { sourceNode, targetNode } = getSourceTargetNodes(edge, props.nodes);
|
||||||
key={edge.id}
|
return (
|
||||||
edge={edge}
|
<Edge
|
||||||
sourceNode={edge.sourceNode}
|
key={edge.id}
|
||||||
targetNode={edge.targetNode}
|
edge={edge}
|
||||||
elementsSelectable={elementsSelectable}
|
sourceNodeWidth={sourceNode?.width}
|
||||||
markerEndId={props.markerEndId}
|
sourceNodeHeight={sourceNode?.height}
|
||||||
onEdgeContextMenu={props.onEdgeContextMenu}
|
sourceNodeX={sourceNode?.position.x}
|
||||||
onEdgeMouseEnter={props.onEdgeMouseEnter}
|
sourceNodeY={sourceNode?.position.y}
|
||||||
onEdgeMouseMove={props.onEdgeMouseMove}
|
sourceNodeHandleBounds={sourceNode?.handleBounds}
|
||||||
onEdgeMouseLeave={props.onEdgeMouseLeave}
|
targetNodeWidth={targetNode?.width}
|
||||||
edgeUpdaterRadius={props.edgeUpdaterRadius}
|
targetNodeHeight={targetNode?.height}
|
||||||
onEdgeDoubleClick={props.onEdgeDoubleClick}
|
targetNodeX={targetNode?.position.x}
|
||||||
onEdgeUpdateStart={props.onEdgeUpdateStart}
|
targetNodeY={targetNode?.position.y}
|
||||||
onEdgeUpdateEnd={props.onEdgeUpdateEnd}
|
targetNodeHandleBounds={targetNode?.handleBounds}
|
||||||
onEdgeUpdate={props.onEdgeUpdate}
|
elementsSelectable={elementsSelectable}
|
||||||
edgeTypes={props.edgeTypes}
|
markerEndId={props.markerEndId}
|
||||||
/>
|
onEdgeContextMenu={props.onEdgeContextMenu}
|
||||||
))}
|
onEdgeMouseEnter={props.onEdgeMouseEnter}
|
||||||
|
onEdgeMouseMove={props.onEdgeMouseMove}
|
||||||
|
onEdgeMouseLeave={props.onEdgeMouseLeave}
|
||||||
|
edgeUpdaterRadius={props.edgeUpdaterRadius}
|
||||||
|
onEdgeDoubleClick={props.onEdgeDoubleClick}
|
||||||
|
onEdgeUpdateStart={props.onEdgeUpdateStart}
|
||||||
|
onEdgeUpdateEnd={props.onEdgeUpdateEnd}
|
||||||
|
onEdgeUpdate={props.onEdgeUpdate}
|
||||||
|
edgeTypes={props.edgeTypes}
|
||||||
|
connectionMode={connectionMode}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
{renderConnectionLine && (
|
{renderConnectionLine && (
|
||||||
<ConnectionLine
|
<ConnectionLine
|
||||||
connectionNodeId={connectionNodeId!}
|
connectionNodeId={connectionNodeId!}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
HandleElement,
|
HandleElement,
|
||||||
Transform,
|
Transform,
|
||||||
Edge,
|
Edge,
|
||||||
|
Rect,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
export function createEdgeTypes(edgeTypes: EdgeTypesType): EdgeTypesType {
|
export function createEdgeTypes(edgeTypes: EdgeTypesType): EdgeTypesType {
|
||||||
@@ -39,11 +40,11 @@ export function createEdgeTypes(edgeTypes: EdgeTypesType): EdgeTypesType {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getHandlePosition(position: Position, node: Node, handle: any | null = null): XYPosition {
|
export function getHandlePosition(position: Position, nodeRect: Rect, handle: any | null = null): XYPosition {
|
||||||
const x = (handle?.x || 0) + node.position.x;
|
const x = (handle?.x || 0) + nodeRect.x;
|
||||||
const y = (handle?.y || 0) + node.position.y;
|
const y = (handle?.y || 0) + nodeRect.y;
|
||||||
const width = handle?.width || node.__rf.width;
|
const width = handle?.width || nodeRect.width;
|
||||||
const height = handle?.height || node.__rf.height;
|
const height = handle?.height || nodeRect.height;
|
||||||
|
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case Position.Top:
|
case Position.Top:
|
||||||
@@ -94,15 +95,15 @@ interface EdgePositions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getEdgePositions = (
|
export const getEdgePositions = (
|
||||||
sourceNode: Node,
|
sourceNodeRect: Rect,
|
||||||
sourceHandle: HandleElement | unknown,
|
sourceHandle: HandleElement | unknown,
|
||||||
sourcePosition: Position,
|
sourcePosition: Position,
|
||||||
targetNode: Node,
|
targetNodeRect: Rect,
|
||||||
targetHandle: HandleElement | unknown,
|
targetHandle: HandleElement | unknown,
|
||||||
targetPosition: Position
|
targetPosition: Position
|
||||||
): EdgePositions => {
|
): EdgePositions => {
|
||||||
const sourceHandlePos = getHandlePosition(sourcePosition, sourceNode, sourceHandle);
|
const sourceHandlePos = getHandlePosition(sourcePosition, sourceNodeRect, sourceHandle);
|
||||||
const targetHandlePos = getHandlePosition(targetPosition, targetNode, targetHandle);
|
const targetHandlePos = getHandlePosition(targetPosition, targetNodeRect, targetHandle);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sourceX: sourceHandlePos.x,
|
sourceX: sourceHandlePos.x,
|
||||||
@@ -169,16 +170,3 @@ export const getSourceTargetNodes = (edge: Edge, nodes: Node[]): SourceTargetNod
|
|||||||
{ sourceNode: null, targetNode: null } as SourceTargetNode
|
{ sourceNode: null, targetNode: null } as SourceTargetNode
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const extendEdgeWithSourceAndTarget = (edge: Edge, nodes: Node[]): Edge => {
|
|
||||||
const { sourceNode, targetNode } = getSourceTargetNodes(edge, nodes);
|
|
||||||
|
|
||||||
if (sourceNode) {
|
|
||||||
edge.sourceNode = sourceNode;
|
|
||||||
}
|
|
||||||
if (targetNode) {
|
|
||||||
edge.targetNode = targetNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
return edge;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ interface FlowRendererProps
|
|||||||
| 'arrowHeadColor'
|
| 'arrowHeadColor'
|
||||||
| 'onlyRenderVisibleElements'
|
| 'onlyRenderVisibleElements'
|
||||||
| 'selectNodesOnDrag'
|
| 'selectNodesOnDrag'
|
||||||
|
| 'nodes'
|
||||||
|
| 'edges'
|
||||||
> {
|
> {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
@@ -54,7 +56,6 @@ const FlowRenderer = ({
|
|||||||
paneMoveable,
|
paneMoveable,
|
||||||
defaultPosition,
|
defaultPosition,
|
||||||
defaultZoom,
|
defaultZoom,
|
||||||
translateExtent,
|
|
||||||
preventScrolling,
|
preventScrolling,
|
||||||
onSelectionDragStart,
|
onSelectionDragStart,
|
||||||
onSelectionDrag,
|
onSelectionDrag,
|
||||||
@@ -106,7 +107,6 @@ const FlowRenderer = ({
|
|||||||
paneMoveable={paneMoveable}
|
paneMoveable={paneMoveable}
|
||||||
defaultPosition={defaultPosition}
|
defaultPosition={defaultPosition}
|
||||||
defaultZoom={defaultZoom}
|
defaultZoom={defaultZoom}
|
||||||
translateExtent={translateExtent}
|
|
||||||
zoomActivationKeyCode={zoomActivationKeyCode}
|
zoomActivationKeyCode={zoomActivationKeyCode}
|
||||||
preventScrolling={preventScrolling}
|
preventScrolling={preventScrolling}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React, { useEffect, useRef, memo } from 'react';
|
import React, { useEffect, useRef, memo } from 'react';
|
||||||
import shallow from 'zustand/shallow';
|
|
||||||
|
|
||||||
import { useStore, useStoreApi } from '../../store';
|
import { useStoreApi } from '../../store';
|
||||||
import FlowRenderer from '../FlowRenderer';
|
import FlowRenderer from '../FlowRenderer';
|
||||||
import NodeRenderer from '../NodeRenderer';
|
import NodeRenderer from '../NodeRenderer';
|
||||||
import EdgeRenderer from '../EdgeRenderer';
|
import EdgeRenderer from '../EdgeRenderer';
|
||||||
@@ -10,17 +9,15 @@ import useZoomPanHelper from '../../hooks/useZoomPanHelper';
|
|||||||
|
|
||||||
import { ReactFlowProps } from '../ReactFlow';
|
import { ReactFlowProps } from '../ReactFlow';
|
||||||
|
|
||||||
import { NodeTypesType, EdgeTypesType, ConnectionLineType, KeyCode, ReactFlowState } from '../../types';
|
import { NodeTypesType, EdgeTypesType, ConnectionLineType, KeyCode } from '../../types';
|
||||||
|
|
||||||
export interface GraphViewProps extends Omit<ReactFlowProps, 'onSelectionChange' | 'nodes' | 'edges'> {
|
export interface GraphViewProps extends Omit<ReactFlowProps, 'onSelectionChange'> {
|
||||||
nodeTypes: NodeTypesType;
|
nodeTypes: NodeTypesType;
|
||||||
edgeTypes: EdgeTypesType;
|
edgeTypes: EdgeTypesType;
|
||||||
selectionKeyCode: KeyCode;
|
selectionKeyCode: KeyCode;
|
||||||
deleteKeyCode: KeyCode;
|
deleteKeyCode: KeyCode;
|
||||||
multiSelectionKeyCode: KeyCode;
|
multiSelectionKeyCode: KeyCode;
|
||||||
connectionLineType: ConnectionLineType;
|
connectionLineType: ConnectionLineType;
|
||||||
snapToGrid: boolean;
|
|
||||||
snapGrid: [number, number];
|
|
||||||
onlyRenderVisibleElements: boolean;
|
onlyRenderVisibleElements: boolean;
|
||||||
defaultZoom: number;
|
defaultZoom: number;
|
||||||
defaultPosition: [number, number];
|
defaultPosition: [number, number];
|
||||||
@@ -28,33 +25,17 @@ export interface GraphViewProps extends Omit<ReactFlowProps, 'onSelectionChange'
|
|||||||
selectNodesOnDrag: boolean;
|
selectNodesOnDrag: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({
|
|
||||||
setOnConnect: s.setOnConnect,
|
|
||||||
setOnConnectStart: s.setOnConnectStart,
|
|
||||||
setOnConnectStop: s.setOnConnectStop,
|
|
||||||
setOnConnectEnd: s.setOnConnectEnd,
|
|
||||||
setSnapGrid: s.setSnapGrid,
|
|
||||||
setSnapToGrid: s.setSnapToGrid,
|
|
||||||
setNodesDraggable: s.setNodesDraggable,
|
|
||||||
setNodesConnectable: s.setNodesConnectable,
|
|
||||||
setElementsSelectable: s.setElementsSelectable,
|
|
||||||
setMinZoom: s.setMinZoom,
|
|
||||||
setMaxZoom: s.setMaxZoom,
|
|
||||||
setTranslateExtent: s.setTranslateExtent,
|
|
||||||
setNodeExtent: s.setNodeExtent,
|
|
||||||
setConnectionMode: s.setConnectionMode,
|
|
||||||
setOnNodesChange: s.setOnNodesChange,
|
|
||||||
setOnEdgesChange: s.setOnEdgesChange,
|
|
||||||
});
|
|
||||||
|
|
||||||
const GraphView = ({
|
const GraphView = ({
|
||||||
|
nodes,
|
||||||
|
edges,
|
||||||
nodeTypes,
|
nodeTypes,
|
||||||
edgeTypes,
|
edgeTypes,
|
||||||
onMove,
|
onMove,
|
||||||
onMoveStart,
|
onMoveStart,
|
||||||
onMoveEnd,
|
onMoveEnd,
|
||||||
onLoad,
|
onLoad,
|
||||||
onElementClick,
|
onNodeClick,
|
||||||
|
onEdgeClick,
|
||||||
onNodeDoubleClick,
|
onNodeDoubleClick,
|
||||||
onEdgeDoubleClick,
|
onEdgeDoubleClick,
|
||||||
onNodeMouseEnter,
|
onNodeMouseEnter,
|
||||||
@@ -68,7 +49,6 @@ const GraphView = ({
|
|||||||
onSelectionDrag,
|
onSelectionDrag,
|
||||||
onSelectionDragStop,
|
onSelectionDragStop,
|
||||||
onSelectionContextMenu,
|
onSelectionContextMenu,
|
||||||
connectionMode,
|
|
||||||
connectionLineType,
|
connectionLineType,
|
||||||
connectionLineStyle,
|
connectionLineStyle,
|
||||||
connectionLineComponent,
|
connectionLineComponent,
|
||||||
@@ -76,24 +56,12 @@ const GraphView = ({
|
|||||||
multiSelectionKeyCode,
|
multiSelectionKeyCode,
|
||||||
zoomActivationKeyCode,
|
zoomActivationKeyCode,
|
||||||
deleteKeyCode,
|
deleteKeyCode,
|
||||||
onConnect,
|
|
||||||
onConnectStart,
|
|
||||||
onConnectStop,
|
|
||||||
onConnectEnd,
|
|
||||||
snapToGrid,
|
|
||||||
snapGrid,
|
|
||||||
onlyRenderVisibleElements,
|
onlyRenderVisibleElements,
|
||||||
nodesDraggable,
|
|
||||||
nodesConnectable,
|
|
||||||
elementsSelectable,
|
elementsSelectable,
|
||||||
selectNodesOnDrag,
|
selectNodesOnDrag,
|
||||||
minZoom,
|
|
||||||
maxZoom,
|
|
||||||
defaultZoom,
|
defaultZoom,
|
||||||
defaultPosition,
|
defaultPosition,
|
||||||
translateExtent,
|
|
||||||
preventScrolling,
|
preventScrolling,
|
||||||
nodeExtent,
|
|
||||||
arrowHeadColor,
|
arrowHeadColor,
|
||||||
markerEndId,
|
markerEndId,
|
||||||
zoomOnScroll,
|
zoomOnScroll,
|
||||||
@@ -114,31 +82,9 @@ const GraphView = ({
|
|||||||
edgeUpdaterRadius,
|
edgeUpdaterRadius,
|
||||||
onEdgeUpdateStart,
|
onEdgeUpdateStart,
|
||||||
onEdgeUpdateEnd,
|
onEdgeUpdateEnd,
|
||||||
onNodesChange,
|
|
||||||
onEdgesChange,
|
|
||||||
}: GraphViewProps) => {
|
}: GraphViewProps) => {
|
||||||
const isInitialized = useRef<boolean>(false);
|
const isInitialized = useRef<boolean>(false);
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
|
|
||||||
const {
|
|
||||||
setOnConnect,
|
|
||||||
setOnConnectStart,
|
|
||||||
setOnConnectStop,
|
|
||||||
setOnConnectEnd,
|
|
||||||
setSnapGrid,
|
|
||||||
setSnapToGrid,
|
|
||||||
setNodesDraggable,
|
|
||||||
setNodesConnectable,
|
|
||||||
setElementsSelectable,
|
|
||||||
setMinZoom,
|
|
||||||
setMaxZoom,
|
|
||||||
setTranslateExtent,
|
|
||||||
setNodeExtent,
|
|
||||||
setConnectionMode,
|
|
||||||
setOnNodesChange,
|
|
||||||
setOnEdgesChange,
|
|
||||||
} = useStore(selector, shallow);
|
|
||||||
|
|
||||||
const { zoomIn, zoomOut, zoomTo, transform, fitView, initialized } = useZoomPanHelper();
|
const { zoomIn, zoomOut, zoomTo, transform, fitView, initialized } = useZoomPanHelper();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -160,102 +106,6 @@ const GraphView = ({
|
|||||||
}
|
}
|
||||||
}, [onLoad, zoomIn, zoomOut, zoomTo, transform, fitView, initialized]);
|
}, [onLoad, zoomIn, zoomOut, zoomTo, transform, fitView, initialized]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (onConnect) {
|
|
||||||
setOnConnect(onConnect);
|
|
||||||
}
|
|
||||||
}, [onConnect]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (onConnectStart) {
|
|
||||||
setOnConnectStart(onConnectStart);
|
|
||||||
}
|
|
||||||
}, [onConnectStart]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (onConnectStop) {
|
|
||||||
setOnConnectStop(onConnectStop);
|
|
||||||
}
|
|
||||||
}, [onConnectStop]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (onConnectEnd) {
|
|
||||||
setOnConnectEnd(onConnectEnd);
|
|
||||||
}
|
|
||||||
}, [onConnectEnd]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof snapToGrid !== 'undefined') {
|
|
||||||
setSnapToGrid(snapToGrid);
|
|
||||||
}
|
|
||||||
}, [snapToGrid]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof snapGrid !== 'undefined') {
|
|
||||||
setSnapGrid(snapGrid);
|
|
||||||
}
|
|
||||||
}, [snapGrid]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof nodesDraggable !== 'undefined') {
|
|
||||||
setNodesDraggable(nodesDraggable);
|
|
||||||
}
|
|
||||||
}, [nodesDraggable]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof nodesConnectable !== 'undefined') {
|
|
||||||
setNodesConnectable(nodesConnectable);
|
|
||||||
}
|
|
||||||
}, [nodesConnectable]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof elementsSelectable !== 'undefined') {
|
|
||||||
setElementsSelectable(elementsSelectable);
|
|
||||||
}
|
|
||||||
}, [elementsSelectable]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof minZoom !== 'undefined') {
|
|
||||||
setMinZoom(minZoom);
|
|
||||||
}
|
|
||||||
}, [minZoom]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof maxZoom !== 'undefined') {
|
|
||||||
setMaxZoom(maxZoom);
|
|
||||||
}
|
|
||||||
}, [maxZoom]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof translateExtent !== 'undefined') {
|
|
||||||
setTranslateExtent(translateExtent);
|
|
||||||
}
|
|
||||||
}, [translateExtent]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof nodeExtent !== 'undefined') {
|
|
||||||
setNodeExtent(nodeExtent);
|
|
||||||
}
|
|
||||||
}, [nodeExtent]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof connectionMode !== 'undefined') {
|
|
||||||
setConnectionMode(connectionMode);
|
|
||||||
}
|
|
||||||
}, [connectionMode]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof onNodesChange !== 'undefined') {
|
|
||||||
setOnNodesChange(onNodesChange);
|
|
||||||
}
|
|
||||||
}, [onNodesChange]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof onEdgesChange !== 'undefined') {
|
|
||||||
setOnEdgesChange(onEdgesChange);
|
|
||||||
}
|
|
||||||
}, [onEdgesChange]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FlowRenderer
|
<FlowRenderer
|
||||||
onPaneClick={onPaneClick}
|
onPaneClick={onPaneClick}
|
||||||
@@ -278,7 +128,6 @@ const GraphView = ({
|
|||||||
paneMoveable={paneMoveable}
|
paneMoveable={paneMoveable}
|
||||||
defaultPosition={defaultPosition}
|
defaultPosition={defaultPosition}
|
||||||
defaultZoom={defaultZoom}
|
defaultZoom={defaultZoom}
|
||||||
translateExtent={translateExtent}
|
|
||||||
onSelectionDragStart={onSelectionDragStart}
|
onSelectionDragStart={onSelectionDragStart}
|
||||||
onSelectionDrag={onSelectionDrag}
|
onSelectionDrag={onSelectionDrag}
|
||||||
onSelectionDragStop={onSelectionDragStop}
|
onSelectionDragStop={onSelectionDragStop}
|
||||||
@@ -287,7 +136,7 @@ const GraphView = ({
|
|||||||
>
|
>
|
||||||
<NodeRenderer
|
<NodeRenderer
|
||||||
nodeTypes={nodeTypes}
|
nodeTypes={nodeTypes}
|
||||||
onElementClick={onElementClick}
|
onNodeClick={onNodeClick}
|
||||||
onNodeDoubleClick={onNodeDoubleClick}
|
onNodeDoubleClick={onNodeDoubleClick}
|
||||||
onNodeMouseEnter={onNodeMouseEnter}
|
onNodeMouseEnter={onNodeMouseEnter}
|
||||||
onNodeMouseMove={onNodeMouseMove}
|
onNodeMouseMove={onNodeMouseMove}
|
||||||
@@ -297,18 +146,16 @@ const GraphView = ({
|
|||||||
onNodeDrag={onNodeDrag}
|
onNodeDrag={onNodeDrag}
|
||||||
onNodeDragStart={onNodeDragStart}
|
onNodeDragStart={onNodeDragStart}
|
||||||
selectNodesOnDrag={selectNodesOnDrag}
|
selectNodesOnDrag={selectNodesOnDrag}
|
||||||
snapToGrid={snapToGrid}
|
|
||||||
snapGrid={snapGrid}
|
|
||||||
onlyRenderVisibleElements={onlyRenderVisibleElements}
|
onlyRenderVisibleElements={onlyRenderVisibleElements}
|
||||||
|
nodes={nodes}
|
||||||
/>
|
/>
|
||||||
<EdgeRenderer
|
<EdgeRenderer
|
||||||
edgeTypes={edgeTypes}
|
edgeTypes={edgeTypes}
|
||||||
onElementClick={onElementClick}
|
onEdgeClick={onEdgeClick}
|
||||||
onEdgeDoubleClick={onEdgeDoubleClick}
|
onEdgeDoubleClick={onEdgeDoubleClick}
|
||||||
connectionLineType={connectionLineType}
|
connectionLineType={connectionLineType}
|
||||||
connectionLineStyle={connectionLineStyle}
|
connectionLineStyle={connectionLineStyle}
|
||||||
connectionLineComponent={connectionLineComponent}
|
connectionLineComponent={connectionLineComponent}
|
||||||
connectionMode={connectionMode}
|
|
||||||
arrowHeadColor={arrowHeadColor}
|
arrowHeadColor={arrowHeadColor}
|
||||||
markerEndId={markerEndId}
|
markerEndId={markerEndId}
|
||||||
onEdgeUpdate={onEdgeUpdate}
|
onEdgeUpdate={onEdgeUpdate}
|
||||||
@@ -320,6 +167,8 @@ const GraphView = ({
|
|||||||
onEdgeUpdateStart={onEdgeUpdateStart}
|
onEdgeUpdateStart={onEdgeUpdateStart}
|
||||||
onEdgeUpdateEnd={onEdgeUpdateEnd}
|
onEdgeUpdateEnd={onEdgeUpdateEnd}
|
||||||
edgeUpdaterRadius={edgeUpdaterRadius}
|
edgeUpdaterRadius={edgeUpdaterRadius}
|
||||||
|
nodes={nodes}
|
||||||
|
edges={edges}
|
||||||
/>
|
/>
|
||||||
</FlowRenderer>
|
</FlowRenderer>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import React, { memo, useMemo, ComponentType, MouseEvent } from 'react';
|
|||||||
import shallow from 'zustand/shallow';
|
import shallow from 'zustand/shallow';
|
||||||
|
|
||||||
import { useStore } from '../../store';
|
import { useStore } from '../../store';
|
||||||
import { Node, NodeTypesType, ReactFlowState, Edge, WrapNodeProps } from '../../types';
|
import { Node, NodeTypesType, ReactFlowState, WrapNodeProps } from '../../types';
|
||||||
interface NodeRendererProps {
|
interface NodeRendererProps {
|
||||||
nodeTypes: NodeTypesType;
|
nodeTypes: NodeTypesType;
|
||||||
selectNodesOnDrag: boolean;
|
selectNodesOnDrag: boolean;
|
||||||
onElementClick?: (event: MouseEvent, element: Node | Edge) => void;
|
onNodeClick?: (event: MouseEvent, element: Node) => void;
|
||||||
onNodeDoubleClick?: (event: MouseEvent, element: Node) => void;
|
onNodeDoubleClick?: (event: MouseEvent, element: Node) => void;
|
||||||
onNodeMouseEnter?: (event: MouseEvent, node: Node) => void;
|
onNodeMouseEnter?: (event: MouseEvent, node: Node) => void;
|
||||||
onNodeMouseMove?: (event: MouseEvent, node: Node) => void;
|
onNodeMouseMove?: (event: MouseEvent, node: Node) => void;
|
||||||
@@ -15,9 +15,8 @@ interface NodeRendererProps {
|
|||||||
onNodeDragStart?: (event: MouseEvent, node: Node) => void;
|
onNodeDragStart?: (event: MouseEvent, node: Node) => void;
|
||||||
onNodeDrag?: (event: MouseEvent, node: Node) => void;
|
onNodeDrag?: (event: MouseEvent, node: Node) => void;
|
||||||
onNodeDragStop?: (event: MouseEvent, node: Node) => void;
|
onNodeDragStop?: (event: MouseEvent, node: Node) => void;
|
||||||
snapToGrid: boolean;
|
|
||||||
snapGrid: [number, number];
|
|
||||||
onlyRenderVisibleElements: boolean;
|
onlyRenderVisibleElements: boolean;
|
||||||
|
nodes: Node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({
|
const selector = (s: ReactFlowState) => ({
|
||||||
@@ -25,15 +24,21 @@ const selector = (s: ReactFlowState) => ({
|
|||||||
nodesDraggable: s.nodesDraggable,
|
nodesDraggable: s.nodesDraggable,
|
||||||
nodesConnectable: s.nodesConnectable,
|
nodesConnectable: s.nodesConnectable,
|
||||||
elementsSelectable: s.elementsSelectable,
|
elementsSelectable: s.elementsSelectable,
|
||||||
nodes: s.nodes,
|
|
||||||
updateNodeDimensions: s.updateNodeDimensions,
|
updateNodeDimensions: s.updateNodeDimensions,
|
||||||
|
snapGrid: s.snapGrid,
|
||||||
|
snapToGrid: s.snapToGrid,
|
||||||
});
|
});
|
||||||
|
|
||||||
const NodeRenderer = (props: NodeRendererProps) => {
|
const NodeRenderer = (props: NodeRendererProps) => {
|
||||||
const { transform, nodesDraggable, nodesConnectable, elementsSelectable, nodes, updateNodeDimensions } = useStore(
|
const {
|
||||||
selector,
|
transform,
|
||||||
shallow
|
nodesDraggable,
|
||||||
);
|
nodesConnectable,
|
||||||
|
elementsSelectable,
|
||||||
|
updateNodeDimensions,
|
||||||
|
snapGrid,
|
||||||
|
snapToGrid,
|
||||||
|
} = useStore(selector, shallow);
|
||||||
|
|
||||||
// const visibleNodes = props.onlyRenderVisibleElements
|
// const visibleNodes = props.onlyRenderVisibleElements
|
||||||
// ? getNodesInside(nodes, { x: 0, y: 0, width, height }, transform, true)
|
// ? getNodesInside(nodes, { x: 0, y: 0, width, height }, transform, true)
|
||||||
@@ -63,7 +68,7 @@ const NodeRenderer = (props: NodeRendererProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="react-flow__nodes" style={transformStyle}>
|
<div className="react-flow__nodes" style={transformStyle}>
|
||||||
{nodes.map((node) => {
|
{props.nodes.map((node) => {
|
||||||
const nodeType = node.type || 'default';
|
const nodeType = node.type || 'default';
|
||||||
const NodeComponent = (props.nodeTypes[nodeType] || props.nodeTypes.default) as ComponentType<WrapNodeProps>;
|
const NodeComponent = (props.nodeTypes[nodeType] || props.nodeTypes.default) as ComponentType<WrapNodeProps>;
|
||||||
|
|
||||||
@@ -74,6 +79,11 @@ const NodeRenderer = (props: NodeRendererProps) => {
|
|||||||
const isDraggable = !!(node.draggable || (nodesDraggable && typeof node.draggable === 'undefined'));
|
const isDraggable = !!(node.draggable || (nodesDraggable && typeof node.draggable === 'undefined'));
|
||||||
const isSelectable = !!(node.selectable || (elementsSelectable && typeof node.selectable === 'undefined'));
|
const isSelectable = !!(node.selectable || (elementsSelectable && typeof node.selectable === 'undefined'));
|
||||||
const isConnectable = !!(node.connectable || (nodesConnectable && typeof node.connectable === 'undefined'));
|
const isConnectable = !!(node.connectable || (nodesConnectable && typeof node.connectable === 'undefined'));
|
||||||
|
const isInitialized =
|
||||||
|
node.width !== null &&
|
||||||
|
node.height !== null &&
|
||||||
|
typeof node.width !== 'undefined' &&
|
||||||
|
typeof node.height !== 'undefined';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NodeComponent
|
<NodeComponent
|
||||||
@@ -89,11 +99,11 @@ const NodeRenderer = (props: NodeRendererProps) => {
|
|||||||
xPos={node.position.x}
|
xPos={node.position.x}
|
||||||
yPos={node.position.y}
|
yPos={node.position.y}
|
||||||
isDragging={node.isDragging}
|
isDragging={node.isDragging}
|
||||||
isInitialized={node.width !== null && node.height !== null}
|
isInitialized={isInitialized}
|
||||||
snapGrid={props.snapGrid}
|
snapGrid={snapGrid}
|
||||||
snapToGrid={props.snapToGrid}
|
snapToGrid={snapToGrid}
|
||||||
selectNodesOnDrag={props.selectNodesOnDrag}
|
selectNodesOnDrag={props.selectNodesOnDrag}
|
||||||
onClick={props.onElementClick}
|
onClick={props.onNodeClick}
|
||||||
onMouseEnter={props.onNodeMouseEnter}
|
onMouseEnter={props.onNodeMouseEnter}
|
||||||
onMouseMove={props.onNodeMouseMove}
|
onMouseMove={props.onNodeMouseMove}
|
||||||
onMouseLeave={props.onNodeMouseLeave}
|
onMouseLeave={props.onNodeMouseLeave}
|
||||||
@@ -103,7 +113,7 @@ const NodeRenderer = (props: NodeRendererProps) => {
|
|||||||
onNodeDrag={props.onNodeDrag}
|
onNodeDrag={props.onNodeDrag}
|
||||||
onNodeDragStop={props.onNodeDragStop}
|
onNodeDragStop={props.onNodeDragStop}
|
||||||
scale={transform[2]}
|
scale={transform[2]}
|
||||||
selected={!!node.selected}
|
isSelected={!!node.isSelected}
|
||||||
isDraggable={isDraggable}
|
isDraggable={isDraggable}
|
||||||
isSelectable={isSelectable}
|
isSelectable={isSelectable}
|
||||||
isConnectable={isConnectable}
|
isConnectable={isConnectable}
|
||||||
|
|||||||
@@ -1,24 +1,21 @@
|
|||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
|
|
||||||
import { Provider, createStore } from '../../store';
|
import { Provider, createStore, useStoreApi } from '../../store';
|
||||||
// import { ReactFlowState } from '../../types';
|
|
||||||
|
|
||||||
// const reactFlowVersionSelector = (s: ReactFlowState) => s.reactFlowVersion;
|
|
||||||
|
|
||||||
const Wrapper: FC = ({ children }) => {
|
const Wrapper: FC = ({ children }) => {
|
||||||
// let isWrapped = useRef<boolean>(true);
|
let isWrapped = true;
|
||||||
|
|
||||||
// try {
|
try {
|
||||||
// useStoreApi();
|
useStoreApi();
|
||||||
// } catch {
|
} catch (e) {
|
||||||
// isWrapped.current = false;
|
isWrapped = false;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if (isWrapped) {
|
if (isWrapped) {
|
||||||
// // we need to wrap it with a fragment because it's not allowed for children to be a ReactNode
|
// we need to wrap it with a fragment because it's not allowed for children to be a ReactNode
|
||||||
// // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18051
|
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18051
|
||||||
// return <>{children}</>;
|
return <>{children}</>;
|
||||||
// }
|
}
|
||||||
|
|
||||||
return <Provider createStore={createStore}>{children}</Provider>;
|
return <Provider createStore={createStore}>{children}</Provider>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import React, {
|
|||||||
import cc from 'classcat';
|
import cc from 'classcat';
|
||||||
|
|
||||||
import GraphView from '../GraphView';
|
import GraphView from '../GraphView';
|
||||||
import ElementUpdater from '../../components/ElementUpdater';
|
import StoreUpdater from '../../components/StoreUpdater';
|
||||||
import DefaultNode from '../../components/Nodes/DefaultNode';
|
import DefaultNode from '../../components/Nodes/DefaultNode';
|
||||||
import InputNode from '../../components/Nodes/InputNode';
|
import InputNode from '../../components/Nodes/InputNode';
|
||||||
import OutputNode from '../../components/Nodes/OutputNode';
|
import OutputNode from '../../components/Nodes/OutputNode';
|
||||||
@@ -38,7 +38,8 @@ import {
|
|||||||
PanOnScrollMode,
|
PanOnScrollMode,
|
||||||
OnEdgeUpdateFunc,
|
OnEdgeUpdateFunc,
|
||||||
NodeExtent,
|
NodeExtent,
|
||||||
ElementChange,
|
NodeChange,
|
||||||
|
EdgeChange,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
import '../../style.css';
|
import '../../style.css';
|
||||||
@@ -60,9 +61,10 @@ const defaultEdgeTypes = {
|
|||||||
export interface ReactFlowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onLoad'> {
|
export interface ReactFlowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onLoad'> {
|
||||||
nodes: Node[];
|
nodes: Node[];
|
||||||
edges: Edge[];
|
edges: Edge[];
|
||||||
onNodesChange?: (nodeChanges: ElementChange[]) => void;
|
onNodesChange?: (nodeChanges: NodeChange[]) => void;
|
||||||
onEdgesChange?: (edgeChanges: ElementChange[]) => void;
|
onEdgesChange?: (edgeChanges: EdgeChange[]) => void;
|
||||||
onElementClick?: (event: ReactMouseEvent, element: Node | Edge) => void;
|
onNodeClick?: (event: React.MouseEvent, node: Node) => void;
|
||||||
|
onEdgeClick?: (event: React.MouseEvent, node: Edge) => void;
|
||||||
onNodeDoubleClick?: (event: ReactMouseEvent, node: Node) => void;
|
onNodeDoubleClick?: (event: ReactMouseEvent, node: Node) => void;
|
||||||
onNodeMouseEnter?: (event: ReactMouseEvent, node: Node) => void;
|
onNodeMouseEnter?: (event: ReactMouseEvent, node: Node) => void;
|
||||||
onNodeMouseMove?: (event: ReactMouseEvent, node: Node) => void;
|
onNodeMouseMove?: (event: ReactMouseEvent, node: Node) => void;
|
||||||
@@ -129,8 +131,6 @@ export interface ReactFlowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'on
|
|||||||
onEdgeUpdateStart?: (event: ReactMouseEvent, edge: Edge) => void;
|
onEdgeUpdateStart?: (event: ReactMouseEvent, edge: Edge) => void;
|
||||||
onEdgeUpdateEnd?: (event: MouseEvent, edge: Edge) => void;
|
onEdgeUpdateEnd?: (event: MouseEvent, edge: Edge) => void;
|
||||||
edgeUpdaterRadius?: number;
|
edgeUpdaterRadius?: number;
|
||||||
nodeTypesId?: string;
|
|
||||||
edgeTypesId?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ReactFlowRefType = HTMLDivElement;
|
export type ReactFlowRefType = HTMLDivElement;
|
||||||
@@ -146,7 +146,8 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
|||||||
className,
|
className,
|
||||||
nodeTypes = defaultNodeTypes,
|
nodeTypes = defaultNodeTypes,
|
||||||
edgeTypes = defaultEdgeTypes,
|
edgeTypes = defaultEdgeTypes,
|
||||||
onElementClick,
|
onNodeClick,
|
||||||
|
onEdgeClick,
|
||||||
onLoad,
|
onLoad,
|
||||||
onMove,
|
onMove,
|
||||||
onMoveStart,
|
onMoveStart,
|
||||||
@@ -212,27 +213,28 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
|||||||
onEdgeUpdateStart,
|
onEdgeUpdateStart,
|
||||||
onEdgeUpdateEnd,
|
onEdgeUpdateEnd,
|
||||||
edgeUpdaterRadius = 10,
|
edgeUpdaterRadius = 10,
|
||||||
nodeTypesId = '1',
|
|
||||||
edgeTypesId = '1',
|
|
||||||
onNodesChange,
|
onNodesChange,
|
||||||
onEdgesChange,
|
onEdgesChange,
|
||||||
...rest
|
...rest
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
const nodeTypesParsed = useMemo(() => createNodeTypes(nodeTypes), [nodeTypesId]);
|
const nodeTypesParsed = useMemo(() => createNodeTypes(nodeTypes), [nodeTypes]);
|
||||||
const edgeTypesParsed = useMemo(() => createEdgeTypes(edgeTypes), [edgeTypesId]);
|
const edgeTypesParsed = useMemo(() => createEdgeTypes(edgeTypes), [edgeTypes]);
|
||||||
const reactFlowClasses = cc(['react-flow', className]);
|
const reactFlowClasses = cc(['react-flow', className]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div {...rest} ref={ref} className={reactFlowClasses}>
|
<div {...rest} ref={ref} className={reactFlowClasses}>
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<GraphView
|
<GraphView
|
||||||
|
nodes={nodes}
|
||||||
|
edges={edges}
|
||||||
onLoad={onLoad}
|
onLoad={onLoad}
|
||||||
onMove={onMove}
|
onMove={onMove}
|
||||||
onMoveStart={onMoveStart}
|
onMoveStart={onMoveStart}
|
||||||
onMoveEnd={onMoveEnd}
|
onMoveEnd={onMoveEnd}
|
||||||
onElementClick={onElementClick}
|
onNodeClick={onNodeClick}
|
||||||
|
onEdgeClick={onEdgeClick}
|
||||||
onNodeMouseEnter={onNodeMouseEnter}
|
onNodeMouseEnter={onNodeMouseEnter}
|
||||||
onNodeMouseMove={onNodeMouseMove}
|
onNodeMouseMove={onNodeMouseMove}
|
||||||
onNodeMouseLeave={onNodeMouseLeave}
|
onNodeMouseLeave={onNodeMouseLeave}
|
||||||
@@ -243,7 +245,6 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
|||||||
onNodeDragStop={onNodeDragStop}
|
onNodeDragStop={onNodeDragStop}
|
||||||
nodeTypes={nodeTypesParsed}
|
nodeTypes={nodeTypesParsed}
|
||||||
edgeTypes={edgeTypesParsed}
|
edgeTypes={edgeTypesParsed}
|
||||||
connectionMode={connectionMode}
|
|
||||||
connectionLineType={connectionLineType}
|
connectionLineType={connectionLineType}
|
||||||
connectionLineStyle={connectionLineStyle}
|
connectionLineStyle={connectionLineStyle}
|
||||||
connectionLineComponent={connectionLineComponent}
|
connectionLineComponent={connectionLineComponent}
|
||||||
@@ -251,24 +252,11 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
|||||||
deleteKeyCode={deleteKeyCode}
|
deleteKeyCode={deleteKeyCode}
|
||||||
multiSelectionKeyCode={multiSelectionKeyCode}
|
multiSelectionKeyCode={multiSelectionKeyCode}
|
||||||
zoomActivationKeyCode={zoomActivationKeyCode}
|
zoomActivationKeyCode={zoomActivationKeyCode}
|
||||||
onConnect={onConnect}
|
|
||||||
onConnectStart={onConnectStart}
|
|
||||||
onConnectStop={onConnectStop}
|
|
||||||
onConnectEnd={onConnectEnd}
|
|
||||||
snapToGrid={snapToGrid}
|
|
||||||
snapGrid={snapGrid}
|
|
||||||
onlyRenderVisibleElements={onlyRenderVisibleElements}
|
onlyRenderVisibleElements={onlyRenderVisibleElements}
|
||||||
nodesDraggable={nodesDraggable}
|
|
||||||
nodesConnectable={nodesConnectable}
|
|
||||||
elementsSelectable={elementsSelectable}
|
|
||||||
selectNodesOnDrag={selectNodesOnDrag}
|
selectNodesOnDrag={selectNodesOnDrag}
|
||||||
minZoom={minZoom}
|
|
||||||
maxZoom={maxZoom}
|
|
||||||
defaultZoom={defaultZoom}
|
defaultZoom={defaultZoom}
|
||||||
defaultPosition={defaultPosition}
|
defaultPosition={defaultPosition}
|
||||||
translateExtent={translateExtent}
|
|
||||||
preventScrolling={preventScrolling}
|
preventScrolling={preventScrolling}
|
||||||
nodeExtent={nodeExtent}
|
|
||||||
arrowHeadColor={arrowHeadColor}
|
arrowHeadColor={arrowHeadColor}
|
||||||
markerEndId={markerEndId}
|
markerEndId={markerEndId}
|
||||||
zoomOnScroll={zoomOnScroll}
|
zoomOnScroll={zoomOnScroll}
|
||||||
@@ -294,10 +282,28 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
|||||||
onEdgeUpdateStart={onEdgeUpdateStart}
|
onEdgeUpdateStart={onEdgeUpdateStart}
|
||||||
onEdgeUpdateEnd={onEdgeUpdateEnd}
|
onEdgeUpdateEnd={onEdgeUpdateEnd}
|
||||||
edgeUpdaterRadius={edgeUpdaterRadius}
|
edgeUpdaterRadius={edgeUpdaterRadius}
|
||||||
|
/>
|
||||||
|
<StoreUpdater
|
||||||
|
nodes={nodes}
|
||||||
|
edges={edges}
|
||||||
|
onConnect={onConnect}
|
||||||
|
onConnectStart={onConnectStart}
|
||||||
|
onConnectStop={onConnectStop}
|
||||||
|
onConnectEnd={onConnectEnd}
|
||||||
|
nodesDraggable={nodesDraggable}
|
||||||
|
nodesConnectable={nodesConnectable}
|
||||||
|
elementsSelectable={elementsSelectable}
|
||||||
|
minZoom={minZoom}
|
||||||
|
maxZoom={maxZoom}
|
||||||
|
nodeExtent={nodeExtent}
|
||||||
onNodesChange={onNodesChange}
|
onNodesChange={onNodesChange}
|
||||||
onEdgesChange={onEdgesChange}
|
onEdgesChange={onEdgesChange}
|
||||||
|
snapToGrid={snapToGrid}
|
||||||
|
snapGrid={snapGrid}
|
||||||
|
connectionMode={connectionMode}
|
||||||
|
translateExtent={translateExtent}
|
||||||
/>
|
/>
|
||||||
<ElementUpdater nodes={nodes} edges={edges} />
|
|
||||||
{onSelectionChange && <SelectionListener onSelectionChange={onSelectionChange} />}
|
{onSelectionChange && <SelectionListener onSelectionChange={onSelectionChange} />}
|
||||||
{children}
|
{children}
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { clamp } from '../../utils';
|
|||||||
import useKeyPress from '../../hooks/useKeyPress';
|
import useKeyPress from '../../hooks/useKeyPress';
|
||||||
import useResizeHandler from '../../hooks/useResizeHandler';
|
import useResizeHandler from '../../hooks/useResizeHandler';
|
||||||
import { useStore, useStoreApi } from '../../store';
|
import { useStore, useStoreApi } from '../../store';
|
||||||
import { FlowTransform, TranslateExtent, PanOnScrollMode, KeyCode, ReactFlowState } from '../../types';
|
import { FlowTransform, PanOnScrollMode, KeyCode, ReactFlowState } from '../../types';
|
||||||
|
|
||||||
interface ZoomPaneProps {
|
interface ZoomPaneProps {
|
||||||
selectionKeyPressed: boolean;
|
selectionKeyPressed: boolean;
|
||||||
@@ -21,7 +21,6 @@ interface ZoomPaneProps {
|
|||||||
paneMoveable?: boolean;
|
paneMoveable?: boolean;
|
||||||
defaultPosition?: [number, number];
|
defaultPosition?: [number, number];
|
||||||
defaultZoom?: number;
|
defaultZoom?: number;
|
||||||
translateExtent?: TranslateExtent;
|
|
||||||
onMove?: (flowTransform?: FlowTransform) => void;
|
onMove?: (flowTransform?: FlowTransform) => void;
|
||||||
onMoveStart?: (flowTransform?: FlowTransform) => void;
|
onMoveStart?: (flowTransform?: FlowTransform) => void;
|
||||||
onMoveEnd?: (flowTransform?: FlowTransform) => void;
|
onMoveEnd?: (flowTransform?: FlowTransform) => void;
|
||||||
@@ -67,7 +66,6 @@ const ZoomPane = ({
|
|||||||
paneMoveable = true,
|
paneMoveable = true,
|
||||||
defaultPosition = [0, 0],
|
defaultPosition = [0, 0],
|
||||||
defaultZoom = 1,
|
defaultZoom = 1,
|
||||||
translateExtent,
|
|
||||||
zoomActivationKeyCode,
|
zoomActivationKeyCode,
|
||||||
preventScrolling = true,
|
preventScrolling = true,
|
||||||
children,
|
children,
|
||||||
@@ -82,14 +80,13 @@ const ZoomPane = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (zoomPane.current) {
|
if (zoomPane.current) {
|
||||||
const state = store.getState();
|
const { minZoom, maxZoom, translateExtent } = store.getState();
|
||||||
const currentTranslateExtent = typeof translateExtent !== 'undefined' ? translateExtent : state.translateExtent;
|
const d3ZoomInstance = zoom().scaleExtent([minZoom, maxZoom]).translateExtent(translateExtent);
|
||||||
const d3ZoomInstance = zoom().scaleExtent([state.minZoom, state.maxZoom]).translateExtent(currentTranslateExtent);
|
|
||||||
const selection = select(zoomPane.current as Element).call(d3ZoomInstance);
|
const selection = select(zoomPane.current as Element).call(d3ZoomInstance);
|
||||||
|
|
||||||
const clampedX = clamp(defaultPosition[0], currentTranslateExtent[0][0], currentTranslateExtent[1][0]);
|
const clampedX = clamp(defaultPosition[0], translateExtent[0][0], translateExtent[1][0]);
|
||||||
const clampedY = clamp(defaultPosition[1], currentTranslateExtent[0][1], currentTranslateExtent[1][1]);
|
const clampedY = clamp(defaultPosition[1], translateExtent[0][1], translateExtent[1][1]);
|
||||||
const clampedZoom = clamp(defaultZoom, state.minZoom, state.maxZoom);
|
const clampedZoom = clamp(defaultZoom, minZoom, maxZoom);
|
||||||
const updatedTransform = zoomIdentity.translate(clampedX, clampedY).scale(clampedZoom);
|
const updatedTransform = zoomIdentity.translate(clampedX, clampedY).scale(clampedZoom);
|
||||||
|
|
||||||
d3ZoomInstance.transform(selection, updatedTransform);
|
d3ZoomInstance.transform(selection, updatedTransform);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import shallow from 'zustand/shallow';
|
|||||||
import { useStore, useStoreApi } from '../store';
|
import { useStore, useStoreApi } from '../store';
|
||||||
import useKeyPress from './useKeyPress';
|
import useKeyPress from './useKeyPress';
|
||||||
import { getConnectedEdges } from '../utils/graph';
|
import { getConnectedEdges } from '../utils/graph';
|
||||||
import { KeyCode, ReactFlowState } from '../types';
|
import { EdgeChange, KeyCode, NodeChange, ReactFlowState } from '../types';
|
||||||
|
|
||||||
interface HookParams {
|
interface HookParams {
|
||||||
deleteKeyCode: KeyCode;
|
deleteKeyCode: KeyCode;
|
||||||
@@ -29,14 +29,17 @@ export default ({ deleteKeyCode, multiSelectionKeyCode }: HookParams): void => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { nodes, edges } = store.getState();
|
const { nodes, edges } = store.getState();
|
||||||
const selectedNodes = nodes.filter((n) => n.selected);
|
const selectedNodes = nodes.filter((n) => n.isSelected);
|
||||||
const selectedEdges = edges.filter((e) => e.selected);
|
const selectedEdges = edges.filter((e) => e.isSelected);
|
||||||
|
|
||||||
if (deleteKeyPressed && (selectedNodes || selectedEdges)) {
|
if (deleteKeyPressed && (selectedNodes || selectedEdges)) {
|
||||||
const connectedEdges = getConnectedEdges(selectedNodes, edges);
|
const connectedEdges = getConnectedEdges(selectedNodes, edges);
|
||||||
|
|
||||||
const nodeChanges = selectedNodes.map((n) => ({ id: n.id, delete: true }));
|
const nodeChanges: NodeChange[] = selectedNodes.map((n) => ({ id: n.id, type: 'remove' }));
|
||||||
const edgeChanges = [...selectedEdges, ...connectedEdges].map((e) => ({ id: e.id, delete: true }));
|
const edgeChanges: EdgeChange[] = [...selectedEdges, ...connectedEdges].map((e) => ({
|
||||||
|
id: e.id,
|
||||||
|
type: 'remove',
|
||||||
|
}));
|
||||||
|
|
||||||
onNodesChange?.(nodeChanges);
|
onNodesChange?.(nodeChanges);
|
||||||
onEdgesChange?.(edgeChanges);
|
onEdgesChange?.(edgeChanges);
|
||||||
|
|||||||
+84
-84
@@ -7,7 +7,6 @@ import {
|
|||||||
ConnectionMode,
|
ConnectionMode,
|
||||||
Node,
|
Node,
|
||||||
Edge,
|
Edge,
|
||||||
ElementChange,
|
|
||||||
NodeDimensionUpdate,
|
NodeDimensionUpdate,
|
||||||
NodeDiffUpdate,
|
NodeDiffUpdate,
|
||||||
XYPosition,
|
XYPosition,
|
||||||
@@ -23,28 +22,24 @@ import {
|
|||||||
OnConnectEndFunc,
|
OnConnectEndFunc,
|
||||||
SetConnectionId,
|
SetConnectionId,
|
||||||
SnapGrid,
|
SnapGrid,
|
||||||
OnElementsChange,
|
NodeChange,
|
||||||
|
OnNodesChange,
|
||||||
|
OnEdgesChange,
|
||||||
|
EdgeChange,
|
||||||
|
NodePositionChange,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import {
|
import { isNode, isEdge, getRectOfNodes, getNodesInside, getConnectedEdges } from '../utils/graph';
|
||||||
parseNode,
|
|
||||||
parseEdge,
|
|
||||||
isNode,
|
|
||||||
isEdge,
|
|
||||||
getRectOfNodes,
|
|
||||||
getNodesInside,
|
|
||||||
getConnectedEdges,
|
|
||||||
} from '../utils/graph';
|
|
||||||
import { extendEdgeWithSourceAndTarget } from '../container/EdgeRenderer/utils';
|
|
||||||
import { getHandleBounds } from '../components/Nodes/utils';
|
import { getHandleBounds } from '../components/Nodes/utils';
|
||||||
|
|
||||||
const { Provider, useStore, useStoreApi } = createContext<ReactFlowState>();
|
const { Provider, useStore, useStoreApi } = createContext<ReactFlowState>();
|
||||||
|
|
||||||
const unselectElements = (elements: Elements) =>
|
const unselectElements = (elements: Elements): NodeChange[] | EdgeChange[] =>
|
||||||
elements
|
elements
|
||||||
.filter((e) => e.selected)
|
.filter((e) => e.isSelected)
|
||||||
.map((e) => ({
|
.map((e) => ({
|
||||||
id: e.id,
|
id: e.id,
|
||||||
change: { selected: false },
|
type: 'select',
|
||||||
|
isSelected: false,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const createStore = () =>
|
const createStore = () =>
|
||||||
@@ -104,53 +99,37 @@ const createStore = () =>
|
|||||||
reactFlowVersion: typeof __REACT_FLOW_VERSION__ !== 'undefined' ? __REACT_FLOW_VERSION__ : '-',
|
reactFlowVersion: typeof __REACT_FLOW_VERSION__ !== 'undefined' ? __REACT_FLOW_VERSION__ : '-',
|
||||||
|
|
||||||
setNodes: (propNodes: Node[]) => {
|
setNodes: (propNodes: Node[]) => {
|
||||||
const { nodes, edges, nodeExtent } = get();
|
const { nodes } = get();
|
||||||
const nextNodes = propNodes.map((propNode: Node) => {
|
const nextNodes = propNodes.map((propNode: Node) => {
|
||||||
const storeNode = nodes.find((node) => node.id === propNode.id);
|
const storeNode = nodes.find((node) => node.id === propNode.id);
|
||||||
|
|
||||||
if (storeNode) {
|
if (storeNode) {
|
||||||
if (typeof propNode.type !== 'undefined' && propNode.type !== storeNode.type) {
|
if (typeof propNode.type !== 'undefined' && propNode.type !== storeNode.type) {
|
||||||
const updatedNode: Node = {
|
|
||||||
...storeNode,
|
|
||||||
...propNode,
|
|
||||||
};
|
|
||||||
// we reset the elements dimensions here in order to force a re-calculation of the bounds.
|
// we reset the elements dimensions here in order to force a re-calculation of the bounds.
|
||||||
// When the type of a node changes it is possible that the number or positions of handles changes too.
|
// When the type of a node changes it is possible that the number or positions of handles changes too.
|
||||||
updatedNode.width = null;
|
return {
|
||||||
return updatedNode;
|
...propNode,
|
||||||
|
width: null,
|
||||||
|
height: null,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseNode(propNode, nodeExtent);
|
return propNode;
|
||||||
});
|
});
|
||||||
|
|
||||||
const updatedEdges = edges.map((edge) => extendEdgeWithSourceAndTarget(edge, nextNodes));
|
|
||||||
|
|
||||||
set({
|
set({
|
||||||
nodes: nextNodes,
|
nodes: nextNodes,
|
||||||
edges: updatedEdges,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setEdges: (propEdges: Edge[]) => {
|
setEdges: (edges: Edge[]) => {
|
||||||
const { edges, nodes } = get();
|
set({ edges });
|
||||||
|
|
||||||
const nextEdges = propEdges.map((propEdge: Edge) => {
|
|
||||||
const storeEdge = edges.find((se) => se.id === propEdge.id);
|
|
||||||
|
|
||||||
if (storeEdge) {
|
|
||||||
return parseEdge(propEdge);
|
|
||||||
} else {
|
|
||||||
return extendEdgeWithSourceAndTarget(parseEdge(propEdge), nodes);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
set({ edges: nextEdges });
|
|
||||||
},
|
},
|
||||||
updateNodeDimensions: (updates: NodeDimensionUpdate[]) => {
|
updateNodeDimensions: (updates: NodeDimensionUpdate[]) => {
|
||||||
const { onNodesChange, nodes, transform } = get();
|
const { onNodesChange, nodes, transform } = get();
|
||||||
|
|
||||||
const initialChanges: ElementChange[] = [];
|
const initialChanges: NodeChange[] = [];
|
||||||
const nodesToChange: ElementChange[] = nodes.reduce((res, node) => {
|
const nodesToChange: NodeChange[] = nodes.reduce((res, node) => {
|
||||||
const update = updates.find((u) => u.id === node.id);
|
const update = updates.find((u) => u.id === node.id);
|
||||||
if (update) {
|
if (update) {
|
||||||
const dimensions = getDimensions(update.nodeElement);
|
const dimensions = getDimensions(update.nodeElement);
|
||||||
@@ -163,12 +142,10 @@ const createStore = () =>
|
|||||||
const handleBounds = getHandleBounds(update.nodeElement, transform[2]);
|
const handleBounds = getHandleBounds(update.nodeElement, transform[2]);
|
||||||
const change = {
|
const change = {
|
||||||
id: node.id,
|
id: node.id,
|
||||||
change: {
|
type: 'dimensions',
|
||||||
...dimensions,
|
dimensions,
|
||||||
handleBounds,
|
handleBounds,
|
||||||
},
|
} as NodeChange;
|
||||||
} as ElementChange;
|
|
||||||
|
|
||||||
res.push(change);
|
res.push(change);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,28 +153,37 @@ const createStore = () =>
|
|||||||
return res;
|
return res;
|
||||||
}, initialChanges);
|
}, initialChanges);
|
||||||
|
|
||||||
if (onNodesChange) {
|
onNodesChange?.(nodesToChange);
|
||||||
onNodesChange(nodesToChange);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
updateNodePosDiff: ({ id, diff, isDragging }: NodeDiffUpdate) => {
|
updateNodePosition: ({ id, diff, isDragging }: NodeDiffUpdate) => {
|
||||||
const { onNodesChange, nodes } = get();
|
const { onNodesChange, nodes, nodeExtent } = get();
|
||||||
|
|
||||||
if (onNodesChange && diff) {
|
if (onNodesChange) {
|
||||||
const matchingNodes = nodes.filter((n) => n.id === id || n.selected);
|
const matchingNodes = nodes.filter((n) => n.id === id || n.isSelected);
|
||||||
|
|
||||||
if (matchingNodes?.length) {
|
if (matchingNodes?.length) {
|
||||||
onNodesChange(
|
onNodesChange(
|
||||||
matchingNodes.map((n) => ({
|
matchingNodes.map((n) => {
|
||||||
id: n.id,
|
const change: NodePositionChange = {
|
||||||
change: {
|
id: n.id,
|
||||||
position: {
|
type: 'position',
|
||||||
x: n.position.x + diff.x,
|
isDragging: !!isDragging,
|
||||||
y: n.position.y + diff.y,
|
};
|
||||||
isDragging,
|
|
||||||
},
|
if (diff) {
|
||||||
},
|
change.position = nodeExtent
|
||||||
}))
|
? clampPosition(
|
||||||
|
{
|
||||||
|
x: n.position.x + diff.x,
|
||||||
|
y: n.position.y + diff.y,
|
||||||
|
},
|
||||||
|
nodeExtent
|
||||||
|
)
|
||||||
|
: { x: n.position.x + diff.x, y: n.position.y + diff.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
return change;
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,15 +216,11 @@ const createStore = () =>
|
|||||||
};
|
};
|
||||||
|
|
||||||
const selectedNodes = getNodesInside(nodes, nextUserSelectRect, transform, false, true);
|
const selectedNodes = getNodesInside(nodes, nextUserSelectRect, transform, false, true);
|
||||||
const selectedEdges = getConnectedEdges(selectedNodes, edges);
|
const selectedEdgeIds = getConnectedEdges(selectedNodes, edges).map((e) => e.id);
|
||||||
|
const selectedNodeIds = selectedNodes.map((n) => n.id);
|
||||||
|
|
||||||
if (selectedNodes?.length) {
|
onNodesChange?.(nodes.map((n) => ({ id: n.id, type: 'select', isSelected: selectedNodeIds.includes(n.id) })));
|
||||||
onNodesChange?.(selectedNodes.map((n) => ({ id: n.id, change: { selected: true } })));
|
onEdgesChange?.(edges.map((e) => ({ id: e.id, type: 'select', isSelected: selectedEdgeIds.includes(e.id) })));
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedEdges?.length) {
|
|
||||||
onEdgesChange?.(selectedEdges.map((n) => ({ id: n.id, change: { selected: true } })));
|
|
||||||
}
|
|
||||||
|
|
||||||
set({
|
set({
|
||||||
userSelectionRect: nextUserSelectRect,
|
userSelectionRect: nextUserSelectRect,
|
||||||
@@ -246,7 +228,7 @@ const createStore = () =>
|
|||||||
},
|
},
|
||||||
unsetUserSelection: () => {
|
unsetUserSelection: () => {
|
||||||
const { userSelectionRect, nodes } = get();
|
const { userSelectionRect, nodes } = get();
|
||||||
const selectedNodes = nodes.filter((node) => node.selected);
|
const selectedNodes = nodes.filter((node) => node.isSelected);
|
||||||
|
|
||||||
const stateUpdate = {
|
const stateUpdate = {
|
||||||
selectionActive: false,
|
selectionActive: false,
|
||||||
@@ -274,21 +256,32 @@ const createStore = () =>
|
|||||||
let changedEdges;
|
let changedEdges;
|
||||||
|
|
||||||
if (multiSelectionActive) {
|
if (multiSelectionActive) {
|
||||||
changedNodes = selectedElementsArr.filter(isNode).map((node) => ({ id: node.id, change: { selected: true } }));
|
changedNodes = selectedElementsArr
|
||||||
changedEdges = selectedElementsArr.filter(isEdge).map((edge) => ({ id: edge.id, change: { selected: true } }));
|
.filter(isNode)
|
||||||
|
.map((node) => ({ id: node.id, type: 'select', isSelected: true }));
|
||||||
|
changedEdges = selectedElementsArr
|
||||||
|
.filter(isEdge)
|
||||||
|
.map((edge) => ({ id: edge.id, type: 'select', isSelected: true }));
|
||||||
} else {
|
} else {
|
||||||
changedNodes = nodes.map((node) => ({
|
changedNodes = nodes.map((node) => ({
|
||||||
id: node.id,
|
id: node.id,
|
||||||
change: { selected: selectedElementsArr.some((e) => e.id === node.id) },
|
type: 'select',
|
||||||
|
isSelected: selectedElementsArr.some((e) => e.id === node.id),
|
||||||
}));
|
}));
|
||||||
changedEdges = edges.map((edge) => ({
|
changedEdges = edges.map((edge) => ({
|
||||||
id: edge.id,
|
id: edge.id,
|
||||||
change: { selected: selectedElementsArr.some((e) => e.id === edge.id) },
|
type: 'select',
|
||||||
|
isSelected: selectedElementsArr.some((e) => e.id === edge.id),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
onNodesChange?.(changedNodes);
|
if (changedNodes.length) {
|
||||||
onEdgesChange?.(changedEdges);
|
onNodesChange?.(changedNodes as NodeChange[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changedEdges.length) {
|
||||||
|
onEdgesChange?.(changedEdges as EdgeChange[]);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
initD3Zoom: ({ d3Zoom, d3Selection, d3ZoomHandler, transform }: InitD3ZoomPayload) =>
|
initD3Zoom: ({ d3Zoom, d3Selection, d3ZoomHandler, transform }: InitD3ZoomPayload) =>
|
||||||
set({
|
set({
|
||||||
@@ -319,8 +312,15 @@ const createStore = () =>
|
|||||||
resetSelectedElements: () => {
|
resetSelectedElements: () => {
|
||||||
const { nodes, edges, onNodesChange, onEdgesChange } = get();
|
const { nodes, edges, onNodesChange, onEdgesChange } = get();
|
||||||
|
|
||||||
onNodesChange?.(unselectElements(nodes));
|
const nodesToUnselect = unselectElements(nodes) as NodeChange[];
|
||||||
onEdgesChange?.(unselectElements(edges));
|
const edgesToUnselect = unselectElements(edges) as EdgeChange[];
|
||||||
|
|
||||||
|
if (nodesToUnselect.length) {
|
||||||
|
onNodesChange?.(nodesToUnselect);
|
||||||
|
}
|
||||||
|
if (edgesToUnselect.length) {
|
||||||
|
onEdgesChange?.(edgesToUnselect);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
setNodeExtent: (nodeExtent: NodeExtent) =>
|
setNodeExtent: (nodeExtent: NodeExtent) =>
|
||||||
set({
|
set({
|
||||||
@@ -354,8 +354,8 @@ const createStore = () =>
|
|||||||
setElementsSelectable: (elementsSelectable: boolean) => set({ elementsSelectable }),
|
setElementsSelectable: (elementsSelectable: boolean) => set({ elementsSelectable }),
|
||||||
setMultiSelectionActive: (multiSelectionActive: boolean) => set({ multiSelectionActive }),
|
setMultiSelectionActive: (multiSelectionActive: boolean) => set({ multiSelectionActive }),
|
||||||
setConnectionMode: (connectionMode: ConnectionMode) => set({ connectionMode }),
|
setConnectionMode: (connectionMode: ConnectionMode) => set({ connectionMode }),
|
||||||
setOnNodesChange: (onNodesChange: OnElementsChange) => set({ onNodesChange }),
|
setOnNodesChange: (onNodesChange: OnNodesChange) => set({ onNodesChange }),
|
||||||
setOnEdgesChange: (onEdgesChange: OnElementsChange) => set({ onEdgesChange }),
|
setOnEdgesChange: (onEdgesChange: OnEdgesChange) => set({ onEdgesChange }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export { Provider, useStore, createStore, useStoreApi };
|
export { Provider, useStore, createStore, useStoreApi };
|
||||||
|
|||||||
+48
-21
@@ -9,11 +9,32 @@ export type Elements<T = any> = Array<FlowElement<T>>;
|
|||||||
|
|
||||||
export type Transform = [number, number, number];
|
export type Transform = [number, number, number];
|
||||||
|
|
||||||
export type ElementChange = {
|
export type NodeDimensionChange = {
|
||||||
id: string;
|
id: string;
|
||||||
change?: any;
|
type: 'dimensions';
|
||||||
delete?: boolean;
|
dimensions: Dimensions;
|
||||||
|
handleBounds?: NodeHandleBounds;
|
||||||
};
|
};
|
||||||
|
export type NodePositionChange = {
|
||||||
|
id: string;
|
||||||
|
type: 'position';
|
||||||
|
position?: XYPosition;
|
||||||
|
isDragging?: boolean;
|
||||||
|
};
|
||||||
|
export type NodeSelectionChange = {
|
||||||
|
id: string;
|
||||||
|
type: 'select';
|
||||||
|
isSelected: boolean;
|
||||||
|
};
|
||||||
|
export type NodeRemoveChange = {
|
||||||
|
id: string;
|
||||||
|
type: 'remove';
|
||||||
|
};
|
||||||
|
export type NodeChange = NodeDimensionChange | NodePositionChange | NodeSelectionChange | NodeRemoveChange;
|
||||||
|
|
||||||
|
export type EdgeSelectionChange = NodeSelectionChange;
|
||||||
|
export type EdgeRemoveChange = NodeRemoveChange;
|
||||||
|
export type EdgeChange = EdgeSelectionChange | EdgeRemoveChange;
|
||||||
|
|
||||||
export enum Position {
|
export enum Position {
|
||||||
Left = 'left',
|
Left = 'left',
|
||||||
@@ -41,26 +62,30 @@ export interface Box extends XYPosition {
|
|||||||
|
|
||||||
export type SnapGrid = [number, number];
|
export type SnapGrid = [number, number];
|
||||||
|
|
||||||
|
export type NodeHandleBounds = {
|
||||||
|
source: HandleElement[] | null;
|
||||||
|
target: HandleElement[] | null;
|
||||||
|
};
|
||||||
|
|
||||||
export interface Node<T = any> {
|
export interface Node<T = any> {
|
||||||
id: ElementId;
|
id: ElementId;
|
||||||
position: XYPosition;
|
position: XYPosition;
|
||||||
type?: string;
|
type?: string;
|
||||||
__rf?: any;
|
|
||||||
data?: T;
|
data?: T;
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
className?: string;
|
className?: string;
|
||||||
targetPosition?: Position;
|
targetPosition?: Position;
|
||||||
sourcePosition?: Position;
|
sourcePosition?: Position;
|
||||||
isHidden?: boolean;
|
isHidden?: boolean;
|
||||||
|
isSelected?: boolean;
|
||||||
|
isDragging?: boolean;
|
||||||
draggable?: boolean;
|
draggable?: boolean;
|
||||||
selectable?: boolean;
|
selectable?: boolean;
|
||||||
connectable?: boolean;
|
connectable?: boolean;
|
||||||
dragHandle?: string;
|
dragHandle?: string;
|
||||||
isDragging?: boolean;
|
|
||||||
width?: number | null;
|
width?: number | null;
|
||||||
height?: number | null;
|
height?: number | null;
|
||||||
handleBounds?: any;
|
handleBounds?: NodeHandleBounds;
|
||||||
selected?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ArrowHeadType {
|
export enum ArrowHeadType {
|
||||||
@@ -89,7 +114,7 @@ export interface Edge<T = any> {
|
|||||||
className?: string;
|
className?: string;
|
||||||
sourceNode?: Node;
|
sourceNode?: Node;
|
||||||
targetNode?: Node;
|
targetNode?: Node;
|
||||||
selected?: boolean;
|
isSelected?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum BackgroundVariant {
|
export enum BackgroundVariant {
|
||||||
@@ -116,7 +141,7 @@ export interface WrapEdgeProps<T = any> {
|
|||||||
data?: T;
|
data?: T;
|
||||||
onClick?: (event: React.MouseEvent, edge: Edge) => void;
|
onClick?: (event: React.MouseEvent, edge: Edge) => void;
|
||||||
onEdgeDoubleClick?: (event: React.MouseEvent, edge: Edge) => void;
|
onEdgeDoubleClick?: (event: React.MouseEvent, edge: Edge) => void;
|
||||||
selected: boolean;
|
isSelected: boolean;
|
||||||
animated?: boolean;
|
animated?: boolean;
|
||||||
label?: string | ReactNode;
|
label?: string | ReactNode;
|
||||||
labelStyle?: CSSProperties;
|
labelStyle?: CSSProperties;
|
||||||
@@ -158,7 +183,7 @@ export interface EdgeProps<T = any> {
|
|||||||
sourceY: number;
|
sourceY: number;
|
||||||
targetX: number;
|
targetX: number;
|
||||||
targetY: number;
|
targetY: number;
|
||||||
selected?: boolean;
|
isSelected?: boolean;
|
||||||
animated?: boolean;
|
animated?: boolean;
|
||||||
sourcePosition: Position;
|
sourcePosition: Position;
|
||||||
targetPosition: Position;
|
targetPosition: Position;
|
||||||
@@ -194,7 +219,7 @@ export interface NodeProps<T = any> {
|
|||||||
id: ElementId;
|
id: ElementId;
|
||||||
type: string;
|
type: string;
|
||||||
data: T;
|
data: T;
|
||||||
selected: boolean;
|
isSelected: boolean;
|
||||||
isConnectable: boolean;
|
isConnectable: boolean;
|
||||||
xPos?: number;
|
xPos?: number;
|
||||||
yPos?: number;
|
yPos?: number;
|
||||||
@@ -208,7 +233,7 @@ export interface NodeComponentProps<T = any> {
|
|||||||
id: ElementId;
|
id: ElementId;
|
||||||
type: string;
|
type: string;
|
||||||
data: T;
|
data: T;
|
||||||
selected?: boolean;
|
isSelected?: boolean;
|
||||||
isConnectable: boolean;
|
isConnectable: boolean;
|
||||||
transform?: Transform;
|
transform?: Transform;
|
||||||
xPos?: number;
|
xPos?: number;
|
||||||
@@ -233,7 +258,7 @@ export interface WrapNodeProps<T = any> {
|
|||||||
id: ElementId;
|
id: ElementId;
|
||||||
type: string;
|
type: string;
|
||||||
data: T;
|
data: T;
|
||||||
selected: boolean;
|
isSelected: boolean;
|
||||||
scale: number;
|
scale: number;
|
||||||
xPos: number;
|
xPos: number;
|
||||||
yPos: number;
|
yPos: number;
|
||||||
@@ -271,7 +296,8 @@ export type FitViewParams = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type FlowExportObject<T = any> = {
|
export type FlowExportObject<T = any> = {
|
||||||
elements: Elements<T>;
|
nodes: Node<T>[];
|
||||||
|
edges: Edge<T>[];
|
||||||
position: [number, number];
|
position: [number, number];
|
||||||
zoom: number;
|
zoom: number;
|
||||||
};
|
};
|
||||||
@@ -412,7 +438,8 @@ export type InitD3ZoomPayload = {
|
|||||||
transform: Transform;
|
transform: Transform;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OnElementsChange = (nodes: ElementChange[]) => void;
|
export type OnNodesChange = (nodes: NodeChange[]) => void;
|
||||||
|
export type OnEdgesChange = (nodes: EdgeChange[]) => void;
|
||||||
|
|
||||||
export interface ReactFlowState {
|
export interface ReactFlowState {
|
||||||
width: number;
|
width: number;
|
||||||
@@ -421,8 +448,8 @@ export interface ReactFlowState {
|
|||||||
nodes: Node[];
|
nodes: Node[];
|
||||||
edges: Edge[];
|
edges: Edge[];
|
||||||
selectedNodesBbox: Rect;
|
selectedNodesBbox: Rect;
|
||||||
onNodesChange: OnElementsChange | null;
|
onNodesChange: OnNodesChange | null;
|
||||||
onEdgesChange: OnElementsChange | null;
|
onEdgesChange: OnEdgesChange | null;
|
||||||
|
|
||||||
d3Zoom: ZoomBehavior<Element, unknown> | null;
|
d3Zoom: ZoomBehavior<Element, unknown> | null;
|
||||||
d3Selection: D3Selection<Element, unknown, null, undefined> | null;
|
d3Selection: D3Selection<Element, unknown, null, undefined> | null;
|
||||||
@@ -455,9 +482,9 @@ export interface ReactFlowState {
|
|||||||
reactFlowVersion: string;
|
reactFlowVersion: string;
|
||||||
|
|
||||||
setNodes: (nodes: Node[]) => void;
|
setNodes: (nodes: Node[]) => void;
|
||||||
setEdges: (edges: Edge[]) => void;
|
setEdges: (edges: Edge[], nodes: Node[]) => void;
|
||||||
updateNodeDimensions: (updates: NodeDimensionUpdate[]) => void;
|
updateNodeDimensions: (updates: NodeDimensionUpdate[]) => void;
|
||||||
updateNodePosDiff: (update: NodeDiffUpdate) => void;
|
updateNodePosition: (update: NodeDiffUpdate) => void;
|
||||||
setUserSelection: (mousePos: XYPosition) => void;
|
setUserSelection: (mousePos: XYPosition) => void;
|
||||||
updateUserSelection: (mousePos: XYPosition) => void;
|
updateUserSelection: (mousePos: XYPosition) => void;
|
||||||
unsetUserSelection: () => void;
|
unsetUserSelection: () => void;
|
||||||
@@ -485,8 +512,8 @@ export interface ReactFlowState {
|
|||||||
setElementsSelectable: (elementsSelectable: boolean) => void;
|
setElementsSelectable: (elementsSelectable: boolean) => void;
|
||||||
setMultiSelectionActive: (multiSelectionActive: boolean) => void;
|
setMultiSelectionActive: (multiSelectionActive: boolean) => void;
|
||||||
setConnectionMode: (connectionMode: ConnectionMode) => void;
|
setConnectionMode: (connectionMode: ConnectionMode) => void;
|
||||||
setOnNodesChange: (onNodesChange: OnElementsChange) => void;
|
setOnNodesChange: (onNodesChange: OnNodesChange) => void;
|
||||||
setOnEdgesChange: (onEdgesChange: OnElementsChange) => void;
|
setOnEdgesChange: (onEdgesChange: OnEdgesChange) => void;
|
||||||
|
|
||||||
onConnect?: OnConnectFunc;
|
onConnect?: OnConnectFunc;
|
||||||
onConnectStart?: OnConnectStartFunc;
|
onConnectStart?: OnConnectStartFunc;
|
||||||
|
|||||||
+39
-37
@@ -1,7 +1,6 @@
|
|||||||
import { GetState } from 'zustand';
|
import { GetState } from 'zustand';
|
||||||
|
|
||||||
import { clampPosition, clamp } from '../utils';
|
import { clamp } from '../utils';
|
||||||
import { ReactFlowState } from '../types';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ElementId,
|
ElementId,
|
||||||
@@ -14,8 +13,9 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Connection,
|
Connection,
|
||||||
FlowExportObject,
|
FlowExportObject,
|
||||||
NodeExtent,
|
EdgeChange,
|
||||||
ElementChange,
|
NodeChange,
|
||||||
|
ReactFlowState,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
export const isEdge = (element: Node | Connection | Edge): element is Edge =>
|
export const isEdge = (element: Node | Connection | Edge): element is Edge =>
|
||||||
@@ -147,26 +147,6 @@ export const onLoadProject = (getState: GetState<ReactFlowState>) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const parseNode = (node: Node, nodeExtent: NodeExtent): Node => {
|
|
||||||
if (!node.type) {
|
|
||||||
node.type = 'default';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nodeExtent) {
|
|
||||||
node.position = clampPosition(node.position, nodeExtent);
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseEdge = (edge: Edge): Edge => {
|
|
||||||
if (!edge.type) {
|
|
||||||
edge.type = 'default';
|
|
||||||
}
|
|
||||||
|
|
||||||
return edge;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getBoundsOfBoxes = (box1: Box, box2: Box): Box => ({
|
const getBoundsOfBoxes = (box1: Box, box2: Box): Box => ({
|
||||||
x: Math.min(box1.x, box2.x),
|
x: Math.min(box1.x, box2.x),
|
||||||
y: Math.min(box1.y, box2.y),
|
y: Math.min(box1.y, box2.y),
|
||||||
@@ -269,7 +249,8 @@ export const onLoadToObject = (getState: GetState<ReactFlowState>) => {
|
|||||||
const { nodes = [], edges = [], transform } = getState();
|
const { nodes = [], edges = [], transform } = getState();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
elements: parseElements(nodes, edges),
|
nodes: nodes.map((n) => ({ ...n })),
|
||||||
|
edges: edges.map((e) => ({ ...e })),
|
||||||
position: [transform[0], transform[1]],
|
position: [transform[0], transform[1]],
|
||||||
zoom: transform[2],
|
zoom: transform[2],
|
||||||
};
|
};
|
||||||
@@ -296,30 +277,51 @@ export const getTransformForBounds = (
|
|||||||
return [x, y, clampedZoom];
|
return [x, y, clampedZoom];
|
||||||
};
|
};
|
||||||
|
|
||||||
function applyChanges(changes: ElementChange[], elements: any[]): any[] {
|
function applyChanges(changes: NodeChange[] | EdgeChange[], elements: any[]): any[] {
|
||||||
const initElements: any[] = [];
|
const initElements: any[] = [];
|
||||||
|
|
||||||
return elements.reduce((res: any[], node: any) => {
|
return elements.reduce((res: any[], item: any) => {
|
||||||
const hasChange = changes.find((c) => c.id === node.id);
|
const currentChange = changes.find((c) => c.id === item.id);
|
||||||
|
|
||||||
if (hasChange?.delete) {
|
if (currentChange) {
|
||||||
return res;
|
switch (currentChange.type) {
|
||||||
}
|
case 'dimensions': {
|
||||||
|
res.push({ ...item, ...currentChange.dimensions, handleBounds: currentChange.handleBounds });
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
case 'select': {
|
||||||
|
res.push({ ...item, isSelected: currentChange.isSelected });
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
case 'position': {
|
||||||
|
const updateItem = { ...item };
|
||||||
|
|
||||||
if (hasChange?.change) {
|
if (typeof currentChange.position !== 'undefined') {
|
||||||
res.push({ ...node, ...hasChange.change });
|
updateItem.position = currentChange.position;
|
||||||
} else {
|
}
|
||||||
res.push(node);
|
|
||||||
|
if (typeof currentChange.isDragging !== 'undefined') {
|
||||||
|
updateItem.isDragging = currentChange.isDragging;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.push(updateItem);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
case 'remove': {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res.push(item);
|
||||||
return res;
|
return res;
|
||||||
}, initElements);
|
}, initElements);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyNodeChanges(changes: ElementChange[], nodes: Node[]): Node[] {
|
export function applyNodeChanges(changes: NodeChange[], nodes: Node[]): Node[] {
|
||||||
return applyChanges(changes, nodes) as Node[];
|
return applyChanges(changes, nodes) as Node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyEdgeChanges(changes: ElementChange[], edges: Edge[]): Edge[] {
|
export function applyEdgeChanges(changes: EdgeChange[], edges: Edge[]): Edge[] {
|
||||||
return applyChanges(changes, edges) as Edge[];
|
return applyChanges(changes, edges) as Edge[];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user