Files
xyflow/src/state/index.js
2019-08-19 15:53:10 +02:00

142 lines
4.5 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { zoomIdentity } from 'd3-zoom';
import isEqual from 'fast-deep-equal';
import { getBoundingBox, getNodesInside, getConnectedEdges } from '../graph-utils';
export const SET_EDGES = 'SET_EDGES';
export const SET_NODES = 'SET_NODES';
export const UPDATE_NODE_DATA = 'UPDATE_NODE_DATA';
export const UPDATE_NODE_POS = 'UPDATE_NODE_POS';
export const UPDATE_TRANSFORM = 'UPDATE_TRANSFORM';
export const UPDATE_SIZE = 'UPDATE_SIZE';
export const INIT_D3 = 'INIT_D3';
export const FIT_VIEW = 'FIT_VIEW';
export const ZOOM_IN = 'ZOOM_IN';
export const ZOOM_OUT = 'ZOOM_OUT';
export const UPDATE_SELECTION = 'UPDATE_SELECTION';
export const SET_SELECTION = 'SET_SELECTION';
export const SET_NODES_SELECTION = 'SET_NODES_SELECTION';
export const SET_SELECTED_ELEMENTS = 'SET_SELECTED_ELEMENTS';
export const REMOVE_NODES = 'REMOVE_NODES';
export const initialState = {
width: 0,
height: 0,
transform: [0, 0, 1],
nodes: [],
edges: [],
selectedElements: [],
selectedNodesBbox: { x: 0, y: 0, width: 0, height: 0 },
d3Zoom: null,
d3Selection: null,
d3Initialised: false,
nodesSelectionActive: false,
selectionActive: false,
selection: {}
};
export const reducer = (state, action) => {
switch (action.type) {
case UPDATE_NODE_DATA: {
return {
...state,
nodes: state.nodes.map((n) => {
if (n.id === action.payload.id) {
n.__rg = {
...n.__rg,
...action.payload.data
};
}
return n;
})
};
}
case UPDATE_NODE_POS: {
return {
...state,
nodes: state.nodes.map((n) => {
if (n.id === action.payload.id) {
n.__rg = {
...n.__rg,
position: action.payload.pos
};
}
return n;
})
};
}
case FIT_VIEW: {
const bounds = getBoundingBox(state.nodes);
const maxBoundsSize = Math.max(bounds.width, bounds.height);
const k = Math.min(state.width, state.height) / (maxBoundsSize + (maxBoundsSize * action.payload.padding));
const boundsCenterX = bounds.x + (bounds.width / 2);
const boundsCenterY = bounds.y + (bounds.height / 2);
const transform = [(state.width / 2) - (boundsCenterX * k), (state.height / 2) - (boundsCenterY * k)];
const fittedTransform = zoomIdentity.translate(transform[0], transform[1]).scale(k);
state.d3Selection.call(state.d3Zoom.transform, fittedTransform);
return state;
}
case ZOOM_IN: {
const { transform } = state;
state.d3Zoom.scaleTo(state.d3Selection, transform[2] + 0.2);
return state;
}
case ZOOM_OUT: {
const { transform } = state;
state.d3Zoom.scaleTo(state.d3Selection, transform[2] - 0.2);
return state;
}
case UPDATE_SELECTION: {
const selectedNodes = getNodesInside(state.nodes, action.payload.selection, state.transform);
const selectedEdges = getConnectedEdges(selectedNodes, state.edges);
const nextSelectedElements = [...selectedNodes, ...selectedEdges];
const selectedElementsUpdated = !isEqual(nextSelectedElements, state.selectedElements);
return {
...state,
...action.payload,
selectedElements: selectedElementsUpdated ? nextSelectedElements: state.selectedElements
};
}
case SET_NODES_SELECTION: {
if (!action.payload.nodesSelectionActive) {
return { ...state, nodesSelectionActive: false, selectedElements: [] };
}
const selectedNodes = getNodesInside(state.nodes, action.payload.selection, state.transform);
const selectedNodesBbox = getBoundingBox(selectedNodes);
return { ...state, ...action.payload, selectedNodesBbox };
}
case SET_SELECTED_ELEMENTS: {
const selectedElementsUpdated = !isEqual(action.payload.selectedElements, state.selectedElements);
const selectedElements = selectedElementsUpdated ? action.payload.selectedElements : state.selectedElements;
return { ...state, selectedElements, odesSelectionActive: false };
}
// unused
case REMOVE_NODES: {
const { ids } = action.payload;
const nextEdges = state.edges.filter(e => !ids.includes(e.target) && !ids.includes(e.source));
const nextNodes = state.nodes.filter(n => !ids.includes(n.id));
return { ...state, nodes: nextNodes, edges: nextEdges };
}
case SET_NODES:
case SET_EDGES:
case UPDATE_TRANSFORM:
case INIT_D3:
case UPDATE_SIZE:
case SET_SELECTION:
return { ...state, ...action.payload };
default:
return state;
}
};