Pure-mixin-ify more stuff.

Helps a lot for dragging around the canvas.
This commit is contained in:
Simon Howe
2016-04-06 11:26:25 +02:00
parent 49286cf5ea
commit 4c378283cb
5 changed files with 134 additions and 104 deletions

View File

@@ -9,6 +9,7 @@
"comma-dangle": 0,
"object-curly-spacing": 0,
"react/jsx-closing-bracket-location": 0,
"react/prefer-stateless-function": 0,
"react/sort-comp": 0,
"react/prop-types": 0
}

View File

@@ -1,27 +1,33 @@
import _ from 'lodash';
import React from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import reactMixin from 'react-mixin';
import d3 from 'd3';
import { Motion, spring } from 'react-motion';
import Node from './node';
export default function NodeContainer(props) {
const { dx, dy, focused, layoutPrecision, zoomScale } = props;
const animConfig = [80, 20]; // stiffness, damping
const scaleFactor = focused ? (2 / zoomScale) : 1;
const other = _.omit(props, 'dx', 'dy');
export default class NodeContainer extends React.Component {
render() {
const { dx, dy, focused, layoutPrecision, zoomScale } = this.props;
const animConfig = [80, 20]; // stiffness, damping
const scaleFactor = focused ? (2 / zoomScale) : 1;
const other = _.omit(this.props, 'dx', 'dy');
return (
<Motion style={{
x: spring(dx, animConfig),
y: spring(dy, animConfig),
f: spring(scaleFactor, animConfig)
}}>
{interpolated => {
const transform = `translate(${d3.round(interpolated.x, layoutPrecision)},`
+ `${d3.round(interpolated.y, layoutPrecision)})`;
return <Node {...other} transform={transform} scaleFactor={interpolated.f} />;
}}
</Motion>
);
return (
<Motion style={{
x: spring(dx, animConfig),
y: spring(dy, animConfig),
f: spring(scaleFactor, animConfig)
}}>
{interpolated => {
const transform = `translate(${d3.round(interpolated.x, layoutPrecision)},`
+ `${d3.round(interpolated.y, layoutPrecision)})`;
return <Node {...other} transform={transform} scaleFactor={interpolated.f} />;
}}
</Motion>
);
}
}
reactMixin.onClass(NodeContainer, PureRenderMixin);

View File

@@ -1,16 +1,24 @@
import React from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import reactMixin from 'react-mixin';
import EdgeContainer from './edge-container';
export default function NodesChartEdges({hasSelectedNode, highlightedEdgeIds,
layoutEdges, layoutPrecision, selectedNodeId}) {
return (
<g className="nodes-chart-edges">
{layoutEdges.toIndexedSeq().map(edge => <EdgeContainer key={edge.get('id')}
id={edge.get('id')} source={edge.get('source')} target={edge.get('target')}
points={edge.get('points')} layoutPrecision={layoutPrecision}
highlightedEdgeIds={highlightedEdgeIds} hasSelectedNode={hasSelectedNode}
selectedNodeId={selectedNodeId} />)}
</g>
);
export default class NodesChartEdges extends React.Component {
render() {
const {hasSelectedNode, highlightedEdgeIds, layoutEdges, layoutPrecision,
selectedNodeId} = this.props;
return (
<g className="nodes-chart-edges">
{layoutEdges.toIndexedSeq().map(edge => <EdgeContainer key={edge.get('id')}
id={edge.get('id')} source={edge.get('source')} target={edge.get('target')}
points={edge.get('points')} layoutPrecision={layoutPrecision}
highlightedEdgeIds={highlightedEdgeIds} hasSelectedNode={hasSelectedNode}
selectedNodeId={selectedNodeId} />)}
</g>
);
}
}
reactMixin.onClass(NodesChartEdges, PureRenderMixin);

View File

@@ -1,23 +1,30 @@
import React from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import reactMixin from 'react-mixin';
import NodesChartEdges from './nodes-chart-edges';
import NodesChartNodes from './nodes-chart-nodes';
export default function NodesChartElements(props) {
return (
<g className="nodes-chart-elements" transform={props.transform}>
<NodesChartEdges layoutEdges={props.edges} selectedNodeId={props.selectedNodeId}
highlightedEdgeIds={props.highlightedEdgeIds}
hasSelectedNode={props.hasSelectedNode}
layoutPrecision={props.layoutPrecision} />
<NodesChartNodes layoutNodes={props.nodes} selectedNodeId={props.selectedNodeId}
selectedMetric={props.selectedMetric}
highlightedNodeIds={props.highlightedNodeIds}
hasSelectedNode={props.hasSelectedNode}
adjacentNodes={props.adjacentNodes}
nodeScale={props.nodeScale} onNodeClick={props.onNodeClick}
scale={props.scale} selectedNodeScale={props.selectedNodeScale}
topologyId={props.topologyId} layoutPrecision={props.layoutPrecision} />
</g>
);
export default class NodesChartElements extends React.Component {
render() {
const props = this.props;
return (
<g className="nodes-chart-elements" transform={props.transform}>
<NodesChartEdges layoutEdges={props.edges} selectedNodeId={props.selectedNodeId}
highlightedEdgeIds={props.highlightedEdgeIds}
hasSelectedNode={props.hasSelectedNode}
layoutPrecision={props.layoutPrecision} />
<NodesChartNodes layoutNodes={props.nodes} selectedNodeId={props.selectedNodeId}
selectedMetric={props.selectedMetric}
highlightedNodeIds={props.highlightedNodeIds}
hasSelectedNode={props.hasSelectedNode}
adjacentNodes={props.adjacentNodes}
nodeScale={props.nodeScale} onNodeClick={props.onNodeClick}
scale={props.scale} selectedNodeScale={props.selectedNodeScale}
topologyId={props.topologyId} layoutPrecision={props.layoutPrecision} />
</g>
);
}
}
reactMixin.onClass(NodesChartElements, PureRenderMixin);

View File

@@ -1,68 +1,76 @@
import React from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import reactMixin from 'react-mixin';
import NodeContainer from './node-container';
export default function NodesChartNodes({adjacentNodes, highlightedNodeIds,
layoutNodes, layoutPrecision, nodeScale, onNodeClick, scale,
selectedMetric, selectedNodeScale, selectedNodeId, topologyId}) {
const zoomScale = scale;
export default class NodesChartNodes extends React.Component {
render() {
const {adjacentNodes, highlightedNodeIds,
layoutNodes, layoutPrecision, nodeScale, onNodeClick, scale,
selectedMetric, selectedNodeScale, selectedNodeId, topologyId} = this.props;
// highlighter functions
const setHighlighted = node => node.set('highlighted',
highlightedNodeIds.has(node.get('id')) || selectedNodeId === node.get('id'));
const setFocused = node => node.set('focused', selectedNodeId
&& (selectedNodeId === node.get('id')
|| (adjacentNodes && adjacentNodes.includes(node.get('id')))));
const setBlurred = node => node.set('blurred', selectedNodeId && !node.get('focused'));
const zoomScale = scale;
// make sure blurred nodes are in the background
const sortNodes = node => {
if (node.get('blurred')) {
return 0;
}
if (node.get('highlighted')) {
return 2;
}
return 1;
};
// highlighter functions
const setHighlighted = node => node.set('highlighted',
highlightedNodeIds.has(node.get('id')) || selectedNodeId === node.get('id'));
const setFocused = node => node.set('focused', selectedNodeId
&& (selectedNodeId === node.get('id')
|| (adjacentNodes && adjacentNodes.includes(node.get('id')))));
const setBlurred = node => node.set('blurred', selectedNodeId && !node.get('focused'));
// TODO: think about pulling this up into the store.
const metric = node => (
node.get('metrics') && node.get('metrics')
.filter(m => m.get('id') === selectedMetric)
.first()
);
// make sure blurred nodes are in the background
const sortNodes = node => {
if (node.get('blurred')) {
return 0;
}
if (node.get('highlighted')) {
return 2;
}
return 1;
};
const nodesToRender = layoutNodes.toIndexedSeq()
.map(setHighlighted)
.map(setFocused)
.map(setBlurred)
.sortBy(sortNodes);
// TODO: think about pulling this up into the store.
const metric = node => (
node.get('metrics') && node.get('metrics')
.filter(m => m.get('id') === selectedMetric)
.first()
);
return (
<g className="nodes-chart-nodes">
{nodesToRender.map(node => <NodeContainer
blurred={node.get('blurred')}
focused={node.get('focused')}
highlighted={node.get('highlighted')}
topologyId={topologyId}
shape={node.get('shape')}
stack={node.get('stack')}
onClick={onNodeClick}
key={node.get('id')}
id={node.get('id')}
label={node.get('label')}
pseudo={node.get('pseudo')}
nodeCount={node.get('nodeCount')}
subLabel={node.get('subLabel')}
metric={metric(node)}
rank={node.get('rank')}
layoutPrecision={layoutPrecision}
selectedNodeScale={selectedNodeScale}
nodeScale={nodeScale}
zoomScale={zoomScale}
dx={node.get('x')}
dy={node.get('y')} />)}
</g>
);
const nodesToRender = layoutNodes.toIndexedSeq()
.map(setHighlighted)
.map(setFocused)
.map(setBlurred)
.sortBy(sortNodes);
return (
<g className="nodes-chart-nodes">
{nodesToRender.map(node => <NodeContainer
blurred={node.get('blurred')}
focused={node.get('focused')}
highlighted={node.get('highlighted')}
topologyId={topologyId}
shape={node.get('shape')}
stack={node.get('stack')}
onClick={onNodeClick}
key={node.get('id')}
id={node.get('id')}
label={node.get('label')}
pseudo={node.get('pseudo')}
nodeCount={node.get('nodeCount')}
subLabel={node.get('subLabel')}
metric={metric(node)}
rank={node.get('rank')}
layoutPrecision={layoutPrecision}
selectedNodeScale={selectedNodeScale}
nodeScale={nodeScale}
zoomScale={zoomScale}
dx={node.get('x')}
dy={node.get('y')} />)}
</g>
);
}
}
reactMixin.onClass(NodesChartNodes, PureRenderMixin);