mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-03 02:00:43 +00:00
Adds node-shapes to the canvas
- circle - rect (w/ radius) - fluffy cloud shape - hexagons - stacks
This commit is contained in:
14
client/app/scripts/charts/node-shape-circle.js
Normal file
14
client/app/scripts/charts/node-shape-circle.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function NodeShapeCircle({highlighted, size, color}) {
|
||||
return (
|
||||
<g className="shape">
|
||||
{highlighted &&
|
||||
<circle r={size * 0.7} className="highlighted"></circle>}
|
||||
|
||||
<circle r={size * 0.5} className="border" stroke={color}></circle>
|
||||
<circle r={size * 0.45} className="shadow"></circle>
|
||||
<circle r={Math.max(2, size * 0.125)} className="node"></circle>
|
||||
</g>
|
||||
);
|
||||
}
|
||||
41
client/app/scripts/charts/node-shape-cloud.js
Normal file
41
client/app/scripts/charts/node-shape-cloud.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import d3 from 'd3';
|
||||
|
||||
const CLOUD_PATH = 'M 1920,384 Q 1920,225 1807.5,112.5 1695,0 1536,0 H 448 Q 263,0 131.5,131.5 0,263 0,448 0,580 71,689.5 142,799 258,853 q -2,28 -2,43 0,212 150,362 150,150 362,150 158,0 286.5,-88 128.5,-88 187.5,-230 70,62 166,62 106,0 181,-75 75,-75 75,-181 0,-75 -41,-138 129,-30 213,-134.5 84,-104.5 84,-239.5 z';
|
||||
|
||||
function toPoint(stringPair) {
|
||||
return stringPair.split(',').map(p => parseFloat(p, 10));
|
||||
}
|
||||
|
||||
function getExtents(svgPath) {
|
||||
const points = svgPath.split(' ').filter(s => s.length > 1).map(toPoint);
|
||||
return [d3.extent(points, p => p[0]), d3.extent(points, p => p[1])];
|
||||
}
|
||||
|
||||
export default function NodeShapeCloud({highlighted, size, color}) {
|
||||
const [[minx, maxx], [miny, maxy]] = getExtents(CLOUD_PATH);
|
||||
const width = (maxx - minx);
|
||||
const height = (maxy - miny);
|
||||
const cx = width / 2;
|
||||
const cy = height / 2;
|
||||
const pathSize = (width + height) / 2;
|
||||
const baseScale = (size * 2) / pathSize;
|
||||
|
||||
const pathProps = (v) => {
|
||||
return {
|
||||
d: CLOUD_PATH,
|
||||
transform: `scale(-${v * baseScale}) translate(-${cx},-${cy})`,
|
||||
style: {strokeWidth: 1 / baseScale}
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<g className="shape">
|
||||
{highlighted &&
|
||||
<path className="highlighted" {...pathProps(0.7)} />}
|
||||
<path className="border" stroke={color} {...pathProps(0.5)} />
|
||||
<path className="shadow" {...pathProps(0.45)} />
|
||||
<circle className="node" r={Math.max(2, (size * 0.125))} />
|
||||
</g>
|
||||
);
|
||||
}
|
||||
44
client/app/scripts/charts/node-shape-hex.js
Normal file
44
client/app/scripts/charts/node-shape-hex.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import d3 from 'd3';
|
||||
|
||||
const line = d3.svg.line()
|
||||
.interpolate('cardinal-closed')
|
||||
.tension(0.25);
|
||||
|
||||
function getWidth(h) {
|
||||
return (Math.sqrt(3) / 2) * h;
|
||||
}
|
||||
|
||||
function getPoints(h) {
|
||||
const w = getWidth(h);
|
||||
const points = [
|
||||
[w * 0.5, 0],
|
||||
[w, 0.25 * h],
|
||||
[w, 0.75 * h],
|
||||
[w * 0.5, h],
|
||||
[0, 0.75 * h],
|
||||
[0, 0.25 * h]
|
||||
];
|
||||
|
||||
return line(points);
|
||||
}
|
||||
|
||||
|
||||
export default function NodeShapeHex({highlighted, size, color}) {
|
||||
const pathProps = (v) => {
|
||||
return {
|
||||
d: getPoints(size * v * 2),
|
||||
transform: `rotate(90) translate(-${size * getWidth(v)}, -${size * v})`
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<g className="shape">
|
||||
{highlighted &&
|
||||
<path className="highlighted" {...pathProps(0.7)} />}
|
||||
<path className="border" stroke={color} {...pathProps(0.5)} />
|
||||
<path className="shadow" {...pathProps(0.45)} />
|
||||
<circle className="node" r={Math.max(2, (size * 0.125))} />
|
||||
</g>
|
||||
);
|
||||
}
|
||||
10
client/app/scripts/charts/node-shape-rounded-square.js
Normal file
10
client/app/scripts/charts/node-shape-rounded-square.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import NodeShapeSquare from './node-shape-square';
|
||||
|
||||
// TODO how to express a cmp in terms of another cmp? (Rather than a sub-cmp as here).
|
||||
|
||||
export default function NodeShapeRoundedSquare(props) {
|
||||
return (
|
||||
<NodeShapeSquare {...props} rx="0.4" ry="0.4" />
|
||||
);
|
||||
}
|
||||
24
client/app/scripts/charts/node-shape-square.js
Normal file
24
client/app/scripts/charts/node-shape-square.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function NodeShapeSquare({highlighted, size, color, rx = 0, ry = 0}) {
|
||||
const rectProps = (v) => {
|
||||
return {
|
||||
width: v * size * 2,
|
||||
height: v * size * 2,
|
||||
rx: v * size * rx,
|
||||
ry: v * size * ry,
|
||||
transform: `translate(-${size * v}, -${size * v})`
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<g className="shape">
|
||||
{highlighted &&
|
||||
<rect className="highlighted" {...rectProps(0.7)} />}
|
||||
|
||||
<rect className="border" stroke={color} {...rectProps(0.5)} />
|
||||
<rect className="shadow" {...rectProps(0.45)} />
|
||||
<circle className="node" r={Math.max(2, (size * 0.125))} />
|
||||
</g>
|
||||
);
|
||||
}
|
||||
19
client/app/scripts/charts/node-shape-stack.js
Normal file
19
client/app/scripts/charts/node-shape-stack.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
|
||||
export default function NodeShapeCircleStack(props) {
|
||||
const propsNoHighlight = _.clone(props);
|
||||
const Shape = props.shape;
|
||||
delete propsNoHighlight.highlighted;
|
||||
return (
|
||||
<g className="stack">
|
||||
<g transform="translate(0, 4)">
|
||||
<Shape {...propsNoHighlight} />
|
||||
</g>
|
||||
<Shape {...props} />
|
||||
<g transform="translate(0, -4)">
|
||||
<Shape {...propsNoHighlight} />
|
||||
</g>
|
||||
</g>
|
||||
);
|
||||
}
|
||||
@@ -5,6 +5,42 @@ import { Motion, spring } from 'react-motion';
|
||||
import { clickNode, enterNode, leaveNode } from '../actions/app-actions';
|
||||
import { getNodeColor } from '../utils/color-utils';
|
||||
|
||||
import NodeShapeCircle from './node-shape-circle';
|
||||
import NodeShapeStack from './node-shape-stack';
|
||||
import NodeShapeRoundedSquare from './node-shape-rounded-square';
|
||||
import NodeShapeHex from './node-shape-hex';
|
||||
import NodeShapeCloud from './node-shape-cloud';
|
||||
|
||||
function stackedShape(Shape) {
|
||||
const factory = React.createFactory(NodeShapeStack);
|
||||
|
||||
return function(props) {
|
||||
return factory(Object.assign({}, props, {shape: Shape}));
|
||||
};
|
||||
}
|
||||
|
||||
const nodeShapes = {
|
||||
'hosts': NodeShapeCircle,
|
||||
'containers': NodeShapeHex,
|
||||
'containers-by-hostname': stackedShape(NodeShapeHex),
|
||||
'containers-by-image': stackedShape(NodeShapeHex),
|
||||
'applications': NodeShapeRoundedSquare,
|
||||
'applications-by-name': stackedShape(NodeShapeRoundedSquare)
|
||||
};
|
||||
|
||||
function isTheInternet(id) {
|
||||
return id === 'theinternet';
|
||||
}
|
||||
|
||||
function getNodeShape({id, pseudo, topologyId}) {
|
||||
if (isTheInternet(id)) {
|
||||
return NodeShapeCloud;
|
||||
} else if (pseudo) {
|
||||
return NodeShapeCircle;
|
||||
}
|
||||
return nodeShapes[topologyId];
|
||||
}
|
||||
|
||||
export default class Node extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
@@ -52,8 +88,11 @@ export default class Node extends React.Component {
|
||||
if (this.props.pseudo) {
|
||||
classNames.push('pseudo');
|
||||
}
|
||||
|
||||
const classes = classNames.join(' ');
|
||||
|
||||
const NodeShapeType = getNodeShape(this.props);
|
||||
|
||||
return (
|
||||
<Motion style={{
|
||||
x: spring(this.props.dx, animConfig),
|
||||
@@ -69,10 +108,10 @@ export default class Node extends React.Component {
|
||||
return (
|
||||
<g className={classes} transform={transform} id={props.id}
|
||||
onClick={onMouseClick} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
|
||||
{props.highlighted && <circle r={nodeScale(0.7 * interpolated.f)} className="highlighted"></circle>}
|
||||
<circle r={nodeScale(0.5 * interpolated.f)} className="border" stroke={color}></circle>
|
||||
<circle r={nodeScale(0.45 * interpolated.f)} className="shadow"></circle>
|
||||
<circle r={Math.max(2, nodeScale(0.125 * interpolated.f))} className="node"></circle>
|
||||
<NodeShapeType
|
||||
size={nodeScale(interpolated.f)}
|
||||
color={color}
|
||||
{...props} />
|
||||
<text className="node-label" textAnchor="middle" style={{fontSize: interpolated.labelFontSize}}
|
||||
x="0" y={interpolated.labelOffsetY + nodeScale(0.5 * interpolated.f)}>
|
||||
{label}
|
||||
|
||||
@@ -142,6 +142,7 @@ export default class NodesChart extends React.Component {
|
||||
blurred={node.get('blurred')}
|
||||
focused={node.get('focused')}
|
||||
highlighted={node.get('highlighted')}
|
||||
topologyId={this.props.topologyId}
|
||||
onClick={onNodeClick}
|
||||
key={node.get('id')}
|
||||
id={node.get('id')}
|
||||
|
||||
@@ -303,6 +303,10 @@ h2 {
|
||||
}
|
||||
}
|
||||
|
||||
g.stack g.shape .border {
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
g.node {
|
||||
cursor: pointer;
|
||||
transition: opacity .5s ease-in-out;
|
||||
@@ -357,28 +361,29 @@ h2 {
|
||||
}
|
||||
|
||||
|
||||
circle.border {
|
||||
stroke-width: @node-border-stroke-width;
|
||||
fill: none;
|
||||
}
|
||||
.shape {
|
||||
.border {
|
||||
stroke-width: @node-border-stroke-width;
|
||||
fill: @background-color;
|
||||
}
|
||||
|
||||
circle.shadow {
|
||||
stroke: none;
|
||||
fill: @background-lighter-color;
|
||||
}
|
||||
.shadow {
|
||||
stroke: none;
|
||||
fill: @background-lighter-color;
|
||||
}
|
||||
|
||||
circle.node {
|
||||
fill: @text-color;
|
||||
}
|
||||
.node {
|
||||
fill: @text-color;
|
||||
}
|
||||
|
||||
circle.highlighted {
|
||||
fill: @weave-blue;
|
||||
fill-opacity: @node-highlight-fill-opacity;
|
||||
stroke: @weave-blue;
|
||||
stroke-width: @node-highlight-stroke-width;
|
||||
stroke-opacity: @node-highlight-stroke-opacity;
|
||||
.highlighted {
|
||||
fill: @weave-blue;
|
||||
fill-opacity: @node-highlight-fill-opacity;
|
||||
stroke: @weave-blue;
|
||||
stroke-width: @node-highlight-stroke-width;
|
||||
stroke-opacity: @node-highlight-stroke-opacity;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.details {
|
||||
|
||||
Reference in New Issue
Block a user