Upgrade eslint, react and react-motion

* get rid of material-ui
* reduced bundle size by 20%
This commit is contained in:
David Kaltschmidt
2015-11-11 15:27:36 +01:00
parent a74178b722
commit acbf8a665d
13 changed files with 226 additions and 430 deletions

View File

@@ -85,7 +85,7 @@ module.exports = {
WebapiUtils.doControl(
probeId,
nodeId,
control,
control
);
},

View File

@@ -1,7 +1,8 @@
const _ = require('lodash');
const d3 = require('d3');
const React = require('react');
const Spring = require('react-motion').Spring;
const Motion = require('react-motion').Motion;
const spring = require('react-motion').spring;
const AppActions = require('../actions/app-actions');
@@ -15,8 +16,8 @@ const animConfig = [80, 20]; // stiffness, bounce
const flattenPoints = function(points) {
const flattened = {};
points.forEach(function(point, i) {
flattened['x' + i] = {val: point.x, config: animConfig};
flattened['y' + i] = {val: point.y, config: animConfig};
flattened['x' + i] = spring(point.x, animConfig);
flattened['y' + i] = spring(point.y, animConfig);
});
return flattened;
};
@@ -29,7 +30,7 @@ const extractPoints = function(points) {
if (!extracted[index]) {
extracted[index] = {};
}
extracted[index][axis] = value.val;
extracted[index][axis] = value;
});
return extracted;
};
@@ -66,7 +67,7 @@ const Edge = React.createClass({
const classes = classNames.join(' ');
return (
<Spring endValue={points}>
<Motion style={points}>
{function(interpolated) {
const path = line(extractPoints(interpolated));
return (
@@ -76,7 +77,7 @@ const Edge = React.createClass({
</g>
);
}}
</Spring>
</Motion>
);
},

View File

@@ -1,5 +1,6 @@
const React = require('react');
const Spring = require('react-motion').Spring;
const Motion = require('react-motion').Motion;
const spring = require('react-motion').spring;
const AppActions = require('../actions/app-actions');
const NodeColorMixin = require('../mixins/node-color-mixin');
@@ -42,30 +43,30 @@ const Node = React.createClass({
const classes = classNames.join(' ');
return (
<Spring endValue={{
x: {val: this.props.dx, config: animConfig},
y: {val: this.props.dy, config: animConfig},
f: {val: scaleFactor, config: animConfig}
<Motion style={{
x: spring(this.props.dx, animConfig),
y: spring(this.props.dy, animConfig),
f: spring(scaleFactor, animConfig)
}}>
{function(interpolated) {
const transform = `translate(${interpolated.x.val},${interpolated.y.val})`;
const transform = `translate(${interpolated.x},${interpolated.y})`;
return (
<g className={classes} transform={transform} id={props.id}
onClick={onMouseClick} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
{props.highlighted && <circle r={scale(0.7 * interpolated.f.val)} className="highlighted"></circle>}
<circle r={scale(0.5 * interpolated.f.val)} className="border" stroke={color}></circle>
<circle r={scale(0.45 * interpolated.f.val)} className="shadow"></circle>
<circle r={Math.max(2, scale(0.125 * interpolated.f.val))} className="node"></circle>
<text className="node-label" textAnchor="middle" x="0" y={labelOffsetY + scale(0.5 * interpolated.f.val)}>
{props.highlighted && <circle r={scale(0.7 * interpolated.f)} className="highlighted"></circle>}
<circle r={scale(0.5 * interpolated.f)} className="border" stroke={color}></circle>
<circle r={scale(0.45 * interpolated.f)} className="shadow"></circle>
<circle r={Math.max(2, scale(0.125 * interpolated.f))} className="node"></circle>
<text className="node-label" textAnchor="middle" x="0" y={labelOffsetY + scale(0.5 * interpolated.f)}>
{label}
</text>
<text className="node-sublabel" textAnchor="middle" x="0" y={subLabelOffsetY + scale(0.5 * interpolated.f.val)}>
<text className="node-sublabel" textAnchor="middle" x="0" y={subLabelOffsetY + scale(0.5 * interpolated.f)}>
{subLabel}
</text>
</g>
);
}}
</Spring>
</Motion>
);
},

View File

@@ -4,7 +4,8 @@ const debug = require('debug')('scope:nodes-chart');
const React = require('react');
const makeMap = require('immutable').Map;
const timely = require('timely');
const Spring = require('react-motion').Spring;
const Motion = require('react-motion').Motion;
const spring = require('react-motion').spring;
const AppActions = require('../actions/app-actions');
const AppStore = require('../stores/app-store');
@@ -199,7 +200,7 @@ const NodesChart = React.createClass({
render: function() {
const nodeElements = this.renderGraphNodes(this.state.nodes, this.state.nodeScale);
const edgeElements = this.renderGraphEdges(this.state.edges, this.state.nodeScale);
let scale = this.state.scale;
const scale = this.state.scale;
// only animate shift behavior, not panning
const panTranslate = this.state.panTranslate;
@@ -213,15 +214,16 @@ const NodesChart = React.createClass({
const svgClassNames = this.state.maxNodesExceeded || nodeElements.size === 0 ? 'hide' : '';
const errorEmpty = this.renderEmptyTopologyError(AppStore.isTopologyEmpty());
const errorMaxNodesExceeded = this.renderMaxNodesError(this.state.maxNodesExceeded);
const motionConfig = [80, 20];
return (
<div className="nodes-chart">
{errorEmpty}
{errorMaxNodesExceeded}
<svg width="100%" height="100%" className={svgClassNames} onClick={this.handleMouseClick}>
<Spring endValue={{val: translate, config: [80, 20]}}>
<Motion style={{x: spring(translate[0], motionConfig), y: spring(translate[1], motionConfig)}}>
{function(interpolated) {
let interpolatedTranslate = wasShifted ? interpolated.val : panTranslate;
const interpolatedTranslate = wasShifted ? [interpolated.x, interpolated.y] : panTranslate;
const transform = 'translate(' + interpolatedTranslate + ')' +
' scale(' + scale + ')';
return (
@@ -235,7 +237,7 @@ const NodesChart = React.createClass({
</g>
);
}}
</Spring>
</Motion>
</svg>
</div>
);
@@ -291,14 +293,14 @@ const NodesChart = React.createClass({
centerSelectedNode: function(props, state) {
let stateNodes = state.nodes;
let stateEdges = state.edges;
let selectedLayoutNode = stateNodes.get(props.selectedNodeId);
const selectedLayoutNode = stateNodes.get(props.selectedNodeId);
if (!selectedLayoutNode) {
return {};
}
const adjacency = AppStore.getAdjacentNodes(props.selectedNodeId);
let adjacentLayoutNodeIds = [];
const adjacentLayoutNodeIds = [];
adjacency.forEach(function(adjacentId) {
// filter loopback

View File

@@ -7,7 +7,7 @@ const NodesError = React.createClass({
if (this.props.hidden) {
classNames += ' hide';
}
let iconClassName = 'fa ' + this.props.faIconClass;
const iconClassName = 'fa ' + this.props.faIconClass;
return (
<div className={classNames}>

View File

@@ -1,3 +1,6 @@
// polyfill
Object.assign = require('object-assign');
const dagre = require('dagre');
const debug = require('debug')('scope:nodes-layout');
const makeMap = require('immutable').Map;
@@ -181,7 +184,7 @@ function hasSameEndpoints(cachedEdge, nodes) {
* @return {Object} layout clone
*/
function cloneLayout(layout, nodes, edges) {
const clone = {...layout, nodes, edges};
const clone = Object.assign({}, layout, {nodes, edges});
return clone;
}

View File

@@ -31,7 +31,7 @@ describe('NodeDetails', () => {
const c = TestUtils.renderIntoDocument(<NodeDetails nodes={nodes} nodeId={nodeId} details={details} />);
const title = TestUtils.findRenderedDOMComponentWithClass(c, 'node-details-header-label');
expect(title.getDOMNode().textContent).toBe('Node 1');
expect(title.textContent).toBe('Node 1');
});
});

View File

@@ -1,5 +1,4 @@
const React = require('react');
const mui = require('material-ui');
const Logo = require('./logo');
const AppStore = require('../stores/app-store');
@@ -13,8 +12,6 @@ const Details = require('./details');
const Nodes = require('./nodes');
const RouterUtils = require('../utils/router-utils');
const ThemeManager = new mui.Styles.ThemeManager();
const ESC_KEY_CODE = 27;
function getStateFromStores() {
@@ -67,12 +64,6 @@ const App = React.createClass({
}
},
getChildContext: function() {
return {
muiTheme: ThemeManager.getCurrentTheme()
};
},
render: function() {
const showingDetails = this.state.selectedNodeId;
const versionString = this.state.version ? 'Version ' + this.state.version : '';
@@ -115,9 +106,6 @@ const App = React.createClass({
);
},
childContextTypes: {
muiTheme: React.PropTypes.object
}
});
module.exports = App;

View File

@@ -1,6 +1,4 @@
const React = require('react');
const mui = require('material-ui');
const Paper = mui.Paper;
const NodeDetails = require('./node-details');
@@ -9,9 +7,11 @@ const Details = React.createClass({
render: function() {
return (
<div id="details">
<Paper zDepth={3} style={{height: '100%', paddingBottom: 8}}>
<div style={{height: '100%', paddingBottom: 8, borderRadius: 2,
backgroundColor: '#fff',
boxShadow: '0 10px 30px rgba(0, 0, 0, 0.19), 0 6px 10px rgba(0, 0, 0, 0.23)'}}>
<NodeDetails {...this.props} />
</Paper>
</div>
</div>
);
}

View File

@@ -2,9 +2,10 @@ require('font-awesome-webpack');
require('../styles/main.less');
const React = require('react');
const ReactDOM = require('react-dom');
const App = require('./components/app.js');
React.render(
ReactDOM.render(
<App/>,
document.getElementById('app'));

View File

@@ -242,186 +242,186 @@ const AppStore = assign({}, EventEmitter.prototype, {
AppStore.registeredCallback = function(payload) {
switch (payload.type) {
case ActionTypes.CHANGE_TOPOLOGY_OPTION:
if (topologyOptions.getIn([payload.topologyId, payload.option])
!== payload.value) {
nodes = nodes.clear();
}
topologyOptions = topologyOptions.setIn(
[payload.topologyId, payload.option],
payload.value
);
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.CLEAR_CONTROL_ERROR:
controlError = null;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.CLICK_CLOSE_DETAILS:
selectedNodeId = null;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.CLICK_NODE:
if (payload.nodeId === selectedNodeId) {
// clicking same node twice unsets the selection
selectedNodeId = null;
} else {
selectedNodeId = payload.nodeId;
}
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.CLICK_TOPOLOGY:
selectedNodeId = null;
if (payload.topologyId !== currentTopologyId) {
setTopology(payload.topologyId);
nodes = nodes.clear();
}
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.CLOSE_WEBSOCKET:
websocketClosed = true;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.DO_CONTROL:
controlPending = true;
controlError = null;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.ENTER_EDGE:
mouseOverEdgeId = payload.edgeId;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.ENTER_NODE:
mouseOverNodeId = payload.nodeId;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.HIT_ESC_KEY:
nodeDetails = null;
selectedNodeId = null;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.LEAVE_EDGE:
mouseOverEdgeId = null;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.LEAVE_NODE:
mouseOverNodeId = null;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.OPEN_WEBSOCKET:
// flush nodes cache after re-connect
case ActionTypes.CHANGE_TOPOLOGY_OPTION:
if (topologyOptions.getIn([payload.topologyId, payload.option])
!== payload.value) {
nodes = nodes.clear();
websocketClosed = false;
}
topologyOptions = topologyOptions.setIn(
[payload.topologyId, payload.option],
payload.value
);
AppStore.emit(AppStore.CHANGE_EVENT);
break;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.CLEAR_CONTROL_ERROR:
controlError = null;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.DO_CONTROL_ERROR:
controlPending = false;
controlError = payload.error;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.CLICK_CLOSE_DETAILS:
selectedNodeId = null;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.DO_CONTROL_SUCCESS:
controlPending = false;
controlError = null;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.CLICK_NODE:
if (payload.nodeId === selectedNodeId) {
// clicking same node twice unsets the selection
selectedNodeId = null;
} else {
selectedNodeId = payload.nodeId;
}
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.RECEIVE_ERROR:
errorUrl = payload.errorUrl;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.CLICK_TOPOLOGY:
selectedNodeId = null;
if (payload.topologyId !== currentTopologyId) {
setTopology(payload.topologyId);
nodes = nodes.clear();
}
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.RECEIVE_NODE_DETAILS:
errorUrl = null;
nodeDetails = payload.details;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.CLOSE_WEBSOCKET:
websocketClosed = true;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.RECEIVE_NODES_DELTA:
const emptyMessage = !payload.delta.add && !payload.delta.remove
&& payload.delta.update;
case ActionTypes.DO_CONTROL:
controlPending = true;
controlError = null;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
if (!emptyMessage) {
debug('RECEIVE_NODES_DELTA',
'remove', _.size(payload.delta.remove),
'update', _.size(payload.delta.update),
'add', _.size(payload.delta.add));
case ActionTypes.ENTER_EDGE:
mouseOverEdgeId = payload.edgeId;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.ENTER_NODE:
mouseOverNodeId = payload.nodeId;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.HIT_ESC_KEY:
nodeDetails = null;
selectedNodeId = null;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.LEAVE_EDGE:
mouseOverEdgeId = null;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.LEAVE_NODE:
mouseOverNodeId = null;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.OPEN_WEBSOCKET:
// flush nodes cache after re-connect
nodes = nodes.clear();
websocketClosed = false;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.DO_CONTROL_ERROR:
controlPending = false;
controlError = payload.error;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.DO_CONTROL_SUCCESS:
controlPending = false;
controlError = null;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.RECEIVE_ERROR:
errorUrl = payload.errorUrl;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.RECEIVE_NODE_DETAILS:
errorUrl = null;
nodeDetails = payload.details;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.RECEIVE_NODES_DELTA:
const emptyMessage = !payload.delta.add && !payload.delta.remove
&& payload.delta.update;
if (!emptyMessage) {
debug('RECEIVE_NODES_DELTA',
'remove', _.size(payload.delta.remove),
'update', _.size(payload.delta.update),
'add', _.size(payload.delta.add));
}
errorUrl = null;
// nodes that no longer exist
_.each(payload.delta.remove, function(nodeId) {
// in case node disappears before mouseleave event
if (mouseOverNodeId === nodeId) {
mouseOverNodeId = null;
}
errorUrl = null;
// nodes that no longer exist
_.each(payload.delta.remove, function(nodeId) {
// in case node disappears before mouseleave event
if (mouseOverNodeId === nodeId) {
mouseOverNodeId = null;
}
if (nodes.has(nodeId) && _.contains(mouseOverEdgeId, nodeId)) {
mouseOverEdgeId = null;
}
nodes = nodes.delete(nodeId);
});
// update existing nodes
_.each(payload.delta.update, function(node) {
nodes = nodes.set(node.id, nodes.get(node.id).merge(makeNode(node)));
});
// add new nodes
_.each(payload.delta.add, function(node) {
nodes = nodes.set(node.id, Immutable.fromJS(makeNode(node)));
});
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.RECEIVE_TOPOLOGIES:
errorUrl = null;
topologies = processTopologies(payload.topologies);
setTopology(currentTopologyId);
// only set on first load, if options are not already set via route
if (!topologiesLoaded && topologyOptions.size === 0) {
setDefaultTopologyOptions(topologies);
if (nodes.has(nodeId) && _.contains(mouseOverEdgeId, nodeId)) {
mouseOverEdgeId = null;
}
topologiesLoaded = true;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
nodes = nodes.delete(nodeId);
});
case ActionTypes.RECEIVE_API_DETAILS:
errorUrl = null;
version = payload.version;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
// update existing nodes
_.each(payload.delta.update, function(node) {
nodes = nodes.set(node.id, nodes.get(node.id).merge(makeNode(node)));
});
case ActionTypes.ROUTE_TOPOLOGY:
routeSet = true;
if (currentTopologyId !== payload.state.topologyId) {
nodes = nodes.clear();
}
setTopology(payload.state.topologyId);
// add new nodes
_.each(payload.delta.add, function(node) {
nodes = nodes.set(node.id, Immutable.fromJS(makeNode(node)));
});
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.RECEIVE_TOPOLOGIES:
errorUrl = null;
topologies = processTopologies(payload.topologies);
setTopology(currentTopologyId);
// only set on first load, if options are not already set via route
if (!topologiesLoaded && topologyOptions.size === 0) {
setDefaultTopologyOptions(topologies);
selectedNodeId = payload.state.selectedNodeId;
topologyOptions = Immutable.fromJS(payload.state.topologyOptions)
|| topologyOptions;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
}
topologiesLoaded = true;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
default:
break;
case ActionTypes.RECEIVE_API_DETAILS:
errorUrl = null;
version = payload.version;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
case ActionTypes.ROUTE_TOPOLOGY:
routeSet = true;
if (currentTopologyId !== payload.state.topologyId) {
nodes = nodes.clear();
}
setTopology(payload.state.topologyId);
setDefaultTopologyOptions(topologies);
selectedNodeId = payload.state.selectedNodeId;
topologyOptions = Immutable.fromJS(payload.state.topologyOptions)
|| topologyOptions;
AppStore.emit(AppStore.CHANGE_EVENT);
break;
default:
break;
}
};