Skip to content

Commit 58353f7

Browse files
authored
Merge pull request #5668 from plotly/fixup-hover-filters
Fixup hover filters
2 parents c783c33 + e18bb61 commit 58353f7

File tree

11 files changed

+314
-183
lines changed

11 files changed

+314
-183
lines changed

src/components/fx/hover.js

+37-93
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ var Drawing = require('../drawing');
1414
var Color = require('../color');
1515
var dragElement = require('../dragelement');
1616
var Axes = require('../../plots/cartesian/axes');
17-
var alignPeriod = require('../../plots/cartesian/align_period');
1817
var Registry = require('../../registry');
1918

2019
var helpers = require('./helpers');
@@ -491,6 +490,7 @@ function _hover(gd, evt, subplot, noHoverEvent) {
491490
if(hoverdistance !== 0) {
492491
if(trace._module && trace._module.hoverPoints) {
493492
var newPoints = trace._module.hoverPoints(pointData, xval, yval, _mode, {
493+
finiteRange: true,
494494
hoverLayer: fullLayout._hoverlayer
495495
});
496496

@@ -643,80 +643,51 @@ function _hover(gd, evt, subplot, noHoverEvent) {
643643
}
644644
}
645645

646-
hoverData.sort(function(d1, d2) { return d1.distance - d2.distance; });
646+
var sortHoverData = function() {
647+
hoverData.sort(function(d1, d2) { return d1.distance - d2.distance; });
647648

648-
// move period positioned points to the end of list
649-
hoverData = orderPeriod(hoverData, hovermode);
649+
// move period positioned points and box/bar-like traces to the end of the list
650+
hoverData = orderRangePoints(hoverData, hovermode);
651+
};
652+
sortHoverData();
650653

651654
// If in compare mode, select every point at position
652655
if(
653656
helpers.isXYhover(_mode) &&
654657
hoverData[0].length !== 0 &&
655658
hoverData[0].trace.type !== 'splom' // TODO: add support for splom
656659
) {
657-
var initLen = hoverData.length;
658660
var winningPoint = hoverData[0];
659661

660662
var customXVal = customVal('x', winningPoint, fullLayout);
661663
var customYVal = customVal('y', winningPoint, fullLayout);
662664

663665
findHoverPoints(customXVal, customYVal);
664666

665-
// also find start, middle and end point for period
666-
var axLetter = hovermode.charAt(0);
667-
if(winningPoint.trace[axLetter + 'period']) {
668-
var v = winningPoint[axLetter + 'LabelVal'];
669-
var ax = winningPoint[axLetter + 'a'];
670-
var T = {};
671-
T[axLetter + 'period'] = winningPoint.trace[axLetter + 'period'];
672-
T[axLetter + 'period0'] = winningPoint.trace[axLetter + 'period0'];
673-
674-
T[axLetter + 'periodalignment'] = 'start';
675-
var start = alignPeriod(T, ax, axLetter, [v])[0];
676-
677-
T[axLetter + 'periodalignment'] = 'middle';
678-
var middle = alignPeriod(T, ax, axLetter, [v])[0];
679-
680-
T[axLetter + 'periodalignment'] = 'end';
681-
var end = alignPeriod(T, ax, axLetter, [v])[0];
682-
683-
if(axLetter === 'x') {
684-
findHoverPoints(start, customYVal);
685-
findHoverPoints(middle, customYVal);
686-
findHoverPoints(end, customYVal);
687-
} else {
688-
findHoverPoints(customXVal, start);
689-
findHoverPoints(customXVal, middle);
690-
findHoverPoints(customXVal, end);
691-
}
692-
693-
var k;
694-
var seen = {};
695-
for(k = 0; k < initLen; k++) {
696-
seen[hoverData[k].trace.index] = true;
667+
var finalPoints = [];
668+
var seen = {};
669+
var insert = function(hd) {
670+
var type = hd.trace.type;
671+
var key = (
672+
type === 'box' ||
673+
type === 'violin' ||
674+
type === 'ohlc' ||
675+
type === 'candlestick'
676+
) ? hoverDataKey(hd) : hd.trace.index;
677+
if(!seen[key]) {
678+
seen[key] = true;
679+
finalPoints.push(hd);
697680
}
681+
};
698682

699-
// remove non-period aditions and traces that seen before
700-
for(k = hoverData.length - 1; k >= initLen; k--) {
701-
if(
702-
seen[hoverData[k].trace.index] ||
703-
!hoverData[k].trace[axLetter + 'period']
704-
) {
705-
hoverData.splice(k, 1);
706-
}
707-
}
683+
// insert the winnig point first
684+
insert(winningPoint);
685+
// override from the end
686+
for(var k = hoverData.length - 1; k > 0; k--) {
687+
insert(hoverData[k]);
708688
}
709-
710-
// Remove duplicated hoverData points
711-
// note that d3 also filters identical points in the rendering steps
712-
var repeated = {};
713-
hoverData = hoverData.filter(function(hd) {
714-
var key = hoverDataKey(hd);
715-
if(!repeated[key]) {
716-
repeated[key] = true;
717-
return repeated[key];
718-
}
719-
});
689+
hoverData = finalPoints;
690+
sortHoverData();
720691
}
721692

722693
// lastly, emit custom hover/unhover events
@@ -820,9 +791,7 @@ function createHoverText(hoverData, opts, gd) {
820791
var xa = c0.xa;
821792
var ya = c0.ya;
822793
var axLetter = hovermode.charAt(0);
823-
var v0 = c0[axLetter + 'LabelVal'];
824794
var t0 = c0[axLetter + 'Label'];
825-
var t00 = (String(t0) || '').split(' ')[0];
826795
var outerContainerBB = outerContainer.node().getBoundingClientRect();
827796
var outerTop = outerContainerBB.top;
828797
var outerWidth = outerContainerBB.width;
@@ -1011,44 +980,13 @@ function createHoverText(hoverData, opts, gd) {
1011980
}
1012981

1013982
label.attr('transform', strTranslate(lx, ly));
1014-
1015-
// remove the "close but not quite" points
1016-
// because of error bars, only take up to a space
1017-
hoverData = filterClosePoints(hoverData);
1018983
});
1019984

1020-
function filterClosePoints(hoverData) {
1021-
return hoverData.filter(function(d) {
1022-
if(d.zLabelVal !== undefined) return true;
1023-
if((d[axLetter + 'Label'] || '').split(' ')[0] === t00) return true;
1024-
if(d.trace[axLetter + 'period']) {
1025-
var v = d[axLetter + 'LabelVal'];
1026-
var ax = d[axLetter + 'a'];
1027-
var trace = {};
1028-
trace[axLetter + 'period'] = d.trace[axLetter + 'period'];
1029-
trace[axLetter + 'period0'] = d.trace[axLetter + 'period0'];
1030-
1031-
trace[axLetter + 'periodalignment'] = 'start';
1032-
var start = alignPeriod(trace, ax, axLetter, [v])[0];
1033-
1034-
trace[axLetter + 'periodalignment'] = 'end';
1035-
var end = alignPeriod(trace, ax, axLetter, [v])[0];
1036-
1037-
if(v0 >= start && v0 < end) return true;
1038-
}
1039-
1040-
return false;
1041-
});
1042-
}
1043-
1044985
// Show a single hover label
1045986
if(helpers.isUnifiedHover(hovermode)) {
1046987
// Delete leftover hover labels from other hovermodes
1047988
container.selectAll('g.hovertext').remove();
1048989

1049-
// similarly to compare mode, we remove the "close but not quite together" points
1050-
if((t0 !== undefined) && (c0.distance <= opts.hoverdistance)) hoverData = filterClosePoints(hoverData);
1051-
1052990
// Return early if nothing is hovered on
1053991
if(hoverData.length === 0) return;
1054992

@@ -1929,23 +1867,29 @@ function plainText(s, len) {
19291867
});
19301868
}
19311869

1932-
function orderPeriod(hoverData, hovermode) {
1870+
function orderRangePoints(hoverData, hovermode) {
19331871
var axLetter = hovermode.charAt(0);
19341872

19351873
var first = [];
1874+
var second = [];
19361875
var last = [];
19371876

19381877
for(var i = 0; i < hoverData.length; i++) {
19391878
var d = hoverData[i];
19401879

1941-
if(d.trace[axLetter + 'period']) {
1880+
if(
1881+
Registry.traceIs(d.trace, 'bar-like') ||
1882+
Registry.traceIs(d.trace, 'box-violin')
1883+
) {
19421884
last.push(d);
1885+
} else if(d.trace[axLetter + 'period']) {
1886+
second.push(d);
19431887
} else {
19441888
first.push(d);
19451889
}
19461890
}
19471891

1948-
return first.concat(last);
1892+
return first.concat(second).concat(last);
19491893
}
19501894

19511895
function customVal(axLetter, winningPoint, fullLayout) {

src/traces/bar/hover.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ var getLineWidth = require('./helpers').getLineWidth;
99
var hoverLabelText = require('../../plots/cartesian/axes').hoverLabelText;
1010
var BADNUM = require('../../constants/numerical').BADNUM;
1111

12-
function hoverPoints(pointData, xval, yval, hovermode) {
13-
var barPointData = hoverOnBars(pointData, xval, yval, hovermode);
12+
function hoverPoints(pointData, xval, yval, hovermode, opts) {
13+
var barPointData = hoverOnBars(pointData, xval, yval, hovermode, opts);
1414

1515
if(barPointData) {
1616
var cd = barPointData.cd;
@@ -24,7 +24,7 @@ function hoverPoints(pointData, xval, yval, hovermode) {
2424
}
2525
}
2626

27-
function hoverOnBars(pointData, xval, yval, hovermode) {
27+
function hoverOnBars(pointData, xval, yval, hovermode, opts) {
2828
var cd = pointData.cd;
2929
var trace = cd[0].trace;
3030
var t = cd[0].t;
@@ -67,6 +67,8 @@ function hoverOnBars(pointData, xval, yval, hovermode) {
6767
};
6868

6969
function inbox(_minPos, _maxPos, maxDistance) {
70+
if(opts.finiteRange) maxDistance = 0;
71+
7072
// add a little to the pseudo-distance for wider bars, so that like scatter,
7173
// if you are over two overlapping bars, the narrower one wins.
7274
return Fx.inbox(_minPos - posVal, _maxPos - posVal,

src/traces/funnel/hover.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ var opacity = require('../../components/color').opacity;
44
var hoverOnBars = require('../bar/hover').hoverOnBars;
55
var formatPercent = require('../../lib').formatPercent;
66

7-
module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
8-
var point = hoverOnBars(pointData, xval, yval, hovermode);
7+
module.exports = function hoverPoints(pointData, xval, yval, hovermode, opts) {
8+
var point = hoverOnBars(pointData, xval, yval, hovermode, opts);
99
if(!point) return;
1010

1111
var cd = point.cd;

src/traces/histogram/hover.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
var barHover = require('../bar/hover').hoverPoints;
44
var hoverLabelText = require('../../plots/cartesian/axes').hoverLabelText;
55

6-
module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
7-
var pts = barHover(pointData, xval, yval, hovermode);
6+
module.exports = function hoverPoints(pointData, xval, yval, hovermode, opts) {
7+
var pts = barHover(pointData, xval, yval, hovermode, opts);
88

99
if(!pts) return;
1010

src/traces/ohlc/hover.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ function hoverPoints(pointData, xval, yval, hovermode) {
2323
return hoverOnPoints(pointData, xval, yval, hovermode);
2424
}
2525

26-
function getClosestPoint(pointData, xval, yval, hovermode) {
26+
function _getClosestPoint(pointData, xval, yval, hovermode) {
2727
var cd = pointData.cd;
2828
var xa = pointData.xa;
2929
var trace = cd[0].trace;
@@ -95,7 +95,7 @@ function hoverSplit(pointData, xval, yval, hovermode) {
9595
var t = cd[0].t;
9696
var closeBoxData = [];
9797

98-
var closestPoint = getClosestPoint(pointData, xval, yval, hovermode);
98+
var closestPoint = _getClosestPoint(pointData, xval, yval, hovermode);
9999
// skip the rest (for this trace) if we didn't find a close point
100100
if(!closestPoint) return [];
101101

@@ -150,7 +150,7 @@ function hoverOnPoints(pointData, xval, yval, hovermode) {
150150
var trace = cd[0].trace;
151151
var t = cd[0].t;
152152

153-
var closestPoint = getClosestPoint(pointData, xval, yval, hovermode);
153+
var closestPoint = _getClosestPoint(pointData, xval, yval, hovermode);
154154
// skip the rest (for this trace) if we didn't find a close point
155155
if(!closestPoint) return [];
156156

src/traces/waterfall/hover.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ var DIRSYMBOL = {
1010
decreasing: delta.DECREASING.SYMBOL
1111
};
1212

13-
module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
14-
var point = hoverOnBars(pointData, xval, yval, hovermode);
13+
module.exports = function hoverPoints(pointData, xval, yval, hovermode, opts) {
14+
var point = hoverOnBars(pointData, xval, yval, hovermode, opts);
1515
if(!point) return;
1616

1717
var cd = point.cd;

test/jasmine/tests/bar_test.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -2296,7 +2296,7 @@ describe('bar hover', function() {
22962296

22972297
function _hover(gd, xval, yval, hovermode) {
22982298
var pointData = getPointData(gd);
2299-
var pts = Bar.hoverPoints(pointData, xval, yval, hovermode);
2299+
var pts = Bar.hoverPoints(pointData, xval, yval, hovermode, {});
23002300
if(!pts) return false;
23012301

23022302
var pt = pts[0];
@@ -2663,8 +2663,8 @@ describe('bar hover', function() {
26632663
barmode: m
26642664
})
26652665
.then(function() {
2666-
var pt0 = Bar.hoverPoints(getPointData(gd, 0), 0, 1, 'x')[0];
2667-
var pt1 = Bar.hoverPoints(getPointData(gd, 1), 0, 1, 'x')[0];
2666+
var pt0 = Bar.hoverPoints(getPointData(gd, 0), 0, 1, 'x', {})[0];
2667+
var pt1 = Bar.hoverPoints(getPointData(gd, 1), 0, 1, 'x', {})[0];
26682668

26692669
expect(pt0.yLabelVal).toBe(0, 'y label value for data[0]');
26702670
expect(pt1.yLabelVal).toBe(1, 'y label value for data[1]');

test/jasmine/tests/funnel_test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1309,7 +1309,7 @@ describe('funnel hover', function() {
13091309

13101310
function _hover(gd, xval, yval, hovermode) {
13111311
var pointData = getPointData(gd);
1312-
var pts = Funnel.hoverPoints(pointData, xval, yval, hovermode);
1312+
var pts = Funnel.hoverPoints(pointData, xval, yval, hovermode, {});
13131313
if(!pts) return false;
13141314

13151315
var pt = pts[0];

0 commit comments

Comments
 (0)