148 lines
3.5 KiB
JavaScript
148 lines
3.5 KiB
JavaScript
/* global document */
|
|
|
|
const {
|
|
map,
|
|
prop,
|
|
groupBy,
|
|
flatten,
|
|
addIndex,
|
|
mapObjIndexed,
|
|
values,
|
|
} = require('ramda');
|
|
|
|
const React = require('react');
|
|
|
|
const r = require('r-dom');
|
|
|
|
const plusMinus = require('../../utils/plus-minus');
|
|
|
|
const {
|
|
GraphView: GraphViewBase,
|
|
} = require('./base');
|
|
|
|
const mapIndexed = addIndex(map);
|
|
|
|
const Satellite = () => r(React.Fragment);
|
|
|
|
const satelliteSpread = 36;
|
|
|
|
class GraphView extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
|
|
this.state = {
|
|
edgesByTargetNodeKey: {},
|
|
satelliteNodesByTargetNodeKey: {},
|
|
};
|
|
|
|
this.graph = React.createRef();
|
|
|
|
Object.assign(this, {
|
|
onSwapEdge: this.onSwapEdge.bind(this),
|
|
onNodeMove: this.onNodeMove.bind(this),
|
|
|
|
renderNode: this.renderNode.bind(this),
|
|
renderNodeText: this.renderNodeText.bind(this),
|
|
});
|
|
}
|
|
|
|
static getDerivedStateFromProps(props) {
|
|
const { nodeKey, edgeKey } = props;
|
|
|
|
const edgesByTargetNodeKey = groupBy(prop('target'), props.edges);
|
|
const satelliteNodesByTargetNodeKey = map(map(edge => ({
|
|
[nodeKey]: `${edge.target}__satellite__${edge[edgeKey]}`,
|
|
edge: edge[edgeKey],
|
|
source: edge.source,
|
|
target: edge.target,
|
|
type: 'satellite',
|
|
})), edgesByTargetNodeKey);
|
|
|
|
return { edgesByTargetNodeKey, satelliteNodesByTargetNodeKey };
|
|
}
|
|
|
|
static repositionSatellites(position, satelliteNodes) {
|
|
satelliteNodes.forEach((satelliteNode, i) => {
|
|
satelliteNode.x = position.x;
|
|
satelliteNode.y = position.y +
|
|
(satelliteSpread * plusMinus(i)) +
|
|
((satelliteSpread / 2) * ((satelliteNodes.length + 1) % 2));
|
|
});
|
|
}
|
|
|
|
onSwapEdge(sourceNode, targetNode, edge) {
|
|
this.props.onSwapEdge(sourceNode, targetNode, edge);
|
|
|
|
const { nodeKey } = this.props;
|
|
|
|
const createdEdgeId = `edge-${sourceNode[nodeKey]}-${targetNode[nodeKey]}-container`;
|
|
const createdEdge = document.getElementById(createdEdgeId);
|
|
createdEdge.remove();
|
|
this.graph.current.forceUpdate();
|
|
}
|
|
|
|
onNodeMove(position, nodeId, shiftKey) {
|
|
const { nodeKey } = this.props;
|
|
const satelliteNodes = this.state.satelliteNodesByTargetNodeKey[nodeId];
|
|
if (satelliteNodes) {
|
|
this.constructor.repositionSatellites(position, satelliteNodes);
|
|
satelliteNodes.forEach(satelliteNode => {
|
|
this.graph.current.handleNodeMove(satelliteNode, satelliteNode[nodeKey], shiftKey);
|
|
});
|
|
}
|
|
}
|
|
|
|
renderNode(nodeRef, dgo, key, selected, hovered) {
|
|
if (dgo.type !== 'satellite') {
|
|
return this.props.renderNode(nodeRef, dgo, key, selected, hovered);
|
|
}
|
|
|
|
return r(Satellite);
|
|
}
|
|
|
|
renderNodeText(dgo) {
|
|
if (dgo.type !== 'satellite') {
|
|
return this.props.renderNodeText(dgo);
|
|
}
|
|
|
|
return r(React.Fragment);
|
|
}
|
|
|
|
render() {
|
|
const { nodeKey } = this.props;
|
|
const { edgesByTargetNodeKey, satelliteNodesByTargetNodeKey } = this.state;
|
|
|
|
const nodes = flatten(map(node => {
|
|
const satelliteNodes = satelliteNodesByTargetNodeKey[node[nodeKey]] || [];
|
|
this.constructor.repositionSatellites(node, satelliteNodes);
|
|
return satelliteNodes.concat(node);
|
|
}, this.props.nodes));
|
|
|
|
const edges = flatten(values(mapObjIndexed((edges, target) => mapIndexed((edge, i) => ({
|
|
id: edge.id,
|
|
source: edge.source,
|
|
target: satelliteNodesByTargetNodeKey[target][i][nodeKey],
|
|
originalTarget: edge.target,
|
|
index: edge.index,
|
|
type: edge.type,
|
|
}), edges), edgesByTargetNodeKey)));
|
|
|
|
return r(GraphViewBase, {
|
|
...this.props,
|
|
|
|
ref: this.graph,
|
|
|
|
nodes,
|
|
edges,
|
|
|
|
onSwapEdge: this.onSwapEdge,
|
|
onNodeMove: this.onNodeMove,
|
|
|
|
renderNode: this.renderNode,
|
|
renderNodeText: this.renderNodeText,
|
|
});
|
|
}
|
|
}
|
|
|
|
module.exports = { GraphView };
|