Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Add a custom hover control for bar charts. #1209

Merged
merged 1 commit into from

2 participants

Mike Cooper ricky rosario
Mike Cooper
Owner

The default hover detail for Rickshaw works really well for line plots and scatter plots, but pretty crappy for bar charts, which we are using. This adds a subclass of the default hover detail that is specialized for bar charts.

I'm going to try and get this accepted up stream at some point.

r?

Mike Cooper mythmon Add a custom hover control for bar charts.
I'm going to try and get this accepted up stream at some point.
ed37a08
ricky rosario
Owner

oh i like that much better! ship it! r+

Mike Cooper mythmon merged commit ed37a08 into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 19, 2013
  1. Mike Cooper

    Add a custom hover control for bar charts.

    mythmon authored
    I'm going to try and get this accepted up stream at some point.
This page is out of date. Refresh to see the latest.
153 media/js/Rickshaw.Graph.BarHoverDetail.js
View
@@ -0,0 +1,153 @@
+Rickshaw.namespace('Rickshaw.Graph.BarHoverDetail');
+
+/* This is a mostly intact version of Rickshaw.Graph.HoverDetail that
+ * is modified to work nicer with the Bar renderer. The data point
+ * chosen is based on the rectangle rendered by the Bar renderer, and
+ * the tool tip points to the center of that rectangle.
+ *
+ * The `update` method has changed to modify the hover behavior, and the
+ * `render` method has changed to modify the tool tip behavior.
+ */
+
+Rickshaw.Graph.BarHoverDetail = Rickshaw.Class.create(Rickshaw.Graph.HoverDetail, {
+
+ update: function(e) {
+
+ e = e || this.lastEvent;
+ if (!e) return;
+ this.lastEvent = e;
+
+ if (!e.target.nodeName.match(/^(path|svg|rect)$/)) return;
+
+ var graph = this.graph;
+ var barWidth = graph.renderer.barWidth() + graph.renderer.gapSize;
+
+ var eventX = e.offsetX || e.layerX;
+ var eventY = e.offsetY || e.layerY;
+
+ var j = 0;
+ var points = [];
+ var nearestPoint;
+
+ // Iterate through each series, and find the point that most closely
+ // matches the mouse pointer.
+ this.graph.series.active().forEach( function(series) {
+
+ var data = this.graph.stackedData[j++];
+ var domainX = graph.x.invert(eventX);
+
+ var domainIndexScale = d3.scale.linear()
+ .domain([data[0].x, data.slice(-1)[0].x])
+ .range([0, data.length - 1]);
+
+ var approximateIndex = Math.round(domainIndexScale(domainX));
+ var dataIndex = Math.min(approximateIndex || 0, data.length - 1);
+
+ var i = approximateIndex;
+ while (i < data.length - 1) {
+
+ if (!data[i] || !data[i + 1]) break;
+
+ if (data[i].x <= domainX && data[i + 1].x > domainX) {
+ dataIndex = i;
+ break;
+ }
+
+ if (data[i + 1].x <= domainX) { i++; } else { i--; }
+ }
+
+ if (dataIndex < 0) dataIndex = 0;
+ var value = data[dataIndex];
+
+ var left = graph.x(value.x);
+ var right = left + barWidth;
+ var bottom = graph.y(value.y0);
+ var top = graph.y(value.y + value.y0);
+
+ var point = {
+ series: series,
+ value: value,
+ order: j,
+ name: series.name
+ };
+
+ if (left <= eventX && eventX < right &&
+ top <= eventY && eventY < bottom) {
+
+ nearestPoint = point;
+ }
+
+ points.push(point);
+
+ }, this );
+
+ var renderArgs = {
+ points: points,
+ detail: points,
+ mouseX: eventX,
+ mouseY: eventY
+ };
+
+ if (nearestPoint) {
+ nearestPoint.active = true;
+ }
+
+ if (this.visible) {
+ this.render(renderArgs);
+ }
+ },
+
+ render: function(args) {
+
+ var graph = this.graph;
+ var points = args.points;
+ var barWidth = graph.renderer.barWidth() + graph.renderer.gapSize;
+
+ var point = points.filter(function(p) {return p.active;}).shift();
+
+ if (!point || point.value.y === null) {
+ return;
+ }
+
+ var formattedXValue = this.xFormatter(point.value.x);
+ var formattedYValue = this.yFormatter(point.value.y);
+
+ this.element.innerHTML = '';
+ this.element.style.left = (graph.x(point.value.x) + barWidth / 2) + 'px';
+
+ var xLabel = document.createElement('div');
+
+ xLabel.className = 'x_label';
+ xLabel.innerHTML = formattedXValue;
+ this.element.appendChild(xLabel);
+
+ var item = document.createElement('div');
+
+ item.className = 'item';
+ item.innerHTML = this.formatter(point.series, point.value.x, point.value.y,
+ formattedXValue, formattedYValue, point);
+ item.style.top = this.graph.y(point.value.y0 + point.value.y / 2) + 'px';
+
+ this.element.appendChild(item);
+
+ var dot = document.createElement('div');
+
+ dot.className = 'dot';
+ dot.style.top = item.style.top;
+ dot.style.borderColor = point.series.color;
+
+ this.element.appendChild(dot);
+
+ if (point.active) {
+ item.className = 'item active';
+ dot.className = 'dot active';
+ }
+
+ this.show();
+
+ if (typeof this.onRender == 'function') {
+ this.onRender(args);
+ }
+ }
+});
+
2  media/js/questions.stats.js
View
@@ -11,7 +11,7 @@ function makeGraph($elem, data) {
renderer: 'bar'
});
- var hoverDetail = new Rickshaw.Graph.HoverDetail( {
+ var hoverDetail = new Rickshaw.Graph.BarHoverDetail( {
graph: graph
});
1  settings.py
View
@@ -678,6 +678,7 @@ def JINJA_CONFIG():
'js/libs/d3.v3.min.js',
'js/libs/d3.layout.min.js',
'js/libs/rickshaw.min.js',
+ 'js/Rickshaw.Graph.BarHoverDetail.js',
'js/questions.stats.js',
),
'mobile/questions': (
Something went wrong with that request. Please try again.