mirror of
https://github.com/yldio/copilot.git
synced 2024-11-28 14:10:04 +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 { colors } from '../../shared/constants';
|
||||
import PropTypes from './prop-types';
|
||||
import { Baseline } from '../../../shared/composers';
|
||||
import Constants from '../constants';
|
||||
import React from 'react';
|
||||
|
||||
const StyledLine = styled.line`
|
||||
stroke: ${colors.base.secondaryActive};
|
||||
stroke-width: 1.5;
|
||||
`;
|
||||
|
||||
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;
|
||||
`;
|
||||
import {
|
||||
GraphLinkLine,
|
||||
GraphLinkCircle,
|
||||
GraphLinkArrow
|
||||
} from './shapes';
|
||||
|
||||
const getAngleFromPoints = (source, target) => {
|
||||
|
||||
@ -81,8 +67,7 @@ const getPositions = (halfWidth, halfHeight, halfCorner=0) => ([{
|
||||
|
||||
const GraphLink = ({
|
||||
data,
|
||||
index,
|
||||
nodeSize
|
||||
index
|
||||
}) => {
|
||||
|
||||
const {
|
||||
@ -91,10 +76,11 @@ const GraphLink = ({
|
||||
} = 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 {
|
||||
width,
|
||||
height
|
||||
} = nodeSize;
|
||||
} = Constants.nodeSize;
|
||||
|
||||
const halfWidth = width/2;
|
||||
const halfHeight = height/2;
|
||||
@ -109,7 +95,7 @@ const GraphLink = ({
|
||||
|
||||
return (
|
||||
<g>
|
||||
<StyledLine
|
||||
<GraphLinkLine
|
||||
x1={sourcePosition.x}
|
||||
x2={targetPosition.x}
|
||||
y1={sourcePosition.y}
|
||||
@ -121,18 +107,18 @@ const GraphLink = ({
|
||||
`translate(${targetPosition.x}, ${targetPosition.y}) rotate(${arrowAngle})`
|
||||
}
|
||||
>
|
||||
<StyledCircle
|
||||
<GraphLinkCircle
|
||||
cx={0}
|
||||
cy={0}
|
||||
r={9}
|
||||
/>
|
||||
<StyledArrow
|
||||
<GraphLinkArrow
|
||||
x1={-1}
|
||||
x2={2}
|
||||
y1={-3}
|
||||
y2={0}
|
||||
/>
|
||||
<StyledArrow
|
||||
<GraphLinkArrow
|
||||
x1={-1}
|
||||
x2={2}
|
||||
y1={3}
|
||||
@ -145,8 +131,7 @@ const GraphLink = ({
|
||||
|
||||
GraphLink.propTypes = {
|
||||
data: React.PropTypes.object.isRequired,
|
||||
index: React.PropTypes.number.isRequired,
|
||||
nodeSize: PropTypes.Size
|
||||
index: React.PropTypes.number.isRequired
|
||||
};
|
||||
|
||||
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 TopologyGraphNode from './graph-node';
|
||||
import TopologyGraphLink from './graph-link';
|
||||
import TopologyGraphNodeButton from './graph-node-button';
|
||||
import TopologyGraphNodeMetrics from './graph-node-metrics';
|
||||
|
||||
export {
|
||||
TopologyGraph,
|
||||
TopologyGraphNode,
|
||||
TopologyGraphLink,
|
||||
TopologyGraphNodeButton,
|
||||
TopologyGraphNodeMetrics
|
||||
TopologyGraphLink
|
||||
};
|
||||
|
@ -26,13 +26,7 @@ storiesOf('Topology', module)
|
||||
}, {
|
||||
name: 'Network',
|
||||
stat: '5.9KB/sec'
|
||||
}],
|
||||
x: 0,
|
||||
y: 0
|
||||
}}
|
||||
size={{
|
||||
width: 180,
|
||||
height: 156
|
||||
}]
|
||||
}}
|
||||
connected={false}
|
||||
/>
|
||||
|
@ -27,7 +27,7 @@ let dragInfo = {
|
||||
};
|
||||
|
||||
class TopologyGraph extends React.Component {
|
||||
|
||||
|
||||
componentWillMount() {
|
||||
const services = this.props.services;
|
||||
|
||||
@ -111,19 +111,23 @@ class TopologyGraph extends React.Component {
|
||||
links
|
||||
} = this.state;
|
||||
|
||||
const node = (nodeId) =>
|
||||
nodes.reduce((acc, simNode, index) => {
|
||||
return simNode.id === nodeId ? simNode : acc;
|
||||
}, {});
|
||||
const simNode = (nodeId) =>
|
||||
nodes.reduce((acc, simNode, index) =>
|
||||
simNode.id === nodeId ? simNode : acc, {});
|
||||
|
||||
const nodesData = services.map((service, index) => ({
|
||||
...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) => ({
|
||||
source: node(link.source.id),
|
||||
target: node(link.target.id)
|
||||
source: nodeData(link.source.id),
|
||||
target: nodeData(link.target.id)
|
||||
}));
|
||||
|
||||
const onDragStart = (evt, nodeId) => {
|
||||
@ -207,7 +211,6 @@ class TopologyGraph extends React.Component {
|
||||
key={index}
|
||||
data={n}
|
||||
index={index}
|
||||
size={nodeSize}
|
||||
onDragStart={onDragStart}
|
||||
connected
|
||||
/>
|
||||
@ -218,7 +221,6 @@ class TopologyGraph extends React.Component {
|
||||
key={index}
|
||||
data={l}
|
||||
index={index}
|
||||
nodeSize={nodeSize}
|
||||
/>
|
||||
));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user