Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixup hover filters #5668

Merged
merged 12 commits into from
May 24, 2021
130 changes: 37 additions & 93 deletions src/components/fx/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ var Drawing = require('../drawing');
var Color = require('../color');
var dragElement = require('../dragelement');
var Axes = require('../../plots/cartesian/axes');
var alignPeriod = require('../../plots/cartesian/align_period');
var Registry = require('../../registry');

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

Expand Down Expand Up @@ -643,80 +643,51 @@ function _hover(gd, evt, subplot, noHoverEvent) {
}
}

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

// move period positioned points to the end of list
hoverData = orderPeriod(hoverData, hovermode);
// move period positioned points and box/bar-like traces to the end of the list
hoverData = orderRangePoints(hoverData, hovermode);
};
sortHoverData();

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

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

findHoverPoints(customXVal, customYVal);

// also find start, middle and end point for period
var axLetter = hovermode.charAt(0);
if(winningPoint.trace[axLetter + 'period']) {
var v = winningPoint[axLetter + 'LabelVal'];
var ax = winningPoint[axLetter + 'a'];
var T = {};
T[axLetter + 'period'] = winningPoint.trace[axLetter + 'period'];
T[axLetter + 'period0'] = winningPoint.trace[axLetter + 'period0'];

T[axLetter + 'periodalignment'] = 'start';
var start = alignPeriod(T, ax, axLetter, [v])[0];

T[axLetter + 'periodalignment'] = 'middle';
var middle = alignPeriod(T, ax, axLetter, [v])[0];

T[axLetter + 'periodalignment'] = 'end';
var end = alignPeriod(T, ax, axLetter, [v])[0];

if(axLetter === 'x') {
findHoverPoints(start, customYVal);
findHoverPoints(middle, customYVal);
findHoverPoints(end, customYVal);
} else {
findHoverPoints(customXVal, start);
findHoverPoints(customXVal, middle);
findHoverPoints(customXVal, end);
}

var k;
var seen = {};
for(k = 0; k < initLen; k++) {
seen[hoverData[k].trace.index] = true;
var finalPoints = [];
var seen = {};
var insert = function(hd) {
var type = hd.trace.type;
var key = (
type === 'box' ||
type === 'violin' ||
type === 'ohlc' ||
type === 'candlestick'
) ? hoverDataKey(hd) : hd.trace.index;
if(!seen[key]) {
seen[key] = true;
finalPoints.push(hd);
}
};

// remove non-period aditions and traces that seen before
for(k = hoverData.length - 1; k >= initLen; k--) {
if(
seen[hoverData[k].trace.index] ||
!hoverData[k].trace[axLetter + 'period']
) {
hoverData.splice(k, 1);
}
}
// insert the winnig point first
insert(winningPoint);
// override from the end
for(var k = hoverData.length - 1; k > 0; k--) {
insert(hoverData[k]);
}

// Remove duplicated hoverData points
// note that d3 also filters identical points in the rendering steps
var repeated = {};
hoverData = hoverData.filter(function(hd) {
var key = hoverDataKey(hd);
if(!repeated[key]) {
repeated[key] = true;
return repeated[key];
}
});
hoverData = finalPoints;
sortHoverData();
}

// lastly, emit custom hover/unhover events
Expand Down Expand Up @@ -820,9 +791,7 @@ function createHoverText(hoverData, opts, gd) {
var xa = c0.xa;
var ya = c0.ya;
var axLetter = hovermode.charAt(0);
var v0 = c0[axLetter + 'LabelVal'];
var t0 = c0[axLetter + 'Label'];
var t00 = (String(t0) || '').split(' ')[0];
var outerContainerBB = outerContainer.node().getBoundingClientRect();
var outerTop = outerContainerBB.top;
var outerWidth = outerContainerBB.width;
Expand Down Expand Up @@ -1011,44 +980,13 @@ function createHoverText(hoverData, opts, gd) {
}

label.attr('transform', strTranslate(lx, ly));

// remove the "close but not quite" points
// because of error bars, only take up to a space
hoverData = filterClosePoints(hoverData);
});

function filterClosePoints(hoverData) {
return hoverData.filter(function(d) {
if(d.zLabelVal !== undefined) return true;
if((d[axLetter + 'Label'] || '').split(' ')[0] === t00) return true;
if(d.trace[axLetter + 'period']) {
var v = d[axLetter + 'LabelVal'];
var ax = d[axLetter + 'a'];
var trace = {};
trace[axLetter + 'period'] = d.trace[axLetter + 'period'];
trace[axLetter + 'period0'] = d.trace[axLetter + 'period0'];

trace[axLetter + 'periodalignment'] = 'start';
var start = alignPeriod(trace, ax, axLetter, [v])[0];

trace[axLetter + 'periodalignment'] = 'end';
var end = alignPeriod(trace, ax, axLetter, [v])[0];

if(v0 >= start && v0 < end) return true;
}

return false;
});
}

// Show a single hover label
if(helpers.isUnifiedHover(hovermode)) {
// Delete leftover hover labels from other hovermodes
container.selectAll('g.hovertext').remove();

// similarly to compare mode, we remove the "close but not quite together" points
if((t0 !== undefined) && (c0.distance <= opts.hoverdistance)) hoverData = filterClosePoints(hoverData);

// Return early if nothing is hovered on
if(hoverData.length === 0) return;

Expand Down Expand Up @@ -1929,23 +1867,29 @@ function plainText(s, len) {
});
}

