var BarChart = React.createClass({
mixins: [ReactRouter.Navigation, ReactRouter.State, SVGChartMixin, ChartDataMixin],
sorts: ['commits', 'delta'],
numElements: 15,
barHeight: 30,
barMargin: 5,
getInitialState: function() {
return {
item: this.props.items[0],
sort: 'commits',
rawData: [],
points: [],
min: 0,
max: 1
};
},
componentDidMount: function() {
this.calculateViewBoxWidth();
window.addEventListener('resize', this.calculateViewBoxWidth);
this.componentWillReceiveProps(this.props);
},
componentWillReceiveProps: function(newProps) {
this.setState({
item: newProps.items[0],
sort: 'commits',
state: 'newProps'
}, this.fetchData);
},
shouldComponentUpdate: function(newProps, newState) {
if (!newState.canvasWidth) {
return false;
}
if (newState.state !== 'newPoints') {
return false;
}
return true;
},
handleFilter: function(thing, i) {
if (thing === 'item' && this.props.items[i] !== this.state.item) {
this.setState({
item: this.props.items[i],
state: 'newProps'
}, this.fetchData);
} else if (thing === 'sort' && this.sorts[i] !== this.state.sort) {
this.setState({
sort: this.sorts[i],
state: 'newProps'
}, this.handleNewData);
}
},
handleClick: function(point) {
var params = {org: this.getParams().org};
params[this.state.item] = point.item;
this.transitionTo(this.state.item, params);
},
handleNewData: function() {
var min = 0, max = 1;
var points = _.chain(this.state.rawData)
.sort(function(a, b) {
return Math.abs(b[this.state.sort]) - Math.abs(a[this.state.sort]);
}.bind(this))
.take(this.numElements)
.value();
for (var i = points.length; i < this.numElements; i++) {
var point = {};
point[this.state.sort] = 0;
points.push(point);
}
// console.log("Setting points!");
this.setState({
points: points,
min: _.min(points, this.state.sort)[this.state.sort],
max: _.max(points, this.state.sort)[this.state.sort],
state: 'newPoints'
});
},
height: function() {
if (this.state.points.length === 0) {
return 0;
} else {
return this.y(this.state.points.length) - this.barMargin;
}
},
y: function(i) {
return i*(this.barHeight + this.barMargin);
},
render: function() {
// console.log("Render barchart!", this.state);
return (
);
},
renderBar: function(point, i) {
var maxWidth = this.state.canvasWidth,
val = point[this.state.sort],
min = this.state.min,
max = this.state.max,
max2 = (min < 0 ? max - min : max),
width = Math.floor(Math.abs(val)/max2*maxWidth),
height = this.barHeight,
offset = (min < 0 ? -min : 0)/max2*maxWidth,
x = (min >= 0 ? 0 : offset - (val >= 0 ? 0 : width)),
y = this.y(i);
return (
);
}
});
var Bar = React.createClass({
mixins: [ReactRouter.Navigation, ChartAnimationMixin],
// easing: '0.075 0.82 0.165 1', // easeOutCirc
easing: '0.175 0.885 0.32 1.275', // easeOutBack
height: 30,
labelPaddingH: 5, // Label horizontal padding
labelPaddingV: 2, // Label vertical padding
labelMarginV: 5, // Same as padding
labelHeight: 16, // Text size
labelOuterHeight: 20, // labelHeight + 2*labelPaddingV,
getInitialState: function() {
return {
labelX: 0,
lastLabelX: 2*this.labelPaddingH
};
},
componentDidMount: function() {
this.calculateLabelPosition();
},
componentWillReceiveProps: function(newProps) {
if (_.isEqual(this.props, newProps)) {
return;
}
// console.log("New bar props!", newProps.item, newProps.x, newProps.width);
this.setState({
lastBarX: (this.props.x !== undefined ? this.props.x : newProps.x),
lastBarWidth: (this.props.width !== undefined ? this.props.width : newProps.width),
lastLabelX: this.state.labelX
}, this.calculateLabelPosition);
},
calculateLabelPosition: function() {
var val = this.props.value,
offset = this.props.offset,
width = this.props.width,
label = this.props.item + ': ' + val,
labelWidth = textWidth(label),
labelOuterWidth = labelWidth + 2*this.labelPaddingH,
labelOffsetWidth = labelOuterWidth + 2*this.labelPaddingH,
labelMarginV = (this.props.height - this.labelOuterHeight)/2,
labelX,
labelY = this.props.y + this.labelOuterHeight + 1, // 1 is magic
barX = this.props.x,
barX2 = barX + width;
if (offset === 0) {
labelX = 2*this.labelPaddingH;
} else {
if (val < 0) {
if (offset >= labelOffsetWidth) {
labelX = offset - labelOffsetWidth + 2*this.labelPaddingH;
} else {
labelX = offset + 2*this.labelPaddingH;
}
} else {
if (offset + labelOffsetWidth <= this.props.max) {
labelX = offset + 2*this.labelPaddingH;
} else {
labelX = offset - labelOffsetWidth + 2*this.labelPaddingH;
}
}
}
this.setState({
labelX: labelX
}, this.animateAll);
},
animateAll: function() {
// console.log("animate bar!", this.state, this.props);
this.clearAnimations(this.refs.bar);
this.clearAnimations(this.refs.underlay);
this.animate(this.refs.bar, 'width', this.state.lastBarWidth, this.props.width);
this.animate(this.refs.bar, 'x', this.state.lastBarX, this.props.x);
var ph = this.labelPaddingH;
this.animate(this.refs.underlay, 'x', this.state.lastLabelX - ph, this.state.labelX - ph);
// this.animate(this.refs.label, 'x', this.state.lastLabelX, this.state.labelX);
},
render: function() {
var label = this.props.item ? (this.props.item + ': ' + this.props.value) : '',
labelWidth = textWidth(label),
labelOuterWidth = labelWidth + 2*this.labelPaddingH;
// var width = this.state.lastBarWidth === 0 ? this.props.width : this.state.lastBarWidth;
return (
{label}
);
}
});