142 lines
4.5 KiB
JavaScript
142 lines
4.5 KiB
JavaScript
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;
|
||
}
|
||
}; |