mirror of
https://github.com/yldio/copilot.git
synced 2024-11-28 14:10:04 +02:00
SHow dragged node on top and refactor
This commit is contained in:
parent
f82fee8a7f
commit
858a0a2c24
@ -126,17 +126,8 @@ const metricByInterval = (data = [], {
|
|||||||
const q1 = statistics.quantile(data, 0.25);
|
const q1 = statistics.quantile(data, 0.25);
|
||||||
const median = statistics.median(data);
|
const median = statistics.median(data);
|
||||||
const q3 = statistics.quantile(data, 0.75);
|
const q3 = statistics.quantile(data, 0.75);
|
||||||
|
const max = statistics.max(data);
|
||||||
const iqr = q3-q1;
|
const min = statistics.min(data);
|
||||||
const outlierMultiplier = 1.5;
|
|
||||||
let max = statistics.max(data);
|
|
||||||
if(max < q3 + iqr*outlierMultiplier) {
|
|
||||||
max = q3;
|
|
||||||
}
|
|
||||||
let min = statistics.min(data);
|
|
||||||
if(min > q1 - iqr*outlierMultiplier){
|
|
||||||
min = q3;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
start: sample.start.valueOf(),
|
start: sample.start.valueOf(),
|
||||||
|
2
ui/src/components/topology/big-data.js
Normal file
2
ui/src/components/topology/big-data.js
Normal file
File diff suppressed because one or more lines are too long
53
ui/src/components/topology/graph-link/arrow.js
Normal file
53
ui/src/components/topology/graph-link/arrow.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { Baseline } from '../../../shared/composers';
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
GraphLinkCircle,
|
||||||
|
GraphLinkArrowLine
|
||||||
|
} from './shapes';
|
||||||
|
|
||||||
|
const GraphLinkArrow = ({
|
||||||
|
data,
|
||||||
|
index
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const {
|
||||||
|
targetPosition,
|
||||||
|
arrowAngle
|
||||||
|
} = data;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<g
|
||||||
|
transform={
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
`translate(${targetPosition.x}, ${targetPosition.y}) rotate(${arrowAngle})`
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<GraphLinkCircle
|
||||||
|
cx={0}
|
||||||
|
cy={0}
|
||||||
|
r={9}
|
||||||
|
/>
|
||||||
|
<GraphLinkArrowLine
|
||||||
|
x1={-1}
|
||||||
|
x2={2}
|
||||||
|
y1={-3}
|
||||||
|
y2={0}
|
||||||
|
/>
|
||||||
|
<GraphLinkArrowLine
|
||||||
|
x1={-1}
|
||||||
|
x2={2}
|
||||||
|
y1={3}
|
||||||
|
y2={0}
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
GraphLinkArrow.propTypes = {
|
||||||
|
data: React.PropTypes.object.isRequired,
|
||||||
|
index: React.PropTypes.number
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Baseline(
|
||||||
|
GraphLinkArrow
|
||||||
|
);
|
100
ui/src/components/topology/graph-link/functions.js
Normal file
100
ui/src/components/topology/graph-link/functions.js
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
import Constants from '../constants';
|
||||||
|
|
||||||
|
const getAngleFromPoints = (source, target) => {
|
||||||
|
|
||||||
|
const lineAngle = Math.atan2(target.y-source.y, target.x - source.x);
|
||||||
|
const lineAngleDeg = lineAngle*180/Math.PI;
|
||||||
|
const zeroToThreeSixty = lineAngleDeg < 0 ? 360 + lineAngleDeg : lineAngleDeg;
|
||||||
|
|
||||||
|
return zeroToThreeSixty;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPosition = (angle, positions, position, noCorners=false) => {
|
||||||
|
const positionIndex = noCorners ?
|
||||||
|
Math.round(angle/90)*2 : Math.round(angle/45);
|
||||||
|
const offsetPosition = positions[positionIndex];
|
||||||
|
return {
|
||||||
|
id: offsetPosition.id,
|
||||||
|
x: position.x + offsetPosition.x,
|
||||||
|
y: position.y + offsetPosition.y
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPositions = (rect, halfCorner=0) => ([{
|
||||||
|
id: 'r',
|
||||||
|
x: rect.right,
|
||||||
|
y: 0
|
||||||
|
}, {
|
||||||
|
id: 'br',
|
||||||
|
x: rect.right - halfCorner,
|
||||||
|
y: rect.bottom - halfCorner
|
||||||
|
}, {
|
||||||
|
id: 'b',
|
||||||
|
x: 0,
|
||||||
|
y: rect.bottom
|
||||||
|
}, {
|
||||||
|
id: 'bl',
|
||||||
|
x: rect.left + halfCorner,
|
||||||
|
y: rect.bottom - halfCorner
|
||||||
|
}, {
|
||||||
|
id: 'l',
|
||||||
|
x: rect.left,
|
||||||
|
y: 0
|
||||||
|
}, {
|
||||||
|
id: 'tl',
|
||||||
|
x: rect.left + halfCorner,
|
||||||
|
y: rect.top + halfCorner
|
||||||
|
}, {
|
||||||
|
id: 't',
|
||||||
|
x: 0,
|
||||||
|
y: rect.top
|
||||||
|
}, {
|
||||||
|
id: 'tr',
|
||||||
|
x: rect.right- halfCorner,
|
||||||
|
y: rect.top + halfCorner
|
||||||
|
},{
|
||||||
|
id: 'r',
|
||||||
|
x: rect.right,
|
||||||
|
y: 0
|
||||||
|
}]);
|
||||||
|
|
||||||
|
const getRect = (data) => {
|
||||||
|
return data.children ?
|
||||||
|
Constants.nodeRectWithChildren :
|
||||||
|
Constants.nodeRect;
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculateLineLayout = ({
|
||||||
|
source,
|
||||||
|
target
|
||||||
|
}) => {
|
||||||
|
// actually, this will need to be got dynamically, in case them things are different sizes
|
||||||
|
// yeah right, now you'll get to do exactly that
|
||||||
|
|
||||||
|
const sourceRect = getRect(source);
|
||||||
|
const targetRect= getRect(target);
|
||||||
|
|
||||||
|
const halfCorner = 2;
|
||||||
|
|
||||||
|
const sourcePositions = getPositions(sourceRect, halfCorner);
|
||||||
|
const sourceAngle = getAngleFromPoints(source, target);
|
||||||
|
const sourcePosition = getPosition(sourceAngle, sourcePositions, source);
|
||||||
|
|
||||||
|
const targetPositions = getPositions(targetRect, halfCorner);
|
||||||
|
const targetAngle = getAngleFromPoints(target, sourcePosition);
|
||||||
|
const targetPosition = getPosition(targetAngle, targetPositions, target); //, true);
|
||||||
|
|
||||||
|
const arrowAngle = getAngleFromPoints(sourcePosition, targetPosition);
|
||||||
|
|
||||||
|
return {
|
||||||
|
source,
|
||||||
|
target,
|
||||||
|
sourcePosition,
|
||||||
|
targetPosition,
|
||||||
|
arrowAngle
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
calculateLineLayout
|
||||||
|
};
|
@ -1,143 +1,32 @@
|
|||||||
import { Baseline } from '../../../shared/composers';
|
import { Baseline } from '../../../shared/composers';
|
||||||
import Constants from '../constants';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
GraphLinkLine,
|
GraphLinkLine
|
||||||
GraphLinkCircle,
|
|
||||||
GraphLinkArrow
|
|
||||||
} from './shapes';
|
} from './shapes';
|
||||||
|
|
||||||
const getAngleFromPoints = (source, target) => {
|
|
||||||
|
|
||||||
const lineAngle = Math.atan2(target.y-source.y, target.x - source.x);
|
|
||||||
const lineAngleDeg = lineAngle*180/Math.PI;
|
|
||||||
const zeroToThreeSixty = lineAngleDeg < 0 ? 360 + lineAngleDeg : lineAngleDeg;
|
|
||||||
|
|
||||||
return zeroToThreeSixty;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPosition = (angle, positions, position, noCorners=false) => {
|
|
||||||
const positionIndex = noCorners ?
|
|
||||||
Math.round(angle/90)*2 : Math.round(angle/45);
|
|
||||||
const offsetPosition = positions[positionIndex];
|
|
||||||
return {
|
|
||||||
id: offsetPosition.id,
|
|
||||||
x: position.x + offsetPosition.x,
|
|
||||||
y: position.y + offsetPosition.y
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPositions = (rect, halfCorner=0) => ([{
|
|
||||||
id: 'r',
|
|
||||||
x: rect.right,
|
|
||||||
y: 0
|
|
||||||
}, {
|
|
||||||
id: 'br',
|
|
||||||
x: rect.right - halfCorner,
|
|
||||||
y: rect.bottom - halfCorner
|
|
||||||
}, {
|
|
||||||
id: 'b',
|
|
||||||
x: 0,
|
|
||||||
y: rect.bottom
|
|
||||||
}, {
|
|
||||||
id: 'bl',
|
|
||||||
x: rect.left + halfCorner,
|
|
||||||
y: rect.bottom - halfCorner
|
|
||||||
}, {
|
|
||||||
id: 'l',
|
|
||||||
x: rect.left,
|
|
||||||
y: 0
|
|
||||||
}, {
|
|
||||||
id: 'tl',
|
|
||||||
x: rect.left + halfCorner,
|
|
||||||
y: rect.top + halfCorner
|
|
||||||
}, {
|
|
||||||
id: 't',
|
|
||||||
x: 0,
|
|
||||||
y: rect.top
|
|
||||||
}, {
|
|
||||||
id: 'tr',
|
|
||||||
x: rect.right- halfCorner,
|
|
||||||
y: rect.top + halfCorner
|
|
||||||
},{
|
|
||||||
id: 'r',
|
|
||||||
x: rect.right,
|
|
||||||
y: 0
|
|
||||||
}]);
|
|
||||||
|
|
||||||
const getRect = (data) => {
|
|
||||||
return data.children ?
|
|
||||||
Constants.nodeRectWithChildren :
|
|
||||||
Constants.nodeRect;
|
|
||||||
};
|
|
||||||
|
|
||||||
const GraphLink = ({
|
const GraphLink = ({
|
||||||
data,
|
data,
|
||||||
index
|
index
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
source,
|
sourcePosition,
|
||||||
target
|
targetPosition
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
// actually, this will need to be got dynamically, in case them things are different sizes
|
|
||||||
// yeah right, now you'll get to do exactly that
|
|
||||||
|
|
||||||
const sourceRect = getRect(source);
|
|
||||||
const targetRect= getRect(target);
|
|
||||||
|
|
||||||
const halfCorner = 2;
|
|
||||||
|
|
||||||
const sourcePositions = getPositions(sourceRect, halfCorner);
|
|
||||||
const sourceAngle = getAngleFromPoints(source, target);
|
|
||||||
const sourcePosition = getPosition(sourceAngle, sourcePositions, source);
|
|
||||||
|
|
||||||
const targetPositions = getPositions(targetRect, halfCorner);
|
|
||||||
const targetAngle = getAngleFromPoints(target, sourcePosition);
|
|
||||||
const targetPosition = getPosition(targetAngle, targetPositions, target); //, true);
|
|
||||||
|
|
||||||
const arrowAngle = getAngleFromPoints(sourcePosition, targetPosition);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<g>
|
<GraphLinkLine
|
||||||
<GraphLinkLine
|
x1={sourcePosition.x}
|
||||||
x1={sourcePosition.x}
|
x2={targetPosition.x}
|
||||||
x2={targetPosition.x}
|
y1={sourcePosition.y}
|
||||||
y1={sourcePosition.y}
|
y2={targetPosition.y}
|
||||||
y2={targetPosition.y}
|
/>
|
||||||
/>
|
|
||||||
<g
|
|
||||||
transform={
|
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
`translate(${targetPosition.x}, ${targetPosition.y}) rotate(${arrowAngle})`
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<GraphLinkCircle
|
|
||||||
cx={0}
|
|
||||||
cy={0}
|
|
||||||
r={9}
|
|
||||||
/>
|
|
||||||
<GraphLinkArrow
|
|
||||||
x1={-1}
|
|
||||||
x2={2}
|
|
||||||
y1={-3}
|
|
||||||
y2={0}
|
|
||||||
/>
|
|
||||||
<GraphLinkArrow
|
|
||||||
x1={-1}
|
|
||||||
x2={2}
|
|
||||||
y1={3}
|
|
||||||
y2={0}
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
GraphLink.propTypes = {
|
GraphLink.propTypes = {
|
||||||
data: React.PropTypes.object.isRequired,
|
data: React.PropTypes.object.isRequired,
|
||||||
index: React.PropTypes.number.isRequired
|
index: React.PropTypes.number
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Baseline(
|
export default Baseline(
|
||||||
|
@ -12,7 +12,7 @@ export const GraphLinkCircle = styled.circle`
|
|||||||
stroke-width: 1.5;
|
stroke-width: 1.5;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const GraphLinkArrow = styled.line`
|
export const GraphLinkArrowLine = styled.line`
|
||||||
stroke: ${colors.base.white};
|
stroke: ${colors.base.white};
|
||||||
stroke-width: 2;
|
stroke-width: 2;
|
||||||
stroke-linecap: round;
|
stroke-linecap: round;
|
||||||
|
@ -67,7 +67,7 @@ const GraphNode = ({
|
|||||||
|
|
||||||
const onStart = (evt) => {
|
const onStart = (evt) => {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
onDragStart(evt, data.id);
|
onDragStart(evt, data.uuid);
|
||||||
};
|
};
|
||||||
|
|
||||||
const nodeRectEvents = connected ? {
|
const nodeRectEvents = connected ? {
|
||||||
|
@ -14,6 +14,16 @@ const rectRadius = (size) => {
|
|||||||
return Math.round(hypotenuse(width, height)/2);
|
return Math.round(hypotenuse(width, height)/2);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const forcePlayAnimation = (simulation, animationTicks) => {
|
||||||
|
const n = Math.ceil(
|
||||||
|
Math.log(
|
||||||
|
simulation.alphaMin()) / Math.log(
|
||||||
|
1 - simulation.alphaDecay())) - animationTicks;
|
||||||
|
for (var i = 0; i < n; ++i) {
|
||||||
|
simulation.tick();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const createLinks = (services) =>
|
const createLinks = (services) =>
|
||||||
services.reduce((acc, service, index) =>
|
services.reduce((acc, service, index) =>
|
||||||
service.connections ?
|
service.connections ?
|
||||||
@ -28,13 +38,12 @@ const createLinks = (services) =>
|
|||||||
const createSimulation = (
|
const createSimulation = (
|
||||||
services,
|
services,
|
||||||
svgSize,
|
svgSize,
|
||||||
onTick,
|
animationTicks = 0
|
||||||
onEnd
|
|
||||||
) => {
|
) => {
|
||||||
// This is not going to work given that as well as the d3 layout stuff, other things might be at play too
|
// This is not going to work given that as well as the d3 layout stuff, other things might be at play too
|
||||||
// We should pass two objects to the components - one for positioning and one for data
|
// We should pass two objects to the components - one for positioning and one for data
|
||||||
const nodes = services.map((service, index) => ({
|
const nodes = services.map((service, index) => ({
|
||||||
id: service.uuid,
|
uuid: service.uuid,
|
||||||
index: index
|
index: index
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -47,15 +56,17 @@ const createSimulation = (
|
|||||||
|
|
||||||
const nodeRadius = rectRadius(Constants.nodeSizeWithChildren);
|
const nodeRadius = rectRadius(Constants.nodeSizeWithChildren);
|
||||||
|
|
||||||
|
const simulation = forceSimulation(nodes)
|
||||||
|
.force('link', forceLink(links).id(d => d.uuid))
|
||||||
|
.force('collide', forceCollide(nodeRadius))
|
||||||
|
.force('center', forceCenter(width/2, height/2));
|
||||||
|
|
||||||
|
forcePlayAnimation(simulation, animationTicks);
|
||||||
|
|
||||||
return ({
|
return ({
|
||||||
simulation: forceSimulation(nodes)
|
nodes,
|
||||||
.force('link', forceLink(links).id(d => d.id))
|
links,
|
||||||
.force('collide', forceCollide(nodeRadius))
|
simulation
|
||||||
.force('center', forceCenter(width/2, height/2))
|
|
||||||
.on('tick', onTick)
|
|
||||||
.on('end', onEnd),
|
|
||||||
nodes: nodes,
|
|
||||||
links: links
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import { Baseline } from '../../shared/composers';
|
|||||||
import Input from '../form/input';
|
import Input from '../form/input';
|
||||||
import Select from '../form/select';
|
import Select from '../form/select';
|
||||||
import { TopologyGraph } from './';
|
import { TopologyGraph } from './';
|
||||||
import data from './data';
|
import data from './wp-data';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const StyledForm = styled.form`
|
const StyledForm = styled.form`
|
||||||
|
@ -6,11 +6,16 @@ import README from './readme.md';
|
|||||||
|
|
||||||
import StoryHelper from './story-helper';
|
import StoryHelper from './story-helper';
|
||||||
import GraphNode from './graph-node';
|
import GraphNode from './graph-node';
|
||||||
|
import TopologyGraph from './topology-graph';
|
||||||
|
import data from './big-data';
|
||||||
|
|
||||||
storiesOf('Topology', module)
|
storiesOf('Topology', module)
|
||||||
.add('5 services', withReadme(README, () => (
|
.add('Wordpress example', withReadme(README, () => (
|
||||||
<StoryHelper />
|
<StoryHelper />
|
||||||
)))
|
)))
|
||||||
|
.add('Many services example', withReadme(README, () => (
|
||||||
|
<TopologyGraph services={data} />
|
||||||
|
)))
|
||||||
.add('Consul', withReadme(README, () => (
|
.add('Consul', withReadme(README, () => (
|
||||||
<svg width={180} height={159}>
|
<svg width={180} height={159}>
|
||||||
<GraphNode
|
<GraphNode
|
||||||
|
@ -1,105 +1,56 @@
|
|||||||
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Baseline } from '../../shared/composers';
|
import { Baseline } from '../../shared/composers';
|
||||||
import {
|
|
||||||
createSimulation//,
|
|
||||||
//updateSimulation
|
|
||||||
} from './graph-simulation';
|
|
||||||
import Constants from './constants';
|
import Constants from './constants';
|
||||||
|
|
||||||
|
import {
|
||||||
|
createSimulation
|
||||||
|
} from './graph-simulation';
|
||||||
|
|
||||||
|
import {
|
||||||
|
calculateLineLayout
|
||||||
|
} from './graph-link/functions';
|
||||||
|
|
||||||
import GraphNode from './graph-node';
|
import GraphNode from './graph-node';
|
||||||
import GraphLink from './graph-link';
|
import GraphLink from './graph-link';
|
||||||
import React from 'react';
|
import GraphLinkArrow from './graph-link/arrow';
|
||||||
|
|
||||||
|
|
||||||
const StyledSvg = styled.svg`
|
const StyledSvg = styled.svg`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 1400px;
|
height: 1400px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
let dragInfo = {
|
|
||||||
dragging: false,
|
|
||||||
nodeId: null,
|
|
||||||
position: null
|
|
||||||
};
|
|
||||||
|
|
||||||
class TopologyGraph extends React.Component {
|
class TopologyGraph extends React.Component {
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
const services = this.props.services.reduce((acc, service, index) => {
|
|
||||||
if(service.id !== 'consul') acc.push(service);
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
|
const services = this.getServicesWithoutConsul();
|
||||||
const svgSize = this.getSvgSize();
|
const svgSize = this.getSvgSize();
|
||||||
const simulationData = createSimulation(
|
|
||||||
services,
|
|
||||||
svgSize//,
|
|
||||||
//() => this.forceUpdate(),
|
|
||||||
//() => this.forceUpdate()
|
|
||||||
);
|
|
||||||
|
|
||||||
const simulation = simulationData.simulation;
|
|
||||||
|
|
||||||
const n = Math.ceil(
|
|
||||||
Math.log(
|
|
||||||
simulation.alphaMin()) / Math.log(
|
|
||||||
1 - simulation.alphaDecay()));
|
|
||||||
for (var i = 0; i < n; ++i) {
|
|
||||||
simulation.tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState(simulationData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*componentWillReceiveProps(nextProps) {
|
|
||||||
// either, we'll have more services
|
|
||||||
// or, we'll have less services
|
|
||||||
// or, data of services had changed =>
|
|
||||||
// do shallow check on objects and links, if no change, don't do rerender
|
|
||||||
// otherwise, redo them bitches = by what I mean to update the simulation
|
|
||||||
// try freezing exisiting ones... then adding another
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
nodes,
|
nodes,
|
||||||
links
|
links,
|
||||||
} = this.state;
|
simulation
|
||||||
|
} = createSimulation(
|
||||||
|
services,
|
||||||
|
svgSize
|
||||||
|
);
|
||||||
|
|
||||||
const services = nextProps.services.reduce((acc, service, index) => {
|
this.setState({
|
||||||
|
nodes,
|
||||||
|
links,
|
||||||
|
simulation
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getServicesWithoutConsul() {
|
||||||
|
|
||||||
|
return this.props.services.reduce((acc, service, index) => {
|
||||||
if(service.id !== 'consul') acc.push(service);
|
if(service.id !== 'consul') acc.push(service);
|
||||||
return acc;
|
return acc;
|
||||||
}, []);
|
}, []);
|
||||||
// TODO this here means we'll need to evaluate whether to we have more links!
|
}
|
||||||
|
|
||||||
// this is tmp for the compare above
|
|
||||||
if(services !== nodes.length) {
|
|
||||||
const simulation = this.state.simulation;
|
|
||||||
const nextSimulationData = updateSimulation(
|
|
||||||
simulation,
|
|
||||||
services,
|
|
||||||
nodes,
|
|
||||||
links,
|
|
||||||
svgSize,
|
|
||||||
() => this.forceUpdate(),
|
|
||||||
() => this.forceUpdate()
|
|
||||||
);
|
|
||||||
|
|
||||||
const nextSimulation = nextSimulationData.simulation;
|
|
||||||
// console.log('nextSimulationData.nodes = ', nextSimulationData.nodes);
|
|
||||||
|
|
||||||
const n = Math.ceil(
|
|
||||||
Math.log(
|
|
||||||
nextSimulation.alphaMin()) / Math.log(
|
|
||||||
1 - nextSimulation.alphaDecay()));
|
|
||||||
for (var i = 0; i < n; ++i) {
|
|
||||||
nextSimulation.tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
//this.state.simulation.nodes().forEach((node, index) => {
|
|
||||||
// delete node.fx;
|
|
||||||
// delete node.fy;
|
|
||||||
//});
|
|
||||||
|
|
||||||
this.setState(nextSimulationData);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
getSvgSize() {
|
getSvgSize() {
|
||||||
if(document.getElementById('topology-svg')) {
|
if(document.getElementById('topology-svg')) {
|
||||||
@ -116,7 +67,7 @@ class TopologyGraph extends React.Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
constrain(x, y, children=false) {
|
constrainNodePosition(x, y, children=false) {
|
||||||
const svgSize = this.getSvgSize();
|
const svgSize = this.getSvgSize();
|
||||||
|
|
||||||
const nodeRect = children ?
|
const nodeRect = children ?
|
||||||
@ -142,6 +93,40 @@ class TopologyGraph extends React.Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findNode(nodeUuid) {
|
||||||
|
|
||||||
|
return this.state.nodes.reduce((acc, simNode, index) =>
|
||||||
|
simNode.uuid === nodeUuid ? simNode : acc, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
getConsulNodePosition() {
|
||||||
|
|
||||||
|
const svgSize = this.getSvgSize();
|
||||||
|
const x = svgSize.width - Constants.nodeSize.width;
|
||||||
|
return {
|
||||||
|
x,
|
||||||
|
y: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getConstrainedNodePosition(nodeUuid, children=false) {
|
||||||
|
const node = this.findNode(nodeUuid);
|
||||||
|
return this.constrainNodePosition(node.x, node.y, children);
|
||||||
|
}
|
||||||
|
|
||||||
|
findNodeData(nodesData, nodeUuid) {
|
||||||
|
return nodesData.reduce((acc, nodeData, index) =>
|
||||||
|
nodeData.uuid === nodeUuid ? nodeData : acc, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
setDragInfo(dragging, nodeUuid=null, position={}) {
|
||||||
|
this.dragInfo = {
|
||||||
|
dragging,
|
||||||
|
nodeUuid,
|
||||||
|
position
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -154,55 +139,42 @@ class TopologyGraph extends React.Component {
|
|||||||
links
|
links
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const simNode = (nodeId) =>
|
|
||||||
nodes.reduce((acc, simNode, index) =>
|
|
||||||
simNode.id === nodeId ? simNode : acc, {});
|
|
||||||
|
|
||||||
const svgSize = this.getSvgSize();
|
|
||||||
const nodesData = services.map((service, index) => {
|
const nodesData = services.map((service, index) => {
|
||||||
const sNode = service.id === 'consul' ? {
|
const nodePosition = service.id === 'consul' ?
|
||||||
x: svgSize.width - Constants.nodeSize.width,
|
this.getConsulNodePosition() :
|
||||||
y: 0
|
this.getConstrainedNodePosition(service.uuid, service.children);
|
||||||
} : simNode(service.uuid);
|
|
||||||
|
|
||||||
const constrained = {
|
|
||||||
...sNode,
|
|
||||||
...this.constrain(sNode.x, sNode.y, service.children)
|
|
||||||
};
|
|
||||||
|
|
||||||
return ({
|
return ({
|
||||||
...service,
|
...service,
|
||||||
...constrained
|
...nodePosition
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const nodeData = (nodeId) =>
|
|
||||||
nodesData.reduce((acc, nodeData, index) =>
|
|
||||||
nodeData.id === nodeId ? nodeData : acc, {});
|
|
||||||
// TODO links will need to know whether a service has children
|
// TODO links will need to know whether a service has children
|
||||||
// if it does, the height of it will be different
|
// if it does, the height of it will be different
|
||||||
const linksData = links.map((link, index) => ({
|
const linksData = links.map((link, index) => ({
|
||||||
source: nodeData(link.source.id),
|
source: this.findNodeData(nodesData, link.source.uuid),
|
||||||
target: nodeData(link.target.id)
|
target: this.findNodeData(nodesData, link.target.uuid)
|
||||||
}));
|
})).map((linkData, index) => calculateLineLayout(linkData, index ));
|
||||||
|
|
||||||
const onDragStart = (evt, nodeId) => {
|
const onDragStart = (evt, nodeId) => {
|
||||||
// it's this node's position that we'll need to update
|
// it's this node's position that we'll need to update
|
||||||
dragInfo.dragging = true;
|
|
||||||
dragInfo.nodeId = nodeId;
|
|
||||||
|
|
||||||
const x = evt.changedTouches ? evt.changedTouches[0].pageX : evt.clientX;
|
const x = evt.changedTouches ? evt.changedTouches[0].pageX : evt.clientX;
|
||||||
const y = evt.changedTouches ? evt.changedTouches[0].pageY : evt.clientY;
|
const y = evt.changedTouches ? evt.changedTouches[0].pageY : evt.clientY;
|
||||||
|
|
||||||
dragInfo.position = {
|
this.setDragInfo(
|
||||||
x,
|
true,
|
||||||
y
|
nodeId,
|
||||||
};
|
{
|
||||||
|
x,
|
||||||
|
y
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDragMove = (evt) => {
|
const onDragMove = (evt) => {
|
||||||
|
if ( this.dragInfo && this.dragInfo.dragging ) {
|
||||||
if ( dragInfo.dragging ) {
|
|
||||||
|
|
||||||
const x = evt.changedTouches
|
const x = evt.changedTouches
|
||||||
? evt.changedTouches[0].pageX
|
? evt.changedTouches[0].pageX
|
||||||
@ -212,12 +184,12 @@ class TopologyGraph extends React.Component {
|
|||||||
: evt.clientY;
|
: evt.clientY;
|
||||||
|
|
||||||
const offset = {
|
const offset = {
|
||||||
x: x - dragInfo.position.x,
|
x: x - this.dragInfo.position.x,
|
||||||
y: y - dragInfo.position.y
|
y: y - this.dragInfo.position.y
|
||||||
};
|
};
|
||||||
|
|
||||||
const dragNodes = nodes.map((simNode, index) => {
|
const dragNodes = nodes.map((simNode, index) => {
|
||||||
if ( simNode.id === dragInfo.nodeId ) {
|
if ( simNode.uuid === this.dragInfo.nodeUuid ) {
|
||||||
return ({
|
return ({
|
||||||
...simNode,
|
...simNode,
|
||||||
x: simNode.x + offset.x,
|
x: simNode.x + offset.x,
|
||||||
@ -233,25 +205,25 @@ class TopologyGraph extends React.Component {
|
|||||||
nodes: dragNodes
|
nodes: dragNodes
|
||||||
});
|
});
|
||||||
|
|
||||||
dragInfo.position = {
|
this.setDragInfo(
|
||||||
x,
|
true,
|
||||||
y
|
this.dragInfo.nodeUuid,
|
||||||
};
|
{
|
||||||
|
x,
|
||||||
|
y
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDragEnd = (evt) => {
|
const onDragEnd = (evt) => {
|
||||||
dragInfo = {
|
this.setDragInfo(false);
|
||||||
dragging: false,
|
|
||||||
nodeId: null,
|
|
||||||
position: {}
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onTitleClick = (serviceUUID) =>
|
const onTitleClick = (serviceUUID) =>
|
||||||
this.props.onNodeTitleClick(serviceUUID);
|
this.props.onNodeTitleClick(serviceUUID);
|
||||||
|
|
||||||
const renderedNodes = nodesData.map((n, index) => (
|
const renderedNode = (n, index) => (
|
||||||
<GraphNode
|
<GraphNode
|
||||||
key={index}
|
key={index}
|
||||||
data={n}
|
data={n}
|
||||||
@ -261,15 +233,54 @@ class TopologyGraph extends React.Component {
|
|||||||
onQuickActions={onQuickActions}
|
onQuickActions={onQuickActions}
|
||||||
connected={n.id !== 'consul'}
|
connected={n.id !== 'consul'}
|
||||||
/>
|
/>
|
||||||
));
|
);
|
||||||
|
|
||||||
const renderedLinks = linksData.map((l, index) => (
|
const renderedLink = (l, index) => (
|
||||||
<GraphLink
|
<GraphLink
|
||||||
key={index}
|
key={index}
|
||||||
data={l}
|
data={l}
|
||||||
index={index}
|
index={index}
|
||||||
/>
|
/>
|
||||||
));
|
);
|
||||||
|
|
||||||
|
const renderedLinkArrow = (l, index) => (
|
||||||
|
<GraphLinkArrow
|
||||||
|
key={index}
|
||||||
|
data={l}
|
||||||
|
index={index}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderedNodes = this.dragInfo && this.dragInfo.dragging ?
|
||||||
|
nodesData.filter((n, index) => n.uuid !== this.dragInfo.nodeUuid)
|
||||||
|
.map((n, index) => renderedNode(n, index)) :
|
||||||
|
nodesData.map((n, index) => renderedNode(n, index));
|
||||||
|
|
||||||
|
const renderedLinks = linksData.map((l, index) => renderedLink(l, index));
|
||||||
|
|
||||||
|
const renderedLinkArrows = this.dragInfo && this.dragInfo.dragging ?
|
||||||
|
linksData.filter((l, index) => l.target.uuid !== this.dragInfo.nodeUuid)
|
||||||
|
.map((l, index) => renderedLinkArrow(l, index)) :
|
||||||
|
linksData.map((l, index) => renderedLinkArrow(l, index));
|
||||||
|
|
||||||
|
const dragNode = !this.dragInfo || !this.dragInfo.dragging ? null :
|
||||||
|
renderedNode(
|
||||||
|
nodesData.reduce((dragNode, n, index) => {
|
||||||
|
if(n.uuid === this.dragInfo.nodeUuid) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
return dragNode;
|
||||||
|
}, {}));
|
||||||
|
|
||||||
|
const dragLinkArrow = !this.dragInfo || !this.dragInfo.dragging ||
|
||||||
|
renderedLinkArrows.length === renderedLinks.length ? null :
|
||||||
|
renderedLinkArrow(
|
||||||
|
linksData.reduce((dragLinkArrow, l, index) => {
|
||||||
|
if(l.target.uuid === this.dragInfo.nodeUuid) {
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
return dragLinkArrow;
|
||||||
|
}, {}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledSvg
|
<StyledSvg
|
||||||
@ -286,6 +297,15 @@ class TopologyGraph extends React.Component {
|
|||||||
<g>
|
<g>
|
||||||
{renderedLinks}
|
{renderedLinks}
|
||||||
</g>
|
</g>
|
||||||
|
<g>
|
||||||
|
{renderedLinkArrows}
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
{dragNode}
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
{dragLinkArrow}
|
||||||
|
</g>
|
||||||
</StyledSvg>
|
</StyledSvg>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user