diff --git a/client/app/scripts/actions/app-actions.js b/client/app/scripts/actions/app-actions.js
index 2aa9916284..3a662bbb83 100644
--- a/client/app/scripts/actions/app-actions.js
+++ b/client/app/scripts/actions/app-actions.js
@@ -39,6 +39,13 @@ module.exports = {
WebapiUtils.getNodesDelta(AppStore.getCurrentTopologyUrl());
},
+ enterEdge: function(edgeId) {
+ AppDispatcher.dispatch({
+ type: ActionTypes.ENTER_EDGE,
+ edgeId: edgeId
+ });
+ },
+
enterNode: function(nodeId) {
AppDispatcher.dispatch({
type: ActionTypes.ENTER_NODE,
@@ -53,6 +60,13 @@ module.exports = {
RouterUtils.updateRoute();
},
+ leaveEdge: function(edgeId) {
+ AppDispatcher.dispatch({
+ type: ActionTypes.LEAVE_EDGE,
+ edgeId: edgeId
+ });
+ },
+
leaveNode: function(nodeId) {
AppDispatcher.dispatch({
type: ActionTypes.LEAVE_NODE,
diff --git a/client/app/scripts/charts/edge.js b/client/app/scripts/charts/edge.js
new file mode 100644
index 0000000000..c7bb9f4ccb
--- /dev/null
+++ b/client/app/scripts/charts/edge.js
@@ -0,0 +1,34 @@
+const d3 = require('d3');
+const React = require('react');
+
+const AppActions = require('../actions/app-actions');
+
+const line = d3.svg.line()
+ .interpolate('basis')
+ .x(function(d) { return d.x; })
+ .y(function(d) { return d.y; });
+
+const Edge = React.createClass({
+
+ render: function() {
+ const className = this.props.highlighted ? 'edge highlighted' : 'edge';
+
+ return (
+
+
+
+
+ );
+ },
+
+ handleMouseEnter: function(ev) {
+ AppActions.enterEdge(ev.currentTarget.id);
+ },
+
+ handleMouseLeave: function(ev) {
+ AppActions.leaveEdge(ev.currentTarget.id);
+ }
+
+});
+
+module.exports = Edge;
diff --git a/client/app/scripts/charts/node.js b/client/app/scripts/charts/node.js
index fa8e46efa3..d09089a10a 100644
--- a/client/app/scripts/charts/node.js
+++ b/client/app/scripts/charts/node.js
@@ -1,6 +1,7 @@
const React = require('react');
const tweenState = require('react-tween-state');
+const AppActions = require('../actions/app-actions');
const NodeColorMixin = require('../mixins/node-color-mixin');
const Node = React.createClass({
@@ -47,7 +48,9 @@ const Node = React.createClass({
const className = this.props.highlighted ? 'node highlighted' : 'node';
return (
-
+
+ {this.props.highlighted && }
@@ -55,7 +58,16 @@ const Node = React.createClass({
{this.props.subLabel}
);
+ },
+
+ handleMouseEnter: function(ev) {
+ AppActions.enterNode(ev.currentTarget.id);
+ },
+
+ handleMouseLeave: function(ev) {
+ AppActions.leaveNode(ev.currentTarget.id);
}
+
});
module.exports = Node;
diff --git a/client/app/scripts/charts/nodes-chart.js b/client/app/scripts/charts/nodes-chart.js
index 91b1034dd0..5a5d8648fa 100644
--- a/client/app/scripts/charts/nodes-chart.js
+++ b/client/app/scripts/charts/nodes-chart.js
@@ -2,6 +2,8 @@ const _ = require('lodash');
const d3 = require('d3');
const React = require('react');
+const Edge = require('./edge');
+const Naming = require('../constants/naming');
const NodesLayout = require('./nodes-layout');
const Node = require('./node');
@@ -12,11 +14,6 @@ const MARGINS = {
bottom: 0
};
-const line = d3.svg.line()
- .interpolate('basis')
- .x(function(d) { return d.x; })
- .y(function(d) { return d.y; });
-
const NodesChart = React.createClass({
getInitialState: function() {
@@ -77,9 +74,9 @@ const NodesChart = React.createClass({
return fingerprint.join(';');
},
- getGraphNodes: function(nodes, scale) {
+ renderGraphNodes: function(nodes, scale) {
return _.map(nodes, function(node) {
- const highlighted = _.includes(this.props.highlightedNodes, node.id);
+ const highlighted = _.includes(this.props.highlightedNodeIds, node.id);
return (
+
);
- });
+ }, this);
},
render: function() {
- const nodeElements = this.getGraphNodes(this.state.nodes, this.state.nodeScale);
- const edgeElements = this.getGraphEdges(this.state.edges, this.state.nodeScale);
+ const nodeElements = this.renderGraphNodes(this.state.nodes, this.state.nodeScale);
+ const edgeElements = this.renderGraphEdges(this.state.edges, this.state.nodeScale);
const transform = 'translate(' + this.state.translate + ')' +
' scale(' + this.state.scale + ')';
@@ -131,12 +129,17 @@ const NodesChart = React.createClass({
_.each(topology, function(node, id) {
nodes[id] = prevNodes[id] || {};
+
+ // initialize position for new nodes
_.defaults(nodes[id], {
x: centerX,
y: centerY,
textAnchor: 'start'
});
+
+ // copy relevant fields to state nodes
_.assign(nodes[id], {
+ adjacency: node.adjacency,
id: id,
label: node.label_major,
subLabel: node.label_minor,
@@ -153,7 +156,7 @@ const NodesChart = React.createClass({
_.each(topology, function(node) {
_.each(node.adjacency, function(adjacent) {
const edge = [node.id, adjacent];
- const edgeId = edge.join('-');
+ const edgeId = edge.join(Naming.EDGE_ID_SEPARATOR);
if (!edges[edgeId]) {
const source = nodes[edge[0]];
diff --git a/client/app/scripts/components/app.js b/client/app/scripts/components/app.js
index 97cdc112e3..309b56a289 100644
--- a/client/app/scripts/components/app.js
+++ b/client/app/scripts/components/app.js
@@ -19,6 +19,8 @@ function getStateFromStores() {
currentTopology: AppStore.getCurrentTopology(),
connectionState: AppStore.getConnectionState(),
currentGrouping: AppStore.getCurrentGrouping(),
+ highlightedEdgeIds: AppStore.getHighlightedEdgeIds(),
+ highlightedNodeIds: AppStore.getHighlightedNodeIds(),
selectedNodeId: AppStore.getSelectedNodeId(),
nodeDetails: AppStore.getNodeDetails(),
nodes: AppStore.getNodes(),
@@ -67,7 +69,8 @@ const App = React.createClass({
-
+
);
}
diff --git a/client/app/scripts/components/nodes.js b/client/app/scripts/components/nodes.js
index 945428b6ae..4d1896d867 100644
--- a/client/app/scripts/components/nodes.js
+++ b/client/app/scripts/components/nodes.js
@@ -31,8 +31,10 @@ const Nodes = React.createClass({
return (