mirror of
https://github.com/yldio/copilot.git
synced 2024-11-10 21:30:06 +02:00
Add primary and secondaryto topology and refactor
This commit is contained in:
parent
62bd05ffb8
commit
e71b1e6fde
67
ui/src/components/topology/constants.js
Normal file
67
ui/src/components/topology/constants.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
const Lengths = {
|
||||||
|
paddingLeft: 18,
|
||||||
|
nodeWidth: 180
|
||||||
|
};
|
||||||
|
|
||||||
|
const Sizes = {
|
||||||
|
buttonSize: {
|
||||||
|
width: 40,
|
||||||
|
height: 48
|
||||||
|
},
|
||||||
|
contentSize: {
|
||||||
|
width: Lengths.nodeWidth,
|
||||||
|
height: 101 // This is the height w/o info comp
|
||||||
|
},
|
||||||
|
nodeSize: {
|
||||||
|
width: Lengths.nodeWidth,
|
||||||
|
height: 156
|
||||||
|
},
|
||||||
|
nodeSizeWithChildren: {
|
||||||
|
width: Lengths.nodeWidth,
|
||||||
|
height: 276
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Points = {
|
||||||
|
buttonPosition: {
|
||||||
|
x: Lengths.nodeWidth - Sizes.buttonSize.width,
|
||||||
|
y: 0
|
||||||
|
},
|
||||||
|
contentPosition: {
|
||||||
|
x: 0,
|
||||||
|
y: Sizes.buttonSize.height
|
||||||
|
},
|
||||||
|
infoPosition: {
|
||||||
|
x: Lengths.paddingLeft,
|
||||||
|
y: 11
|
||||||
|
},
|
||||||
|
metricsPosition: {
|
||||||
|
x: Lengths.paddingLeft,
|
||||||
|
y: 41
|
||||||
|
},
|
||||||
|
subtitlePosition: {
|
||||||
|
x: Lengths.paddingLeft,
|
||||||
|
y: 23
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Rects = {
|
||||||
|
buttonRect: {
|
||||||
|
...Sizes.buttonSize,
|
||||||
|
...Points.buttonPosition
|
||||||
|
},
|
||||||
|
contentRect: {
|
||||||
|
...Sizes.contentSize,
|
||||||
|
...Points.contentPosition
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const Constants = {
|
||||||
|
...Lengths,
|
||||||
|
...Sizes,
|
||||||
|
...Points,
|
||||||
|
...Rects
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Constants;
|
@ -1,25 +1,11 @@
|
|||||||
import styled from 'styled-components';
|
import { Baseline } from '../../../shared/composers';
|
||||||
import { Baseline } from '../../shared/composers';
|
import Constants from '../constants';
|
||||||
import { colors } from '../../shared/constants';
|
|
||||||
import PropTypes from './prop-types';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import {
|
||||||
const StyledLine = styled.line`
|
GraphLinkLine,
|
||||||
stroke: ${colors.base.secondaryActive};
|
GraphLinkCircle,
|
||||||
stroke-width: 1.5;
|
GraphLinkArrow
|
||||||
`;
|
} from './shapes';
|
||||||
|
|
||||||
const StyledCircle = styled.circle`
|
|
||||||
stroke: ${colors.base.secondaryActive};
|
|
||||||
fill: ${colors.base.secondary};
|
|
||||||
stroke-width: 1.5;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledArrow = styled.line`
|
|
||||||
stroke: ${colors.base.white};
|
|
||||||
stroke-width: 2;
|
|
||||||
stroke-linecap: round;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const getAngleFromPoints = (source, target) => {
|
const getAngleFromPoints = (source, target) => {
|
||||||
|
|
||||||
@ -81,8 +67,7 @@ const getPositions = (halfWidth, halfHeight, halfCorner=0) => ([{
|
|||||||
|
|
||||||
const GraphLink = ({
|
const GraphLink = ({
|
||||||
data,
|
data,
|
||||||
index,
|
index
|
||||||
nodeSize
|
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -91,10 +76,11 @@ const GraphLink = ({
|
|||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
// actually, this will need to be got dynamically, in case them things are different sizes
|
// 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 {
|
const {
|
||||||
width,
|
width,
|
||||||
height
|
height
|
||||||
} = nodeSize;
|
} = Constants.nodeSize;
|
||||||
|
|
||||||
const halfWidth = width/2;
|
const halfWidth = width/2;
|
||||||
const halfHeight = height/2;
|
const halfHeight = height/2;
|
||||||
@ -109,7 +95,7 @@ const GraphLink = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<g>
|
<g>
|
||||||
<StyledLine
|
<GraphLinkLine
|
||||||
x1={sourcePosition.x}
|
x1={sourcePosition.x}
|
||||||
x2={targetPosition.x}
|
x2={targetPosition.x}
|
||||||
y1={sourcePosition.y}
|
y1={sourcePosition.y}
|
||||||
@ -121,18 +107,18 @@ const GraphLink = ({
|
|||||||
`translate(${targetPosition.x}, ${targetPosition.y}) rotate(${arrowAngle})`
|
`translate(${targetPosition.x}, ${targetPosition.y}) rotate(${arrowAngle})`
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<StyledCircle
|
<GraphLinkCircle
|
||||||
cx={0}
|
cx={0}
|
||||||
cy={0}
|
cy={0}
|
||||||
r={9}
|
r={9}
|
||||||
/>
|
/>
|
||||||
<StyledArrow
|
<GraphLinkArrow
|
||||||
x1={-1}
|
x1={-1}
|
||||||
x2={2}
|
x2={2}
|
||||||
y1={-3}
|
y1={-3}
|
||||||
y2={0}
|
y2={0}
|
||||||
/>
|
/>
|
||||||
<StyledArrow
|
<GraphLinkArrow
|
||||||
x1={-1}
|
x1={-1}
|
||||||
x2={2}
|
x2={2}
|
||||||
y1={3}
|
y1={3}
|
||||||
@ -145,8 +131,7 @@ const GraphLink = ({
|
|||||||
|
|
||||||
GraphLink.propTypes = {
|
GraphLink.propTypes = {
|
||||||
data: React.PropTypes.object.isRequired,
|
data: React.PropTypes.object.isRequired,
|
||||||
index: React.PropTypes.number.isRequired,
|
index: React.PropTypes.number.isRequired
|
||||||
nodeSize: PropTypes.Size
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Baseline(
|
export default Baseline(
|
19
ui/src/components/topology/graph-link/shapes.js
Normal file
19
ui/src/components/topology/graph-link/shapes.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
import { colors } from '../../../shared/constants';
|
||||||
|
|
||||||
|
export const GraphLinkLine = styled.line`
|
||||||
|
stroke: ${colors.base.secondaryActive};
|
||||||
|
stroke-width: 1.5;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GraphLinkCircle = styled.circle`
|
||||||
|
stroke: ${colors.base.secondaryActive};
|
||||||
|
fill: ${colors.base.secondary};
|
||||||
|
stroke-width: 1.5;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GraphLinkArrow = styled.line`
|
||||||
|
stroke: ${colors.base.white};
|
||||||
|
stroke-width: 2;
|
||||||
|
stroke-linecap: round;
|
||||||
|
`;
|
@ -1,65 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Baseline } from '../../shared/composers';
|
|
||||||
import { colors } from '../../shared/constants';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
const StyledButton = styled.rect`
|
|
||||||
opacity: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledButtonCircle = styled.circle`
|
|
||||||
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const GraphNodeButton = ({
|
|
||||||
connected,
|
|
||||||
buttonRect,
|
|
||||||
onButtonClick,
|
|
||||||
index
|
|
||||||
}) => {
|
|
||||||
|
|
||||||
const buttonCircleRadius = 2;
|
|
||||||
const buttonCircleSpacing = 2;
|
|
||||||
const buttonCircleY =
|
|
||||||
(buttonRect.height - buttonCircleRadius*4 - buttonCircleSpacing*2)/2;
|
|
||||||
const buttonCircles = [1,2,3].map((item, index) => (
|
|
||||||
<StyledButtonCircle
|
|
||||||
cx={buttonRect.width/2}
|
|
||||||
cy={buttonCircleY + (buttonCircleRadius*2 + buttonCircleSpacing)*index}
|
|
||||||
key={index}
|
|
||||||
r={2}
|
|
||||||
connected={connected}
|
|
||||||
/>
|
|
||||||
));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<g transform={`translate(${buttonRect.x}, ${buttonRect.y})`}>
|
|
||||||
<StyledButton
|
|
||||||
height={buttonRect.height}
|
|
||||||
onClick={onButtonClick}
|
|
||||||
onKeyDown={onButtonClick}
|
|
||||||
width={buttonRect.width}
|
|
||||||
role='button'
|
|
||||||
tabIndex={100 + index}
|
|
||||||
/>
|
|
||||||
{buttonCircles}
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
GraphNodeButton.propTypes = {
|
|
||||||
buttonRect: React.PropTypes.shape({
|
|
||||||
x: React.PropTypes.number,
|
|
||||||
y: React.PropTypes.number,
|
|
||||||
width: React.PropTypes.number,
|
|
||||||
height: React.PropTypes.number
|
|
||||||
}).isRequired,
|
|
||||||
connected: React.PropTypes.bool,
|
|
||||||
index: React.PropTypes.number.isRequired,
|
|
||||||
onButtonClick: React.PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Baseline(
|
|
||||||
GraphNodeButton
|
|
||||||
);
|
|
@ -1,69 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Baseline } from '../../shared/composers';
|
|
||||||
import { colors } from '../../shared/constants';
|
|
||||||
import DataCentresIcon from './icon-data-centers.svg';
|
|
||||||
import InstancesIcon from './icon-instances.svg';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
const StyledText = styled.text`
|
|
||||||
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
|
|
||||||
font-size: 12px;
|
|
||||||
opacity: 0.8;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledInstancesIcon = styled(InstancesIcon)`
|
|
||||||
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledDataCentresIcon = styled(DataCentresIcon)`
|
|
||||||
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const GraphNodeInfo = ({
|
|
||||||
connected,
|
|
||||||
datacentres,
|
|
||||||
instances,
|
|
||||||
healthy,
|
|
||||||
infoPosition
|
|
||||||
}) => {
|
|
||||||
|
|
||||||
return (
|
|
||||||
<g transform={`translate(${infoPosition.x}, ${infoPosition.y})`}>
|
|
||||||
<g transform={'translate(0, 2)'}>
|
|
||||||
<StyledInstancesIcon connected={connected} />
|
|
||||||
</g>
|
|
||||||
<StyledText
|
|
||||||
x={23}
|
|
||||||
y={12}
|
|
||||||
connected={connected}
|
|
||||||
>
|
|
||||||
{`${datacentres} inst.`}
|
|
||||||
</StyledText>
|
|
||||||
<g transform={'translate(82, 0)'}>
|
|
||||||
<StyledDataCentresIcon connected={connected} />
|
|
||||||
</g>
|
|
||||||
<StyledText
|
|
||||||
x={96}
|
|
||||||
y={12}
|
|
||||||
connected={connected}
|
|
||||||
>
|
|
||||||
{`${instances} DCs`}
|
|
||||||
</StyledText>
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
GraphNodeInfo.propTypes = {
|
|
||||||
connected: React.PropTypes.bool,
|
|
||||||
datacentres: React.PropTypes.number,
|
|
||||||
healthy: React.PropTypes.bool,
|
|
||||||
infoPosition: React.PropTypes.shape({
|
|
||||||
x: React.PropTypes.number,
|
|
||||||
y: React.PropTypes.number
|
|
||||||
}),
|
|
||||||
instances: React.PropTypes.number
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Baseline(
|
|
||||||
GraphNodeInfo
|
|
||||||
);
|
|
@ -1,51 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Baseline } from '../../shared/composers';
|
|
||||||
import { colors } from '../../shared/constants';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
const StyledText = styled.text`
|
|
||||||
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
|
|
||||||
font-size: 12px;
|
|
||||||
opacity: 0.8;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const GraphNodeMetrics = ({
|
|
||||||
connected,
|
|
||||||
metrics,
|
|
||||||
metricsPosition
|
|
||||||
}) => {
|
|
||||||
|
|
||||||
const metricSpacing = 18;
|
|
||||||
const metricsText = metrics.map((metric, index) => (
|
|
||||||
<StyledText
|
|
||||||
key={index}
|
|
||||||
x={0}
|
|
||||||
y={12 + metricSpacing*index}
|
|
||||||
connected={connected}
|
|
||||||
>
|
|
||||||
{`${metric.name}: ${metric.value}`}
|
|
||||||
</StyledText>
|
|
||||||
));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<g transform={`translate(${metricsPosition.x}, ${metricsPosition.y})`}>
|
|
||||||
{metricsText}
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
GraphNodeMetrics.propTypes = {
|
|
||||||
connected: React.PropTypes.bool,
|
|
||||||
metrics: React.PropTypes.arrayOf(React.PropTypes.shape({
|
|
||||||
name: React.PropTypes.string,
|
|
||||||
stat: React.PropTypes.string
|
|
||||||
})),
|
|
||||||
metricsPosition: React.PropTypes.shape({
|
|
||||||
x: React.PropTypes.number,
|
|
||||||
y: React.PropTypes.number
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Baseline(
|
|
||||||
GraphNodeMetrics
|
|
||||||
);
|
|
@ -1,188 +0,0 @@
|
|||||||
import { Baseline } from '../../shared/composers';
|
|
||||||
import { colors } from '../../shared/constants';
|
|
||||||
import PropTypes from './prop-types';
|
|
||||||
// import HeartIcon from './icon-heart.svg';
|
|
||||||
import GraphNodeButton from './graph-node-button';
|
|
||||||
// import GraphNodeInfo from './graph-node-info';
|
|
||||||
import GraphNodeMetrics from './graph-node-metrics';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const StyledRect = styled.rect`
|
|
||||||
stroke: ${props => props.connected ?
|
|
||||||
colors.base.secondaryActive : colors.base.grey};
|
|
||||||
fill: ${props => props.connected ? colors.base.secondary : colors.base.white};
|
|
||||||
stroke-width: 1.5;
|
|
||||||
rx: 4;
|
|
||||||
ry: 4;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledShadowRect = styled.rect`
|
|
||||||
fill: ${props => props.connected ? colors.base.secondary : colors.base.grey};
|
|
||||||
opacity: 0.33;
|
|
||||||
rx: 4;
|
|
||||||
ry: 4;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledLine = styled.line`
|
|
||||||
stroke: ${props => props.connected ?
|
|
||||||
colors.base.secondaryActive : colors.base.grey};
|
|
||||||
stroke-width: 1.5;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledText = styled.text`
|
|
||||||
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const HeartCircle = styled.circle`
|
|
||||||
fill: ${colors.base.green};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const GraphNode = ({
|
|
||||||
connected,
|
|
||||||
data,
|
|
||||||
index,
|
|
||||||
size,
|
|
||||||
onDragStart
|
|
||||||
}) => {
|
|
||||||
|
|
||||||
const {
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
} = size;
|
|
||||||
|
|
||||||
const halfWidth = width/2;
|
|
||||||
const halfHeight = height/2;
|
|
||||||
const lineY = 48;
|
|
||||||
const lineX = 140;
|
|
||||||
const buttonRect = {
|
|
||||||
x: lineX,
|
|
||||||
y: 0,
|
|
||||||
width: 40,
|
|
||||||
height: 48
|
|
||||||
};
|
|
||||||
|
|
||||||
const onButtonClick = (evt) => {
|
|
||||||
// console.log('Rect clicked!!!');
|
|
||||||
};
|
|
||||||
|
|
||||||
const paddingLeft = 18;
|
|
||||||
/*const infoPosition = {
|
|
||||||
x: paddingLeft,
|
|
||||||
y: 59
|
|
||||||
};*/
|
|
||||||
const metricsPosition = {
|
|
||||||
x: paddingLeft,
|
|
||||||
y: 89
|
|
||||||
};
|
|
||||||
|
|
||||||
// const titleBbBox = {x:100, y: 30 - halfHeight};
|
|
||||||
const onStart = (evt) => {
|
|
||||||
evt.preventDefault();
|
|
||||||
onDragStart(evt, data.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
const position = connected ? {
|
|
||||||
x: data.x-halfWidth,
|
|
||||||
y: data.y-halfHeight
|
|
||||||
} : {
|
|
||||||
x: data.x,
|
|
||||||
y: data.y
|
|
||||||
};
|
|
||||||
|
|
||||||
const nodeRect = connected ? (
|
|
||||||
<StyledRect
|
|
||||||
x={0}
|
|
||||||
y={0}
|
|
||||||
width={width}
|
|
||||||
height={height}
|
|
||||||
onMouseDown={onStart}
|
|
||||||
onTouchStart={onStart}
|
|
||||||
connected={connected}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<StyledRect
|
|
||||||
x={0}
|
|
||||||
y={0}
|
|
||||||
width={width}
|
|
||||||
height={height}
|
|
||||||
connected={connected}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<g transform={`translate(${position.x}, ${position.y})`}>
|
|
||||||
<StyledShadowRect
|
|
||||||
x={0}
|
|
||||||
y={3}
|
|
||||||
width={width}
|
|
||||||
height={height}
|
|
||||||
connected={connected}
|
|
||||||
/>
|
|
||||||
{nodeRect}
|
|
||||||
<StyledLine
|
|
||||||
x1={0}
|
|
||||||
y1={lineY}
|
|
||||||
x2={width}
|
|
||||||
y2={lineY}
|
|
||||||
connected={connected}
|
|
||||||
/>
|
|
||||||
<StyledLine
|
|
||||||
x1={lineX}
|
|
||||||
y1={0}
|
|
||||||
x2={lineX}
|
|
||||||
y2={lineY}
|
|
||||||
connected={connected}
|
|
||||||
/>
|
|
||||||
<StyledText
|
|
||||||
x={paddingLeft}
|
|
||||||
y={30}
|
|
||||||
connected={connected}
|
|
||||||
>
|
|
||||||
{data.name}
|
|
||||||
</StyledText>
|
|
||||||
<g transform={`translate(${115}, ${15})`}>
|
|
||||||
<HeartCircle
|
|
||||||
cx={9}
|
|
||||||
cy={9}
|
|
||||||
r={9}
|
|
||||||
/>
|
|
||||||
{/*<HeartIcon />*/}
|
|
||||||
</g>
|
|
||||||
<GraphNodeButton
|
|
||||||
buttonRect={buttonRect}
|
|
||||||
onButtonClick={onButtonClick}
|
|
||||||
index={index}
|
|
||||||
connected={connected}
|
|
||||||
/>
|
|
||||||
{/*<GraphNodeInfo
|
|
||||||
datacentres={data.datacentres}
|
|
||||||
instances={data.instances}
|
|
||||||
healthy
|
|
||||||
infoPosition={infoPosition}
|
|
||||||
connected={connected}
|
|
||||||
/>*/}
|
|
||||||
<GraphNodeMetrics
|
|
||||||
metrics={data.metrics}
|
|
||||||
metricsPosition={metricsPosition}
|
|
||||||
connected={connected}
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
GraphNode.propTypes = {
|
|
||||||
connected: React.PropTypes.bool,
|
|
||||||
data: React.PropTypes.object.isRequired,
|
|
||||||
index: React.PropTypes.number.isRequired,
|
|
||||||
onDragStart: React.PropTypes.func,
|
|
||||||
size: PropTypes.Size
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Baseline(
|
|
||||||
GraphNode
|
|
||||||
);
|
|
63
ui/src/components/topology/graph-node/button.js
Normal file
63
ui/src/components/topology/graph-node/button.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Baseline } from '../../../shared/composers';
|
||||||
|
import Constants from '../constants';
|
||||||
|
import { GraphLine, GraphButtonRect, GraphButtonCircle } from './shapes';
|
||||||
|
|
||||||
|
const GraphNodeButton = ({
|
||||||
|
connected,
|
||||||
|
onButtonClick,
|
||||||
|
index
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
} = Constants.buttonRect;
|
||||||
|
|
||||||
|
const buttonCircleRadius = 2;
|
||||||
|
const buttonCircleSpacing = 2;
|
||||||
|
const buttonCircleY =
|
||||||
|
(height - buttonCircleRadius*4 - buttonCircleSpacing*2)/2;
|
||||||
|
const buttonCircles = [1,2,3].map((item, index) => (
|
||||||
|
<GraphButtonCircle
|
||||||
|
cx={width/2}
|
||||||
|
cy={buttonCircleY + (buttonCircleRadius*2 + buttonCircleSpacing)*index}
|
||||||
|
key={index}
|
||||||
|
r={2}
|
||||||
|
connected={connected}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<g transform={`translate(${x}, ${y})`}>
|
||||||
|
<GraphLine
|
||||||
|
x1={0}
|
||||||
|
y1={0}
|
||||||
|
x2={0}
|
||||||
|
y2={height}
|
||||||
|
connected={connected}
|
||||||
|
/>
|
||||||
|
<GraphButtonRect
|
||||||
|
height={height}
|
||||||
|
onClick={onButtonClick}
|
||||||
|
onKeyDown={onButtonClick}
|
||||||
|
width={width}
|
||||||
|
role='button'
|
||||||
|
tabIndex={100 + index}
|
||||||
|
/>
|
||||||
|
{buttonCircles}
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
GraphNodeButton.propTypes = {
|
||||||
|
connected: React.PropTypes.bool,
|
||||||
|
index: React.PropTypes.number.isRequired,
|
||||||
|
onButtonClick: React.PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Baseline(
|
||||||
|
GraphNodeButton
|
||||||
|
);
|
84
ui/src/components/topology/graph-node/content.js
Normal file
84
ui/src/components/topology/graph-node/content.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Baseline } from '../../../shared/composers';
|
||||||
|
import Constants from '../constants';
|
||||||
|
import { GraphLine, GraphSubtitle } from './shapes';
|
||||||
|
import GraphNodeInfo from './info';
|
||||||
|
import GraphNodeMetrics from './metrics';
|
||||||
|
|
||||||
|
const GraphNodeContent = ({
|
||||||
|
connected,
|
||||||
|
child=false,
|
||||||
|
data,
|
||||||
|
index=0
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
} = Constants.contentRect;
|
||||||
|
|
||||||
|
const contentY = y + height*index;
|
||||||
|
|
||||||
|
const offset = index ? 18 : - 6;
|
||||||
|
|
||||||
|
const nodeInfoPos = child ? {
|
||||||
|
x: Constants.infoPosition.x,
|
||||||
|
y: Constants.infoPosition.y + offset
|
||||||
|
} : Constants.infoPosition;
|
||||||
|
|
||||||
|
const nodeMetricsPos = child ? {
|
||||||
|
x: Constants.metricsPosition.x,
|
||||||
|
y: Constants.metricsPosition.y + offset
|
||||||
|
} : Constants.metricsPosition;
|
||||||
|
|
||||||
|
const nodeSubtitle = child ? (
|
||||||
|
<GraphSubtitle
|
||||||
|
{...Constants.subtitlePosition}
|
||||||
|
connected={connected}
|
||||||
|
>
|
||||||
|
{data.name}
|
||||||
|
</GraphSubtitle>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
const nodeInfo = !child || index ? (
|
||||||
|
<GraphNodeInfo
|
||||||
|
datacentres={data.datacentres}
|
||||||
|
instances={data.instances}
|
||||||
|
healthy
|
||||||
|
connected={connected}
|
||||||
|
pos={nodeInfoPos}
|
||||||
|
/>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<g transform={`translate(${x}, ${contentY})`}>
|
||||||
|
<GraphLine
|
||||||
|
x1={0}
|
||||||
|
y1={0}
|
||||||
|
x2={width}
|
||||||
|
y2={0}
|
||||||
|
connected={connected}
|
||||||
|
/>
|
||||||
|
{nodeSubtitle}
|
||||||
|
{nodeInfo}
|
||||||
|
<GraphNodeMetrics
|
||||||
|
metrics={data.metrics}
|
||||||
|
connected={connected}
|
||||||
|
pos={nodeMetricsPos}
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
GraphNodeContent.propTypes = {
|
||||||
|
child: React.PropTypes.bool,
|
||||||
|
connected: React.PropTypes.bool,
|
||||||
|
data: React.PropTypes.object.isRequired,
|
||||||
|
index: React.PropTypes.number
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Baseline(
|
||||||
|
GraphNodeContent
|
||||||
|
);
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
106
ui/src/components/topology/graph-node/index.js
Normal file
106
ui/src/components/topology/graph-node/index.js
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Baseline } from '../../../shared/composers';
|
||||||
|
import Constants from '../constants';
|
||||||
|
import GraphNodeTitle from './title';
|
||||||
|
import GraphNodeButton from './button';
|
||||||
|
import GraphNodeContent from './content';
|
||||||
|
import {
|
||||||
|
GraphNodeRect,
|
||||||
|
GraphShadowRect
|
||||||
|
} from './shapes';
|
||||||
|
|
||||||
|
const GraphNode = ({
|
||||||
|
connected,
|
||||||
|
data,
|
||||||
|
index,
|
||||||
|
onDragStart
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const {
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
} = data.children ?
|
||||||
|
Constants.nodeSizeWithChildren :
|
||||||
|
Constants.nodeSize;
|
||||||
|
|
||||||
|
const halfWidth = width/2;
|
||||||
|
const halfHeight = height/2;
|
||||||
|
|
||||||
|
let x = 0;
|
||||||
|
let y = 0;
|
||||||
|
if(connected) {
|
||||||
|
x = data.x-halfWidth;
|
||||||
|
y = data.y-halfHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
const onButtonClick = (evt) => {
|
||||||
|
// console.log('Rect clicked!!!');
|
||||||
|
};
|
||||||
|
|
||||||
|
const onStart = (evt) => {
|
||||||
|
evt.preventDefault();
|
||||||
|
onDragStart(evt, data.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const nodeRectEvents = connected ? {
|
||||||
|
onMouseDown: onStart,
|
||||||
|
onTouchStart: onStart
|
||||||
|
} : {};
|
||||||
|
|
||||||
|
const nodeContent = data.children ?
|
||||||
|
data.children.map((d, i) => (
|
||||||
|
<GraphNodeContent
|
||||||
|
key={i}
|
||||||
|
child
|
||||||
|
connected={connected}
|
||||||
|
data={d}
|
||||||
|
index={i}
|
||||||
|
/>
|
||||||
|
)) : (
|
||||||
|
<GraphNodeContent
|
||||||
|
connected={connected}
|
||||||
|
data={data}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<g transform={`translate(${x}, ${y})`}>
|
||||||
|
<GraphShadowRect
|
||||||
|
x={0}
|
||||||
|
y={3}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
connected={connected}
|
||||||
|
/>
|
||||||
|
<GraphNodeRect
|
||||||
|
x={0}
|
||||||
|
y={0}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
connected={connected}
|
||||||
|
{...nodeRectEvents}
|
||||||
|
/>
|
||||||
|
<GraphNodeTitle
|
||||||
|
connected={connected}
|
||||||
|
data={data}
|
||||||
|
/>
|
||||||
|
<GraphNodeButton
|
||||||
|
connected={connected}
|
||||||
|
index={index}
|
||||||
|
onButtonClick={onButtonClick}
|
||||||
|
/>
|
||||||
|
{nodeContent}
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
GraphNode.propTypes = {
|
||||||
|
connected: React.PropTypes.bool,
|
||||||
|
data: React.PropTypes.object.isRequired,
|
||||||
|
index: React.PropTypes.number.isRequired,
|
||||||
|
onDragStart: React.PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Baseline(
|
||||||
|
GraphNode
|
||||||
|
);
|
67
ui/src/components/topology/graph-node/info.js
Normal file
67
ui/src/components/topology/graph-node/info.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import React from 'react';
|
||||||
|
//import styled from 'styled-components';
|
||||||
|
import { Baseline } from '../../../shared/composers';
|
||||||
|
//import { colors } from '../../../shared/constants';
|
||||||
|
//import DataCentresIcon from './icon-data-centers.svg';
|
||||||
|
//import InstancesIcon from './icon-instances.svg';
|
||||||
|
import PropTypes from '../prop-types';
|
||||||
|
import { GraphText } from './shapes';
|
||||||
|
|
||||||
|
/*const StyledInstancesIcon = styled(InstancesIcon)`
|
||||||
|
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledDataCentresIcon = styled(DataCentresIcon)`
|
||||||
|
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
|
||||||
|
`;*/
|
||||||
|
|
||||||
|
const GraphNodeInfo = ({
|
||||||
|
connected,
|
||||||
|
datacentres,
|
||||||
|
instances,
|
||||||
|
healthy,
|
||||||
|
pos
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const {
|
||||||
|
x,
|
||||||
|
y
|
||||||
|
} = pos;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<g transform={`translate(${x}, ${y})`}>
|
||||||
|
<g transform={'translate(0, 2)'}>
|
||||||
|
{/*}<StyledInstancesIcon connected={connected} />*}
|
||||||
|
</g>
|
||||||
|
<GraphText
|
||||||
|
x={23}
|
||||||
|
y={12}
|
||||||
|
connected={connected}
|
||||||
|
>
|
||||||
|
{`${datacentres} inst.`}
|
||||||
|
</GraphText>
|
||||||
|
<g transform={'translate(82, 0)'}>
|
||||||
|
{/*<StyledDataCentresIcon connected={connected} />*/}
|
||||||
|
</g>
|
||||||
|
<GraphText
|
||||||
|
x={96}
|
||||||
|
y={12}
|
||||||
|
connected={connected}
|
||||||
|
>
|
||||||
|
{`${instances} DCs`}
|
||||||
|
</GraphText>
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
GraphNodeInfo.propTypes = {
|
||||||
|
connected: React.PropTypes.bool,
|
||||||
|
datacentres: React.PropTypes.number,
|
||||||
|
healthy: React.PropTypes.bool,
|
||||||
|
instances: React.PropTypes.number,
|
||||||
|
pos: PropTypes.Point.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Baseline(
|
||||||
|
GraphNodeInfo
|
||||||
|
);
|
47
ui/src/components/topology/graph-node/metrics.js
Normal file
47
ui/src/components/topology/graph-node/metrics.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Baseline } from '../../../shared/composers';
|
||||||
|
import PropTypes from '../prop-types';
|
||||||
|
import { GraphText } from './shapes';
|
||||||
|
|
||||||
|
const GraphNodeMetrics = ({
|
||||||
|
connected,
|
||||||
|
metrics,
|
||||||
|
pos
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const {
|
||||||
|
x,
|
||||||
|
y
|
||||||
|
} = pos;
|
||||||
|
|
||||||
|
const metricSpacing = 18;
|
||||||
|
const metricsText = metrics.map((metric, index) => (
|
||||||
|
<GraphText
|
||||||
|
key={index}
|
||||||
|
x={0}
|
||||||
|
y={12 + metricSpacing*index}
|
||||||
|
connected={connected}
|
||||||
|
>
|
||||||
|
{`${metric.name}: ${metric.value}`}
|
||||||
|
</GraphText>
|
||||||
|
));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<g transform={`translate(${x}, ${y})`}>
|
||||||
|
{metricsText}
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
GraphNodeMetrics.propTypes = {
|
||||||
|
connected: React.PropTypes.bool,
|
||||||
|
metrics: React.PropTypes.arrayOf(React.PropTypes.shape({
|
||||||
|
name: React.PropTypes.string.isRequired,
|
||||||
|
value: React.PropTypes.string.isRequired
|
||||||
|
})),
|
||||||
|
pos: PropTypes.Point.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Baseline(
|
||||||
|
GraphNodeMetrics
|
||||||
|
);
|
55
ui/src/components/topology/graph-node/shapes.js
Normal file
55
ui/src/components/topology/graph-node/shapes.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
import { colors } from '../../../shared/constants';
|
||||||
|
|
||||||
|
export const GraphLine = styled.line`
|
||||||
|
stroke: ${props => props.connected ?
|
||||||
|
colors.base.secondaryActive : colors.base.grey};
|
||||||
|
stroke-width: 1.5;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GraphNodeRect = styled.rect`
|
||||||
|
stroke: ${props => props.connected ?
|
||||||
|
colors.base.secondaryActive : colors.base.grey};
|
||||||
|
fill: ${props => props.connected ? colors.base.secondary : colors.base.white};
|
||||||
|
stroke-width: 1.5;
|
||||||
|
rx: 4;
|
||||||
|
ry: 4;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GraphShadowRect = styled.rect`
|
||||||
|
fill: ${props => props.connected ? colors.base.secondary : colors.base.grey};
|
||||||
|
opacity: 0.33;
|
||||||
|
rx: 4;
|
||||||
|
ry: 4;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GraphTitle = styled.text`
|
||||||
|
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GraphSubtitle = styled.text`
|
||||||
|
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GraphText = styled.text`
|
||||||
|
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
|
||||||
|
font-size: 12px;
|
||||||
|
opacity: 0.8;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GraphButtonRect = styled.rect`
|
||||||
|
opacity: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GraphButtonCircle = styled.circle`
|
||||||
|
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GraphHealthyCircle = styled.circle`
|
||||||
|
fill: ${colors.base.green};
|
||||||
|
`;
|
40
ui/src/components/topology/graph-node/title.js
Normal file
40
ui/src/components/topology/graph-node/title.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Baseline } from '../../../shared/composers';
|
||||||
|
import Constants from '../constants';
|
||||||
|
import { GraphTitle, GraphHealthyCircle } from './shapes';
|
||||||
|
// import HeartIcon from './icon-heart.svg';
|
||||||
|
|
||||||
|
const GraphNodeTitle = ({
|
||||||
|
connected,
|
||||||
|
data
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<g>
|
||||||
|
<GraphTitle
|
||||||
|
x={Constants.paddingLeft}
|
||||||
|
y={30}
|
||||||
|
connected={connected}
|
||||||
|
>
|
||||||
|
{data.name}
|
||||||
|
</GraphTitle>
|
||||||
|
<g transform={`translate(${115}, ${15})`}>
|
||||||
|
<GraphHealthyCircle
|
||||||
|
cx={9}
|
||||||
|
cy={9}
|
||||||
|
r={9}
|
||||||
|
/>
|
||||||
|
{/*<HeartIcon />*/}
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
GraphNodeTitle.propTypes = {
|
||||||
|
connected: React.PropTypes.bool,
|
||||||
|
data: React.PropTypes.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Baseline(
|
||||||
|
GraphNodeTitle
|
||||||
|
);
|
@ -1,13 +1,9 @@
|
|||||||
import TopologyGraph from './topology-graph';
|
import TopologyGraph from './topology-graph';
|
||||||
import TopologyGraphNode from './graph-node';
|
import TopologyGraphNode from './graph-node';
|
||||||
import TopologyGraphLink from './graph-link';
|
import TopologyGraphLink from './graph-link';
|
||||||
import TopologyGraphNodeButton from './graph-node-button';
|
|
||||||
import TopologyGraphNodeMetrics from './graph-node-metrics';
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
TopologyGraph,
|
TopologyGraph,
|
||||||
TopologyGraphNode,
|
TopologyGraphNode,
|
||||||
TopologyGraphLink,
|
TopologyGraphLink
|
||||||
TopologyGraphNodeButton,
|
|
||||||
TopologyGraphNodeMetrics
|
|
||||||
};
|
};
|
||||||
|
@ -26,13 +26,7 @@ storiesOf('Topology', module)
|
|||||||
}, {
|
}, {
|
||||||
name: 'Network',
|
name: 'Network',
|
||||||
stat: '5.9KB/sec'
|
stat: '5.9KB/sec'
|
||||||
}],
|
}]
|
||||||
x: 0,
|
|
||||||
y: 0
|
|
||||||
}}
|
|
||||||
size={{
|
|
||||||
width: 180,
|
|
||||||
height: 156
|
|
||||||
}}
|
}}
|
||||||
connected={false}
|
connected={false}
|
||||||
/>
|
/>
|
||||||
|
@ -111,19 +111,23 @@ class TopologyGraph extends React.Component {
|
|||||||
links
|
links
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const node = (nodeId) =>
|
const simNode = (nodeId) =>
|
||||||
nodes.reduce((acc, simNode, index) => {
|
nodes.reduce((acc, simNode, index) =>
|
||||||
return simNode.id === nodeId ? simNode : acc;
|
simNode.id === nodeId ? simNode : acc, {});
|
||||||
}, {});
|
|
||||||
|
|
||||||
const nodesData = services.map((service, index) => ({
|
const nodesData = services.map((service, index) => ({
|
||||||
...service,
|
...service,
|
||||||
...node(service.uuid)
|
...simNode(service.uuid)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const nodeData = (nodeId) =>
|
||||||
|
nodesData.reduce((acc, nodeData, index) =>
|
||||||
|
nodeData.id === nodeId ? nodeData : acc, {});
|
||||||
|
// TODO links will need to know whether a service has children
|
||||||
|
// if it does, the height of it will be different
|
||||||
const linksData = links.map((link, index) => ({
|
const linksData = links.map((link, index) => ({
|
||||||
source: node(link.source.id),
|
source: nodeData(link.source.id),
|
||||||
target: node(link.target.id)
|
target: nodeData(link.target.id)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const onDragStart = (evt, nodeId) => {
|
const onDragStart = (evt, nodeId) => {
|
||||||
@ -207,7 +211,6 @@ class TopologyGraph extends React.Component {
|
|||||||
key={index}
|
key={index}
|
||||||
data={n}
|
data={n}
|
||||||
index={index}
|
index={index}
|
||||||
size={nodeSize}
|
|
||||||
onDragStart={onDragStart}
|
onDragStart={onDragStart}
|
||||||
connected
|
connected
|
||||||
/>
|
/>
|
||||||
@ -218,7 +221,6 @@ class TopologyGraph extends React.Component {
|
|||||||
key={index}
|
key={index}
|
||||||
data={l}
|
data={l}
|
||||||
index={index}
|
index={index}
|
||||||
nodeSize={nodeSize}
|
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user