Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Add a custom hover control for bar charts.

I'm going to try and get this accepted up stream at some point.
  • Loading branch information...
commit ed37a08961fb1582611cf31ea09c5c8539ed921f 1 parent ada8288
Mike Cooper mythmon authored
153 media/js/Rickshaw.Graph.BarHoverDetail.js
... ... @@ -0,0 +1,153 @@
  1 +Rickshaw.namespace('Rickshaw.Graph.BarHoverDetail');
  2 +
  3 +/* This is a mostly intact version of Rickshaw.Graph.HoverDetail that
  4 + * is modified to work nicer with the Bar renderer. The data point
  5 + * chosen is based on the rectangle rendered by the Bar renderer, and
  6 + * the tool tip points to the center of that rectangle.
  7 + *
  8 + * The `update` method has changed to modify the hover behavior, and the
  9 + * `render` method has changed to modify the tool tip behavior.
  10 + */
  11 +
  12 +Rickshaw.Graph.BarHoverDetail = Rickshaw.Class.create(Rickshaw.Graph.HoverDetail, {
  13 +
  14 + update: function(e) {
  15 +
  16 + e = e || this.lastEvent;
  17 + if (!e) return;
  18 + this.lastEvent = e;
  19 +
  20 + if (!e.target.nodeName.match(/^(path|svg|rect)$/)) return;
  21 +
  22 + var graph = this.graph;
  23 + var barWidth = graph.renderer.barWidth() + graph.renderer.gapSize;
  24 +
  25 + var eventX = e.offsetX || e.layerX;
  26 + var eventY = e.offsetY || e.layerY;
  27 +
  28 + var j = 0;
  29 + var points = [];
  30 + var nearestPoint;
  31 +
  32 + // Iterate through each series, and find the point that most closely
  33 + // matches the mouse pointer.
  34 + this.graph.series.active().forEach( function(series) {
  35 +
  36 + var data = this.graph.stackedData[j++];
  37 + var domainX = graph.x.invert(eventX);
  38 +
  39 + var domainIndexScale = d3.scale.linear()
  40 + .domain([data[0].x, data.slice(-1)[0].x])
  41 + .range([0, data.length - 1]);
  42 +
  43 + var approximateIndex = Math.round(domainIndexScale(domainX));
  44 + var dataIndex = Math.min(approximateIndex || 0, data.length - 1);
  45 +
  46 + var i = approximateIndex;
  47 + while (i < data.length - 1) {
  48 +
  49 + if (!data[i] || !data[i + 1]) break;
  50 +
  51 + if (data[i].x <= domainX && data[i + 1].x > domainX) {
  52 + dataIndex = i;
  53 + break;
  54 + }
  55 +
  56 + if (data[i + 1].x <= domainX) { i++; } else { i--; }
  57 + }
  58 +
  59 + if (dataIndex < 0) dataIndex = 0;
  60 + var value = data[dataIndex];
  61 +
  62 + var left = graph.x(value.x);
  63 + var right = left + barWidth;
  64 + var bottom = graph.y(value.y0);
  65 + var top = graph.y(value.y + value.y0);
  66 +
  67 + var point = {
  68 + series: series,
  69 + value: value,
  70 + order: j,
  71 + name: series.name
  72 + };
  73 +
  74 + if (left <= eventX && eventX < right &&
  75 + top <= eventY && eventY < bottom) {
  76 +
  77 + nearestPoint = point;
  78 + }
  79 +
  80 + points.push(point);
  81 +
  82 + }, this );
  83 +
  84 + var renderArgs = {
  85 + points: points,
  86 + detail: points,
  87 + mouseX: eventX,
  88 + mouseY: eventY
  89 + };
  90 +
  91 + if (nearestPoint) {
  92 + nearestPoint.active = true;
  93 + }
  94 +
  95 + if (this.visible) {
  96 + this.render(renderArgs);
  97 + }
  98 + },
  99 +
  100 + render: function(args) {
  101 +
  102 + var graph = this.graph;
  103 + var points = args.points;
  104 + var barWidth = graph.renderer.barWidth() + graph.renderer.gapSize;
  105 +
  106 + var point = points.filter(function(p) {return p.active;}).shift();
  107 +
  108 + if (!point || point.value.y === null) {
  109 + return;
  110 + }
  111 +
  112 + var formattedXValue = this.xFormatter(point.value.x);
  113 + var formattedYValue = this.yFormatter(point.value.y);
  114 +
  115 + this.element.innerHTML = '';
  116 + this.element.style.left = (graph.x(point.value.x) + barWidth / 2) + 'px';
  117 +
  118 + var xLabel = document.createElement('div');
  119 +
  120 + xLabel.className = 'x_label';
  121 + xLabel.innerHTML = formattedXValue;
  122 + this.element.appendChild(xLabel);
  123 +
  124 + var item = document.createElement('div');
  125 +
  126 + item.className = 'item';
  127 + item.innerHTML = this.formatter(point.series, point.value.x, point.value.y,
  128 + formattedXValue, formattedYValue, point);
  129 + item.style.top = this.graph.y(point.value.y0 + point.value.y / 2) + 'px';
  130 +
  131 + this.element.appendChild(item);
  132 +
  133 + var dot = document.createElement('div');
  134 +
  135 + dot.className = 'dot';
  136 + dot.style.top = item.style.top;
  137 + dot.style.borderColor = point.series.color;
  138 +
  139 + this.element.appendChild(dot);
  140 +
  141 + if (point.active) {
  142 + item.className = 'item active';
  143 + dot.className = 'dot active';
  144 + }
  145 +
  146 + this.show();
  147 +
  148 + if (typeof this.onRender == 'function') {
  149 + this.onRender(args);
  150 + }
  151 + }
  152 +});
  153 +
2  media/js/questions.stats.js
@@ -11,7 +11,7 @@ function makeGraph($elem, data) {
11 11 renderer: 'bar'
12 12 });
13 13
14   - var hoverDetail = new Rickshaw.Graph.HoverDetail( {
  14 + var hoverDetail = new Rickshaw.Graph.BarHoverDetail( {
15 15 graph: graph
16 16 });
17 17
1  settings.py
@@ -678,6 +678,7 @@ def JINJA_CONFIG():
678 678 'js/libs/d3.v3.min.js',
679 679 'js/libs/d3.layout.min.js',
680 680 'js/libs/rickshaw.min.js',
  681 + 'js/Rickshaw.Graph.BarHoverDetail.js',
681 682 'js/questions.stats.js',
682 683 ),
683 684 'mobile/questions': (

0 comments on commit ed37a08

Please sign in to comment.
Something went wrong with that request. Please try again.