UNPKG

react-horizontal-bar-chart

Version:

Simple horizontal bar chart react component, with inline tooltips

161 lines (131 loc) 3.57 kB
/** * @jsx React.DOM */ 'use strict'; var React = require('react/addons'); require('../../styles/HBar.css'); require('d3') var Bar = React.createClass({ getDefaultProps: function() { return { width: 0, height: 0, offset: 0 } }, render: function() { return ( <rect className={this.props.focused ? 'focused' : ''} width={this.props.width} height={this.props.height} y={this.props.offset} x={0} onMouseOver={this.props.over} onMouseOut={this.props.out} /> ); } }); var HBar = React.createClass({ getDefaultProps: function() { return { width: 230, height: 300, data: [ {v: 30, label: 'Salut'}, {v: 10, label: 'Mon'}, {v: 5, label: 'Pote'} ] } }, getInitialState: function(){ return { focus: this.props.focus } }, render: function() { var props = this.props; var hbar = this hbar.scales() var data = this.props.data; if (this.props.sort === 'ascending') data.sort(function(p, q){return p.v > q.v}); if (this.props.sort === 'descending') data.sort(function(p, q){return p.v < q.v}); var bars = data.map(function(point, i) { return ( <Bar key={i} width={hbar.xScale(point.v)} height={hbar.yScale.rangeBand()} offset={hbar.yScale(i)} over={hbar.over.bind(hbar, i)} out={hbar.out} focused={hbar.state.focus == i} /> ) }); return ( <svg className="HBar" width={this.props.width} height={this.props.height}> <g>{bars}</g> <line className="axis" x1="0" y1="0" x2="0" y2={this.yScale.rangeExtent()[1]} style={{ strokeWidth: (this.props.width * 0.005) + 'px', visibility: this.props.axis ? 'visible' : 'hidden' }} /> {this.focus()} </svg> ); }, focus: function(){ if (this.state.focus == undefined) return; var i = this.state.focus, point = this.props.data[i]; var v = point.v /* Format the point if an input formatting function is available */ if (this.props.formatter){ v = this.props.formatter(v) } var x = this.xScale(point.v), y = this.yScale(i) + this.yScale.rangeBand() / 2 var wide = x > this.props.width / 2 //the bar is wide, the point label will go inside var margin = this.props.width * 0.03 var style = {fontSize: this.yScale.rangeBand() * 0.6 + 'px'} return ( <g className="focus" style={style}> <text className="inside" y={y} x={x - margin} textAnchor="end" > {wide ? point.label : ''} </text> <text className="outside" y={y} x={x + margin} textAnchor="start" > {wide ? v : point.label + ', ' + v} </text> </g> ) }, over: function(i){ this.setState({ focus: i }) }, out: function(){ this.setState({ focus: null }) }, scales: function(){ var w = this.props.width this.xScale = d3.scale.linear() .domain([0, d3.max(this.props.data, function(p){return p.v})]) // leave some space in the container to displat bar values .range([0, w * 0.8]); this.yScale = d3.scale.ordinal() .domain(d3.range(this.props.data.length)) .rangeBands([0, this.props.height], 1/3, 1/2); } }); module.exports = HBar;