mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-03 02:00:43 +00:00
Revert "Upgraded eslint & eslint-config-airbnb"
This commit is contained in:
@@ -1 +1,2 @@
|
||||
app/scripts/vendor/term.js
|
||||
test/
|
||||
|
||||
@@ -7,32 +7,11 @@
|
||||
},
|
||||
"rules": {
|
||||
"comma-dangle": 0,
|
||||
"global-require": 0,
|
||||
"import/no-extraneous-dependencies": [
|
||||
"error",
|
||||
{
|
||||
"devDependencies": true,
|
||||
"optionalDependencies": true,
|
||||
"peerDependencies": true
|
||||
}
|
||||
],
|
||||
"import/prefer-default-export": 0,
|
||||
"jsx-a11y/no-static-element-interactions": 0,
|
||||
"no-param-reassign": 0,
|
||||
"no-restricted-properties": 0,
|
||||
"object-curly-spacing": 0,
|
||||
"react/jsx-closing-bracket-location": 0,
|
||||
"react/jsx-filename-extension": [
|
||||
2,
|
||||
{
|
||||
"extensions": [
|
||||
".js",
|
||||
".jsx"
|
||||
]
|
||||
}
|
||||
],
|
||||
"react/prefer-stateless-function": 0,
|
||||
"react/sort-comp": 0,
|
||||
"react/prop-types": 0,
|
||||
"react/prop-types": 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,7 @@ export function sortOrderChanged(sortedBy, sortedDesc) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: ActionTypes.SORT_ORDER_CHANGED,
|
||||
sortedBy,
|
||||
sortedDesc
|
||||
sortedBy, sortedDesc
|
||||
});
|
||||
updateRoute(getState);
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ describe('NodesLayout', () => {
|
||||
const coords = [];
|
||||
nodes
|
||||
.sortBy(node => node.get('id'))
|
||||
.forEach((node) => {
|
||||
.forEach(node => {
|
||||
coords.push(node.get('x'));
|
||||
coords.push(node.get('y'));
|
||||
});
|
||||
|
||||
@@ -17,8 +17,7 @@ class Edge extends React.Component {
|
||||
const className = classNames('edge', {highlighted, blurred, focused});
|
||||
|
||||
return (
|
||||
<g
|
||||
className={className} onMouseEnter={this.handleMouseEnter}
|
||||
<g className={className} onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave} id={id}>
|
||||
<path d={path} className="shadow" />
|
||||
<path d={path} className="link" />
|
||||
|
||||
@@ -14,13 +14,12 @@ class NodeContainer extends React.Component {
|
||||
const other = omit(this.props, 'dx', 'dy');
|
||||
|
||||
return (
|
||||
<Motion
|
||||
style={{
|
||||
x: spring(dx, animConfig),
|
||||
y: spring(dy, animConfig),
|
||||
f: spring(scaleFactor, animConfig)
|
||||
}}>
|
||||
{(interpolated) => {
|
||||
<Motion style={{
|
||||
x: spring(dx, animConfig),
|
||||
y: spring(dy, animConfig),
|
||||
f: spring(scaleFactor, animConfig)
|
||||
}}>
|
||||
{interpolated => {
|
||||
const transform = `translate(${round(interpolated.x, layoutPrecision)},`
|
||||
+ `${round(interpolated.y, layoutPrecision)})`;
|
||||
return <Node {...other} transform={transform} scaleFactor={interpolated.f} />;
|
||||
|
||||
@@ -26,7 +26,7 @@ function NodeNetworksOverlay({offset, size, stack, networks = makeList()}) {
|
||||
const bars = networks.map((n, i) => (
|
||||
<rect
|
||||
x={x(i)}
|
||||
y={offset - (barHeight * 0.5)}
|
||||
y={offset - barHeight * 0.5}
|
||||
width={x.bandwidth()}
|
||||
height={barHeight}
|
||||
rx={rx}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { getMetricValue, getMetricColor, getClipPathDefinition } from '../utils/metric-utils';
|
||||
import { CANVAS_METRIC_FONT_SIZE } from '../constants/styles';
|
||||
import {getMetricValue, getMetricColor, getClipPathDefinition} from '../utils/metric-utils.js';
|
||||
import {CANVAS_METRIC_FONT_SIZE} from '../constants/styles.js';
|
||||
|
||||
|
||||
export default function NodeShapeCircle({id, highlighted, size, color, metric}) {
|
||||
@@ -17,12 +17,8 @@ export default function NodeShapeCircle({id, highlighted, size, color, metric})
|
||||
{highlighted && <circle r={size * 0.7} className="highlighted" />}
|
||||
<circle r={size * 0.5} className="border" stroke={color} />
|
||||
<circle r={size * 0.45} className="shadow" />
|
||||
{hasMetric && <circle
|
||||
r={size * 0.45}
|
||||
className="metric-fill"
|
||||
style={metricStyle}
|
||||
clipPath={`url(#${clipId})`}
|
||||
/>}
|
||||
{hasMetric && <circle r={size * 0.45} className="metric-fill" style={metricStyle}
|
||||
clipPath={`url(#${clipId})`} />}
|
||||
{highlighted && hasMetric ?
|
||||
<text style={{fontSize}}>{formattedValue}</text> :
|
||||
<circle className="node" r={Math.max(2, (size * 0.125))} />}
|
||||
|
||||
@@ -37,7 +37,8 @@ export default function NodeShapeCloud({highlighted, size, color}) {
|
||||
|
||||
return (
|
||||
<g className="shape shape-cloud">
|
||||
{highlighted && <path className="highlighted" {...pathProps(0.7)} />}
|
||||
{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))} />
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { line, curveCardinalClosed } from 'd3-shape';
|
||||
import { getMetricValue, getMetricColor, getClipPathDefinition } from '../utils/metric-utils';
|
||||
import { CANVAS_METRIC_FONT_SIZE } from '../constants/styles';
|
||||
import { getMetricValue, getMetricColor, getClipPathDefinition } from '../utils/metric-utils.js';
|
||||
import { CANVAS_METRIC_FONT_SIZE } from '../constants/styles.js';
|
||||
|
||||
|
||||
const spline = line()
|
||||
@@ -12,7 +12,7 @@ const spline = line()
|
||||
function polygon(r, sides) {
|
||||
const a = (Math.PI * 2) / sides;
|
||||
const points = [];
|
||||
for (let i = 0; i < sides; i += 1) {
|
||||
for (let i = 0; i < sides; i++) {
|
||||
points.push([r * Math.sin(a * i), -r * Math.cos(a * i)]);
|
||||
}
|
||||
return points;
|
||||
@@ -30,20 +30,15 @@ export default function NodeShapeHeptagon({id, highlighted, size, color, metric}
|
||||
const metricStyle = { fill: getMetricColor(metric) };
|
||||
const className = classNames('shape', { metrics: hasMetric });
|
||||
const fontSize = size * CANVAS_METRIC_FONT_SIZE;
|
||||
const halfSize = size * 0.5;
|
||||
|
||||
return (
|
||||
<g className={className}>
|
||||
{hasMetric && getClipPathDefinition(clipId, size, height, -halfSize, halfSize - height)}
|
||||
{hasMetric && getClipPathDefinition(clipId, size, height, -size * 0.5, size * 0.5 - height)}
|
||||
{highlighted && <path className="highlighted" {...pathProps(0.7)} />}
|
||||
<path className="border" stroke={color} {...pathProps(0.5)} />
|
||||
<path className="shadow" {...pathProps(0.45)} />
|
||||
{hasMetric && <path
|
||||
className="metric-fill"
|
||||
clipPath={`url(#${clipId})`}
|
||||
style={metricStyle}
|
||||
{...pathProps(0.45)}
|
||||
/>}
|
||||
{hasMetric && <path className="metric-fill" clipPath={`url(#${clipId})`}
|
||||
style={metricStyle} {...pathProps(0.45)} />}
|
||||
{highlighted && hasMetric ?
|
||||
<text style={{fontSize}}>{formattedValue}</text> :
|
||||
<circle className="node" r={Math.max(2, (size * 0.125))} />}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { line, curveCardinalClosed } from 'd3-shape';
|
||||
import { getMetricValue, getMetricColor, getClipPathDefinition } from '../utils/metric-utils';
|
||||
import { CANVAS_METRIC_FONT_SIZE } from '../constants/styles';
|
||||
import { getMetricValue, getMetricColor, getClipPathDefinition } from '../utils/metric-utils.js';
|
||||
import { CANVAS_METRIC_FONT_SIZE } from '../constants/styles.js';
|
||||
|
||||
|
||||
const spline = line()
|
||||
@@ -48,22 +48,13 @@ export default function NodeShapeHexagon({id, highlighted, size, color, metric})
|
||||
|
||||
return (
|
||||
<g className={className}>
|
||||
{hasMetric && getClipPathDefinition(
|
||||
clipId,
|
||||
size * (1 + (hexCurve * 2)),
|
||||
height,
|
||||
-(size * hexCurve),
|
||||
(size - height) * (shadowSize * 2)
|
||||
)}
|
||||
{hasMetric && getClipPathDefinition(clipId,
|
||||
size * (1 + hexCurve * 2), height, -size * hexCurve, (size - height) * shadowSize * 2)}
|
||||
{highlighted && <path className="highlighted" {...pathProps(0.7)} />}
|
||||
<path className="border" stroke={color} {...pathProps(0.5)} />
|
||||
<path className="shadow" {...pathProps(shadowSize)} />
|
||||
{hasMetric && <path
|
||||
className="metric-fill"
|
||||
style={metricStyle}
|
||||
clipPath={`url(#${clipId})`}
|
||||
{...pathProps(shadowSize)}
|
||||
/>}
|
||||
{hasMetric && <path className="metric-fill" style={metricStyle}
|
||||
clipPath={`url(#${clipId})`} {...pathProps(shadowSize)} />}
|
||||
{highlighted && hasMetric ?
|
||||
<text style={{fontSize}}>
|
||||
{formattedValue}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { getMetricValue, getMetricColor, getClipPathDefinition } from '../utils/metric-utils';
|
||||
import { CANVAS_METRIC_FONT_SIZE } from '../constants/styles';
|
||||
import {getMetricValue, getMetricColor, getClipPathDefinition} from '../utils/metric-utils.js';
|
||||
import {CANVAS_METRIC_FONT_SIZE} from '../constants/styles.js';
|
||||
|
||||
|
||||
export default function NodeShapeSquare({
|
||||
@@ -28,11 +28,8 @@ export default function NodeShapeSquare({
|
||||
{highlighted && <rect className="highlighted" {...rectProps(0.7)} />}
|
||||
<rect className="border" stroke={color} {...rectProps(0.5, 0.5)} />
|
||||
<rect className="shadow" {...rectProps(0.45, 0.39)} />
|
||||
{hasMetric && <rect
|
||||
className="metric-fill" style={metricStyle}
|
||||
clipPath={`url(#${clipId})`}
|
||||
{...rectProps(0.45, 0.39)}
|
||||
/>}
|
||||
{hasMetric && <rect className="metric-fill" style={metricStyle}
|
||||
clipPath={`url(#${clipId})`} {...rectProps(0.45, 0.39)} />}
|
||||
{highlighted && hasMetric ?
|
||||
<text style={{fontSize}}>
|
||||
{formattedValue}
|
||||
|
||||
@@ -5,8 +5,8 @@ export default function NodeShapeStack(props) {
|
||||
const contrastMode = isContrastMode();
|
||||
const Shape = props.shape;
|
||||
const [dx, dy] = contrastMode ? [0, 8] : [0, 5];
|
||||
const dsx = (props.size + dx) / props.size;
|
||||
const dsy = (props.size + dy) / props.size;
|
||||
const dsx = (props.size * 2 + (dx * 2)) / (props.size * 2);
|
||||
const dsy = (props.size * 2 + (dy * 2)) / (props.size * 2);
|
||||
const hls = [dsx, dsy];
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import classnames from 'classnames';
|
||||
import { Map as makeMap, List as makeList } from 'immutable';
|
||||
@@ -126,12 +127,9 @@ class Node extends React.Component {
|
||||
|
||||
svgLabels(label, subLabel, labelClassName, subLabelClassName, labelOffsetY) :
|
||||
|
||||
<foreignObject
|
||||
style={{pointerEvents: 'none'}}
|
||||
x={labelOffsetX} y={labelOffsetY}
|
||||
<foreignObject style={{pointerEvents: 'none'}} x={labelOffsetX} y={labelOffsetY}
|
||||
width={labelWidth} height="100em">
|
||||
<div
|
||||
className="node-label-wrapper"
|
||||
<div className="node-label-wrapper"
|
||||
style={{pointerEvents: 'all', fontSize, maxWidth: labelWidth}}
|
||||
{...mouseEvents}>
|
||||
<div className={labelClassName}>
|
||||
@@ -151,11 +149,8 @@ class Node extends React.Component {
|
||||
{...this.props} />
|
||||
</g>
|
||||
|
||||
{showingNetworks && <NodeNetworksOverlay
|
||||
offset={networkOffset}
|
||||
size={size} networks={networks}
|
||||
stack={stack}
|
||||
/>}
|
||||
{showingNetworks && <NodeNetworksOverlay offset={networkOffset}
|
||||
size={size} networks={networks} stack={stack} />}
|
||||
</g>
|
||||
);
|
||||
}
|
||||
@@ -166,7 +161,8 @@ class Node extends React.Component {
|
||||
|
||||
handleMouseClick(ev) {
|
||||
ev.stopPropagation();
|
||||
this.props.clickNode(this.props.id, this.props.label, this.shapeRef.getBoundingClientRect());
|
||||
this.props.clickNode(this.props.id, this.props.label,
|
||||
ReactDOM.findDOMNode(this.shapeRef).getBoundingClientRect());
|
||||
}
|
||||
|
||||
handleMouseEnter() {
|
||||
|
||||
@@ -13,7 +13,7 @@ class NodesChartEdges extends React.Component {
|
||||
|
||||
return (
|
||||
<g className="nodes-chart-edges">
|
||||
{layoutEdges.toIndexedSeq().map((edge) => {
|
||||
{layoutEdges.toIndexedSeq().map(edge => {
|
||||
const sourceSelected = selectedNodeId === edge.get('source');
|
||||
const targetSelected = selectedNodeId === edge.get('target');
|
||||
const highlighted = highlightedEdgeIds.has(edge.get('id'));
|
||||
@@ -26,8 +26,8 @@ class NodesChartEdges extends React.Component {
|
||||
!(selectedNetworkNodes.contains(edge.get('source')) &&
|
||||
selectedNetworkNodes.contains(edge.get('target')));
|
||||
const blurred = !highlighted && (otherNodesSelected ||
|
||||
(!focused && noMatches) ||
|
||||
(!focused && noSelectedNetworks));
|
||||
!focused && noMatches ||
|
||||
!focused && noSelectedNetworks);
|
||||
|
||||
return (
|
||||
<EdgeContainer
|
||||
|
||||
@@ -21,12 +21,14 @@ class NodesChartNodes extends React.Component {
|
||||
&& (selectedNodeId === node.get('id')
|
||||
|| (adjacentNodes && adjacentNodes.includes(node.get('id')))));
|
||||
const setBlurred = node => node.set('blurred',
|
||||
(selectedNodeId && !node.get('focused'))
|
||||
|| (searchQuery && !searchNodeMatches.has(node.get('id')) && !node.get('highlighted'))
|
||||
|| (selectedNetwork && !(node.get('networks') || makeList()).find(n => n.get('id') === selectedNetwork)));
|
||||
selectedNodeId && !node.get('focused')
|
||||
|| searchQuery && !searchNodeMatches.has(node.get('id'))
|
||||
&& !node.get('highlighted')
|
||||
|| selectedNetwork
|
||||
&& !(node.get('networks') || makeList()).find(n => n.get('id') === selectedNetwork));
|
||||
|
||||
// make sure blurred nodes are in the background
|
||||
const sortNodes = (node) => {
|
||||
const sortNodes = node => {
|
||||
if (node.get('id') === mouseOverNodeId) {
|
||||
return 3;
|
||||
}
|
||||
@@ -40,7 +42,7 @@ class NodesChartNodes extends React.Component {
|
||||
};
|
||||
|
||||
// TODO: think about pulling this up into the store.
|
||||
const metric = (node) => {
|
||||
const metric = node => {
|
||||
const isHighlighted = topCardNode && topCardNode.details && topCardNode.id === node.get('id');
|
||||
const sourceNode = isHighlighted ? fromJS(topCardNode.details) : node;
|
||||
return sourceNode.get('metrics') && sourceNode.get('metrics')
|
||||
|
||||
@@ -52,7 +52,7 @@ function initEdges(nodes) {
|
||||
nodes.forEach((node, nodeId) => {
|
||||
const adjacency = node.get('adjacency');
|
||||
if (adjacency) {
|
||||
adjacency.forEach((adjacent) => {
|
||||
adjacency.forEach(adjacent => {
|
||||
const edge = [nodeId, adjacent];
|
||||
const edgeId = edge.join(EDGE_ID_SEPARATOR);
|
||||
|
||||
@@ -115,80 +115,6 @@ function updateLayout(width, height, nodes, baseOptions) {
|
||||
}
|
||||
|
||||
|
||||
function centerSelectedNode(props, state) {
|
||||
let stateNodes = state.nodes;
|
||||
let stateEdges = state.edges;
|
||||
if (!stateNodes.has(props.selectedNodeId)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const adjacentNodes = props.adjacentNodes;
|
||||
const adjacentLayoutNodeIds = [];
|
||||
|
||||
adjacentNodes.forEach((adjacentId) => {
|
||||
// filter loopback
|
||||
if (adjacentId !== props.selectedNodeId) {
|
||||
adjacentLayoutNodeIds.push(adjacentId);
|
||||
}
|
||||
});
|
||||
|
||||
// move origin node to center of viewport
|
||||
const zoomScale = state.scale;
|
||||
const translate = [state.panTranslateX, state.panTranslateY];
|
||||
const viewportHalfWidth = ((state.width + props.margins.left) - DETAILS_PANEL_WIDTH) / 2;
|
||||
const viewportHalfHeight = (state.height + props.margins.top) / 2;
|
||||
const centerX = (-translate[0] + viewportHalfWidth) / zoomScale;
|
||||
const centerY = (-translate[1] + viewportHalfHeight) / zoomScale;
|
||||
stateNodes = stateNodes.mergeIn([props.selectedNodeId], {
|
||||
x: centerX,
|
||||
y: centerY
|
||||
});
|
||||
|
||||
// circle layout for adjacent nodes
|
||||
const adjacentCount = adjacentLayoutNodeIds.length;
|
||||
const density = radiusDensity(adjacentCount);
|
||||
const radius = Math.min(state.width, state.height) / density / zoomScale;
|
||||
const offsetAngle = Math.PI / 4;
|
||||
|
||||
stateNodes = stateNodes.map((node, nodeId) => {
|
||||
const index = adjacentLayoutNodeIds.indexOf(nodeId);
|
||||
if (index > -1) {
|
||||
const angle = offsetAngle + ((Math.PI * 2 * index) / adjacentCount);
|
||||
return node.merge({
|
||||
x: centerX + (radius * Math.sin(angle)),
|
||||
y: centerY + (radius * Math.cos(angle))
|
||||
});
|
||||
}
|
||||
return node;
|
||||
});
|
||||
|
||||
// fix all edges for circular nodes
|
||||
stateEdges = stateEdges.map((edge) => {
|
||||
if (edge.get('source') === props.selectedNodeId
|
||||
|| edge.get('target') === props.selectedNodeId
|
||||
|| includes(adjacentLayoutNodeIds, edge.get('source'))
|
||||
|| includes(adjacentLayoutNodeIds, edge.get('target'))) {
|
||||
const source = stateNodes.get(edge.get('source'));
|
||||
const target = stateNodes.get(edge.get('target'));
|
||||
return edge.set('points', fromJS([
|
||||
{x: source.get('x'), y: source.get('y')},
|
||||
{x: target.get('x'), y: target.get('y')}
|
||||
]));
|
||||
}
|
||||
return edge;
|
||||
});
|
||||
|
||||
// auto-scale node size for selected nodes
|
||||
const selectedNodeScale = getNodeScale(adjacentNodes.size, state.width, state.height);
|
||||
|
||||
return {
|
||||
selectedNodeScale,
|
||||
edges: stateEdges,
|
||||
nodes: stateNodes
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
class NodesChart extends React.Component {
|
||||
|
||||
constructor(props, context) {
|
||||
@@ -254,7 +180,7 @@ class NodesChart extends React.Component {
|
||||
assign(state, this.restoreLayout(state));
|
||||
}
|
||||
if (nextProps.selectedNodeId) {
|
||||
assign(state, centerSelectedNode(nextProps, state));
|
||||
assign(state, this.centerSelectedNode(nextProps, state));
|
||||
}
|
||||
|
||||
this.setState(state);
|
||||
@@ -293,8 +219,7 @@ class NodesChart extends React.Component {
|
||||
const layoutPrecision = getLayoutPrecision(nodes.size);
|
||||
return (
|
||||
<div className="nodes-chart">
|
||||
<svg
|
||||
width="100%" height="100%" id="nodes-chart-canvas"
|
||||
<svg width="100%" height="100%" id="nodes-chart-canvas"
|
||||
className={svgClassNames} onClick={this.handleMouseClick}>
|
||||
<g transform="translate(24,24) scale(0.25)">
|
||||
<Logo />
|
||||
@@ -320,6 +245,78 @@ class NodesChart extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
centerSelectedNode(props, state) {
|
||||
let stateNodes = state.nodes;
|
||||
let stateEdges = state.edges;
|
||||
if (!stateNodes.has(props.selectedNodeId)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const adjacentNodes = props.adjacentNodes;
|
||||
const adjacentLayoutNodeIds = [];
|
||||
|
||||
adjacentNodes.forEach(adjacentId => {
|
||||
// filter loopback
|
||||
if (adjacentId !== props.selectedNodeId) {
|
||||
adjacentLayoutNodeIds.push(adjacentId);
|
||||
}
|
||||
});
|
||||
|
||||
// move origin node to center of viewport
|
||||
const zoomScale = state.scale;
|
||||
const translate = [state.panTranslateX, state.panTranslateY];
|
||||
const centerX = (-translate[0] + (state.width + props.margins.left
|
||||
- DETAILS_PANEL_WIDTH) / 2) / zoomScale;
|
||||
const centerY = (-translate[1] + (state.height + props.margins.top) / 2) / zoomScale;
|
||||
stateNodes = stateNodes.mergeIn([props.selectedNodeId], {
|
||||
x: centerX,
|
||||
y: centerY
|
||||
});
|
||||
|
||||
// circle layout for adjacent nodes
|
||||
const adjacentCount = adjacentLayoutNodeIds.length;
|
||||
const density = radiusDensity(adjacentCount);
|
||||
const radius = Math.min(state.width, state.height) / density / zoomScale;
|
||||
const offsetAngle = Math.PI / 4;
|
||||
|
||||
stateNodes = stateNodes.map((node, nodeId) => {
|
||||
const index = adjacentLayoutNodeIds.indexOf(nodeId);
|
||||
if (index > -1) {
|
||||
const angle = offsetAngle + Math.PI * 2 * index / adjacentCount;
|
||||
return node.merge({
|
||||
x: centerX + radius * Math.sin(angle),
|
||||
y: centerY + radius * Math.cos(angle)
|
||||
});
|
||||
}
|
||||
return node;
|
||||
});
|
||||
|
||||
// fix all edges for circular nodes
|
||||
stateEdges = stateEdges.map(edge => {
|
||||
if (edge.get('source') === props.selectedNodeId
|
||||
|| edge.get('target') === props.selectedNodeId
|
||||
|| includes(adjacentLayoutNodeIds, edge.get('source'))
|
||||
|| includes(adjacentLayoutNodeIds, edge.get('target'))) {
|
||||
const source = stateNodes.get(edge.get('source'));
|
||||
const target = stateNodes.get(edge.get('target'));
|
||||
return edge.set('points', fromJS([
|
||||
{x: source.get('x'), y: source.get('y')},
|
||||
{x: target.get('x'), y: target.get('y')}
|
||||
]));
|
||||
}
|
||||
return edge;
|
||||
});
|
||||
|
||||
// auto-scale node size for selected nodes
|
||||
const selectedNodeScale = getNodeScale(adjacentNodes.size, state.width, state.height);
|
||||
|
||||
return {
|
||||
selectedNodeScale,
|
||||
edges: stateEdges,
|
||||
nodes: stateNodes
|
||||
};
|
||||
}
|
||||
|
||||
restoreLayout(state) {
|
||||
// undo any pan/zooming that might have happened
|
||||
this.setZoom(state);
|
||||
@@ -329,7 +326,7 @@ class NodesChart extends React.Component {
|
||||
y: node.get('py')
|
||||
}));
|
||||
|
||||
const edges = state.edges.map((edge) => {
|
||||
const edges = state.edges.map(edge => {
|
||||
if (edge.has('ppoints')) {
|
||||
return edge.set('points', edge.get('ppoints'));
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ const IGNORED_COLUMNS = ['docker_container_ports', 'docker_container_id', 'docke
|
||||
function getColumns(nodes) {
|
||||
const metricColumns = nodes
|
||||
.toList()
|
||||
.flatMap((n) => {
|
||||
.flatMap(n => {
|
||||
const metrics = (n.get('metrics') || makeList())
|
||||
.map(m => makeMap({ id: m.get('id'), label: m.get('label'), dataType: 'number' }));
|
||||
return metrics;
|
||||
@@ -28,7 +28,7 @@ function getColumns(nodes) {
|
||||
|
||||
const metadataColumns = nodes
|
||||
.toList()
|
||||
.flatMap((n) => {
|
||||
.flatMap(n => {
|
||||
const metadata = (n.get('metadata') || makeList())
|
||||
.map(m => makeMap({ id: m.get('id'), label: m.get('label'), dataType: m.get('dataType') }));
|
||||
return metadata;
|
||||
@@ -40,7 +40,7 @@ function getColumns(nodes) {
|
||||
|
||||
const relativesColumns = nodes
|
||||
.toList()
|
||||
.flatMap((n) => {
|
||||
.flatMap(n => {
|
||||
const metadata = (n.get('parents') || makeList())
|
||||
.map(m => makeMap({ id: m.get('topologyId'), label: m.get('topologyId') }));
|
||||
return metadata;
|
||||
|
||||
@@ -55,7 +55,7 @@ function runLayoutEngine(graph, imNodes, imEdges, opts) {
|
||||
});
|
||||
|
||||
// add nodes to the graph if not already there
|
||||
nodes.forEach((node) => {
|
||||
nodes.forEach(node => {
|
||||
const gNodeId = graphNodeId(node.get('id'));
|
||||
if (!graph.hasNode(gNodeId)) {
|
||||
graph.setNode(gNodeId, {
|
||||
@@ -66,7 +66,7 @@ function runLayoutEngine(graph, imNodes, imEdges, opts) {
|
||||
});
|
||||
|
||||
// remove nodes that are no longer there or are 0-degree nodes
|
||||
graph.nodes().forEach((gNodeId) => {
|
||||
graph.nodes().forEach(gNodeId => {
|
||||
const nodeId = fromGraphNodeId(gNodeId);
|
||||
if (!nodes.has(nodeId) || nodes.get(nodeId).get('degree') === 0) {
|
||||
graph.removeNode(gNodeId);
|
||||
@@ -74,7 +74,7 @@ function runLayoutEngine(graph, imNodes, imEdges, opts) {
|
||||
});
|
||||
|
||||
// add edges to the graph if not already there
|
||||
edges.forEach((edge) => {
|
||||
edges.forEach(edge => {
|
||||
const s = graphNodeId(edge.get('source'));
|
||||
const t = graphNodeId(edge.get('target'));
|
||||
if (!graph.hasEdge(s, t)) {
|
||||
@@ -84,7 +84,7 @@ function runLayoutEngine(graph, imNodes, imEdges, opts) {
|
||||
});
|
||||
|
||||
// remove edges that are no longer there
|
||||
graph.edges().forEach((edgeObj) => {
|
||||
graph.edges().forEach(edgeObj => {
|
||||
const edge = [fromGraphNodeId(edgeObj.v), fromGraphNodeId(edgeObj.w)];
|
||||
const edgeId = edge.join(EDGE_ID_SEPARATOR);
|
||||
if (!edges.has(edgeId)) {
|
||||
@@ -97,14 +97,14 @@ function runLayoutEngine(graph, imNodes, imEdges, opts) {
|
||||
|
||||
// apply coordinates to nodes and edges
|
||||
|
||||
graph.nodes().forEach((gNodeId) => {
|
||||
graph.nodes().forEach(gNodeId => {
|
||||
const graphNode = graph.node(gNodeId);
|
||||
const nodeId = fromGraphNodeId(gNodeId);
|
||||
nodes = nodes.setIn([nodeId, 'x'], graphNode.x);
|
||||
nodes = nodes.setIn([nodeId, 'y'], graphNode.y);
|
||||
});
|
||||
|
||||
graph.edges().forEach((graphEdge) => {
|
||||
graph.edges().forEach(graphEdge => {
|
||||
const graphEdgeMeta = graph.edge(graphEdge);
|
||||
const edge = edges.get(graphEdgeMeta.id);
|
||||
let points = fromJS(graphEdgeMeta.points);
|
||||
@@ -165,7 +165,7 @@ export function doLayoutNewNodesOfExistingRank(layout, nodeCache, opts) {
|
||||
const oldNodes = ImmSet.fromKeys(nodeCache);
|
||||
const newNodes = ImmSet.fromKeys(layout.nodes.filter(n => n.get('degree') > 0))
|
||||
.subtract(oldNodes);
|
||||
result.nodes = layout.nodes.map((n) => {
|
||||
result.nodes = layout.nodes.map(n => {
|
||||
if (newNodes.contains(n.get('id'))) {
|
||||
const nodesSameRank = nodeCache.filter(nn => nn.get('rank') === n.get('rank'));
|
||||
if (nodesSameRank.size > 0) {
|
||||
@@ -178,7 +178,7 @@ export function doLayoutNewNodesOfExistingRank(layout, nodeCache, opts) {
|
||||
return n;
|
||||
});
|
||||
|
||||
result.edges = layout.edges.map((edge) => {
|
||||
result.edges = layout.edges.map(edge => {
|
||||
if (!edge.has('points')) {
|
||||
return setSimpleEdgePoints(edge, layout.nodes);
|
||||
}
|
||||
@@ -237,23 +237,23 @@ function layoutSingleNodes(layout, opts) {
|
||||
}
|
||||
|
||||
// default margins
|
||||
offsetX = offsetX || (margins.left + nodeWidth) / 2;
|
||||
offsetY = offsetY || (margins.top + nodeHeight) / 2;
|
||||
offsetX = offsetX || margins.left + nodeWidth / 2;
|
||||
offsetY = offsetY || margins.top + nodeHeight / 2;
|
||||
|
||||
const columns = Math.ceil(Math.sqrt(singleNodes.size));
|
||||
let row = 0;
|
||||
let col = 0;
|
||||
let singleX;
|
||||
let singleY;
|
||||
nodes = nodes.sortBy(node => node.get('rank')).map((node) => {
|
||||
nodes = nodes.sortBy(node => node.get('rank')).map(node => {
|
||||
if (singleNodes.has(node.get('id'))) {
|
||||
if (col === columns) {
|
||||
col = 0;
|
||||
row += 1;
|
||||
row++;
|
||||
}
|
||||
singleX = (col * (nodesep + nodeWidth)) + offsetX;
|
||||
singleY = (row * (ranksep + nodeHeight)) + offsetY;
|
||||
col += 1;
|
||||
singleX = col * (nodesep + nodeWidth) + offsetX;
|
||||
singleY = row * (ranksep + nodeHeight) + offsetY;
|
||||
col++;
|
||||
return node.merge({
|
||||
x: singleX,
|
||||
y: singleY
|
||||
@@ -263,8 +263,8 @@ function layoutSingleNodes(layout, opts) {
|
||||
});
|
||||
|
||||
// adjust layout dimensions if graph is now bigger
|
||||
result.width = Math.max(layout.width, singleX + (nodeWidth / 2) + nodesep);
|
||||
result.height = Math.max(layout.height, singleY + (nodeHeight / 2) + ranksep);
|
||||
result.width = Math.max(layout.width, singleX + nodeWidth / 2 + nodesep);
|
||||
result.height = Math.max(layout.height, singleY + nodeHeight / 2 + ranksep);
|
||||
result.nodes = nodes;
|
||||
}
|
||||
|
||||
@@ -290,12 +290,12 @@ export function shiftLayoutToCenter(layout, opts) {
|
||||
if (layout.width < width) {
|
||||
const xMin = layout.nodes.minBy(n => n.get('x'));
|
||||
const xMax = layout.nodes.maxBy(n => n.get('x'));
|
||||
offsetX = ((width - (xMin.get('x') + xMax.get('x'))) / 2) + margins.left;
|
||||
offsetX = (width - (xMin.get('x') + xMax.get('x'))) / 2 + margins.left;
|
||||
}
|
||||
if (layout.height < height) {
|
||||
const yMin = layout.nodes.minBy(n => n.get('y'));
|
||||
const yMax = layout.nodes.maxBy(n => n.get('y'));
|
||||
offsetY = ((height - (yMin.get('y') + yMax.get('y'))) / 2) + margins.top;
|
||||
offsetY = (height - (yMin.get('y') + yMax.get('y'))) / 2 + margins.top;
|
||||
}
|
||||
|
||||
if (offsetX || offsetY) {
|
||||
@@ -412,7 +412,7 @@ function copyLayoutProperties(layout, nodeCache, edgeCache) {
|
||||
const result = Object.assign({}, layout);
|
||||
result.nodes = layout.nodes.map(node => (nodeCache.has(node.get('id'))
|
||||
? node.merge(nodeCache.get(node.get('id'))) : node));
|
||||
result.edges = layout.edges.map((edge) => {
|
||||
result.edges = layout.edges.map(edge => {
|
||||
if (edgeCache.has(edge.get('id'))
|
||||
&& hasSameEndpoints(edgeCache.get(edge.get('id')), result.nodes)) {
|
||||
return edge.merge(edgeCache.get(edge.get('id')));
|
||||
@@ -453,10 +453,9 @@ export function doLayout(immNodes, immEdges, opts) {
|
||||
const useCache = !options.forceRelayout && cachedLayout && nodeCache && edgeCache;
|
||||
let layout;
|
||||
|
||||
layoutRuns += 1;
|
||||
++layoutRuns;
|
||||
if (useCache && !hasUnseenNodes(immNodes, nodeCache)) {
|
||||
layoutRunsTrivial += 1;
|
||||
log('skip layout, trivial adjustment', layoutRunsTrivial, layoutRuns);
|
||||
log('skip layout, trivial adjustment', ++layoutRunsTrivial, layoutRuns);
|
||||
layout = cloneLayout(cachedLayout, immNodes, immEdges);
|
||||
// copy old properties, works also if nodes get re-added
|
||||
layout = copyLayoutProperties(layout, nodeCache, edgeCache);
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Provider } from 'react-redux';
|
||||
import configureStore from '../../stores/configureStore';
|
||||
|
||||
// need ES5 require to keep automocking off
|
||||
const NodeDetails = require('../node-details.js').default.WrappedComponent;
|
||||
const NodeDetails = require('../node-details.js').NodeDetails;
|
||||
|
||||
describe('NodeDetails', () => {
|
||||
let nodes;
|
||||
@@ -34,8 +34,7 @@ describe('NodeDetails', () => {
|
||||
details = {label: 'Node 1'};
|
||||
const c = TestUtils.renderIntoDocument(
|
||||
<Provider store={configureStore()}>
|
||||
<NodeDetails
|
||||
nodes={nodes}
|
||||
<NodeDetails nodes={nodes}
|
||||
topologyId="containers"
|
||||
nodeId={nodeId} details={details}
|
||||
/>
|
||||
|
||||
@@ -3,13 +3,13 @@ import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import Logo from './logo';
|
||||
import Footer from './footer';
|
||||
import Sidebar from './sidebar';
|
||||
import Footer from './footer.js';
|
||||
import Sidebar from './sidebar.js';
|
||||
import HelpPanel from './help-panel';
|
||||
import Search from './search';
|
||||
import Status from './status';
|
||||
import Topologies from './topologies';
|
||||
import TopologyOptions from './topology-options';
|
||||
import Status from './status.js';
|
||||
import Topologies from './topologies.js';
|
||||
import TopologyOptions from './topology-options.js';
|
||||
import { getApiDetails, getTopologies } from '../utils/web-api-utils';
|
||||
import { focusSearch, pinNextMetric, hitBackspace, hitEnter, hitEsc, unpinMetric,
|
||||
selectMetric, toggleHelp, toggleGridMode } from '../actions/app-actions';
|
||||
@@ -18,8 +18,10 @@ import Nodes from './nodes';
|
||||
import GridModeSelector from './grid-mode-selector';
|
||||
import MetricSelector from './metric-selector';
|
||||
import NetworkSelector from './networks-selector';
|
||||
import DebugToolbar, { showingDebugToolbar, toggleDebugToolbar } from './debug-toolbar';
|
||||
import { getRouter, getUrlState } from '../utils/router-utils';
|
||||
import { getRouter } from '../utils/router-utils';
|
||||
import DebugToolbar, { showingDebugToolbar,
|
||||
toggleDebugToolbar } from './debug-toolbar.js';
|
||||
import { getUrlState } from '../utils/router-utils';
|
||||
import { getActiveTopologyOptions } from '../utils/topology-utils';
|
||||
|
||||
const BACKSPACE_KEY_CODE = 8;
|
||||
|
||||
@@ -5,7 +5,9 @@ import { connect } from 'react-redux';
|
||||
import { sampleSize, sample, random, range, flattenDeep } from 'lodash';
|
||||
import { fromJS, Set as makeSet } from 'immutable';
|
||||
import { hsl } from 'd3-color';
|
||||
|
||||
import debug from 'debug';
|
||||
const log = debug('scope:debug-panel');
|
||||
|
||||
import ActionTypes from '../constants/action-types';
|
||||
import { receiveNodesDelta } from '../actions/app-actions';
|
||||
@@ -27,7 +29,7 @@ voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occa
|
||||
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`;
|
||||
|
||||
const sampleArray = (collection, n = 4) => sampleSize(collection, random(n));
|
||||
const log = debug('scope:debug-panel');
|
||||
|
||||
|
||||
const shapeTypes = {
|
||||
square: ['Process', 'Processes'],
|
||||
@@ -78,7 +80,7 @@ function label(shape, stacked) {
|
||||
|
||||
|
||||
function addAllVariants(dispatch) {
|
||||
const newNodes = flattenDeep(STACK_VARIANTS.map(stack => (SHAPES.map((s) => {
|
||||
const newNodes = flattenDeep(STACK_VARIANTS.map(stack => (SHAPES.map(s => {
|
||||
if (!stack) return [deltaAdd(label(s, stack), [], s, stack, 1)];
|
||||
return NODE_COUNTS.map(n => deltaAdd(label(s, stack), [], s, stack, n));
|
||||
}))));
|
||||
@@ -237,16 +239,16 @@ class DebugToolbar extends React.Component {
|
||||
const ns = this.props.nodes;
|
||||
const nodeNames = ns.keySeq().toJS();
|
||||
this.asyncDispatch(receiveNodesDelta({
|
||||
add: this.createRandomNodes(7),
|
||||
add: this._addNodes(7),
|
||||
update: sampleArray(nodeNames).map(n => ({
|
||||
id: n,
|
||||
adjacency: sampleArray(nodeNames),
|
||||
}), nodeNames.length),
|
||||
remove: this.randomExistingNode(),
|
||||
remove: this._removeNode(),
|
||||
}));
|
||||
}
|
||||
|
||||
createRandomNodes(n, prefix = 'zing') {
|
||||
_addNodes(n, prefix = 'zing') {
|
||||
const ns = this.props.nodes;
|
||||
const nodeNames = ns.keySeq().toJS();
|
||||
const newNodeNames = range(ns.size, ns.size + n).map(i => (
|
||||
@@ -254,7 +256,7 @@ class DebugToolbar extends React.Component {
|
||||
`${prefix}${i}`
|
||||
));
|
||||
const allNodes = nodeNames.concat(newNodeNames);
|
||||
return newNodeNames.map(name => deltaAdd(
|
||||
return newNodeNames.map((name) => deltaAdd(
|
||||
name,
|
||||
sampleArray(allNodes),
|
||||
sample(SHAPES),
|
||||
@@ -267,13 +269,13 @@ class DebugToolbar extends React.Component {
|
||||
addNodes(n, prefix = 'zing') {
|
||||
setTimeout(() => {
|
||||
this.asyncDispatch(receiveNodesDelta({
|
||||
add: this.createRandomNodes(n, prefix)
|
||||
add: this._addNodes(n, prefix)
|
||||
}));
|
||||
log('added nodes', n);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
randomExistingNode() {
|
||||
_removeNode() {
|
||||
const ns = this.props.nodes;
|
||||
const nodeNames = ns.keySeq().toJS();
|
||||
return [nodeNames[random(nodeNames.length - 1)]];
|
||||
@@ -281,7 +283,7 @@ class DebugToolbar extends React.Component {
|
||||
|
||||
removeNode() {
|
||||
this.asyncDispatch(receiveNodesDelta({
|
||||
remove: this.randomExistingNode()
|
||||
remove: this._removeNode()
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -291,7 +293,7 @@ class DebugToolbar extends React.Component {
|
||||
return (
|
||||
<div className="debug-panel">
|
||||
<div>
|
||||
<strong>Add nodes </strong>
|
||||
<label>Add nodes </label>
|
||||
<button onClick={() => this.addNodes(1)}>+1</button>
|
||||
<button onClick={() => this.addNodes(10)}>+10</button>
|
||||
<input type="number" onChange={this.onChange} value={this.state.nodesToAdd} />
|
||||
@@ -306,7 +308,7 @@ class DebugToolbar extends React.Component {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<strong>Logging </strong>
|
||||
<label>Logging</label>
|
||||
<button onClick={() => enableLog('*')}>scope:*</button>
|
||||
<button onClick={() => enableLog('dispatcher')}>scope:dispatcher</button>
|
||||
<button onClick={() => enableLog('app-key-press')}>scope:app-key-press</button>
|
||||
@@ -315,7 +317,7 @@ class DebugToolbar extends React.Component {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<strong>Colors </strong>
|
||||
<label>Colors</label>
|
||||
<button onClick={this.toggleColors}>toggle</button>
|
||||
</div>
|
||||
|
||||
@@ -338,7 +340,7 @@ class DebugToolbar extends React.Component {
|
||||
{LABEL_PREFIXES.map(r => (
|
||||
<tr key={r}>
|
||||
{LABEL_PREFIXES.map(c => (
|
||||
<td key={c} title={`(${r}, ${c})`} style={{backgroundColor: fn(r, c)}} />
|
||||
<td key={c} title={`(${r}, ${c})`} style={{backgroundColor: fn(r, c)}}></td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
@@ -347,19 +349,19 @@ class DebugToolbar extends React.Component {
|
||||
))}
|
||||
|
||||
<div>
|
||||
<strong>State </strong>
|
||||
<label>state</label>
|
||||
<button onClick={() => this.setLoading(true)}>Set doing initial load</button>
|
||||
<button onClick={() => this.setLoading(false)}>Stop</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<strong>Short-lived nodes </strong>
|
||||
<label>Short-lived nodes</label>
|
||||
<button onClick={() => this.setShortLived()}>Toggle short-lived nodes</button>
|
||||
<button onClick={() => this.setIntermittent()}>Toggle intermittent nodes</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<strong>Measure React perf for </strong>
|
||||
<label>Measure React perf for </label>
|
||||
<button onClick={() => startPerf(2)}>2s</button>
|
||||
<button onClick={() => startPerf(5)}>5s</button>
|
||||
<button onClick={() => startPerf(10)}>10s</button>
|
||||
|
||||
@@ -3,11 +3,8 @@ import { connect } from 'react-redux';
|
||||
|
||||
import NodeDetails from './node-details';
|
||||
import EmbeddedTerminal from './embedded-terminal';
|
||||
import {
|
||||
DETAILS_PANEL_WIDTH as WIDTH,
|
||||
DETAILS_PANEL_OFFSET as OFFSET,
|
||||
DETAILS_PANEL_MARGINS as MARGINS
|
||||
} from '../constants/styles';
|
||||
import { DETAILS_PANEL_WIDTH as WIDTH, DETAILS_PANEL_OFFSET as OFFSET,
|
||||
DETAILS_PANEL_MARGINS as MARGINS } from '../constants/styles';
|
||||
|
||||
class DetailsCard extends React.Component {
|
||||
|
||||
@@ -33,15 +30,15 @@ class DetailsCard extends React.Component {
|
||||
const scaleY = origin.height / (window.innerHeight - MARGINS.bottom - MARGINS.top) / 2;
|
||||
const scaleX = origin.width / WIDTH / 2;
|
||||
const centerX = window.innerWidth - MARGINS.right - (WIDTH / 2);
|
||||
const centerY = (panelHeight / 2) + MARGINS.top;
|
||||
const dx = (origin.left + (origin.width / 2)) - centerX;
|
||||
const dy = (origin.top + (origin.height / 2)) - centerY;
|
||||
const centerY = (panelHeight) / 2 + MARGINS.top;
|
||||
const dx = (origin.left + origin.width / 2) - centerX;
|
||||
const dy = (origin.top + origin.height / 2) - centerY;
|
||||
transform = `translate(${dx}px, ${dy}px) scale(${scaleX},${scaleY})`;
|
||||
} else {
|
||||
// stack effect: shift top cards to the left, shrink lower cards vertically
|
||||
const shiftX = -1 * this.props.index * OFFSET;
|
||||
const position = this.props.cardCount - this.props.index - 1; // reverse index
|
||||
const scaleY = (position === 0) ? 1 : (panelHeight - (2 * OFFSET * position)) / panelHeight;
|
||||
const scaleY = position === 0 ? 1 : (panelHeight - 2 * OFFSET * position) / panelHeight;
|
||||
if (scaleY !== 1) {
|
||||
transform = `translateX(${shiftX}px) scaleY(${scaleY})`;
|
||||
} else {
|
||||
|
||||
@@ -9,12 +9,10 @@ class Details extends React.Component {
|
||||
// render all details as cards, later cards go on top
|
||||
return (
|
||||
<div className="details">
|
||||
{details.toIndexedSeq().map((obj, index) => (
|
||||
<DetailsCard
|
||||
key={obj.id} index={index} cardCount={details.size}
|
||||
nodeControlStatus={controlStatus.get(obj.id)} {...obj}
|
||||
/>
|
||||
))}
|
||||
{details.toIndexedSeq().map((obj, index) => <DetailsCard key={obj.id}
|
||||
index={index} cardCount={details.size}
|
||||
nodeControlStatus={controlStatus.get(obj.id)} {...obj} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,10 +4,8 @@ import LogMonitor from 'redux-devtools-log-monitor';
|
||||
import DockMonitor from 'redux-devtools-dock-monitor';
|
||||
|
||||
export default createDevTools(
|
||||
<DockMonitor
|
||||
defaultIsVisible={false}
|
||||
toggleVisibilityKey="ctrl-h"
|
||||
changePositionKey="ctrl-w">
|
||||
<DockMonitor defaultIsVisible={false}
|
||||
toggleVisibilityKey="ctrl-h" changePositionKey="ctrl-w">
|
||||
<LogMonitor />
|
||||
</DockMonitor>
|
||||
);
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import moment from 'moment';
|
||||
|
||||
import Plugins from './plugins';
|
||||
import Plugins from './plugins.js';
|
||||
import { getUpdateBufferSize } from '../utils/update-buffer-utils';
|
||||
import { contrastModeUrl, isContrastMode } from '../utils/contrast-utils';
|
||||
import { clickDownloadGraph, clickForceRelayout, clickPauseUpdate,
|
||||
@@ -48,11 +48,8 @@ class Footer extends React.Component {
|
||||
<div className="footer">
|
||||
|
||||
<div className="footer-status">
|
||||
{versionUpdate && <a
|
||||
className="footer-versionupdate"
|
||||
title={versionUpdateTitle}
|
||||
href={versionUpdate.downloadUrl}
|
||||
target="_blank" rel="noopener noreferrer">
|
||||
{versionUpdate && <a className="footer-versionupdate"
|
||||
title={versionUpdateTitle} href={versionUpdate.downloadUrl} target="_blank">
|
||||
Update available: {versionUpdate.version}
|
||||
</a>}
|
||||
<span className="footer-label">Version</span>
|
||||
@@ -70,14 +67,11 @@ class Footer extends React.Component {
|
||||
{pauseLabel !== '' && <span className="footer-label">{pauseLabel}</span>}
|
||||
<span className="fa fa-pause" />
|
||||
</a>
|
||||
<a
|
||||
className="footer-icon"
|
||||
onClick={this.props.clickForceRelayout}
|
||||
<a className="footer-icon" onClick={this.props.clickForceRelayout}
|
||||
title={forceRelayoutTitle}>
|
||||
<span className="fa fa-refresh" />
|
||||
</a>
|
||||
<a
|
||||
className="footer-icon" onClick={this.props.clickDownloadGraph}
|
||||
<a className="footer-icon" onClick={this.props.clickDownloadGraph}
|
||||
title="Save canvas as SVG (does not include search highlighting)">
|
||||
<span className="fa fa-download" />
|
||||
</a>
|
||||
@@ -87,13 +81,11 @@ class Footer extends React.Component {
|
||||
<a className="footer-icon" href={otherContrastModeUrl} title={otherContrastModeTitle}>
|
||||
<span className="fa fa-adjust" />
|
||||
</a>
|
||||
<a
|
||||
className="footer-icon" title="Report an issue"
|
||||
href="https://gitreports.com/issue/weaveworks/scope"
|
||||
target="_blank" rel="noopener noreferrer">
|
||||
<a className="footer-icon" href="https://gitreports.com/issue/weaveworks/scope" target="_blank" title="Report an issue">
|
||||
<span className="fa fa-bug" />
|
||||
</a>
|
||||
<a className="footer-icon" onClick={this.props.toggleHelp} title="Show help">
|
||||
<a className="footer-icon" onClick={this.props.toggleHelp}
|
||||
title="Show help">
|
||||
<span className="fa fa-question" />
|
||||
</a>
|
||||
</div>
|
||||
@@ -114,11 +106,6 @@ function mapStateToProps(state) {
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
{
|
||||
clickDownloadGraph,
|
||||
clickForceRelayout,
|
||||
clickPauseUpdate,
|
||||
clickResumeUpdate,
|
||||
toggleHelp
|
||||
}
|
||||
{ clickDownloadGraph, clickForceRelayout, clickPauseUpdate,
|
||||
clickResumeUpdate, toggleHelp }
|
||||
)(Footer);
|
||||
|
||||
@@ -4,21 +4,6 @@ import classNames from 'classnames';
|
||||
|
||||
import { toggleGridMode } from '../actions/app-actions';
|
||||
|
||||
|
||||
const Item = (icons, label, isSelected, onClick) => {
|
||||
const className = classNames('grid-mode-selector-action', {
|
||||
'grid-mode-selector-action-selected': isSelected
|
||||
});
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
onClick={onClick} >
|
||||
<span className={icons} style={{fontSize: 12}} />
|
||||
<span>{label}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
class GridModeSelector extends React.Component {
|
||||
|
||||
constructor(props, context) {
|
||||
@@ -36,14 +21,28 @@ class GridModeSelector extends React.Component {
|
||||
return this.props.toggleGridMode(false);
|
||||
}
|
||||
|
||||
renderItem(icons, label, isSelected, onClick) {
|
||||
const className = classNames('grid-mode-selector-action', {
|
||||
'grid-mode-selector-action-selected': isSelected
|
||||
});
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
onClick={onClick} >
|
||||
<span className={icons} style={{fontSize: 12}} />
|
||||
<span>{label}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { gridMode } = this.props;
|
||||
|
||||
return (
|
||||
<div className="grid-mode-selector">
|
||||
<div className="grid-mode-selector-wrapper">
|
||||
{Item('fa fa-share-alt', 'Graph', !gridMode, this.disableGridMode)}
|
||||
{Item('fa fa-table', 'Table', gridMode, this.enableGridMode)}
|
||||
{this.renderItem('fa fa-share-alt', 'Graph', !gridMode, this.disableGridMode)}
|
||||
{this.renderItem('fa fa-table', 'Table', gridMode, this.enableGridMode)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -85,7 +85,7 @@ function renderSearches(searches) {
|
||||
{searches.map(({term, label}) => (
|
||||
<div key={term} className="help-panel-search-row">
|
||||
<div className="help-panel-search-row-term">
|
||||
<i className="fa fa-search search-label-icon" />
|
||||
<i className="fa fa-search search-label-icon"></i>
|
||||
{term}
|
||||
</div>
|
||||
<div className="help-panel-search-row-term-label">{label}</div>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint react/jsx-first-prop-new-line: "off" */
|
||||
/* eslint max-len: "off" */
|
||||
import React from 'react';
|
||||
|
||||
|
||||
@@ -6,22 +6,25 @@ import MatchedText from './matched-text';
|
||||
const SHOW_ROW_COUNT = 2;
|
||||
const MAX_MATCH_LENGTH = 24;
|
||||
|
||||
|
||||
const Match = match => (
|
||||
<div className="matched-results-match" key={match.label}>
|
||||
<div className="matched-results-match-wrapper">
|
||||
<span className="matched-results-match-label">
|
||||
{match.label}:
|
||||
</span>
|
||||
<MatchedText
|
||||
text={match.text} match={match}
|
||||
maxLength={MAX_MATCH_LENGTH}
|
||||
truncate={match.truncate} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
class MatchedResults extends React.Component {
|
||||
|
||||
renderMatch(matches, field) {
|
||||
const match = matches.get(field);
|
||||
const text = match.text;
|
||||
|
||||
return (
|
||||
<div className="matched-results-match" key={match.label}>
|
||||
<div className="matched-results-match-wrapper">
|
||||
<span className="matched-results-match-label">
|
||||
{match.label}:
|
||||
</span>
|
||||
<MatchedText text={text} match={match} maxLength={MAX_MATCH_LENGTH}
|
||||
truncate={match.truncate} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { matches, style } = this.props;
|
||||
|
||||
@@ -41,7 +44,7 @@ class MatchedResults extends React.Component {
|
||||
|
||||
return (
|
||||
<div className="matched-results" style={style}>
|
||||
{matches.keySeq().take(SHOW_ROW_COUNT).map(fieldId => Match(matches.get(fieldId)))}
|
||||
{matches.keySeq().take(SHOW_ROW_COUNT).map(fieldId => this.renderMatch(matches, fieldId))}
|
||||
{moreFieldMatches && <div className="matched-results-more" title={moreFieldMatchesTitle}>
|
||||
{`${moreFieldMatches.size} more matches`}
|
||||
</div>}
|
||||
|
||||
@@ -84,7 +84,7 @@ class MatchedText extends React.Component {
|
||||
render() {
|
||||
const { match, text, truncate, maxLength } = this.props;
|
||||
|
||||
const showFullValue = !truncate || (match && (match.start + match.length) > truncate);
|
||||
const showFullValue = !truncate || match && match.start + match.length > truncate;
|
||||
const displayText = showFullValue ? text : text.slice(0, truncate);
|
||||
|
||||
if (!match) {
|
||||
|
||||
@@ -45,7 +45,7 @@ class MetricSelectorItem extends React.Component {
|
||||
onMouseOver={this.onMouseOver}
|
||||
onClick={this.onMouseClick}>
|
||||
{metric.get('label')}
|
||||
{isPinned && <span className="fa fa-thumb-tack" />}
|
||||
{isPinned && <span className="fa fa-thumb-tack"></span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ class NetworkSelectorItem extends React.Component {
|
||||
onClick={this.onMouseClick}
|
||||
style={style}>
|
||||
{network.get('label')}
|
||||
{isPinned && <span className="fa fa-thumb-tack" />}
|
||||
{isPinned && <span className="fa fa-thumb-tack"></span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ function getTruncationText(count) {
|
||||
+ ` (${count} extra entries not included). We are working to remove this limitation.`;
|
||||
}
|
||||
|
||||
class NodeDetails extends React.Component {
|
||||
export class NodeDetails extends React.Component {
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
@@ -53,10 +53,8 @@ class NodeDetails extends React.Component {
|
||||
return (
|
||||
<div className="node-details-tools-wrapper">
|
||||
<div className="node-details-tools">
|
||||
{showSwitchTopology && <span
|
||||
title={topologyTitle}
|
||||
className="fa fa-long-arrow-left"
|
||||
onClick={this.handleShowTopologyForNode}>
|
||||
{showSwitchTopology && <span title={topologyTitle}
|
||||
className="fa fa-long-arrow-left" onClick={this.handleShowTopologyForNode}>
|
||||
<span>Show in <span>{this.props.topologyId.replace(/-/g, ' ')}</span></span>
|
||||
</span>}
|
||||
<span title="Close details" className="fa fa-close" onClick={this.handleClickClose} />
|
||||
@@ -170,8 +168,7 @@ class NodeDetails extends React.Component {
|
||||
</div>
|
||||
|
||||
{showControls && <div className="node-details-controls-wrapper" style={styles.controls}>
|
||||
<NodeDetailsControls
|
||||
nodeId={this.props.nodeId}
|
||||
<NodeDetailsControls nodeId={this.props.nodeId}
|
||||
controls={details.controls}
|
||||
pending={pending}
|
||||
error={error} />
|
||||
@@ -187,23 +184,20 @@ class NodeDetails extends React.Component {
|
||||
<NodeDetailsInfo rows={details.metadata} matches={nodeMatches.get('metadata')} />
|
||||
</div>}
|
||||
|
||||
{details.connections && details.connections.map(connections => (
|
||||
<div className="node-details-content-section" key={connections.id}>
|
||||
<NodeDetailsTable
|
||||
{...connections}
|
||||
nodes={connections.connections}
|
||||
nodeIdKey="nodeId"
|
||||
/>
|
||||
{details.connections && details.connections.map(connections => <div
|
||||
className="node-details-content-section" key={connections.id}>
|
||||
<NodeDetailsTable {...connections} nodes={connections.connections}
|
||||
nodeIdKey="nodeId" />
|
||||
</div>
|
||||
))}
|
||||
)}
|
||||
|
||||
{details.children && details.children.map(children => (
|
||||
<div className="node-details-content-section" key={children.topologyId}>
|
||||
{details.children && details.children.map(children => <div
|
||||
className="node-details-content-section" key={children.topologyId}>
|
||||
<NodeDetailsTable {...children} />
|
||||
</div>
|
||||
))}
|
||||
)}
|
||||
|
||||
{details.tables && details.tables.length > 0 && details.tables.map((table) => {
|
||||
{details.tables && details.tables.length > 0 && details.tables.map(table => {
|
||||
if (table.rows.length > 0) {
|
||||
return (
|
||||
<div className="node-details-content-section" key={table.id}>
|
||||
@@ -214,8 +208,7 @@ class NodeDetails extends React.Component {
|
||||
<Warning text={getTruncationText(table.truncationCount)} />
|
||||
</span>}
|
||||
</div>
|
||||
<NodeDetailsLabels
|
||||
rows={table.rows} controls={table.controls}
|
||||
<NodeDetailsLabels rows={table.rows} controls={table.controls}
|
||||
matches={nodeMatches.get('tables')} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -21,7 +21,7 @@ export default function NodeDetailsControls({controls, error, nodeId, pending})
|
||||
{sortBy(controls, 'rank').map(control => <NodeDetailsControlButton
|
||||
nodeId={nodeId} control={control} pending={pending} key={control.id} />)}
|
||||
</span>
|
||||
{controls && <span title="Applying..." className={spinnerClassName} />}
|
||||
{controls && <span title="Applying..." className={spinnerClassName}></span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,10 +6,9 @@ import { formatMetric } from '../../utils/string-utils';
|
||||
function NodeDetailsHealthItem(props) {
|
||||
return (
|
||||
<div className="node-details-health-item">
|
||||
<div className="node-details-health-item-value">{formatMetric(props.value, props)}</div>
|
||||
<div className="node-details-health-item-value">{formatMetric(props.value, props)}</div>
|
||||
<div className="node-details-health-item-sparkline">
|
||||
<Sparkline
|
||||
data={props.samples} max={props.max} format={props.format}
|
||||
<Sparkline data={props.samples} max={props.max} format={props.format}
|
||||
first={props.first} last={props.last} />
|
||||
</div>
|
||||
<div className="node-details-health-item-label">{props.label}</div>
|
||||
|
||||
@@ -33,13 +33,10 @@ export default class NodeDetailsHealth extends React.Component {
|
||||
<div className="node-details-health" style={{flexWrap, justifyContent}}>
|
||||
<div className="node-details-health-wrapper">
|
||||
{primeMetrics.map(item => <NodeDetailsHealthItem key={item.id} {...item} />)}
|
||||
{showOverflow && <NodeDetailsHealthOverflow
|
||||
items={overflowMetrics}
|
||||
handleClick={this.handleClickMore}
|
||||
/>}
|
||||
{showOverflow && <NodeDetailsHealthOverflow items={overflowMetrics}
|
||||
handleClick={this.handleClickMore} />}
|
||||
</div>
|
||||
<ShowMore
|
||||
handleClick={this.handleClickMore} collection={this.props.metrics}
|
||||
<ShowMore handleClick={this.handleClickMore} collection={this.props.metrics}
|
||||
expanded={this.state.expanded} notShown={notShown} hideNumber />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -38,7 +38,7 @@ export default class NodeDetailsInfo extends React.Component {
|
||||
|
||||
return (
|
||||
<div className="node-details-info">
|
||||
{rows.map((field) => {
|
||||
{rows.map(field => {
|
||||
const { value, title } = formatDataType(field);
|
||||
return (
|
||||
<div className="node-details-info-field" key={field.id}>
|
||||
@@ -54,8 +54,7 @@ export default class NodeDetailsInfo extends React.Component {
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<ShowMore
|
||||
handleClick={this.handleClickMore} collection={this.props.rows}
|
||||
<ShowMore handleClick={this.handleClickMore} collection={this.props.rows}
|
||||
expanded={this.state.expanded} notShown={notShown} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -6,14 +6,6 @@ import MatchedText from '../matched-text';
|
||||
import NodeDetailsControlButton from './node-details-control-button';
|
||||
import ShowMore from '../show-more';
|
||||
|
||||
|
||||
const Controls = controls => (
|
||||
<div className="node-details-labels-controls">
|
||||
{sortBy(controls, 'rank').map(control => <NodeDetailsControlButton
|
||||
nodeId={control.nodeId} control={control} key={control.id} />)}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default class NodeDetailsLabels extends React.Component {
|
||||
|
||||
constructor(props, context) {
|
||||
@@ -23,6 +15,7 @@ export default class NodeDetailsLabels extends React.Component {
|
||||
limit: this.DEFAULT_LIMIT,
|
||||
};
|
||||
this.handleLimitClick = this.handleLimitClick.bind(this);
|
||||
this.renderControls = this.renderControls.bind(this);
|
||||
}
|
||||
|
||||
handleLimitClick() {
|
||||
@@ -30,6 +23,15 @@ export default class NodeDetailsLabels extends React.Component {
|
||||
this.setState({limit});
|
||||
}
|
||||
|
||||
renderControls(controls) {
|
||||
return (
|
||||
<div className="node-details-labels-controls">
|
||||
{sortBy(controls, 'rank').map(control => <NodeDetailsControlButton
|
||||
nodeId={control.nodeId} control={control} key={control.id} />)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { controls, matches = makeMap() } = this.props;
|
||||
let rows = this.props.rows;
|
||||
@@ -47,12 +49,10 @@ export default class NodeDetailsLabels extends React.Component {
|
||||
|
||||
return (
|
||||
<div className="node-details-labels">
|
||||
{controls && Controls(controls)}
|
||||
{rows.map(field => (
|
||||
<div className="node-details-labels-field" key={field.id}>
|
||||
<div
|
||||
className="node-details-labels-field-label truncate"
|
||||
title={field.label} key={field.id}>
|
||||
{controls && this.renderControls(controls)}
|
||||
{rows.map(field => (<div className="node-details-labels-field" key={field.id}>
|
||||
<div className="node-details-labels-field-label truncate" title={field.label}
|
||||
key={field.id}>
|
||||
{field.label}
|
||||
</div>
|
||||
<div className="node-details-labels-field-value truncate" title={field.value}>
|
||||
@@ -60,8 +60,7 @@ export default class NodeDetailsLabels extends React.Component {
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<ShowMore
|
||||
handleClick={this.handleLimitClick} collection={this.props.rows}
|
||||
<ShowMore handleClick={this.handleLimitClick} collection={this.props.rows}
|
||||
expanded={expanded} notShown={notShown} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { clickRelative } from '../../actions/app-actions';
|
||||
@@ -9,29 +10,18 @@ class NodeDetailsRelativesLink extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
this.saveNodeRef = this.saveNodeRef.bind(this);
|
||||
}
|
||||
|
||||
handleClick(ev) {
|
||||
ev.preventDefault();
|
||||
this.props.dispatch(clickRelative(
|
||||
this.props.id,
|
||||
this.props.topologyId,
|
||||
this.props.label,
|
||||
this.node.getBoundingClientRect()
|
||||
));
|
||||
}
|
||||
|
||||
saveNodeRef(ref) {
|
||||
this.node = ref;
|
||||
this.props.dispatch(clickRelative(this.props.id, this.props.topologyId,
|
||||
this.props.label, ReactDOM.findDOMNode(this).getBoundingClientRect()));
|
||||
}
|
||||
|
||||
render() {
|
||||
const title = `View in ${this.props.topologyId}: ${this.props.label}`;
|
||||
return (
|
||||
<span
|
||||
className="node-details-relatives-link" title={title}
|
||||
onClick={this.handleClick} ref={this.saveNodeRef}>
|
||||
<span className="node-details-relatives-link" title={title} onClick={this.handleClick}>
|
||||
<MatchedText text={this.props.label} match={this.props.match} />
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -39,11 +39,8 @@ export default class NodeDetailsRelatives extends React.Component {
|
||||
key={relative.id}
|
||||
match={matches.get(relative.id)}
|
||||
{...relative} />))}
|
||||
{showLimitAction && <span
|
||||
className="node-details-relatives-more"
|
||||
onClick={this.handleLimitClick}>
|
||||
{limitActionText}
|
||||
</span>}
|
||||
{showLimitAction && <span className="node-details-relatives-more"
|
||||
onClick={this.handleLimitClick}>{limitActionText}</span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { clickRelative } from '../../actions/app-actions';
|
||||
@@ -8,21 +9,12 @@ class NodeDetailsTableNodeLink extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
this.saveNodeRef = this.saveNodeRef.bind(this);
|
||||
}
|
||||
|
||||
handleClick(ev) {
|
||||
ev.preventDefault();
|
||||
this.props.dispatch(clickRelative(
|
||||
this.props.nodeId,
|
||||
this.props.topologyId,
|
||||
this.props.label,
|
||||
this.node.getBoundingClientRect()
|
||||
));
|
||||
}
|
||||
|
||||
saveNodeRef(ref) {
|
||||
this.node = ref;
|
||||
this.props.dispatch(clickRelative(this.props.nodeId, this.props.topologyId,
|
||||
this.props.label, ReactDOM.findDOMNode(this).getBoundingClientRect()));
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -31,9 +23,8 @@ class NodeDetailsTableNodeLink extends React.Component {
|
||||
|
||||
if (linkable) {
|
||||
return (
|
||||
<span
|
||||
className="node-details-table-node-link" title={title}
|
||||
ref={this.saveNodeRef} onClick={this.handleClick}>
|
||||
<span className="node-details-table-node-link" title={title}
|
||||
onClick={this.handleClick}>
|
||||
{label}
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import NodeDetailsTableNodeLink from './node-details-table-node-link';
|
||||
@@ -7,9 +8,9 @@ import { formatDataType } from '../../utils/string-utils';
|
||||
|
||||
function getValuesForNode(node) {
|
||||
const values = {};
|
||||
['metrics', 'metadata'].forEach((collection) => {
|
||||
['metrics', 'metadata'].forEach(collection => {
|
||||
if (node[collection]) {
|
||||
node[collection].forEach((field) => {
|
||||
node[collection].forEach(field => {
|
||||
const result = Object.assign({}, field);
|
||||
result.valueType = collection;
|
||||
values[field.id] = result;
|
||||
@@ -17,7 +18,7 @@ function getValuesForNode(node) {
|
||||
}
|
||||
});
|
||||
|
||||
(node.parents || []).forEach((p) => {
|
||||
(node.parents || []).forEach(p => {
|
||||
values[p.topologyId] = {
|
||||
id: p.topologyId,
|
||||
label: p.topologyId,
|
||||
@@ -39,9 +40,7 @@ function renderValues(node, columns = [], columnStyles = []) {
|
||||
if (field.valueType === 'metadata') {
|
||||
const {value, title} = formatDataType(field);
|
||||
return (
|
||||
<td
|
||||
className="node-details-table-node-value truncate"
|
||||
title={title}
|
||||
<td className="node-details-table-node-value truncate" title={title}
|
||||
style={style}
|
||||
key={field.id}>
|
||||
{value}
|
||||
@@ -50,9 +49,7 @@ function renderValues(node, columns = [], columnStyles = []) {
|
||||
}
|
||||
if (field.valueType === 'relatives') {
|
||||
return (
|
||||
<td
|
||||
className="node-details-table-node-value truncate"
|
||||
title={field.value}
|
||||
<td className="node-details-table-node-value truncate" title={field.value}
|
||||
style={style}
|
||||
key={field.id}>
|
||||
{<NodeDetailsTableNodeLink linkable nodeId={field.relative.id} {...field.relative} />}
|
||||
@@ -78,15 +75,15 @@ export default class NodeDetailsTableRow extends React.Component {
|
||||
//
|
||||
this.mouseDragOrigin = [0, 0];
|
||||
|
||||
this.saveLabelElementRef = this.saveLabelElementRef.bind(this);
|
||||
this.storeLabelRef = this.storeLabelRef.bind(this);
|
||||
this.onMouseDown = this.onMouseDown.bind(this);
|
||||
this.onMouseUp = this.onMouseUp.bind(this);
|
||||
this.onMouseEnter = this.onMouseEnter.bind(this);
|
||||
this.onMouseLeave = this.onMouseLeave.bind(this);
|
||||
}
|
||||
|
||||
saveLabelElementRef(ref) {
|
||||
this.labelElement = ref;
|
||||
storeLabelRef(ref) {
|
||||
this.labelEl = ref;
|
||||
}
|
||||
|
||||
onMouseEnter() {
|
||||
@@ -117,7 +114,7 @@ export default class NodeDetailsTableRow extends React.Component {
|
||||
}
|
||||
|
||||
const { node, onClick } = this.props;
|
||||
onClick(ev, node, this.labelElement);
|
||||
onClick(ev, node, ReactDOM.findDOMNode(this.labelEl));
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -135,9 +132,8 @@ export default class NodeDetailsTableRow extends React.Component {
|
||||
onMouseEnter={onMouseEnterRow && this.onMouseEnter}
|
||||
onMouseLeave={onMouseLeaveRow && this.onMouseLeave}
|
||||
className={className}>
|
||||
<td
|
||||
className="node-details-table-node-label truncate"
|
||||
ref={this.saveLabelElementRef} style={firstColumnStyle}>
|
||||
<td ref={this.storeLabelRef} className="node-details-table-node-label truncate"
|
||||
style={firstColumnStyle}>
|
||||
{this.props.renderIdCell(Object.assign(node, {topologyId, nodeId}))}
|
||||
</td>
|
||||
{values}
|
||||
@@ -148,5 +144,5 @@ export default class NodeDetailsTableRow extends React.Component {
|
||||
|
||||
|
||||
NodeDetailsTableRow.defaultProps = {
|
||||
renderIdCell: props => <NodeDetailsTableNodeLink {...props} />
|
||||
renderIdCell: (props) => <NodeDetailsTableNodeLink {...props} />
|
||||
};
|
||||
|
||||
@@ -113,13 +113,13 @@ function getNodeValue(node, header) {
|
||||
|
||||
|
||||
function getValueForSortedBy(sortedByHeader) {
|
||||
return node => maybeToLower(getNodeValue(node, sortedByHeader));
|
||||
return (node) => maybeToLower(getNodeValue(node, sortedByHeader));
|
||||
}
|
||||
|
||||
|
||||
function getMetaDataSorters(nodes) {
|
||||
// returns an array of sorters that will take a node
|
||||
return get(nodes, [0, 'metadata'], []).map((field, index) => (node) => {
|
||||
return get(nodes, [0, 'metadata'], []).map((field, index) => node => {
|
||||
const nodeMetadataField = node.metadata && node.metadata[index];
|
||||
if (nodeMetadataField) {
|
||||
if (isNumber(nodeMetadataField)) {
|
||||
@@ -225,7 +225,7 @@ export default class NodeDetailsTable extends React.Component {
|
||||
<tr>
|
||||
{headers.map((header, i) => {
|
||||
const headerClasses = ['node-details-table-header', 'truncate'];
|
||||
const onHeaderClick = (ev) => {
|
||||
const onHeaderClick = ev => {
|
||||
this.handleHeaderClick(ev, header.id, sortedBy, sortedDesc);
|
||||
};
|
||||
// sort by first metric by default
|
||||
@@ -243,8 +243,7 @@ export default class NodeDetailsTable extends React.Component {
|
||||
header.label;
|
||||
|
||||
return (
|
||||
<td
|
||||
className={headerClasses.join(' ')} style={style} onClick={onHeaderClick}
|
||||
<td className={headerClasses.join(' ')} style={style} onClick={onHeaderClick}
|
||||
title={header.label} key={header.id}>
|
||||
{isSortedAsc
|
||||
&& <span className="node-details-table-header-sorter fa fa-caret-up" />}
|
||||
@@ -279,15 +278,14 @@ export default class NodeDetailsTable extends React.Component {
|
||||
const className = classNames('node-details-table-wrapper-wrapper', this.props.className);
|
||||
|
||||
return (
|
||||
<div className={className} style={this.props.style}>
|
||||
<div className={className}
|
||||
style={this.props.style}>
|
||||
<div className="node-details-table-wrapper">
|
||||
<table className="node-details-table">
|
||||
<thead>
|
||||
{this.renderHeaders(sortedBy, sortedDesc)}
|
||||
</thead>
|
||||
<tbody
|
||||
style={this.props.tbodyStyle}
|
||||
onMouseEnter={onMouseEnter}
|
||||
<tbody style={this.props.tbodyStyle} onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}>
|
||||
{nodes && nodes.map(node => (
|
||||
<NodeDetailsTableRow
|
||||
|
||||
@@ -4,7 +4,7 @@ import { connect } from 'react-redux';
|
||||
import NodesChart from '../charts/nodes-chart';
|
||||
import NodesGrid from '../charts/nodes-grid';
|
||||
import NodesError from '../charts/nodes-error';
|
||||
import DelayedShow from '../utils/delayed-show';
|
||||
import { DelayedShow } from '../utils/delayed-show';
|
||||
import { Loading, getNodeType } from './loading';
|
||||
import { isTopologyEmpty } from '../utils/topology-utils';
|
||||
import { CANVAS_MARGINS } from '../constants/styles';
|
||||
@@ -13,20 +13,6 @@ const navbarHeight = 194;
|
||||
const marginTop = 0;
|
||||
|
||||
|
||||
const EmptyTopologyError = show => (
|
||||
<NodesError faIconClass="fa-circle-thin" hidden={!show}>
|
||||
<div className="heading">Nothing to show. This can have any of these reasons:</div>
|
||||
<ul>
|
||||
<li>We haven't received any reports from probes recently.
|
||||
Are the probes properly configured?</li>
|
||||
<li>There are nodes, but they're currently hidden. Check the view options
|
||||
in the bottom-left if they allow for showing hidden nodes.</li>
|
||||
<li>Containers view only: you're not running Docker,
|
||||
or you don't have any containers.</li>
|
||||
</ul>
|
||||
</NodesError>
|
||||
);
|
||||
|
||||
class Nodes extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
@@ -46,6 +32,22 @@ class Nodes extends React.Component {
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
}
|
||||
|
||||
renderEmptyTopologyError(show) {
|
||||
return (
|
||||
<NodesError faIconClass="fa-circle-thin" hidden={!show}>
|
||||
<div className="heading">Nothing to show. This can have any of these reasons:</div>
|
||||
<ul>
|
||||
<li>We haven't received any reports from probes recently.
|
||||
Are the probes properly configured?</li>
|
||||
<li>There are nodes, but they're currently hidden. Check the view options
|
||||
in the bottom-left if they allow for showing hidden nodes.</li>
|
||||
<li>Containers view only: you're not running Docker,
|
||||
or you don't have any containers.</li>
|
||||
</ul>
|
||||
</NodesError>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { topologyEmpty, gridMode, topologiesLoaded, nodesLoaded, topologies,
|
||||
currentTopology } = this.props;
|
||||
@@ -58,11 +60,16 @@ class Nodes extends React.Component {
|
||||
itemType={getNodeType(currentTopology, topologies)}
|
||||
show={topologiesLoaded && !nodesLoaded} />
|
||||
</DelayedShow>
|
||||
{EmptyTopologyError(topologiesLoaded && nodesLoaded && topologyEmpty)}
|
||||
{this.renderEmptyTopologyError(topologiesLoaded && nodesLoaded && topologyEmpty)}
|
||||
|
||||
{gridMode ?
|
||||
<NodesGrid {...this.state} nodeSize="24" margins={CANVAS_MARGINS} /> :
|
||||
<NodesChart {...this.state} margins={CANVAS_MARGINS} />}
|
||||
<NodesGrid {...this.state}
|
||||
nodeSize="24"
|
||||
margins={CANVAS_MARGINS}
|
||||
/> :
|
||||
<NodesChart {...this.state}
|
||||
margins={CANVAS_MARGINS}
|
||||
/>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,25 +3,24 @@ import { connect } from 'react-redux';
|
||||
import classNames from 'classnames';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
|
||||
|
||||
const Plugin = ({id, label, description, status}) => {
|
||||
const error = status !== 'ok';
|
||||
const className = classNames({ error });
|
||||
const title = `Plugin description: ${description}<br />Status: ${status}`;
|
||||
|
||||
// Inner span to hold styling so we don't effect the "before:content"
|
||||
return (
|
||||
<span className="plugins-plugin" key={id}>
|
||||
<span className={className} data-tip={title} data-multiline>
|
||||
{error && <span className="plugins-plugin-icon fa fa-exclamation-circle" />}
|
||||
{label || id}
|
||||
</span>
|
||||
<ReactTooltip class="tooltip" effect="solid" offset={{right: 7}} />
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
class Plugins extends React.Component {
|
||||
renderPlugin({id, label, description, status}) {
|
||||
const error = status !== 'ok';
|
||||
const className = classNames({ error });
|
||||
const title = `Plugin description: ${description}<br />Status: ${status}`;
|
||||
|
||||
// Inner span to hold styling so we don't effect the "before:content"
|
||||
return (
|
||||
<span className="plugins-plugin" key={id}>
|
||||
<span className={className} data-tip={title} data-multiline>
|
||||
{error && <span className="plugins-plugin-icon fa fa-exclamation-circle" />}
|
||||
{label || id}
|
||||
</span>
|
||||
<ReactTooltip class="tooltip" effect="solid" offset={{right: 7}} />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const hasPlugins = this.props.plugins && this.props.plugins.size > 0;
|
||||
return (
|
||||
@@ -29,7 +28,8 @@ class Plugins extends React.Component {
|
||||
<span className="plugins-label">
|
||||
Plugins:
|
||||
</span>
|
||||
{hasPlugins && this.props.plugins.toIndexedSeq().map(plugin => Plugin(plugin.toJS()))}
|
||||
{hasPlugins && this.props.plugins.toIndexedSeq()
|
||||
.map(plugin => this.renderPlugin(plugin.toJS()))}
|
||||
{!hasPlugins && <span className="plugins-empty">n/a</span>}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import classnames from 'classnames';
|
||||
import { debounce } from 'lodash';
|
||||
@@ -47,7 +48,6 @@ class Search extends React.Component {
|
||||
this.handleBlur = this.handleBlur.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.handleFocus = this.handleFocus.bind(this);
|
||||
this.saveQueryInputRef = this.saveQueryInputRef.bind(this);
|
||||
this.doSearch = debounce(this.doSearch.bind(this), 200);
|
||||
this.state = {
|
||||
value: ''
|
||||
@@ -81,10 +81,6 @@ class Search extends React.Component {
|
||||
this.props.doSearch(value);
|
||||
}
|
||||
|
||||
saveQueryInputRef(ref) {
|
||||
this.queryInput = ref;
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
// when cleared from the outside, reset internal state
|
||||
if (this.props.searchQuery !== nextProps.searchQuery && nextProps.searchQuery === '') {
|
||||
@@ -94,9 +90,9 @@ class Search extends React.Component {
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.props.searchFocused) {
|
||||
this.queryInput.focus();
|
||||
ReactDOM.findDOMNode(this.refs.queryInput).focus();
|
||||
} else if (!this.state.value) {
|
||||
this.queryInput.blur();
|
||||
ReactDOM.findDOMNode(this.refs.queryInput).blur();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,14 +121,13 @@ class Search extends React.Component {
|
||||
<div className="search-input">
|
||||
{showPinnedSearches && pinnedSearches.toIndexedSeq()
|
||||
.map(query => <SearchItem query={query} key={query} />)}
|
||||
<input
|
||||
className="search-input-field" type="text" id={inputId}
|
||||
<input className="search-input-field" type="text" id={inputId}
|
||||
value={value} onChange={this.handleChange}
|
||||
onFocus={this.handleFocus} onBlur={this.handleBlur}
|
||||
disabled={disabled} ref={this.saveQueryInputRef} />
|
||||
disabled={disabled} ref="queryInput" />
|
||||
</div>
|
||||
<div className="search-label">
|
||||
<i className="fa fa-search search-label-icon" />
|
||||
<i className="fa fa-search search-label-icon"></i>
|
||||
<label className="search-label-hint" htmlFor={inputId}>
|
||||
Search
|
||||
</label>
|
||||
|
||||
@@ -81,14 +81,10 @@ export default class Sparkline extends React.Component {
|
||||
return (
|
||||
<div title={title}>
|
||||
<svg width={this.props.width} height={this.props.height}>
|
||||
<path
|
||||
className="sparkline" fill="none" stroke={this.props.strokeColor}
|
||||
strokeWidth={this.props.strokeWidth} d={this.line(data)}
|
||||
/>
|
||||
<circle
|
||||
className="sparkcircle" cx={lastX} cy={lastY} fill="#46466a"
|
||||
fillOpacity="0.6" stroke="none" r={this.props.circleDiameter}
|
||||
/>
|
||||
<path className="sparkline" fill="none" stroke={this.props.strokeColor}
|
||||
strokeWidth={this.props.strokeWidth} ref="path" d={this.line(data)} />
|
||||
<circle className="sparkcircle" cx={lastX} cy={lastY} fill="#46466a"
|
||||
fillOpacity="0.6" stroke="none" r={this.props.circleDiameter} />
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
@@ -97,7 +93,7 @@ export default class Sparkline extends React.Component {
|
||||
}
|
||||
|
||||
Sparkline.propTypes = {
|
||||
data: React.PropTypes.arrayOf(React.PropTypes.object).isRequired
|
||||
data: React.PropTypes.array.isRequired
|
||||
};
|
||||
|
||||
Sparkline.defaultProps = {
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
/* eslint no-return-assign: "off", react/jsx-no-bind: "off" */
|
||||
import debug from 'debug';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import classNames from 'classnames';
|
||||
import { debounce } from 'lodash';
|
||||
import Term from 'xterm';
|
||||
|
||||
import { clickCloseTerminal } from '../actions/app-actions';
|
||||
import { getNeutralColor } from '../utils/color-utils';
|
||||
import { setDocumentTitle } from '../utils/title-utils';
|
||||
import { getPipeStatus, basePath, doResizeTty } from '../utils/web-api-utils';
|
||||
import Term from 'xterm';
|
||||
|
||||
const wsProto = location.protocol === 'https:' ? 'wss' : 'ws';
|
||||
const wsUrl = `${wsProto}://${location.host}${basePath(location.pathname)}`;
|
||||
@@ -72,7 +73,7 @@ function openNewWindow(url, bcr, minWidth = 200) {
|
||||
};
|
||||
|
||||
const windowOptionsString = Object.keys(windowOptions)
|
||||
.map(k => `${k}=${windowOptions[k]}`)
|
||||
.map((k) => `${k}=${windowOptions[k]}`)
|
||||
.join(',');
|
||||
|
||||
window.open(url, '', windowOptionsString);
|
||||
@@ -96,8 +97,6 @@ class Terminal extends React.Component {
|
||||
|
||||
this.handleCloseClick = this.handleCloseClick.bind(this);
|
||||
this.handlePopoutTerminal = this.handlePopoutTerminal.bind(this);
|
||||
this.saveInnerFlexRef = this.saveInnerFlexRef.bind(this);
|
||||
this.saveNodeRef = this.saveNodeRef.bind(this);
|
||||
this.handleResize = this.handleResize.bind(this);
|
||||
this.handleResizeDebounced = debounce(this.handleResize, 500);
|
||||
}
|
||||
@@ -124,7 +123,7 @@ class Terminal extends React.Component {
|
||||
}
|
||||
this.socket = null;
|
||||
const wereConnected = this.state.connected;
|
||||
if (this.isComponentMounted) {
|
||||
if (this._isMounted) {
|
||||
// Calling setState on an unmounted component will throw a warning.
|
||||
// `connected` will get set to false by `componentWillUnmount`.
|
||||
this.setState({connected: false});
|
||||
@@ -177,13 +176,14 @@ class Terminal extends React.Component {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.isComponentMounted = true;
|
||||
this._isMounted = true;
|
||||
if (this.props.connect) {
|
||||
this.mountTerminal();
|
||||
}
|
||||
}
|
||||
|
||||
mountTerminal() {
|
||||
const component = this;
|
||||
this.term = new Term({
|
||||
cols: this.state.cols,
|
||||
rows: this.state.rows,
|
||||
@@ -192,7 +192,8 @@ class Terminal extends React.Component {
|
||||
scrollback: 10000,
|
||||
});
|
||||
|
||||
this.term.open(this.innerFlex);
|
||||
const innerNode = ReactDOM.findDOMNode(component.innerFlex);
|
||||
this.term.open(innerNode);
|
||||
this.term.on('data', (data) => {
|
||||
this.scrollToBottom();
|
||||
if (this.socket) {
|
||||
@@ -216,7 +217,7 @@ class Terminal extends React.Component {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isComponentMounted = false;
|
||||
this._isMounted = false;
|
||||
this.setState({connected: false});
|
||||
log('cwu terminal');
|
||||
|
||||
@@ -262,15 +263,16 @@ class Terminal extends React.Component {
|
||||
const paramString = JSON.stringify(this.props);
|
||||
this.props.dispatch(clickCloseTerminal(this.getPipeId()));
|
||||
|
||||
const bcr = this.node.getBoundingClientRect();
|
||||
const minWidth = (this.state.characterWidth * 80) + (8 * 2);
|
||||
const bcr = ReactDOM.findDOMNode(this).getBoundingClientRect();
|
||||
const minWidth = this.state.characterWidth * 80 + (8 * 2);
|
||||
openNewWindow(`terminal.html#!/state/${paramString}`, bcr, minWidth);
|
||||
}
|
||||
|
||||
handleResize() {
|
||||
const innerNode = ReactDOM.findDOMNode(this.innerFlex);
|
||||
// scrollbar === 16px
|
||||
const width = this.innerFlex.clientWidth - (2 * 8) - 16;
|
||||
const height = this.innerFlex.clientHeight - (2 * 8);
|
||||
const width = innerNode.clientWidth - (2 * 8) - 16;
|
||||
const height = innerNode.clientHeight - (2 * 8);
|
||||
const cols = Math.floor(width / this.state.characterWidth);
|
||||
const rows = Math.floor(height / this.state.characterHeight);
|
||||
|
||||
@@ -311,8 +313,7 @@ class Terminal extends React.Component {
|
||||
onClick={this.handlePopoutTerminal}>
|
||||
Pop out
|
||||
</span>
|
||||
<span
|
||||
title="Close" className="terminal-header-tools-item-icon fa fa-close"
|
||||
<span title="Close" className="terminal-header-tools-item-icon fa fa-close"
|
||||
onClick={this.handleCloseClick} />
|
||||
</div>
|
||||
<span className="terminal-header-title">{this.getTitle()}</span>
|
||||
@@ -356,14 +357,6 @@ class Terminal extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
saveNodeRef(ref) {
|
||||
this.node = ref;
|
||||
}
|
||||
|
||||
saveInnerFlexRef(ref) {
|
||||
this.innerFlex = ref;
|
||||
}
|
||||
|
||||
render() {
|
||||
const innerFlexStyle = {
|
||||
opacity: this.state.connected ? 1 : 0.8,
|
||||
@@ -377,10 +370,13 @@ class Terminal extends React.Component {
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="terminal-wrapper" ref={this.saveNodeRef}>
|
||||
<div className="terminal-wrapper">
|
||||
{this.isEmbedded() && this.getTerminalHeader()}
|
||||
<div className={innerClassName} style={innerFlexStyle} ref={this.saveInnerFlexRef}>
|
||||
<div style={innerStyle} />
|
||||
<div
|
||||
ref={(ref) => this.innerFlex = ref}
|
||||
className={innerClassName}
|
||||
style={innerFlexStyle} >
|
||||
<div style={innerStyle} ref={(ref) => this.inner = ref} />
|
||||
</div>
|
||||
{this.getTerminalStatusBar()}
|
||||
</div>
|
||||
|
||||
@@ -4,18 +4,6 @@ import classnames from 'classnames';
|
||||
|
||||
import { clickTopology } from '../actions/app-actions';
|
||||
|
||||
|
||||
function basicTopologyInfo(topology, searchMatchCount) {
|
||||
const info = [
|
||||
`Nodes: ${topology.getIn(['stats', 'node_count'])}`,
|
||||
`Connections: ${topology.getIn(['stats', 'edge_count'])}`
|
||||
];
|
||||
if (searchMatchCount) {
|
||||
info.push(`Search Matches: ${searchMatchCount}`);
|
||||
}
|
||||
return info.join('\n');
|
||||
}
|
||||
|
||||
class Topologies extends React.Component {
|
||||
|
||||
constructor(props, context) {
|
||||
@@ -33,15 +21,14 @@ class Topologies extends React.Component {
|
||||
const topologyId = subTopology.get('id');
|
||||
const searchMatches = this.props.searchNodeMatches.get(subTopology.get('id'));
|
||||
const searchMatchCount = searchMatches ? searchMatches.size : 0;
|
||||
const title = basicTopologyInfo(subTopology, searchMatchCount);
|
||||
const title = this.renderTitle(subTopology, searchMatchCount);
|
||||
const className = classnames('topologies-sub-item', {
|
||||
'topologies-sub-item-active': isActive,
|
||||
'topologies-sub-item-matched': searchMatchCount
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
className={className} title={title} key={topologyId} rel={topologyId}
|
||||
<div className={className} title={title} key={topologyId} rel={topologyId}
|
||||
onClick={this.onTopologyClick}>
|
||||
<div className="topologies-sub-item-label">
|
||||
{subTopology.get('name')}
|
||||
@@ -50,6 +37,15 @@ class Topologies extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
renderTitle(topology, searchMatchCount) {
|
||||
let title = `Nodes: ${topology.getIn(['stats', 'node_count'])}\n`
|
||||
+ `Connections: ${topology.getIn(['stats', 'node_count'])}`;
|
||||
if (searchMatchCount) {
|
||||
title = `${title}\nSearch Matches: ${searchMatchCount}`;
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
renderTopology(topology) {
|
||||
const isActive = topology === this.props.currentTopology;
|
||||
const searchMatches = this.props.searchNodeMatches.get(topology.get('id'));
|
||||
@@ -59,7 +55,7 @@ class Topologies extends React.Component {
|
||||
'topologies-item-main-matched': searchMatchCount
|
||||
});
|
||||
const topologyId = topology.get('id');
|
||||
const title = basicTopologyInfo(topology, searchMatchCount);
|
||||
const title = this.renderTitle(topology, searchMatchCount);
|
||||
|
||||
return (
|
||||
<div className="topologies-item" key={topologyId}>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
require('font-awesome-webpack');
|
||||
require('../styles/contrast.less');
|
||||
require('../images/favicon.ico');
|
||||
|
||||
import 'babel-polyfill';
|
||||
import 'font-awesome-webpack';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import '../styles/main.less';
|
||||
import '../images/favicon.ico';
|
||||
import configureStore from './stores/configureStore';
|
||||
|
||||
const store = configureStore();
|
||||
|
||||
@@ -149,13 +149,8 @@ export default ComposedComponent => class extends React.Component {
|
||||
.filter(dateFilter);
|
||||
|
||||
const lastValue = samples.length > 0 ? samples[samples.length - 1].value : null;
|
||||
const slidingWindow = {
|
||||
first: movingFirstDate,
|
||||
last: movingLastDate,
|
||||
value: lastValue,
|
||||
samples,
|
||||
max
|
||||
};
|
||||
const slidingWindow = {first: movingFirstDate,
|
||||
last: movingLastDate, max, samples, value: lastValue};
|
||||
|
||||
return <ComposedComponent {...this.props} {...slidingWindow} />;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
require('font-awesome-webpack');
|
||||
require('../styles/main.less');
|
||||
require('../images/favicon.ico');
|
||||
|
||||
import 'babel-polyfill';
|
||||
import 'font-awesome-webpack';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import configureStore from './stores/configureStore.dev';
|
||||
|
||||
import DevTools from './components/dev-tools';
|
||||
import Immutable from 'immutable';
|
||||
import installDevTools from 'immutable-devtools';
|
||||
|
||||
import '../styles/main.less';
|
||||
import '../images/favicon.ico';
|
||||
import configureStore from './stores/configureStore.dev';
|
||||
import DevTools from './components/dev-tools';
|
||||
|
||||
installDevTools(Immutable);
|
||||
|
||||
const store = configureStore();
|
||||
|
||||
function renderApp() {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
require('font-awesome-webpack');
|
||||
require('../styles/main.less');
|
||||
require('../images/favicon.ico');
|
||||
|
||||
import 'babel-polyfill';
|
||||
import 'font-awesome-webpack';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import '../styles/main.less';
|
||||
import '../images/favicon.ico';
|
||||
import configureStore from './stores/configureStore';
|
||||
|
||||
const store = configureStore();
|
||||
|
||||
@@ -95,7 +95,7 @@ function setTopology(state, topologyId) {
|
||||
}
|
||||
|
||||
function setDefaultTopologyOptions(state, topologyList) {
|
||||
topologyList.forEach((topology) => {
|
||||
topologyList.forEach(topology => {
|
||||
let defaultOptions = makeOrderedMap();
|
||||
if (topology.has('options') && topology.get('options')) {
|
||||
topology.get('options').forEach((option) => {
|
||||
@@ -230,7 +230,7 @@ export function rootReducer(state = initialState, action) {
|
||||
}
|
||||
|
||||
case ActionTypes.CLICK_PAUSE_UPDATE: {
|
||||
return state.set('updatePausedAt', new Date());
|
||||
return state.set('updatePausedAt', new Date);
|
||||
}
|
||||
|
||||
case ActionTypes.CLICK_RELATIVE: {
|
||||
@@ -371,13 +371,13 @@ export function rootReducer(state = initialState, action) {
|
||||
|
||||
case ActionTypes.ENTER_EDGE: {
|
||||
// highlight adjacent nodes
|
||||
state = state.update('highlightedNodeIds', (highlightedNodeIds) => {
|
||||
state = state.update('highlightedNodeIds', highlightedNodeIds => {
|
||||
highlightedNodeIds = highlightedNodeIds.clear();
|
||||
return highlightedNodeIds.union(action.edgeId.split(EDGE_ID_SEPARATOR));
|
||||
});
|
||||
|
||||
// highlight edge
|
||||
state = state.update('highlightedEdgeIds', (highlightedEdgeIds) => {
|
||||
state = state.update('highlightedEdgeIds', highlightedEdgeIds => {
|
||||
highlightedEdgeIds = highlightedEdgeIds.clear();
|
||||
return highlightedEdgeIds.add(action.edgeId);
|
||||
});
|
||||
@@ -392,18 +392,18 @@ export function rootReducer(state = initialState, action) {
|
||||
state = state.set('mouseOverNodeId', nodeId);
|
||||
|
||||
// highlight adjacent nodes
|
||||
state = state.update('highlightedNodeIds', (highlightedNodeIds) => {
|
||||
state = state.update('highlightedNodeIds', highlightedNodeIds => {
|
||||
highlightedNodeIds = highlightedNodeIds.clear();
|
||||
highlightedNodeIds = highlightedNodeIds.add(nodeId);
|
||||
return highlightedNodeIds.union(adjacentNodes);
|
||||
});
|
||||
|
||||
// highlight edge
|
||||
state = state.update('highlightedEdgeIds', (highlightedEdgeIds) => {
|
||||
state = state.update('highlightedEdgeIds', highlightedEdgeIds => {
|
||||
highlightedEdgeIds = highlightedEdgeIds.clear();
|
||||
if (adjacentNodes.size > 0) {
|
||||
// all neighbour combinations because we dont know which direction exists
|
||||
highlightedEdgeIds = highlightedEdgeIds.union(adjacentNodes.flatMap(adjacentId => [
|
||||
highlightedEdgeIds = highlightedEdgeIds.union(adjacentNodes.flatMap((adjacentId) => [
|
||||
[adjacentId, nodeId].join(EDGE_ID_SEPARATOR),
|
||||
[nodeId, adjacentId].join(EDGE_ID_SEPARATOR)
|
||||
]));
|
||||
@@ -491,7 +491,7 @@ export function rootReducer(state = initialState, action) {
|
||||
|
||||
// disregard if node is not selected anymore
|
||||
if (state.hasIn(['nodeDetails', action.details.id])) {
|
||||
state = state.updateIn(['nodeDetails', action.details.id], (obj) => {
|
||||
state = state.updateIn(['nodeDetails', action.details.id], obj => {
|
||||
const result = Object.assign({}, obj);
|
||||
result.notFound = false;
|
||||
result.details = action.details;
|
||||
@@ -554,7 +554,7 @@ export function rootReducer(state = initialState, action) {
|
||||
|
||||
// TODO move this setting of networks as toplevel node field to backend,
|
||||
// to not rely on field IDs here. should be determined by topology implementer
|
||||
state = state.update('nodes', nodes => nodes.map((node) => {
|
||||
state = state.update('nodes', nodes => nodes.map(node => {
|
||||
if (node.has('metadata')) {
|
||||
const networks = node.get('metadata')
|
||||
.find(field => field.get('id') === 'docker_container_networks');
|
||||
@@ -605,7 +605,7 @@ export function rootReducer(state = initialState, action) {
|
||||
|
||||
case ActionTypes.RECEIVE_NOT_FOUND: {
|
||||
if (state.hasIn(['nodeDetails', action.nodeId])) {
|
||||
state = state.updateIn(['nodeDetails', action.nodeId], (obj) => {
|
||||
state = state.updateIn(['nodeDetails', action.nodeId], obj => {
|
||||
const result = Object.assign({}, obj);
|
||||
result.notFound = true;
|
||||
return result;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import debug from 'debug';
|
||||
import { identity } from 'lodash';
|
||||
import { createSelector, createSelectorCreator, defaultMemoize } from 'reselect';
|
||||
import { Map as makeMap, is, Set } from 'immutable';
|
||||
|
||||
@@ -45,8 +44,9 @@ function mergeDeepKeyIntersection(mapA, mapB) {
|
||||
// "their results", so use the result of the wrapped selector as the argument to another selector
|
||||
// here to memoize it and get what we want.
|
||||
//
|
||||
const createDeepEqualSelector = createSelectorCreator(defaultMemoize, is);
|
||||
const returnPreviousRefIfEqual = selector => createDeepEqualSelector(selector, identity);
|
||||
const _createDeepEqualSelector = createSelectorCreator(defaultMemoize, is);
|
||||
const _identity = v => v;
|
||||
const returnPreviousRefIfEqual = (selector) => _createDeepEqualSelector(selector, _identity);
|
||||
|
||||
|
||||
//
|
||||
@@ -60,7 +60,7 @@ const allNodesSelector = state => state.get('nodes');
|
||||
export const nodesSelector = returnPreviousRefIfEqual(
|
||||
createSelector(
|
||||
allNodesSelector,
|
||||
allNodes => allNodes.filter(node => !node.get('filtered'))
|
||||
(allNodes) => allNodes.filter(node => !node.get('filtered'))
|
||||
)
|
||||
);
|
||||
|
||||
@@ -71,7 +71,7 @@ export const adjacentNodesSelector = returnPreviousRefIfEqual(getAdjacentNodes);
|
||||
export const nodeAdjacenciesSelector = returnPreviousRefIfEqual(
|
||||
createSelector(
|
||||
nodesSelector,
|
||||
nodes => nodes.map(n => makeMap({
|
||||
(nodes) => nodes.map(n => makeMap({
|
||||
id: n.get('id'),
|
||||
adjacency: n.get('adjacency'),
|
||||
}))
|
||||
@@ -81,7 +81,7 @@ export const nodeAdjacenciesSelector = returnPreviousRefIfEqual(
|
||||
|
||||
export const dataNodesSelector = createSelector(
|
||||
nodesSelector,
|
||||
nodes => nodes.map((node, id) => makeMap({
|
||||
(nodes) => nodes.map((node, id) => makeMap({
|
||||
id,
|
||||
label: node.get('label'),
|
||||
pseudo: node.get('pseudo'),
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
require('../styles/main.less');
|
||||
require('../images/favicon.ico');
|
||||
|
||||
import 'babel-polyfill';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import '../styles/main.less';
|
||||
import '../images/favicon.ico';
|
||||
import configureStore from './stores/configureStore';
|
||||
|
||||
const store = configureStore();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { range } from 'lodash';
|
||||
import _ from 'lodash';
|
||||
|
||||
describe('ArrayUtils', () => {
|
||||
const ArrayUtils = require('../array-utils');
|
||||
@@ -7,42 +7,34 @@ describe('ArrayUtils', () => {
|
||||
const f = ArrayUtils.uniformSelect;
|
||||
|
||||
it('it should select the array elements uniformly, including the endpoints', () => {
|
||||
{
|
||||
const arr = ['x', 'y'];
|
||||
expect(f(arr, 3)).toEqual(['x', 'y']);
|
||||
expect(f(arr, 2)).toEqual(['x', 'y']);
|
||||
}
|
||||
expect(f(['x', 'y'], 3)).toEqual(['x', 'y']);
|
||||
expect(f(['x', 'y'], 2)).toEqual(['x', 'y']);
|
||||
|
||||
{
|
||||
const arr = ['A', 'B', 'C', 'D', 'E'];
|
||||
expect(f(arr, 6)).toEqual(['A', 'B', 'C', 'D', 'E']);
|
||||
expect(f(arr, 5)).toEqual(['A', 'B', 'C', 'D', 'E']);
|
||||
expect(f(arr, 4)).toEqual(['A', 'B', 'D', 'E']);
|
||||
expect(f(arr, 3)).toEqual(['A', 'C', 'E']);
|
||||
expect(f(arr, 2)).toEqual(['A', 'E']);
|
||||
}
|
||||
expect(f(['A', 'B', 'C', 'D', 'E'], 6)).toEqual(['A', 'B', 'C', 'D', 'E']);
|
||||
expect(f(['A', 'B', 'C', 'D', 'E'], 5)).toEqual(['A', 'B', 'C', 'D', 'E']);
|
||||
expect(f(['A', 'B', 'C', 'D', 'E'], 4)).toEqual(['A', 'B', 'D', 'E']);
|
||||
expect(f(['A', 'B', 'C', 'D', 'E'], 3)).toEqual(['A', 'C', 'E']);
|
||||
expect(f(['A', 'B', 'C', 'D', 'E'], 2)).toEqual(['A', 'E']);
|
||||
|
||||
{
|
||||
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
||||
expect(f(arr, 12)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
|
||||
expect(f(arr, 11)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
|
||||
expect(f(arr, 10)).toEqual([1, 2, 3, 4, 5, 7, 8, 9, 10, 11]);
|
||||
expect(f(arr, 9)).toEqual([1, 2, 3, 5, 6, 7, 9, 10, 11]);
|
||||
expect(f(arr, 8)).toEqual([1, 2, 4, 5, 7, 8, 10, 11]);
|
||||
expect(f(arr, 7)).toEqual([1, 2, 4, 6, 8, 10, 11]);
|
||||
expect(f(arr, 6)).toEqual([1, 3, 5, 7, 9, 11]);
|
||||
expect(f(arr, 5)).toEqual([1, 3, 6, 9, 11]);
|
||||
expect(f(arr, 4)).toEqual([1, 4, 8, 11]);
|
||||
expect(f(arr, 3)).toEqual([1, 6, 11]);
|
||||
expect(f(arr, 2)).toEqual([1, 11]);
|
||||
}
|
||||
expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 12)).toEqual(
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
|
||||
);
|
||||
expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 11)).toEqual(
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
|
||||
expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 10)).toEqual([1, 2, 3, 4, 5, 7, 8, 9, 10, 11]
|
||||
);
|
||||
expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 9)).toEqual([1, 2, 3, 5, 6, 7, 9, 10, 11]);
|
||||
expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 8)).toEqual([1, 2, 4, 5, 7, 8, 10, 11]);
|
||||
expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 7)).toEqual([1, 2, 4, 6, 8, 10, 11]);
|
||||
expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 6)).toEqual([1, 3, 5, 7, 9, 11]);
|
||||
expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 5)).toEqual([1, 3, 6, 9, 11]);
|
||||
expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 4)).toEqual([1, 4, 8, 11]);
|
||||
expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 3)).toEqual([1, 6, 11]);
|
||||
expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 2)).toEqual([1, 11]);
|
||||
|
||||
{
|
||||
const arr = range(1, 10001);
|
||||
expect(f(arr, 4)).toEqual([1, 3334, 6667, 10000]);
|
||||
expect(f(arr, 3)).toEqual([1, 5000, 10000]);
|
||||
expect(f(arr, 2)).toEqual([1, 10000]);
|
||||
}
|
||||
expect(f(_.range(1, 10001), 4)).toEqual([1, 3334, 6667, 10000]);
|
||||
expect(f(_.range(1, 10001), 3)).toEqual([1, 5000, 10000]);
|
||||
expect(f(_.range(1, 10001), 2)).toEqual([1, 10000]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -154,7 +154,7 @@ describe('SearchUtils', () => {
|
||||
const fun = SearchUtils.makeRegExp;
|
||||
|
||||
it('should make a regexp from any string', () => {
|
||||
expect(fun().source).toEqual((new RegExp()).source);
|
||||
expect(fun().source).toEqual((new RegExp).source);
|
||||
expect(fun('que').source).toEqual((new RegExp('que')).source);
|
||||
// invalid string
|
||||
expect(fun('que[').source).toEqual((new RegExp('que\\[')).source);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { range } from 'lodash';
|
||||
import _ from 'lodash';
|
||||
|
||||
export function uniformSelect(array, size) {
|
||||
if (size > array.length) {
|
||||
return array;
|
||||
}
|
||||
|
||||
return range(size).map(index =>
|
||||
array[parseInt(index * (array.length / (size - (1 - 1e-9))), 10)]
|
||||
return _.range(size).map(index =>
|
||||
array[parseInt(index * array.length / (size - 1 + 1e-9), 10)]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ const letterRange = endLetterRange - startLetterRange;
|
||||
export function text2degree(text) {
|
||||
const input = text.substr(0, 2).toUpperCase();
|
||||
let num = 0;
|
||||
for (let i = 0; i < input.length; i += 1) {
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
const charCode = Math.max(Math.min(input[i].charCodeAt(), endLetterRange), startLetterRange);
|
||||
num += Math.pow(letterRange, input.length - i - 1) * (charCode - startLetterRange);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { zipObject } from 'lodash';
|
||||
import _ from 'lodash';
|
||||
import { scaleLinear } from 'd3-scale';
|
||||
import { extent } from 'd3-array';
|
||||
|
||||
@@ -104,9 +104,10 @@ function mergeMetrics(node) {
|
||||
return node;
|
||||
}
|
||||
return Object.assign({}, node, {
|
||||
metrics: (metrics[node.shape] || [])
|
||||
metrics: _(metrics[node.shape])
|
||||
.map((fn, name) => [name, fn(node)])
|
||||
.fromPairs()
|
||||
.value()
|
||||
});
|
||||
}
|
||||
|
||||
@@ -120,7 +121,7 @@ function handleAdd(nodes) {
|
||||
|
||||
|
||||
function handleUpdated(updatedNodes, prevNodes) {
|
||||
const modifiedNodesIndex = zipObject((updatedNodes || []).map(n => [n.id, n]));
|
||||
const modifiedNodesIndex = _.zipObject((updatedNodes || []).map(n => [n.id, n]));
|
||||
return prevNodes.toIndexedSeq().toJS().map(n => (
|
||||
Object.assign({}, mergeMetrics(n), modifiedNodesIndex[n.id])
|
||||
));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
|
||||
export default class DelayedShow extends React.Component {
|
||||
export class DelayedShow extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
|
||||
@@ -18,7 +18,7 @@ function setInlineStyles(svg, target, emptySvgDeclarationComputed) {
|
||||
function explicitlySetStyle(element, targetEl) {
|
||||
const cSSStyleDeclarationComputed = getComputedStyle(element);
|
||||
let computedStyleStr = '';
|
||||
each(cSSStyleDeclarationComputed, (key) => {
|
||||
each(cSSStyleDeclarationComputed, key => {
|
||||
const value = cSSStyleDeclarationComputed.getPropertyValue(key);
|
||||
if (value !== emptySvgDeclarationComputed.getPropertyValue(key) && !cssSkipValues[value]) {
|
||||
computedStyleStr += `${key}:${value};`;
|
||||
@@ -55,7 +55,8 @@ function setInlineStyles(svg, target, emptySvgDeclarationComputed) {
|
||||
// hardcode computed css styles inside svg
|
||||
const allElements = traverse(svg);
|
||||
const allTargetElements = traverse(target);
|
||||
for (let i = allElements.length - 1; i >= 0; i -= 1) {
|
||||
let i = allElements.length;
|
||||
while (i--) {
|
||||
explicitlySetStyle(allElements[i], allTargetElements[i]);
|
||||
}
|
||||
|
||||
@@ -73,11 +74,11 @@ function download(source, name) {
|
||||
if (name) {
|
||||
filename = name;
|
||||
} else if (window.document.title) {
|
||||
filename = `${window.document.title.replace(/[^a-z0-9]/gi, '-').toLowerCase()}-${(+new Date())}`;
|
||||
filename = `${window.document.title.replace(/[^a-z0-9]/gi, '-').toLowerCase()}-${(+new Date)}`;
|
||||
}
|
||||
|
||||
const url = window.URL.createObjectURL(new Blob(source,
|
||||
{ type: 'text/xml' }
|
||||
{type: 'text\/xml'}
|
||||
));
|
||||
|
||||
const a = document.createElement('a');
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { includes } from 'lodash';
|
||||
import { scaleLog } from 'd3-scale';
|
||||
import React from 'react';
|
||||
|
||||
import { formatMetricSvg } from './string-utils';
|
||||
import { colors } from './color-utils';
|
||||
import React from 'react';
|
||||
|
||||
|
||||
export function getClipPathDefinition(clipId, size, height,
|
||||
x = -size * 0.5, y = (size * 0.5) - height) {
|
||||
x = -size * 0.5, y = size * 0.5 - height) {
|
||||
return (
|
||||
<defs>
|
||||
<clipPath id={clipId}>
|
||||
@@ -44,7 +44,7 @@ export function getMetricValue(metric, size) {
|
||||
let displayedValue = Number(value).toFixed(1);
|
||||
if (displayedValue > 0 && (!max || displayedValue < max)) {
|
||||
const baseline = 0.1;
|
||||
displayedValue = (valuePercentage * (1 - (baseline * 2))) + baseline;
|
||||
displayedValue = valuePercentage * (1 - baseline * 2) + baseline;
|
||||
} else if (displayedValue >= m.max && displayedValue > 0) {
|
||||
displayedValue = 1;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { fromJS, List as makeList } from 'immutable';
|
||||
|
||||
export function getNetworkNodes(nodes) {
|
||||
const networks = {};
|
||||
nodes.forEach(node => (node.get('networks') || makeList()).forEach((n) => {
|
||||
nodes.forEach(node => (node.get('networks') || makeList()).forEach(n => {
|
||||
const networkId = n.get('id');
|
||||
networks[networkId] = (networks[networkId] || []).concat([node.get('id')]);
|
||||
}));
|
||||
|
||||
@@ -132,7 +132,7 @@ export function searchTopology(nodes, { prefix, query, metric, comp, value }) {
|
||||
|
||||
// metadata
|
||||
if (node.get('metadata')) {
|
||||
node.get('metadata').forEach((field) => {
|
||||
node.get('metadata').forEach(field => {
|
||||
const keyPath = [nodeId, 'metadata', field.get('id')];
|
||||
nodeMatches = findNodeMatch(nodeMatches, keyPath, field.get('value'),
|
||||
query, prefix, field.get('label'), field.get('truncate'));
|
||||
@@ -141,7 +141,7 @@ export function searchTopology(nodes, { prefix, query, metric, comp, value }) {
|
||||
|
||||
// parents and relatives
|
||||
if (node.get('parents')) {
|
||||
node.get('parents').forEach((parent) => {
|
||||
node.get('parents').forEach(parent => {
|
||||
const keyPath = [nodeId, 'parents', parent.get('id')];
|
||||
nodeMatches = findNodeMatch(nodeMatches, keyPath, parent.get('label'),
|
||||
query, prefix, parent.get('topologyId'));
|
||||
@@ -153,7 +153,7 @@ export function searchTopology(nodes, { prefix, query, metric, comp, value }) {
|
||||
if (tables) {
|
||||
tables.forEach((table) => {
|
||||
if (table.get('rows')) {
|
||||
table.get('rows').forEach((field) => {
|
||||
table.get('rows').forEach(field => {
|
||||
const keyPath = [nodeId, 'tables', field.get('id')];
|
||||
nodeMatches = findNodeMatch(nodeMatches, keyPath, field.get('value'),
|
||||
query, prefix, field.get('label'));
|
||||
@@ -164,7 +164,7 @@ export function searchTopology(nodes, { prefix, query, metric, comp, value }) {
|
||||
} else if (metric) {
|
||||
const metrics = node.get('metrics');
|
||||
if (metrics) {
|
||||
metrics.forEach((field) => {
|
||||
metrics.forEach(field => {
|
||||
const keyPath = [nodeId, 'metrics', field.get('id')];
|
||||
nodeMatches = findNodeMatchMetric(nodeMatches, keyPath, field.get('value'),
|
||||
field.get('label'), metric, comp, value);
|
||||
@@ -291,7 +291,7 @@ export function applyPinnedSearches(state) {
|
||||
|
||||
const pinnedSearches = state.get('pinnedSearches');
|
||||
if (pinnedSearches.size > 0) {
|
||||
state.get('pinnedSearches').forEach((query) => {
|
||||
state.get('pinnedSearches').forEach(query => {
|
||||
const parsed = parseQuery(query);
|
||||
if (parsed) {
|
||||
const nodeMatches = searchTopology(state.get('nodes'), parsed);
|
||||
|
||||
@@ -3,7 +3,7 @@ import debug from 'debug';
|
||||
const log = debug('scope:storage-utils');
|
||||
|
||||
// localStorage detection
|
||||
const storage = (typeof Storage) !== 'undefined' ? window.localStorage : null;
|
||||
const storage = typeof(Storage) !== 'undefined' ? window.localStorage : null;
|
||||
|
||||
export function storageGet(key, defaultValue) {
|
||||
if (storage && storage.getItem(key) !== undefined) {
|
||||
|
||||
@@ -19,7 +19,7 @@ export function getDefaultTopology(topologies) {
|
||||
.flatMap(t => makeList([t]).concat(t.get('sub_topologies', makeList())));
|
||||
|
||||
return flatTopologies
|
||||
.sortBy((t) => {
|
||||
.sortBy(t => {
|
||||
const index = TOPOLOGY_DISPLAY_PRIORITY.indexOf(t.get('id'));
|
||||
return index === -1 ? Infinity : index;
|
||||
})
|
||||
@@ -53,7 +53,7 @@ export function buildTopologyCacheId(topologyId, topologyOptions) {
|
||||
export function findTopologyById(subTree, topologyId) {
|
||||
let foundTopology;
|
||||
|
||||
subTree.forEach((topology) => {
|
||||
subTree.forEach(topology => {
|
||||
if (endsWith(topology.get('url'), topologyId)) {
|
||||
foundTopology = topology;
|
||||
}
|
||||
@@ -66,7 +66,7 @@ export function findTopologyById(subTree, topologyId) {
|
||||
}
|
||||
|
||||
export function updateNodeDegrees(nodes, edges) {
|
||||
return nodes.map((node) => {
|
||||
return nodes.map(node => {
|
||||
const nodeId = node.get('id');
|
||||
const degree = edges.count(edge => edge.get('source') === nodeId
|
||||
|| edge.get('target') === nodeId);
|
||||
@@ -76,7 +76,7 @@ export function updateNodeDegrees(nodes, edges) {
|
||||
|
||||
/* set topology.id and parentId for sub-topologies in place */
|
||||
export function updateTopologyIds(topologies, parentId) {
|
||||
return topologies.map((topology) => {
|
||||
return topologies.map(topology => {
|
||||
const result = Object.assign({}, topology);
|
||||
result.id = topology.url.split('/').pop();
|
||||
if (parentId) {
|
||||
@@ -90,7 +90,7 @@ export function updateTopologyIds(topologies, parentId) {
|
||||
}
|
||||
|
||||
export function addTopologyFullname(topologies) {
|
||||
return topologies.map((t) => {
|
||||
return topologies.map(t => {
|
||||
if (!t.sub_topologies) {
|
||||
return Object.assign({}, t, {fullName: t.name});
|
||||
}
|
||||
@@ -108,10 +108,10 @@ export function addTopologyFullname(topologies) {
|
||||
export function setTopologyUrlsById(topologyUrlsById, topologies) {
|
||||
let urlMap = topologyUrlsById;
|
||||
if (topologies) {
|
||||
topologies.forEach((topology) => {
|
||||
topologies.forEach(topology => {
|
||||
urlMap = urlMap.set(topology.id, topology.url);
|
||||
if (topology.sub_topologies) {
|
||||
topology.sub_topologies.forEach((subTopology) => {
|
||||
topology.sub_topologies.forEach(subTopology => {
|
||||
urlMap = urlMap.set(subTopology.id, subTopology.url);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ function consolidateBuffer() {
|
||||
size(toUpdate), 'remove', size(toRemove));
|
||||
|
||||
// check if an added node in first was updated in second -> add second update
|
||||
toAdd = map(toAdd, (node) => {
|
||||
toAdd = map(toAdd, node => {
|
||||
const updateNode = find(second.update, {id: node.id});
|
||||
if (updateNode) {
|
||||
toUpdate = reject(toUpdate, {id: node.id});
|
||||
@@ -62,7 +62,7 @@ function consolidateBuffer() {
|
||||
// no action needed, successive updates are fine
|
||||
|
||||
// check if an added node in first was removed in second -> dont add, dont remove
|
||||
each(first.add, (node) => {
|
||||
each(first.add, node => {
|
||||
const removedNode = find(second.remove, {id: node.id});
|
||||
if (removedNode) {
|
||||
toAdd = reject(toAdd, {id: node.id});
|
||||
@@ -71,7 +71,7 @@ function consolidateBuffer() {
|
||||
});
|
||||
|
||||
// check if an updated node in first was removed in second -> remove
|
||||
each(first.update, (node) => {
|
||||
each(first.update, node => {
|
||||
const removedNode = find(second.remove, {id: node.id});
|
||||
if (removedNode) {
|
||||
toUpdate = reject(toUpdate, {id: node.id});
|
||||
|
||||
@@ -8,14 +8,7 @@
|
||||
"dependencies": {
|
||||
"babel-polyfill": "6.16.0",
|
||||
"classnames": "2.2.5",
|
||||
"d3-array": "1.0.2",
|
||||
"d3-color": "1.0.2",
|
||||
"d3-format": "1.0.2",
|
||||
"d3-scale": "1.0.4",
|
||||
"d3-selection": "1.0.3",
|
||||
"d3-shape": "1.0.4",
|
||||
"d3-time-format": "2.0.3",
|
||||
"d3-zoom": "1.1.1",
|
||||
"d3": "4.4.0",
|
||||
"dagre": "0.7.4",
|
||||
"debug": "2.3.3",
|
||||
"filesize": "3.3.0",
|
||||
@@ -45,7 +38,7 @@
|
||||
"devDependencies": {
|
||||
"autoprefixer": "6.5.3",
|
||||
"babel-core": "6.18.2",
|
||||
"babel-eslint": "7.1.1",
|
||||
"babel-eslint": "5.0.0",
|
||||
"babel-jest": "17.0.2",
|
||||
"babel-loader": "6.2.8",
|
||||
"babel-plugin-lodash": "3.2.10",
|
||||
@@ -53,12 +46,11 @@
|
||||
"babel-preset-react": "6.16.0",
|
||||
"clean-webpack-plugin": "0.1.14",
|
||||
"css-loader": "0.26.1",
|
||||
"eslint": "3.11.1",
|
||||
"eslint-config-airbnb": "13.0.0",
|
||||
"eslint-loader": "1.6.1",
|
||||
"eslint-plugin-import": "2.2.0",
|
||||
"eslint-plugin-jsx-a11y": "2.2.3",
|
||||
"eslint-plugin-react": "6.8.0",
|
||||
"eslint": "2.4.0",
|
||||
"eslint-config-airbnb": "6.1.0",
|
||||
"eslint-loader": "1.3.0",
|
||||
"eslint-plugin-jasmine": "1.6.0",
|
||||
"eslint-plugin-react": "4.2.2",
|
||||
"extract-text-webpack-plugin": "1.0.1",
|
||||
"file-loader": "0.9.0",
|
||||
"html-webpack-plugin": "2.24.1",
|
||||
|
||||
Reference in New Issue
Block a user