diff --git a/client/app/scripts/charts/nodes-chart.js b/client/app/scripts/charts/nodes-chart.js
index 3ce0c85f3..301b36576 100644
--- a/client/app/scripts/charts/nodes-chart.js
+++ b/client/app/scripts/charts/nodes-chart.js
@@ -12,7 +12,8 @@ import { DETAILS_PANEL_WIDTH } from '../constants/styles';
import Logo from '../components/logo';
import { doLayout } from './nodes-layout';
import NodesChartElements from './nodes-chart-elements';
-import { getActiveTopologyOptions, getAdjacentNodes } from '../utils/topology-utils';
+import { getActiveTopologyOptions, getAdjacentNodes,
+ isSameTopology } from '../utils/topology-utils';
const log = debug('scope:nodes-chart');
@@ -40,8 +41,8 @@ class NodesChart extends React.Component {
scale: 1,
selectedNodeScale: d3.scale.linear(),
hasZoomed: false,
- height: props.height || 0,
- width: props.width || 0,
+ height: 0,
+ width: 0,
zoomCache: {}
};
}
@@ -60,7 +61,7 @@ class NodesChart extends React.Component {
// re-apply cached canvas zoom/pan to d3 behavior (or set defaul values)
const defaultZoom = { scale: 1, panTranslateX: 0, panTranslateY: 0, hasZoomed: false };
const nextZoom = this.state.zoomCache[nextProps.topologyId] || defaultZoom;
- if (this.zoom && nextZoom) {
+ if (nextZoom) {
this.zoom.scale(nextZoom.scale);
this.zoom.translate([nextZoom.panTranslateX, nextZoom.panTranslateY]);
}
@@ -78,13 +79,11 @@ class NodesChart extends React.Component {
}
// reset layout dimensions only when forced
- // state.height = nextProps.height;
- // state.width = nextProps.width;
state.height = nextProps.forceRelayout ? nextProps.height : (state.height || nextProps.height);
state.width = nextProps.forceRelayout ? nextProps.width : (state.width || nextProps.width);
// _.assign(state, this.updateGraphState(nextProps, state));
- if (nextProps.forceRelayout || nextProps.nodes !== this.props.nodes) {
+ if (nextProps.forceRelayout || !isSameTopology(nextProps.nodes, this.props.nodes)) {
_.assign(state, this.updateGraphState(nextProps, state));
}
@@ -100,10 +99,6 @@ class NodesChart extends React.Component {
componentDidMount() {
// distinguish pan/zoom from click
- if (this.props.noZoom) {
- return;
- }
-
this.isZooming = false;
this.zoom = d3.behavior.zoom()
@@ -115,10 +110,6 @@ class NodesChart extends React.Component {
}
componentWillUnmount() {
- if (this.props.noZoom) {
- return;
- }
-
// undoing .call(zoom)
d3.select('.nodes-chart svg')
.on('mousedown.zoom', null)
@@ -294,10 +285,8 @@ class NodesChart extends React.Component {
restoreLayout(state) {
// undo any pan/zooming that might have happened
- if (this.zoom) {
- this.zoom.scale(state.scale);
- this.zoom.translate([state.panTranslateX, state.panTranslateY]);
- }
+ this.zoom.scale(state.scale);
+ this.zoom.translate([state.panTranslateX, state.panTranslateY]);
const nodes = state.nodes.map(node => node.merge({
x: node.get('px'),
@@ -326,10 +315,6 @@ class NodesChart extends React.Component {
const stateEdges = this.initEdges(props.nodes, stateNodes);
const nodeScale = this.getNodeScale(props.nodes, state.width, state.height);
const nextState = { nodeScale };
- const nodeOrder = props.nodeOrder || makeMap(stateNodes
- .toList()
- .sortBy(n => n.get('label'))
- .map((n, i) => [n.get('id'), i]));
const options = {
width: state.width,
@@ -338,8 +323,7 @@ class NodesChart extends React.Component {
margins: props.margins,
forceRelayout: props.forceRelayout,
topologyId: this.props.topologyId,
- topologyOptions: this.props.topologyOptions,
- nodeOrder
+ topologyOptions: this.props.topologyOptions
};
const timedLayouter = timely(doLayout);
@@ -366,12 +350,10 @@ class NodesChart extends React.Component {
const zoomFactor = Math.min(xFactor, yFactor);
let zoomScale = this.state.scale;
- if (!this.props.noZoom && !state.hasZoomed && zoomFactor > 0 && zoomFactor < 1) {
+ if (!state.hasZoomed && zoomFactor > 0 && zoomFactor < 1) {
zoomScale = zoomFactor;
// saving in d3's behavior cache
- if (this.zoom) {
- this.zoom.scale(zoomFactor);
- }
+ this.zoom.scale(zoomFactor);
}
nextState.scale = zoomScale;
@@ -390,7 +372,7 @@ class NodesChart extends React.Component {
const nodeSize = expanse / 3; // single node should fill a third of the screen
const maxNodeSize = expanse / 10;
const normalizedNodeSize = Math.min(nodeSize / Math.sqrt(nodes.size), maxNodeSize);
- return this.state.nodeScale.copy().range([0, this.props.nodeSize || normalizedNodeSize]);
+ return this.state.nodeScale.copy().range([0, normalizedNodeSize]);
}
zoomed() {
diff --git a/client/app/scripts/charts/nodes-grid.js b/client/app/scripts/charts/nodes-grid.js
index e78a2a9bd..8e791a3c6 100644
--- a/client/app/scripts/charts/nodes-grid.js
+++ b/client/app/scripts/charts/nodes-grid.js
@@ -54,20 +54,19 @@ function getColumns(nodes) {
function renderIdCell(props) {
- const style = {
+ const iconStyle = {
width: 16,
flex: 'none',
color: getNodeColor(props.rank, props.label_major)
};
+ const showSubLabel = Boolean(props.pseudo);
return (
-
-
-
- {props.label} {props.pseudo &&
- {props.label_minor}}
-
+
+
+ {props.label} {showSubLabel &&
+ {props.label_minor}}
);
@@ -79,33 +78,18 @@ class NodesGrid extends React.Component {
constructor(props, context) {
super(props, context);
- this.renderIdCell = this.renderIdCell.bind(this);
- this.clickRow = this.clickRow.bind(this);
-
+ this.onClickRow = this.onClickRow.bind(this);
this.onSortChange = this.onSortChange.bind(this);
- this.onMouseEnterRow = this.onMouseEnterRow.bind(this);
- this.onMouseLeaveRow = this.onMouseLeaveRow.bind(this);
}
- clickRow(ev, node, el) {
+ onClickRow(ev, node, el) {
+ // TODO: do this better
if (ev.target.className === 'node-details-table-node-link') {
return;
}
this.props.clickNode(node.id, node.label, el.getBoundingClientRect());
}
- renderIdCell(props) {
- return renderIdCell(props);
- }
-
- onMouseEnterRow() {
- this.props.clickPauseUpdate();
- }
-
- onMouseLeaveRow() {
- this.props.clickResumeUpdate();
- }
-
onSortChange(sortBy, sortedDesc) {
this.props.sortOrderChanged(sortBy, sortedDesc);
}
@@ -140,16 +124,17 @@ class NodesGrid extends React.Component {
+ limit={1000}
+ {...detailsData}
+ />
);
}
diff --git a/client/app/scripts/charts/nodes-layout.js b/client/app/scripts/charts/nodes-layout.js
index 0c0392466..116e35f4d 100644
--- a/client/app/scripts/charts/nodes-layout.js
+++ b/client/app/scripts/charts/nodes-layout.js
@@ -1,6 +1,5 @@
import dagre from 'dagre';
import debug from 'debug';
-import d3 from 'd3';
import { fromJS, Map as makeMap, Set as ImmSet } from 'immutable';
import { EDGE_ID_SEPARATOR } from '../constants/naming';
@@ -50,9 +49,7 @@ function runLayoutEngine(graph, imNodes, imEdges, opts) {
// configure node margins
graph.setGraph({
nodesep,
- ranksep,
- // rankdir: 'LR',
- // align: 'UL'
+ ranksep
});
// add nodes to the graph if not already there
@@ -179,17 +176,18 @@ function layoutSingleNodes(layout, opts) {
offsetY = offsetY || margins.top + nodeHeight / 2;
const columns = Math.ceil(Math.sqrt(singleNodes.size));
- const rows = Math.ceil(singleNodes.size / columns);
let row = 0;
let col = 0;
+ let singleX;
+ let singleY;
nodes = nodes.sortBy(node => node.get('rank')).map(node => {
if (singleNodes.has(node.get('id'))) {
if (col === columns) {
col = 0;
row++;
}
- const singleX = col * (nodesep + nodeWidth) + offsetX;
- const singleY = row * (ranksep + nodeHeight) + offsetY;
+ singleX = col * (nodesep + nodeWidth) + offsetX;
+ singleY = row * (ranksep + nodeHeight) + offsetY;
col++;
return node.merge({
x: singleX,
@@ -200,8 +198,8 @@ function layoutSingleNodes(layout, opts) {
});
// adjust layout dimensions if graph is now bigger
- result.width = Math.max(layout.width, columns * nodeWidth + (columns - 1) * nodesep);
- result.height = Math.max(layout.height, rows * nodeHeight + (rows - 1) * ranksep);
+ result.width = Math.max(layout.width, singleX + nodeWidth / 2 + nodesep);
+ result.height = Math.max(layout.height, singleY + nodeHeight / 2 + ranksep);
result.nodes = nodes;
}
@@ -246,7 +244,6 @@ function shiftLayoutToCenter(layout, opts) {
return result;
}
-
/**
* Adds `points` array to edge based on location of source and target
* @param {Map} edge new edge
@@ -262,39 +259,6 @@ function setSimpleEdgePoints(edge, nodeCache) {
]));
}
-
-export function uniqueRowConstraint(layout, options) {
- const result = Object.assign({}, layout);
- const scale = options.scale || DEFAULT_SCALE;
- const nodeHeight = scale(NODE_SIZE_FACTOR);
- const nodeWidth = scale(NODE_SIZE_FACTOR);
- const margins = options.margins || DEFAULT_MARGINS;
-
- const rowHeight = options.height / layout.nodes.size;
- const nodeOrder = options.nodeOrder || makeMap(layout.nodes
- .toList()
- .sortBy(n => n.get('y'))
- .map((n, i) => [n.get('id'), i]));
-
- const nodeXs = layout.nodes.map(n => n.get('x')).toList().toJS();
- const xScale = d3.scale.linear()
- .domain(d3.extent(nodeXs))
- .range([nodeWidth, options.width - nodeWidth])
- .clamp(false);
-
- result.nodes = layout.nodes.map(node => node.merge({
- x: xScale(node.get('x')),
- y: nodeOrder.get(node.get('id')) * rowHeight + nodeHeight * 0.5 + margins.top + 2
- }));
-
- result.edges = layout.edges.map(edge => (
- setSimpleEdgePoints(edge, result.nodes)
- ));
-
- return result;
-}
-
-
/**
* Determine if nodes were added between node sets
* @param {Map} nodes new Map of nodes
@@ -391,7 +355,6 @@ export function doLayout(immNodes, immEdges, opts) {
let layout;
++layoutRuns;
- // if (false && !options.forceRelayout && cachedLayout && nodeCache && edgeCache
if (!options.forceRelayout && cachedLayout && nodeCache && edgeCache
&& !hasUnseenNodes(immNodes, nodeCache)) {
log('skip layout, trivial adjustment', ++layoutRunsTrivial, layoutRuns);
@@ -407,7 +370,6 @@ export function doLayout(immNodes, immEdges, opts) {
}
layout = layoutSingleNodes(layout, opts);
layout = shiftLayoutToCenter(layout, opts);
- // layout = uniqueRowConstraint(layout, opts);
}
// cache results
diff --git a/client/app/scripts/components/debug-toolbar.js b/client/app/scripts/components/debug-toolbar.js
index 8d2a4e703..ae61fff59 100644
--- a/client/app/scripts/components/debug-toolbar.js
+++ b/client/app/scripts/components/debug-toolbar.js
@@ -184,7 +184,7 @@ class DebugToolbar extends React.Component {
addNodes(n, prefix = 'zing') {
const ns = this.props.nodes;
const nodeNames = ns.keySeq().toJS();
- const newNodeNames = _.range(nodeNames.length, nodeNames.length + n).map(i => (
+ const newNodeNames = _.range(ns.size, ns.size + n).map(i => (
// `${randomLetter()}${randomLetter()}-zing`
`${prefix}${i}`
));
diff --git a/client/app/scripts/components/examples.js b/client/app/scripts/components/examples.js
deleted file mode 100644
index 43a82b439..000000000
--- a/client/app/scripts/components/examples.js
+++ /dev/null
@@ -1,150 +0,0 @@
-/* eslint no-unused-vars: "off" */
-import React from 'react';
-import _ from 'lodash';
-import NodesChart from '../charts/nodes-chart';
-import NodesGrid from '../charts/nodes-grid';
-import { deltaAdd, makeNodes } from './debug-toolbar';
-import { fromJS, Map as makeMap, Set as makeSet } from 'immutable';
-
-
-function clog(v) {
- console.log(v);
- return v;
-}
-
-function randomGraph(n) {
- return makeMap(makeNodes(n, 'ewq', 4, 'hexagon').map(d => [d.id, fromJS(d)]));
-}
-
-function deltaAddSimple(name, adjacency = []) {
- return deltaAdd(name, adjacency, 'circle', false, 1, '');
-}
-
-
-function makeIds(n) {
- return _.range(n).map(i => `n${i}`);
-}
-
-
-function disconnectedGraph(n) {
- return makeMap(makeIds(n)
- .map((id) => deltaAddSimple(id))
- .map(d => [d.id, fromJS(d)]));
-}
-
-
-function completeGraph(n) {
- const ids = makeIds(n);
- const allEdges = _.flatMap(ids, i => ids.filter(ii => i !== ii).map(ii => [i, ii]));
- const oneWayEdges = allEdges.filter(edge => _.isEqual(edge, _.sortBy(edge)));
- const adjacencyMap = _(oneWayEdges)
- .groupBy(e => e[0])
- .mapValues(edges => edges.map(e => e[1]))
- .value();
- return makeMap(ids
- .map((id) => deltaAddSimple(id, adjacencyMap[id] || []))
- .map(d => [d.id, fromJS(d)]));
-}
-
-
-function completeGraphBi(n) {
- const ids = makeIds(n);
- const adjacency = (id) => ids.filter(_id => _id !== id);
- return makeMap(ids
- .map((id) => deltaAddSimple(id, adjacency(id)))
- .map(d => [d.id, fromJS(d)]));
-}
-
-
-function flatTree(n) {
- const ids = makeIds(n + 1);
- const p = ids.pop();
- const adjacency = id => id === p ? ids : [];
- return makeMap(ids.concat([p])
- .map((id) => deltaAddSimple(id, adjacency(id)))
- .map(d => [d.id, fromJS(d)]));
-}
-
-function proxyGraph(n) {
- const ids = makeIds(n * 2 + 1);
- const p = ids.pop();
- const topIds = _.take(ids, n);
- const bottomIds = _.drop(ids, n);
- const adjacencyMap = Object.assign({
- [p]: bottomIds
- }, _.fromPairs(topIds.map(id => [id, [p]])));
-
- return makeMap(ids.concat([p])
- .map((id) => deltaAddSimple(id, adjacencyMap[id] || []))
- .map(d => [d.id, fromJS(d)]));
-}
-
-
-function chart(nodes,
- n,
- style = { width: 250, height: 250 },
- margins = { top: 0, left: 0, right: 0, bottom: 0 },
- nodeSize = null) {
- return (
-
-
-
- );
-}
-
-
-function variants() {
- const nCharts = 5;
- const width = 250;
- const style = {width: nCharts * (width + 16)};
- const generators = [
- { id: 'disconnectedGraph', fn: disconnectedGraph },
- { id: 'completeGraphBi', fn: completeGraphBi },
- { id: 'completeGraph', fn: completeGraph },
- { id: 'flatTree', fn: flatTree },
- { id: 'proxyGraph', fn: proxyGraph }
- ];
- return (
-
- {_.reverse(generators).map(({id, fn}) => (
-
- {_.range(1, nCharts + 1).map(i => (
- chart(fn(i), i)
- ))}
-
- ))}
-
- );
-}
-
-
-function gridView() {
- const nodes = randomGraph(50).map(node => node.remove('label').remove('label_minor'));
- const nodeSize = 24;
- return (
-
- );
-}
-
-
-export class Examples extends React.Component {
- render() {
- return (
-
- {gridView()}
- {false && variants()}
-
- );
- }
-}
diff --git a/client/app/scripts/components/node-details/node-details-table.js b/client/app/scripts/components/node-details/node-details-table.js
index cd9f17fbf..d8bfc32ff 100644
--- a/client/app/scripts/components/node-details/node-details-table.js
+++ b/client/app/scripts/components/node-details/node-details-table.js
@@ -1,6 +1,5 @@
import _ from 'lodash';
import React from 'react';
-import { Map as makeMap } from 'immutable';
import classNames from 'classnames';
import ShowMore from '../show-more';
@@ -69,7 +68,6 @@ function getValueForSortBy(sortBy) {
}
-/*
function getMetaDataSorters(nodes) {
// returns an array of sorters that will take a node
return _.get(nodes, [0, 'metadata'], []).map((field, index) => node => {
@@ -83,15 +81,14 @@ function getMetaDataSorters(nodes) {
return null;
});
}
-*/
function getSortedNodes(nodes, columns, sortBy, sortedDesc) {
const sortedNodes = _.sortBy(
nodes,
getValueForSortBy(sortBy || getDefaultSortBy(columns, nodes)),
- 'label'
- // getMetaDataSorters(nodes)
+ 'label',
+ getMetaDataSorters(nodes)
);
if (sortedDesc) {
sortedNodes.reverse();
@@ -217,12 +214,6 @@ export default class NodeDetailsTable extends React.Component {
nodes = nodes.slice(0, this.state.limit);
}
- const nodeOrderJS = (nodes || []).map((n, i) => [n.id, i]);
- const nodeOrder = makeMap(nodeOrderJS);
- const childrenWithProps = React.Children.map(this.props.children, (child) => (
- React.cloneElement(child, { nodeOrder })
- ));
-
const className = classNames('node-details-table-wrapper-wrapper', this.props.className);
return (
@@ -257,7 +248,6 @@ export default class NodeDetailsTable extends React.Component {
expanded={expanded}
notShown={notShown} />
- {childrenWithProps}
);
}
diff --git a/client/app/scripts/examples-main.js b/client/app/scripts/examples-main.js
deleted file mode 100644
index 562e2f508..000000000
--- a/client/app/scripts/examples-main.js
+++ /dev/null
@@ -1,10 +0,0 @@
-require('../styles/main.less');
-require('../../node_modules/fixed-data-table/dist/fixed-data-table.css');
-require('../images/favicon.ico');
-
-import React from 'react';
-import ReactDOM from 'react-dom';
-
-import { Examples } from './components/examples.js';
-
-ReactDOM.render(, document.getElementById('app'));
diff --git a/client/app/styles/main.less b/client/app/styles/main.less
index ee97b40fb..e58463a9e 100644
--- a/client/app/styles/main.less
+++ b/client/app/styles/main.less
@@ -1508,24 +1508,9 @@ h2 {
}
//
-// Examples
+// Nodes grid.
//
-.examples {
- background-color: @background-average-color;
- .example-chart {
- position: relative;
- }
-}
-
-.nodes-chart-examples {
- .example-chart {
- margin: 8px;
- display: inline-block;
- border: 1px solid steelBlue;
- }
-}
-
.nodes-grid {
tr {
@@ -1538,19 +1523,16 @@ h2 {
&-id-column {
margin: -3px -4px;
- padding: 2px 2px;
-
- .content {
- display: flex;
- div {
- flex: 1;
- }
+ padding: 2px 4px;
+ display: flex;
+ div {
+ flex: 1;
}
}
.node-details-table-wrapper-wrapper {
- flex: 1;
+ flex: 1;
display: flex;
flex-direction: row;
width: 100%;
diff --git a/client/webpack.local.config.js b/client/webpack.local.config.js
index 40d1a96c9..4698cb5cb 100644
--- a/client/webpack.local.config.js
+++ b/client/webpack.local.config.js
@@ -15,13 +15,7 @@ var HtmlWebpackPlugin = require('html-webpack-plugin');
*/
// Inject websocket url to dev backend
-
-var WEBPACK_SERVER_HOST = process.env.WEBPACK_SERVER_HOST || 'localhost';
-var COMMON_DEPS = [
- 'webpack-dev-server/client?http://' + WEBPACK_SERVER_HOST + ':4041',
- 'webpack/hot/only-dev-server',
- './app/scripts/debug'
-];
+ var WEBPACK_SERVER_HOST = process.env.WEBPACK_SERVER_HOST || 'localhost';
module.exports = {
@@ -69,11 +63,6 @@ module.exports = {
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
new webpack.IgnorePlugin(/^\.\/locale$/, [/moment$/]),
- new HtmlWebpackPlugin({
- chunks: ['vendors', 'examples-app'],
- template: 'app/html/index.html',
- filename: 'examples.html'
- }),
new HtmlWebpackPlugin({
chunks: ['vendors', 'contrast-app'],
template: 'app/html/index.html',
@@ -115,7 +104,7 @@ module.exports = {
loader: 'json-loader'
},
{
- test: /(\.css|\.less)$/,
+ test: /\.less$/,
loader: 'style-loader!css-loader!postcss-loader!less-loader'
},
{