function orderPeriod(hoverData, hovermode) {
function orderRangePoints(hoverData, hovermode) {
var axLetter = hovermode.charAt(0);

var first = [];
var second = [];
var last = [];

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

if(d.trace[axLetter + 'period']) {
if(
Registry.traceIs(d.trace, 'bar-like') ||
Registry.traceIs(d.trace, 'box-violin')
) {
last.push(d);
} else if(d.trace[axLetter + 'period']) {
second.push(d);
} else {
first.push(d);
}
}

return first.concat(last);
return first.concat(second).concat(last);
}

function customVal(axLetter, winningPoint, fullLayout) {
Expand Down
8 changes: 5 additions & 3 deletions src/traces/bar/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ var getLineWidth = require('./helpers').getLineWidth;
var hoverLabelText = require('../../plots/cartesian/axes').hoverLabelText;
var BADNUM = require('../../constants/numerical').BADNUM;

function hoverPoints(pointData, xval, yval, hovermode) {
var barPointData = hoverOnBars(pointData, xval, yval, hovermode);
function hoverPoints(pointData, xval, yval, hovermode, opts) {
var barPointData = hoverOnBars(pointData, xval, yval, hovermode, opts);

if(barPointData) {
var cd = barPointData.cd;
Expand All @@ -24,7 +24,7 @@ function hoverPoints(pointData, xval, yval, hovermode) {
}
}

function hoverOnBars(pointData, xval, yval, hovermode) {
function hoverOnBars(pointData, xval, yval, hovermode, opts) {
var cd = pointData.cd;
var trace = cd[0].trace;
var t = cd[0].t;
Expand Down Expand Up @@ -67,6 +67,8 @@ function hoverOnBars(pointData, xval, yval, hovermode) {
};

function inbox(_minPos, _maxPos, maxDistance) {
if(opts.finiteRange) maxDistance = 0;

// add a little to the pseudo-distance for wider bars, so that like scatter,
// if you are over two overlapping bars, the narrower one wins.
return Fx.inbox(_minPos - posVal, _maxPos - posVal,
Expand Down
4 changes: 2 additions & 2 deletions src/traces/funnel/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ var opacity = require('../../components/color').opacity;
var hoverOnBars = require('../bar/hover').hoverOnBars;
var formatPercent = require('../../lib').formatPercent;

module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
var point = hoverOnBars(pointData, xval, yval, hovermode);
module.exports = function hoverPoints(pointData, xval, yval, hovermode, opts) {
var point = hoverOnBars(pointData, xval, yval, hovermode, opts);
if(!point) return;

var cd = point.cd;
Expand Down
4 changes: 2 additions & 2 deletions src/traces/histogram/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
var barHover = require('../bar/hover').hoverPoints;
var hoverLabelText = require('../../plots/cartesian/axes').hoverLabelText;

module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
var pts = barHover(pointData, xval, yval, hovermode);
module.exports = function hoverPoints(pointData, xval, yval, hovermode, opts) {
var pts = barHover(pointData, xval, yval, hovermode, opts);

if(!pts) return;

Expand Down
6 changes: 3 additions & 3 deletions src/traces/ohlc/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function hoverPoints(pointData, xval, yval, hovermode) {
return hoverOnPoints(pointData, xval, yval, hovermode);
}

function getClosestPoint(pointData, xval, yval, hovermode) {
function _getClosestPoint(pointData, xval, yval, hovermode) {
var cd = pointData.cd;
var xa = pointData.xa;
var trace = cd[0].trace;
Expand Down Expand Up @@ -95,7 +95,7 @@ function hoverSplit(pointData, xval, yval, hovermode) {
var t = cd[0].t;
var closeBoxData = [];

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

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

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

Expand Down
4 changes: 2 additions & 2 deletions src/traces/waterfall/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ var DIRSYMBOL = {
decreasing: delta.DECREASING.SYMBOL
};

module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
var point = hoverOnBars(pointData, xval, yval, hovermode);
module.exports = function hoverPoints(pointData, xval, yval, hovermode, opts) {
var point = hoverOnBars(pointData, xval, yval, hovermode, opts);
if(!point) return;

var cd = point.cd;
Expand Down
6 changes: 3 additions & 3 deletions test/jasmine/tests/bar_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2296,7 +2296,7 @@ describe('bar hover', function() {

function _hover(gd, xval, yval, hovermode) {
var pointData = getPointData(gd);
var pts = Bar.hoverPoints(pointData, xval, yval, hovermode);
var pts = Bar.hoverPoints(pointData, xval, yval, hovermode, {});
if(!pts) return false;

var pt = pts[0];
Expand Down Expand Up @@ -2663,8 +2663,8 @@ describe('bar hover', function() {
barmode: m
})
.then(function() {
var pt0 = Bar.hoverPoints(getPointData(gd, 0), 0, 1, 'x')[0];
var pt1 = Bar.hoverPoints(getPointData(gd, 1), 0, 1, 'x')[0];
var pt0 = Bar.hoverPoints(getPointData(gd, 0), 0, 1, 'x', {})[0];
var pt1 = Bar.hoverPoints(getPointData(gd, 1), 0, 1, 'x', {})[0];

expect(pt0.yLabelVal).toBe(0, 'y label value for data[0]');
expect(pt1.yLabelVal).toBe(1, 'y label value for data[1]');
Expand Down
2 changes: 1 addition & 1 deletion test/jasmine/tests/funnel_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1309,7 +1309,7 @@ describe('funnel hover', function() {

function _hover(gd, xval, yval, hovermode) {
var pointData = getPointData(gd);
var pts = Funnel.hoverPoints(pointData, xval, yval, hovermode);
var pts = Funnel.hoverPoints(pointData, xval, yval, hovermode, {});
if(!pts) return false;

var pt = pts[0];
Expand Down
Loading