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 (
{this.state.points.map(this.renderBar)}
); }, 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} ); } });