diff --git a/client/app/scripts/charts/node-networks-overlay.js b/client/app/scripts/charts/node-networks-overlay.js
index b728de051..b50841239 100644
--- a/client/app/scripts/charts/node-networks-overlay.js
+++ b/client/app/scripts/charts/node-networks-overlay.js
@@ -5,41 +5,46 @@ import { getNodeColor } from '../utils/color-utils';
import { isContrastMode } from '../utils/contrast-utils';
+const h = 5;
const padding = 0.05;
-const offset = Math.PI;
-const arc = d3.svg.arc()
- .startAngle(d => d.startAngle + offset)
- .endAngle(d => d.endAngle + offset);
-const arcScale = d3.scale.linear()
- .range([Math.PI * 0.25 + padding, Math.PI * 1.75 - padding]);
-
+const rx = 1;
+const ry = rx;
+const labelOffset = 38;
function NodeNetworksOverlay({size, stack, networks = makeList()}) {
- arcScale.domain([0, networks.size]);
- const radius = size * 0.9;
+ const r = size * 0.5;
+ const offset = r + labelOffset;
+ const w = Math.max(size, (size / 4) * networks.size);
+ const x = d3.scale.ordinal()
+ .domain(networks.map((n, i) => i).toJS())
+ .rangeBands([w * -0.5, w * 0.5], padding, 0);
- const paths = networks.map((n, i) => {
- const d = arc({
- padAngle: 0.05,
- innerRadius: radius,
- outerRadius: radius + 4,
- startAngle: arcScale(i),
- endAngle: arcScale(i + 1)
- });
-
- return ();
- });
+ const bars = networks.map((n, i) => (
+
+ ));
let transform = '';
if (stack) {
const contrastMode = isContrastMode();
- const [dx, dy] = contrastMode ? [0, 8] : [0, 5];
+ const [dx, dy] = contrastMode ? [0, 8] : [0, 0];
transform = `translate(${dx}, ${dy * -1.5})`;
}
return (
- {paths.toJS()}
+ {bars.toJS()}
);
}
diff --git a/client/app/scripts/charts/node.js b/client/app/scripts/charts/node.js
index acd942702..fae17bf7d 100644
--- a/client/app/scripts/charts/node.js
+++ b/client/app/scripts/charts/node.js
@@ -107,9 +107,6 @@ class Node extends React.Component {
- {showingNetworks && }
-
{useSvgLabels ?
svgLabels(label, subLabel, labelClassName, subLabelClassName, labelOffsetY) :
@@ -133,6 +130,9 @@ class Node extends React.Component {
color={color}
{...this.props} />
+
+ {showingNetworks && }
);
}
diff --git a/client/app/scripts/charts/nodes-chart-edges.js b/client/app/scripts/charts/nodes-chart-edges.js
index 29d26ffe0..a67eb5287 100644
--- a/client/app/scripts/charts/nodes-chart-edges.js
+++ b/client/app/scripts/charts/nodes-chart-edges.js
@@ -1,6 +1,6 @@
import React from 'react';
import { connect } from 'react-redux';
-import { Map as makeMap } from 'immutable';
+import { Map as makeMap, List as makeList } from 'immutable';
import { hasSelectedNode as hasSelectedNodeFn } from '../utils/topology-utils';
import EdgeContainer from './edge-container';
@@ -9,7 +9,7 @@ class NodesChartEdges extends React.Component {
render() {
const { hasSelectedNode, highlightedEdgeIds, layoutEdges,
layoutPrecision, searchNodeMatches = makeMap(), searchQuery,
- selectedNodeId } = this.props;
+ selectedNodeId, selectedNetwork, selectedNetworkNodes } = this.props;
return (
@@ -18,10 +18,16 @@ class NodesChartEdges extends React.Component {
const targetSelected = selectedNodeId === edge.get('target');
const highlighted = highlightedEdgeIds.has(edge.get('id'));
const focused = hasSelectedNode && (sourceSelected || targetSelected);
- const blurred = !(highlightedEdgeIds.size > 0 && highlighted)
- && ((hasSelectedNode && !sourceSelected && !targetSelected)
- || !focused && searchQuery && !(searchNodeMatches.has(edge.get('source'))
- && searchNodeMatches.has(edge.get('target'))));
+ const otherNodesSelected = hasSelectedNode && !sourceSelected && !targetSelected;
+ const noMatches = searchQuery &&
+ !(searchNodeMatches.has(edge.get('source')) &&
+ searchNodeMatches.has(edge.get('target')));
+ const noSelectedNetworks = selectedNetwork &&
+ !(selectedNetworkNodes.contains(edge.get('source')) &&
+ selectedNetworkNodes.contains(edge.get('target')));
+ const blurred = !highlighted && (otherNodesSelected ||
+ !focused && noMatches ||
+ !focused && noSelectedNetworks);
return (
n.get('id') === selectedNetwork));
// make sure blurred nodes are in the background
const sortNodes = node => {
diff --git a/client/app/scripts/components/debug-toolbar.js b/client/app/scripts/components/debug-toolbar.js
index e52e22876..97145ff7c 100644
--- a/client/app/scripts/components/debug-toolbar.js
+++ b/client/app/scripts/components/debug-toolbar.js
@@ -17,7 +17,9 @@ const SHAPES = ['square', 'hexagon', 'heptagon', 'circle'];
const NODE_COUNTS = [1, 2, 3];
const STACK_VARIANTS = [false, true];
const METRIC_FILLS = [0, 0.1, 50, 99.9, 100];
-const NETWORKS = ['be', 'fe', 'lb', 'db'];
+const NETWORKS = [
+ 'be', 'fe', 'zb', 'db', 're', 'gh', 'jk', 'lol', 'nw'
+].map(n => ({id: n, label: n, colorKey: n}));
const LOREM = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
@@ -45,7 +47,7 @@ const LABEL_PREFIXES = _.range('A'.charCodeAt(), 'Z'.charCodeAt() + 1)
const deltaAdd = (
name, adjacency = [], shape = 'circle', stack = false, nodeCount = 1,
- networks = ['fe', 'be']
+ networks = NETWORKS
) => ({
adjacency,
controls: {},
@@ -176,7 +178,7 @@ class DebugToolbar extends React.Component {
_.sample(SHAPES),
_.sample(STACK_VARIANTS),
_.sample(NODE_COUNTS),
- sample(NETWORKS, 3)
+ sample(NETWORKS, 10)
))
}));
diff --git a/client/app/scripts/reducers/root.js b/client/app/scripts/reducers/root.js
index f304d1a5b..d94c35cf5 100644
--- a/client/app/scripts/reducers/root.js
+++ b/client/app/scripts/reducers/root.js
@@ -33,6 +33,7 @@ export const initialState = makeMap({
hostname: '...',
mouseOverEdgeId: null,
mouseOverNodeId: null,
+ networkNodes: makeMap(),
nodeDetails: makeOrderedMap(), // nodeId -> details
nodes: makeOrderedMap(), // nodeId -> node
// nodes cache, infrequently updated, used for search
@@ -81,6 +82,24 @@ function processTopologies(state, nextTopologies) {
return state.mergeDeepIn(['topologies'], immNextTopologies);
}
+function getNetworkNodes(nodes) {
+ const networks = {};
+ nodes.forEach(node => (node.get('networks') || makeList()).forEach(n => {
+ const networkId = n.get('id');
+ networks[networkId] = (networks[networkId] || []).concat([node.get('id')]);
+ }));
+ return fromJS(networks);
+}
+
+function getAvailableNetworks(nodes) {
+ return nodes
+ .valueSeq()
+ .flatMap(node => node.get('networks') || makeList())
+ .toSet()
+ .toList()
+ .sortBy(m => m.get('label'));
+}
+
function setTopology(state, topologyId) {
state = state.set('currentTopology', findTopologyById(
state.get('topologies'), topologyId));
@@ -526,19 +545,15 @@ export function rootReducer(state = initialState, action) {
const networks = node.get('metadata')
.find(field => field.get('id') === 'docker_container_networks');
if (networks) {
- return node.set('networks', fromJS(networks.get('value').split(', ')));
+ return node.set('networks', fromJS(
+ networks.get('value').split(', ').map(n => ({id: n, label: n, colorKey: n}))));
}
}
return node;
}));
- state = state.set('availableNetworks', state.get('nodes')
- .valueSeq()
- .flatMap(node => node.get('networks') || makeList())
- .toSet()
- .toList()
- .sort()
- .map(n => makeMap({id: n, label: n})));
+ state = state.set('networkNodes', getNetworkNodes(state.get('nodes')));
+ state = state.set('availableNetworks', getAvailableNetworks(state.get('nodes')));
// optimize color coding for networks
const networkPrefix = longestCommonPrefix(state.get('availableNetworks')
@@ -547,7 +562,8 @@ export function rootReducer(state = initialState, action) {
if (networkPrefix) {
state = state.update('nodes',
nodes => nodes.map(node => node.update('networks',
- networks => networks.map(n => n.substr(networkPrefix.length)))));
+ networks => networks.map(n => n.set('colorKey',
+ n.get('colorKey').substr(networkPrefix.length))))));
state = state.update('availableNetworks',
networks => networks.map(network => network
@@ -642,6 +658,7 @@ export function rootReducer(state = initialState, action) {
}
if (action.state.pinnedNetwork) {
state = state.set('pinnedNetwork', action.state.pinnedNetwork);
+ state = state.set('selectedNetwork', action.state.pinnedNetwork);
}
if (action.state.controlPipe) {
state = state.set('controlPipes', makeOrderedMap({
diff --git a/client/app/styles/main.less b/client/app/styles/main.less
index bff5b4f64..900d23a91 100644
--- a/client/app/styles/main.less
+++ b/client/app/styles/main.less
@@ -341,9 +341,9 @@ h2 {
transition: opacity .5s @base-ease;
text-align: center;
- .node-networks-overlay {
- fill: none;
- stroke: @background-darker-secondary-color;
+ .node-network {
+ // stroke: @background-lighter-color;
+ // stroke-width: 4px;
}
.node-label,