diff --git a/app/scripts/src/charts/stacked_area_chart.jsx b/app/scripts/src/charts/stacked_area_chart.jsx
index 270b8d8..ab4eaa9 100644
--- a/app/scripts/src/charts/stacked_area_chart.jsx
+++ b/app/scripts/src/charts/stacked_area_chart.jsx
@@ -1,11 +1,12 @@
var StackedAreaChart = React.createClass({
mixins: [ReactRouter.Navigation, ReactRouter.State, SVGChartMixin, ChartDataMixin],
- numElements: 10,
- maxWeeks: 30,
- height: 350,
+ canvasHeight: 350,
xAxisHeight: 20,
+ maxItems: 10,
+ maxWeeks: 30,
+
words: {
items: {
repo: 'repositories',
@@ -27,9 +28,9 @@ var StackedAreaChart = React.createClass({
return {
item: this.props.items[0],
rawData: [],
- top: [],
- weeks: [],
- max: 1
+ topItems: [],
+ weeklyData: [],
+ maxCommitsPerWeek: 1
};
},
@@ -39,19 +40,22 @@ var StackedAreaChart = React.createClass({
},
componentWillReceiveProps: function(newProps) {
+ // If new items are the same as old then don't reset current item
this.setState({
item: (_.isEqual(newProps.items, this.props.items)
? this.state.item
: newProps.items[0]),
- state: 'newProps'
+ state: 'loadingData'
}, this.fetchData);
},
shouldComponentUpdate: function(newProps, newState) {
+ // Don't re-render unless canvas width is calculated
if (!newState.canvasWidth) {
return false;
}
- if (newState.state !== 'newPoints') {
+ // We're working with animations here so we render only in one particular state
+ if (newState.state !== 'pleaseRender') {
return false;
}
return true;
@@ -61,7 +65,7 @@ var StackedAreaChart = React.createClass({
if (this.props.items[i] !== this.state.item) {
this.setState({
item: this.props.items[i],
- state: 'newProps'
+ state: 'loadingData'
}, this.fetchData);
}
},
@@ -83,17 +87,18 @@ var StackedAreaChart = React.createClass({
},
handleNewData: function() {
- // Group commits by items
+ // [week, ...]
var weeksList = _(this.state.rawData).pluck('week').uniq().sort().reverse().take(this.maxWeeks).value();
if (weeksList.length < 2) {
this.setState({
weeks: [],
- state: 'newPoints'
+ state: 'pleaseRender'
});
return;
}
- var counts = _.reduce(this.state.rawData, function(res, el) {
+ // {item: commits, ...}
+ var commitsByItem = _.reduce(this.state.rawData, function(res, el) {
if (weeksList.indexOf(el.week) === -1) {
return res;
}
@@ -105,108 +110,72 @@ var StackedAreaChart = React.createClass({
return res;
}, {});
- // Extract top items from
- var top = _(_.pairs(counts)) // Take [item, count] pairs from counts object
+ // [item, ...]
+ var topItems = _(_.pairs(commitsByItem)) // Take [item, count] pairs from counts object
.sortBy(1).reverse() // sort them by count (descending)
- .take(this.numElements) // take first N pairs
+ .take(this.maxItems) // take first N pairs
.pluck(0) // keep only items, omit the counts
.value();
- for (var i = top.length; i < this.numElements; i++) {
- top[i] = null;
+ for (var i = topItems.length; i < this.maxItems; i++) {
+ topItems[i] = null;
};
- var weeks = _.reduce(this.state.rawData, function(res, el) {
+ // {week: {item: commits, ...}, ...}
+ var weeklyData = _.reduce(this.state.rawData, function(res, el) {
if (weeksList.indexOf(el.week) === -1) {
return res;
}
if (res[el.week] === undefined) {
res[el.week] = {};
}
- if (top.indexOf(el.item) > -1) {
+ if (topItems.indexOf(el.item) > -1) {
res[el.week][el.item] = el.commits;
}
return res;
}, {});
- var max = _.max(_.map(weeksList, function(week){ return _.sum(_.values(weeks[week])); }));
+ var maxCommitsPerWeek = _.max(_.map(weeksList, function(week) {
+ return _.sum(_.values(weeklyData[week]));
+ }));
this.setState({
- top: top,
- weeks: weeks,
- max: max,
- state: 'newPoints'
+ topItems: topItems,
+ weeklyData: weeklyData,
+ maxCommitsPerWeek: maxCommitsPerWeek,
+ state: 'pleaseRender'
});
},
- buildPathD: function(points) {
+ buildPathD: function(dots) {
var maxWidth = this.state.canvasWidth,
- maxHeight = this.height;
+ maxHeight = this.canvasHeight;
- var dots = this.buildDots(points);
- var first = dots.shift();
+ var dots = this.extendDotsWithCoordinates(dots);
+ var first = dots.shift(); // Don't draw a line to the first dot, it should be a move
var d = _.map(dots, function(dot){ return 'L'+ dot.x +','+ dot.y; });
- d.unshift('M'+ first.x +','+ first.y);
- d.push('L'+ maxWidth +','+ maxHeight);
- d.push('L0,'+ maxHeight +' Z');
+ d.unshift('M'+ first.x +','+ first.y); // Prepend first move
+ d.push('L'+ maxWidth +','+ maxHeight); // Draw a line to the bottom right corner
+ d.push('L0,'+ maxHeight +' Z'); // And then to a bottom left corner
return d.join(' ');
},
- buildDots: function(points) {
+ extendDotsWithCoordinates: function(dots) {
var maxWidth = this.state.canvasWidth,
- maxHeight = this.height,
- maxValue = this.state.max,
- len = points.length;
+ maxHeight = this.canvasHeight,
+ maxValue = this.state.maxCommitsPerWeek,
+ len = dots.length;
- return _.map(points, function(point, i) {
- point.x = i/(len-1)*maxWidth;
- point.y = maxHeight - point.point;
- return point;
+ return _.map(dots, function(dot, i) {
+ dot.x = i/(len-1)*maxWidth;
+ dot.y = maxHeight - dot.norm*maxHeight*0.96;
+ return dot;
});
},
render: function() {
- var maxWidth = this.state.canvasWidth,
- maxHeight = this.height,
- top = this.state.top,
- max = this.state.max;
-
- // [week, [{val, point}, ...]]
- var points = _(this.state.weeks)
- .map(function(items, week) {
- var values = _.map(top, function(item) {
- return items[item] || 0;
- });
-
- var sum = 0;
- var points = _.map(values, function(val) {
- sum += val/max*maxHeight*0.96;
- return {
- val: val,
- point: sum
- };
- });
-
- return [parseInt(week, 10), points];
- })
- .sort(0)
- .reverse()
- .take(this.maxWeeks)
- .reverse()
- .value();
-
- // [item, [{val, point}, ...]]
- var paths = _.map(top, function(item, i) {
- var itemPoints = _.map(points, function(pair) {
- return pair[1][i];
- });
- return[item, itemPoints];
- });
-
- var areas = _.map(paths, function(pair, i) {
- var item = pair[0],
- path = pair[1];
-
+ var renderArea = function(pair, i) {
+ var item = pair[0], path = pair[1];
return (