refactor(reducer): handle unset nodes selection action

This commit is contained in:
moklick
2021-02-15 18:21:57 +01:00
parent 5aa78cf2fc
commit 4c76fc5ae3
2 changed files with 76 additions and 92 deletions
+1 -1
View File
@@ -124,7 +124,7 @@ export const setMultiSelectionActive = (multiSelectionActive: boolean) =>
export const setConnectionMode = (connectionMode: ConnectionMode) => export const setConnectionMode = (connectionMode: ConnectionMode) =>
createAction(constants.SET_CONNECTION_MODE, { connectionMode }); createAction(constants.SET_CONNECTION_MODE, { connectionMode });
export const setNodeExtent = (nodeExtent: NodeExtent) => createAction(constants.SET_NODE_EXTENT, { nodeExtent }); export const setNodeExtent = (nodeExtent: NodeExtent) => createAction(constants.SET_NODE_EXTENT, nodeExtent);
export type ReactFlowAction = ReturnType< export type ReactFlowAction = ReturnType<
| typeof setOnConnect | typeof setOnConnect
+75 -91
View File
@@ -1,6 +1,6 @@
import isEqual from 'fast-deep-equal'; import isEqual from 'fast-deep-equal';
import { getDimensions } from '../utils'; import { clampPosition, getDimensions } from '../utils';
import { import {
getNodesInside, getNodesInside,
getConnectedEdges, getConnectedEdges,
@@ -33,44 +33,36 @@ export default function reactFlowReducer(state = initialState, action: ReactFlow
}; };
const { nextNodes, nextEdges } = propElements.reduce((res, propElement): NextElements => { const { nextNodes, nextEdges } = propElements.reduce((res, propElement): NextElements => {
if (isNode(propElement)) { if (isNode(propElement)) {
let storeNode = state.nodes.find((node) => node.id === propElement.id); const storeNode = state.nodes.find((node) => node.id === propElement.id);
// update existing element
if (storeNode) { if (storeNode) {
const positionChanged = const updatedNode: Node = {
storeNode.position.x !== propElement.position.x || storeNode.position.y !== propElement.position.y;
const typeChanged = typeof propElement.type !== 'undefined' && propElement.type !== storeNode.type;
storeNode = {
...storeNode, ...storeNode,
...propElement, ...propElement,
}; };
if (positionChanged) { if (storeNode.position.x !== propElement.position.x || storeNode.position.y !== propElement.position.y) {
storeNode.__rf.position = propElement.position; updatedNode.__rf.position = propElement.position;
} }
if (typeChanged) { if (typeof propElement.type !== 'undefined' && propElement.type !== storeNode.type) {
// 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.
storeNode.__rf.width = null; updatedNode.__rf.width = null;
} }
res.nextNodes.push(storeNode); res.nextNodes.push(updatedNode);
} else { } else {
// add new element
res.nextNodes.push(parseNode(propElement, state.nodeExtent)); res.nextNodes.push(parseNode(propElement, state.nodeExtent));
} }
} else if (isEdge(propElement)) { } else if (isEdge(propElement)) {
let storeEdge = state.edges.find((se) => se.id === propElement.id); const storeEdge = state.edges.find((se) => se.id === propElement.id);
if (storeEdge) { if (storeEdge) {
storeEdge = { res.nextEdges.push({
...storeEdge, ...storeEdge,
...propElement, ...propElement,
}; });
res.nextEdges.push(storeEdge);
} else { } else {
res.nextEdges.push(parseEdge(propElement)); res.nextEdges.push(parseEdge(propElement));
} }
@@ -146,27 +138,22 @@ export default function reactFlowReducer(state = initialState, action: ReactFlow
const nextNodes = state.nodes.map((node) => { const nextNodes = state.nodes.map((node) => {
if (id === node.id || state.selectedElements?.find((sNode) => sNode.id === node.id)) { if (id === node.id || state.selectedElements?.find((sNode) => sNode.id === node.id)) {
if (diff) { const updatedNode = {
return {
...node,
__rf: {
...node.__rf,
isDragging,
position: {
x: node.__rf.position.x + diff.x,
y: node.__rf.position.y + diff.y,
},
},
};
}
return {
...node, ...node,
__rf: { __rf: {
...node.__rf, ...node.__rf,
isDragging, isDragging,
}, },
}; };
if (diff) {
updatedNode.__rf.position = {
x: node.__rf.position.x + diff.x,
y: node.__rf.position.y + diff.y,
};
}
return updatedNode;
} }
return node; return node;
@@ -177,34 +164,29 @@ export default function reactFlowReducer(state = initialState, action: ReactFlow
case constants.SET_USER_SELECTION: { case constants.SET_USER_SELECTION: {
const mousePos = action.payload; const mousePos = action.payload;
const userSelectionRect = {
width: 0,
height: 0,
startX: mousePos.x,
startY: mousePos.y,
x: mousePos.x,
y: mousePos.y,
draw: true,
};
return { return {
...state, ...state,
userSelectionRect,
selectionActive: true, selectionActive: true,
userSelectionRect: {
width: 0,
height: 0,
startX: mousePos.x,
startY: mousePos.y,
x: mousePos.x,
y: mousePos.y,
draw: true,
},
}; };
} }
case constants.UPDATE_USER_SELECTION: { case constants.UPDATE_USER_SELECTION: {
const mousePos = action.payload; const mousePos = action.payload;
const startX = state.userSelectionRect.startX || 0; const startX = state.userSelectionRect.startX ?? 0;
const startY = state.userSelectionRect.startY || 0; const startY = state.userSelectionRect.startY ?? 0;
const negativeX = mousePos.x < startX;
const negativeY = mousePos.y < startY;
const nextUserSelectRect = { const nextUserSelectRect = {
...state.userSelectionRect, ...state.userSelectionRect,
x: negativeX ? mousePos.x : state.userSelectionRect.x, x: mousePos.x < startX ? mousePos.x : state.userSelectionRect.x,
y: negativeY ? mousePos.y : state.userSelectionRect.y, y: mousePos.y < startY ? mousePos.y : state.userSelectionRect.y,
width: Math.abs(mousePos.x - startX), width: Math.abs(mousePos.x - startX),
height: Math.abs(mousePos.y - startY), height: Math.abs(mousePos.y - startY),
}; };
@@ -213,49 +195,41 @@ export default function reactFlowReducer(state = initialState, action: ReactFlow
const selectedEdges = getConnectedEdges(selectedNodes, state.edges); const selectedEdges = getConnectedEdges(selectedNodes, state.edges);
const nextSelectedElements = [...selectedNodes, ...selectedEdges]; const nextSelectedElements = [...selectedNodes, ...selectedEdges];
const selectedElementsUpdated = !isEqual(nextSelectedElements, state.selectedElements); const selectedElementsChanged = !isEqual(nextSelectedElements, state.selectedElements);
const selectedElementsUpdate = selectedElementsChanged
if (selectedElementsUpdated) { ? {
return { selectedElements: nextSelectedElements.length > 0 ? nextSelectedElements : null,
...state, }
selectedElements: nextSelectedElements.length > 0 ? nextSelectedElements : null, : {};
userSelectionRect: nextUserSelectRect,
};
}
return { return {
...state, ...state,
...selectedElementsUpdate,
userSelectionRect: nextUserSelectRect, userSelectionRect: nextUserSelectRect,
}; };
} }
case constants.UNSET_USER_SELECTION: { case constants.UNSET_USER_SELECTION: {
const selectedNodes = state.selectedElements?.filter((node) => isNode(node) && node.__rf) as Node[]; const selectedNodes = state.selectedElements?.filter((node) => isNode(node) && node.__rf) as Node[];
const selectionActive = false; const stateUpdate = {
const userSelectionRect = { ...state,
...state.userSelectionRect, selectionActive: false,
draw: false, userSelectionRect: {
...state.userSelectionRect,
draw: false,
},
}; };
if (!selectedNodes || selectedNodes.length === 0) { if (!selectedNodes || selectedNodes.length === 0) {
return { stateUpdate.selectedElements = null;
...state, stateUpdate.nodesSelectionActive = false;
selectionActive, } else {
userSelectionRect, const selectedNodesBbox = getRectOfNodes(selectedNodes);
selectedElements: null, stateUpdate.selectedNodesBbox = selectedNodesBbox;
nodesSelectionActive: false, stateUpdate.nodesSelectionActive = true;
};
} }
const selectedNodesBbox = getRectOfNodes(selectedNodes); return stateUpdate;
return {
...state,
selectionActive,
userSelectionRect,
selectedNodesBbox,
nodesSelectionActive: true,
};
} }
case constants.SET_SELECTED_ELEMENTS: { case constants.SET_SELECTED_ELEMENTS: {
const elements = action.payload; const elements = action.payload;
@@ -298,9 +272,7 @@ export default function reactFlowReducer(state = initialState, action: ReactFlow
case constants.SET_MINZOOM: { case constants.SET_MINZOOM: {
const minZoom = action.payload; const minZoom = action.payload;
if (state.d3Zoom) { state.d3Zoom?.scaleExtent([minZoom, state.maxZoom]);
state.d3Zoom.scaleExtent([minZoom, state.maxZoom]);
}
return { return {
...state, ...state,
@@ -311,9 +283,7 @@ export default function reactFlowReducer(state = initialState, action: ReactFlow
case constants.SET_MAXZOOM: { case constants.SET_MAXZOOM: {
const maxZoom = action.payload; const maxZoom = action.payload;
if (state.d3Zoom) { state.d3Zoom?.scaleExtent([state.minZoom, maxZoom]);
state.d3Zoom.scaleExtent([state.minZoom, maxZoom]);
}
return { return {
...state, ...state,
@@ -323,20 +293,35 @@ export default function reactFlowReducer(state = initialState, action: ReactFlow
case constants.SET_TRANSLATEEXTENT: { case constants.SET_TRANSLATEEXTENT: {
const translateExtent = action.payload; const translateExtent = action.payload;
if (state.d3Zoom) { state.d3Zoom?.translateExtent(translateExtent);
state.d3Zoom.translateExtent(translateExtent);
}
return { return {
...state, ...state,
translateExtent, translateExtent,
}; };
} }
case constants.SET_NODE_EXTENT: {
const nodeExtent = action.payload;
return {
...state,
nodeExtent,
nodes: state.nodes.map((node) => {
return {
...node,
__rf: {
...node.__rf,
position: clampPosition(node.__rf.position, nodeExtent),
},
};
}),
};
}
case constants.SET_ON_CONNECT: case constants.SET_ON_CONNECT:
case constants.SET_ON_CONNECT_START: case constants.SET_ON_CONNECT_START:
case constants.SET_ON_CONNECT_STOP: case constants.SET_ON_CONNECT_STOP:
case constants.SET_ON_CONNECT_END: case constants.SET_ON_CONNECT_END:
case constants.RESET_SELECTED_ELEMENTS: case constants.RESET_SELECTED_ELEMENTS:
case constants.UNSET_NODES_SELECTION:
case constants.UPDATE_TRANSFORM: case constants.UPDATE_TRANSFORM:
case constants.UPDATE_SIZE: case constants.UPDATE_SIZE:
case constants.SET_CONNECTION_POSITION: case constants.SET_CONNECTION_POSITION:
@@ -349,7 +334,6 @@ export default function reactFlowReducer(state = initialState, action: ReactFlow
case constants.SET_ELEMENTS_SELECTABLE: case constants.SET_ELEMENTS_SELECTABLE:
case constants.SET_MULTI_SELECTION_ACTIVE: case constants.SET_MULTI_SELECTION_ACTIVE:
case constants.SET_CONNECTION_MODE: case constants.SET_CONNECTION_MODE:
case constants.SET_NODE_EXTENT:
return { ...state, ...action.payload }; return { ...state, ...action.payload };
default: default:
return state; return state;