bar.js 6.92 KB

nv.models.bar = function() {
  var margin = {top: 20, right: 10, bottom: 80, left: 60},
      width = 960,
      height = 500,
      animate = 500,
      label ='label',
      rotatedLabel = true,
      showLabels = true,
      id = Math.floor(Math.random() * 10000), //Create semi-unique ID in case user doesn't select one
      color = d3.scale.category20(),
      field ='y',
      title = '';

  var x = d3.scale.ordinal(),
      y = d3.scale.linear(),
      xAxis = d3.svg.axis().scale(x).orient('bottom'),
      yAxis = d3.svg.axis().scale(y).orient('left'),
      dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'tooltipShow', 'tooltipHide');


  function chart(selection) {
    selection.each(function(data) {
      x .domain(data.map(function(d,i) { return d[label]; }))
        .rangeRoundBands([0, width - margin.left - margin.right], .1);


      var min = d3.min(data, function(d) { return d[field] });
      var max = d3.max(data, function(d) { return d[field] });
      var x0 = Math.max(-min, max);
      var x1 = -x0;

      // If we have no negative values, then lets stack this with just positive bars
      if (min >= 0) x1 = 0;

      y .domain([x1, x0])
        .range([height - margin.top - margin.bottom, 0])
        .nice();

      xAxis.ticks( width / 100 );
      yAxis.ticks( height / 36 ).tickSize(-(width - margin.right - margin.left), 0);

      var parent = d3.select(this)
          .on("click", function(d,i) {
            dispatch.chartClick({
                data: d,
                index: i,
                pos: d3.event,
                id: id
            });
          });


      var wrap = parent.selectAll('g.wrap').data([data]);
      var gEnter = wrap.enter();
      gEnter.append("text")
          .attr("class", "title")
          .attr("dy", ".91em")
          .attr("text-anchor", "start")
          .text(title);
      gEnter = gEnter.append('g').attr('class', 'nvd3 wrap').attr('id','wrap-'+id).append('g');



      gEnter.append('g').attr('class', 'x axis');
      gEnter.append('g').attr('class', 'y axis');
      gEnter.append('g').attr('class', 'bars');


      wrap.attr('width', width)
          .attr('height', height);

      var g = wrap.select('g')
          .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');


      var bars = wrap.select('.bars').selectAll('.bar')
          .data(function(d) { return d; });

      bars.exit().remove();


      var barsEnter = bars.enter().append('svg:rect')
          .attr('class', function(d) { return d[field] < 0 ? "bar negative" : "bar positive"})
          .attr("fill", function(d, i) { return color(i); })
          .attr('x', 0 )
          .on('mouseover', function(d,i){
            d3.select(this).classed('hover', true);
            dispatch.tooltipShow({
                label: d[label],
                value: d[field],
                data: d,
                index: i,
                // TODO: Calculate the center to the bar
                pos: [d3.event.pageX, d3.event.pageY],
                id: id
            });

          })
          .on('mouseout', function(d,i){
                d3.select(this).classed('hover', false);
                dispatch.tooltipHide({
                    label: d[label],
                    value: d[field],
                    data: d,
                    index: i,
                    id: id
                });
          })
          .on('click', function(d,i) {
                dispatch.elementClick({
                    label: d[label],
                    value: d[field],
                    data: d,
                    index: i,
                    pos: d3.event,
                    id: id
                });
              d3.event.stopPropagation();
          })
          .on('dblclick', function(d,i) {
              dispatch.elementDblClick({
                  label: d[label],
                  value: d[field],
                  data: d,
                  index: i,
                  pos: d3.event,
                  id: id
              });
              d3.event.stopPropagation();
          });


      bars
          .attr('class', function(d) { return d[field] < 0 ? "bar negative" : "bar positive"})
          .attr('transform', function(d,i) { return 'translate(' + x(d[label]) + ',0)'; })
          .attr('width', x.rangeBand )
          .order()
          .transition()
          .duration(animate)
          .attr('y', function(d) {  return y(Math.max(0, d[field])); })
          .attr('height', function(d) { return Math.abs(y(d[field]) - y(0));  });


      g.select('.x.axis')
          .attr('transform', 'translate(0,' + y.range()[0] + ')')
          .call(xAxis);


      if (rotatedLabel) {
        g.select('.x.axis').selectAll('text').attr('text-anchor','start').attr("transform", function(d) {
          return "rotate(35)translate(" + this.getBBox().height/2 + "," + '0' + ")";
        });
      }
      if (!showLabels) {
        g.select('.x.axis').selectAll('text').attr('fill', 'rgba(0,0,0,0)');
        g.select('.x.axis').selectAll('line').attr('style', 'opacity: 0');
      }
      /*else {
        g.select('.x.axis').selectAll('text').attr('fill', 'rgba(0,0,0,1)');
        g.select('.x.axis').selectAll('line').attr('style', 'opacity: 1');
      }*/



      g.select('.y.axis')
        .call(yAxis);
    });

    return chart;
  }

  chart.margin = function(_) {
    if (!arguments.length) return margin;
    margin = _;
    return chart;
  };

  chart.width = function(_) {
    if (!arguments.length) return width;
    if (margin.left + margin.right + 20 > _)
      width = margin.left + margin.right + 20; // Min width
    else
      width = _;
    return chart;
  };

  chart.height = function(_) {
    if (!arguments.length) return height;
    if (margin.top + margin.bottom + 20 > _)
      height = margin.top + margin.bottom + 20; // Min height
    else
      height = _;
    return chart;
  };

  chart.animate = function(_) {
    if (!arguments.length) return animate;
    animate = _;
    return chart;
  };

  chart.labelField = function(_) {
    if (!arguments.length) return (label);
      label = _;
      return chart;
  };

  chart.dataField = function(_) {
    if (!arguments.length) return (field);
    field = _;
    return chart;
  };

  chart.id = function(_) {
        if (!arguments.length) return id;
        id = _;
        return chart;
  };

  chart.rotatedLabel = function(_) {
        if (!arguments.length) return rotatedLabel;
        rotatedLabel = _;
        return chart;
  };

  chart.showLabels = function(_) {
        if (!arguments.length) return (showLabels);
        showLabels = _;
        return chart;
  };

  chart.title = function(_) {
      if (!arguments.length) return (title);
      title = _;
      return chart;
  };

  chart.xaxis = {};
  // Expose the x-axis' tickFormat method.
  d3.rebind(chart.xaxis, xAxis, 'tickFormat');

  chart.yaxis = {};
  // Expose the y-axis' tickFormat method.
  d3.rebind(chart.yaxis, yAxis, 'tickFormat');

  chart.dispatch = dispatch;

  return chart;
}