Merge pull request #4219 from xyflow/fix/defaultnode-updates

fix(react): default node updates closes #4202
This commit is contained in:
Moritz Klack
2024-04-29 17:28:28 +02:00
committed by GitHub
5 changed files with 37 additions and 26 deletions
@@ -11,6 +11,8 @@ import {
useEdgesState, useEdgesState,
OnConnect, OnConnect,
useNodesInitialized, useNodesInitialized,
Panel,
useReactFlow,
} from '@xyflow/react'; } from '@xyflow/react';
const initialNodes: Node[] = [ const initialNodes: Node[] = [
@@ -49,6 +51,7 @@ const initialEdges: Edge[] = [
]; ];
const UseZoomPanHelperFlow = () => { const UseZoomPanHelperFlow = () => {
const { addNodes } = useReactFlow();
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
@@ -59,6 +62,13 @@ const UseZoomPanHelperFlow = () => {
console.log('initialized', initialized); console.log('initialized', initialized);
}, [initialized]); }, [initialized]);
const addNode = () =>
addNodes({
id: `${Math.random()}`,
data: { label: 'new node' },
position: { x: Math.random() * 400, y: Math.random() * 400 },
});
return ( return (
<ReactFlow <ReactFlow
nodes={nodes} nodes={nodes}
@@ -71,6 +81,9 @@ const UseZoomPanHelperFlow = () => {
> >
<Background /> <Background />
<MiniMap /> <MiniMap />
<Panel>
<button onClick={addNode}>add node</button>
</Panel>
</ReactFlow> </ReactFlow>
); );
}; };
+9 -20
View File
@@ -1,9 +1,11 @@
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { import {
EdgeRemoveChange,
evaluateAbsolutePosition, evaluateAbsolutePosition,
getElementsToRemove, getElementsToRemove,
getOverlappingArea, getOverlappingArea,
isRectObject, isRectObject,
NodeRemoveChange,
nodeToRect, nodeToRect,
type Rect, type Rect,
} from '@xyflow/system'; } from '@xyflow/system';
@@ -11,7 +13,7 @@ import {
import useViewportHelper from './useViewportHelper'; import useViewportHelper from './useViewportHelper';
import { useStoreApi } from './useStore'; import { useStoreApi } from './useStore';
import { useBatchContext } from '../components/BatchProvider'; import { useBatchContext } from '../components/BatchProvider';
import { isNode } from '../utils'; import { elementToRemoveChange, isNode } from '../utils';
import type { ReactFlowInstance, Instance, Node, Edge, InternalNode } from '../types'; import type { ReactFlowInstance, Instance, Node, Edge, InternalNode } from '../types';
/** /**
@@ -87,12 +89,10 @@ export function useReactFlow<NodeType extends Node = Node, EdgeType extends Edge
const { const {
nodes, nodes,
edges, edges,
hasDefaultNodes,
hasDefaultEdges,
onNodesDelete, onNodesDelete,
onEdgesDelete, onEdgesDelete,
onNodesChange, triggerNodeChanges,
onEdgesChange, triggerEdgeChanges,
onDelete, onDelete,
onBeforeDelete, onBeforeDelete,
} = store.getState(); } = store.getState();
@@ -108,28 +108,17 @@ export function useReactFlow<NodeType extends Node = Node, EdgeType extends Edge
const hasMatchingNodes = matchingNodes.length > 0; const hasMatchingNodes = matchingNodes.length > 0;
if (hasMatchingEdges) { if (hasMatchingEdges) {
if (hasDefaultEdges) { const edgeChanges: EdgeRemoveChange[] = matchingEdges.map(elementToRemoveChange);
const nextEdges = edges.filter((e) => !matchingEdges.some((mE) => mE.id === e.id));
store.getState().setEdges(nextEdges);
}
onEdgesDelete?.(matchingEdges); onEdgesDelete?.(matchingEdges);
onEdgesChange?.( triggerEdgeChanges(edgeChanges);
matchingEdges.map((edge) => ({
id: edge.id,
type: 'remove',
}))
);
} }
if (hasMatchingNodes) { if (hasMatchingNodes) {
if (hasDefaultNodes) { const nodeChanges: NodeRemoveChange[] = matchingNodes.map(elementToRemoveChange);
const nextNodes = nodes.filter((n) => !matchingNodes.some((mN) => mN.id === n.id));
store.getState().setNodes(nextNodes);
}
onNodesDelete?.(matchingNodes); onNodesDelete?.(matchingNodes);
onNodesChange?.(matchingNodes.map((node) => ({ id: node.id, type: 'remove' }))); triggerNodeChanges(nodeChanges);
} }
if (hasMatchingNodes || hasMatchingEdges) { if (hasMatchingNodes || hasMatchingEdges) {
+2 -2
View File
@@ -74,7 +74,7 @@ const createStore = ({
// new dimensions and update the nodes. // new dimensions and update the nodes.
updateNodeInternals: (updates) => { updateNodeInternals: (updates) => {
const { const {
onNodesChange, triggerNodeChanges,
fitView, fitView,
nodeLookup, nodeLookup,
parentLookup, parentLookup,
@@ -120,7 +120,7 @@ const createStore = ({
if (debug) { if (debug) {
console.log('React Flow: trigger node changes', changes); console.log('React Flow: trigger node changes', changes);
} }
onNodesChange?.(changes); triggerNodeChanges?.(changes);
} }
}, },
updateNodePositions: (nodeDragItems, dragging = false) => { updateNodePositions: (nodeDragItems, dragging = false) => {
+9
View File
@@ -6,6 +6,8 @@ import {
NodeChange, NodeChange,
NodeSelectionChange, NodeSelectionChange,
EdgeSelectionChange, EdgeSelectionChange,
NodeRemoveChange,
EdgeRemoveChange,
} from '@xyflow/system'; } from '@xyflow/system';
import type { Node, Edge, InternalNode } from '../types'; import type { Node, Edge, InternalNode } from '../types';
@@ -258,3 +260,10 @@ export function getElementsDiffChanges({
return changes; return changes;
} }
export function elementToRemoveChange<T extends Node | Edge>(item: T): NodeRemoveChange | EdgeRemoveChange {
return {
id: item.id,
type: 'remove',
};
}
+4 -4
View File
@@ -395,7 +395,7 @@ export async function getElementsToRemove<NodeType extends NodeBase = NodeBase,
nodes: NodeType[]; nodes: NodeType[];
edges: EdgeType[]; edges: EdgeType[];
}> { }> {
const nodeIds = nodesToRemove.map((node) => node.id); const nodeIds = new Set(nodesToRemove.map((node) => node.id));
const matchingNodes: NodeType[] = []; const matchingNodes: NodeType[] = [];
for (const node of nodes) { for (const node of nodes) {
@@ -403,7 +403,7 @@ export async function getElementsToRemove<NodeType extends NodeBase = NodeBase,
continue; continue;
} }
const isIncluded = nodeIds.includes(node.id); const isIncluded = nodeIds.has(node.id);
const parentHit = !isIncluded && node.parentId && matchingNodes.find((n) => n.id === node.parentId); const parentHit = !isIncluded && node.parentId && matchingNodes.find((n) => n.id === node.parentId);
if (isIncluded || parentHit) { if (isIncluded || parentHit) {
@@ -411,13 +411,13 @@ export async function getElementsToRemove<NodeType extends NodeBase = NodeBase,
} }
} }
const edgeIds = edgesToRemove.map((edge) => edge.id); const edgeIds = new Set(edgesToRemove.map((edge) => edge.id));
const deletableEdges = edges.filter((edge) => edge.deletable !== false); const deletableEdges = edges.filter((edge) => edge.deletable !== false);
const connectedEdges = getConnectedEdges(matchingNodes, deletableEdges); const connectedEdges = getConnectedEdges(matchingNodes, deletableEdges);
const matchingEdges: EdgeType[] = connectedEdges; const matchingEdges: EdgeType[] = connectedEdges;
for (const edge of deletableEdges) { for (const edge of deletableEdges) {
const isIncluded = edgeIds.includes(edge.id); const isIncluded = edgeIds.has(edge.id);
if (isIncluded && !matchingEdges.find((e) => e.id === edge.id)) { if (isIncluded && !matchingEdges.find((e) => e.id === edge.id)) {
matchingEdges.push(edge); matchingEdges.push(edge);