Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/components/fx/click.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var hover = require('./hover').hover;

module.exports = function click(gd, evt, subplot) {
var annotationsDone = Registry.getComponentMethod('annotations', 'onClick')(gd, gd._hoverdata);
var fullLayout = gd._fullLayout;

// fallback to fail-safe in case the plot type's hover method doesn't pass the subplot.
// Ternary, for example, didn't, but it was caught because tested.
Expand All @@ -14,9 +15,20 @@ module.exports = function click(gd, evt, subplot) {
hover(gd, evt, subplot, true);
}

function emitClick() { gd.emit('plotly_click', {points: gd._hoverdata, event: evt}); }
function emitClick() {
var clickData = {points: gd._hoverdata, event: evt};

// get coordinate values from latest hover call, if available
clickData.xaxes ??= gd._hoverXAxes;
clickData.yaxes ??= gd._hoverYAxes;
clickData.xvals ??= gd._hoverXVals;
clickData.yvals ??= gd._hoverYVals;

if(gd._hoverdata && evt && evt.target) {
gd.emit('plotly_click', clickData);
}

if((gd._hoverdata || fullLayout.clickanywhere) && evt && evt.target) {
if(!gd._hoverdata) gd._hoverdata = [];
if(annotationsDone && annotationsDone.then) {
annotationsDone.then(emitClick);
} else emitClick();
Expand Down
28 changes: 26 additions & 2 deletions src/components/fx/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ exports.loneHover = function loneHover(hoverItems, opts) {
y1: y1 + gTop
};

eventData.xPixel = (_x0 + _x1) / 2;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexshoe what's the purpose of these lines?

eventData.yPixel = (_y0 + _y1) / 2;

if (opts.inOut_bbox) {
opts.inOut_bbox.push(eventData.bbox);
}
Expand Down Expand Up @@ -473,6 +476,12 @@ function _hover(gd, evt, subplot, noHoverEvent, eventTarget) {
}
}

// Save coordinate values so clickanywhere can be used without hoveranywhere
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we only do this step if clickanywhere is enabled? Does it matter?

gd._hoverXVals = xvalArray;
gd._hoverYVals = yvalArray;
gd._hoverXAxes = xaArray;
gd._hoverYAxes = yaArray;

// the pixel distance to beat as a matching point
// in 'x' or 'y' mode this resets for each trace
var distance = Infinity;
Expand Down Expand Up @@ -778,6 +787,17 @@ function _hover(gd, evt, subplot, noHoverEvent, eventTarget) {
createSpikelines(gd, spikePoints, spikelineOpts);
}
}

if (fullLayout.hoveranywhere && !noHoverEvent && eventTarget) {
gd.emit('plotly_hover', {
event: evt,
points: [],
xaxes: xaArray,
yaxes: yaArray,
xvals: xvalArray,
yvals: yvalArray
});
}
Comment on lines +791 to +800
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be more maintanable to have this gd.emit('plotly_hover', ...) (for the hoveranywhere case) closer to the gd.emit('plotly_hover', ...) for the normal case (which I believe is on line 950-957). To ensure that the same keys are included in both objects, etc.

I realize we do want to keep the early return on line 801.

Maybe you could add a emitHover() function akin to emitClick(), and then call it in both cases (both here and on line 950).

return result;
}

Expand Down Expand Up @@ -877,6 +897,9 @@ function _hover(gd, evt, subplot, noHoverEvent, eventTarget) {
y0: y0 + gTop,
y1: y1 + gTop
};

eventData.xPixel = (_x0 + _x1) / 2;
eventData.yPixel = (_y0 + _y1) / 2;
}

pt.eventData = [eventData];
Expand Down Expand Up @@ -914,9 +937,10 @@ function _hover(gd, evt, subplot, noHoverEvent, eventTarget) {
}

// don't emit events if called manually
if (!eventTarget || noHoverEvent || !hoverChanged(gd, evt, oldhoverdata)) return;
var _hoverChanged = hoverChanged(gd, evt, oldhoverdata);
if (!eventTarget || noHoverEvent || (!_hoverChanged && !fullLayout.hoveranywhere)) return;

if (oldhoverdata) {
if (oldhoverdata && _hoverChanged) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that when hoveranywhere is enabled, we emit plotly_unhover any time the hover data changes? That doesn't seem like the right behavior to me. Let me know if I'm misunderstanding.

gd.emit('plotly_unhover', {
event: evt,
points: oldhoverdata
Expand Down
2 changes: 2 additions & 0 deletions src/components/fx/hovermode_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ module.exports = function handleHoverModeDefaults(layoutIn, layoutOut) {

coerce('clickmode');
coerce('hoversubplots');
coerce('hoveranywhere');
coerce('clickanywhere');
return coerce('hovermode');
};
22 changes: 22 additions & 0 deletions src/components/fx/layout_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,28 @@ module.exports = {
'when `hovermode` is set to *x*, *x unified*, *y* or *y unified*.',
].join(' ')
},
hoveranywhere: {
valType: 'boolean',
dflt: false,
editType: 'none',
description: [
'If true, `plotly_hover` events will fire for any cursor position',
'within the plot area, not just over traces.',
'When the cursor is not over a trace, the event will have an empty `points` array',
'but will include `xvals` and `yvals` with cursor coordinates in data space.'
].join(' ')
},
clickanywhere: {
valType: 'boolean',
dflt: false,
editType: 'none',
description: [
'If true, `plotly_click` events will fire for any click position',
'within the plot area, not just over traces.',
'When clicking where there is no trace data, the event will have an empty `points` array',
'but will include `xvals` and `yvals` with click coordinates in data space.'
].join(' ')
},
hoverdistance: {
valType: 'integer',
min: -1,
Expand Down
Binary file modified test/image/baselines/map_custom-style.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 6 additions & 6 deletions test/jasmine/tests/click_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ describe('Test click interactions:', function() {
expect(Object.keys(pt).sort()).toEqual([
'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex',
'bbox',
'x', 'y', 'xaxis', 'yaxis'
'x', 'y', 'xaxis', 'yaxis', 'xPixel', 'yPixel'
].sort());
expect(pt.curveNumber).toEqual(0);
expect(pt.pointNumber).toEqual(11);
Expand Down Expand Up @@ -153,7 +153,7 @@ describe('Test click interactions:', function() {
expect(Object.keys(pt).sort()).toEqual([
'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex',
'bbox',
'x', 'y', 'xaxis', 'yaxis'
'x', 'y', 'xaxis', 'yaxis', 'xPixel', 'yPixel'
].sort());
expect(pt.curveNumber).toEqual(0);
expect(pt.pointNumber).toEqual(11);
Expand Down Expand Up @@ -225,7 +225,7 @@ describe('Test click interactions:', function() {
expect(Object.keys(pt).sort()).toEqual([
'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex',
'bbox',
'x', 'y', 'xaxis', 'yaxis'
'x', 'y', 'xaxis', 'yaxis', 'xPixel', 'yPixel'
].sort());
expect(pt.curveNumber).toEqual(0);
expect(pt.pointNumber).toEqual(11);
Expand Down Expand Up @@ -315,7 +315,7 @@ describe('Test click interactions:', function() {
expect(Object.keys(pt).sort()).toEqual([
'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex',
'bbox',
'x', 'y', 'xaxis', 'yaxis'
'x', 'y', 'xaxis', 'yaxis', 'xPixel', 'yPixel'
].sort());
expect(pt.curveNumber).toEqual(0);
expect(pt.pointNumber).toEqual(11);
Expand Down Expand Up @@ -349,7 +349,7 @@ describe('Test click interactions:', function() {
expect(Object.keys(pt).sort()).toEqual([
'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex',
'bbox',
'x', 'y', 'xaxis', 'yaxis'
'x', 'y', 'xaxis', 'yaxis', 'xPixel', 'yPixel'
].sort());
expect(pt.curveNumber).toEqual(0);
expect(pt.pointNumber).toEqual(11);
Expand Down Expand Up @@ -387,7 +387,7 @@ describe('Test click interactions:', function() {
expect(Object.keys(pt).sort()).toEqual([
'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex',
'bbox',
'x', 'y', 'xaxis', 'yaxis'
'x', 'y', 'xaxis', 'yaxis', 'xPixel', 'yPixel'
].sort());
expect(pt.curveNumber).toEqual(0);
expect(pt.pointNumber).toEqual(11);
Expand Down
18 changes: 9 additions & 9 deletions test/jasmine/tests/geo_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -882,7 +882,7 @@ describe('Test geo interactions', function() {
it('should contain the correct fields', function() {
expect(Object.keys(ptData).sort()).toEqual([
'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex', 'bbox',
'lon', 'lat', 'location', 'marker.size'
'lon', 'lat', 'location', 'marker.size', 'xPixel', 'yPixel'
].sort());
expect(cnt).toEqual(1);
});
Expand Down Expand Up @@ -947,7 +947,7 @@ describe('Test geo interactions', function() {
it('should contain the correct fields', function() {
expect(Object.keys(ptData).sort()).toEqual([
'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex', 'bbox',
'lon', 'lat', 'location', 'marker.size'
'lon', 'lat', 'location', 'marker.size', 'xPixel', 'yPixel'
].sort());
});

Expand Down Expand Up @@ -979,7 +979,7 @@ describe('Test geo interactions', function() {
it('should contain the correct fields', function() {
expect(Object.keys(ptData).sort()).toEqual([
'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex', 'bbox',
'lon', 'lat', 'location', 'marker.size'
'lon', 'lat', 'location', 'marker.size', 'xPixel', 'yPixel'
].sort());
});

Expand Down Expand Up @@ -1008,7 +1008,7 @@ describe('Test geo interactions', function() {
it('should contain the correct fields', function() {
expect(Object.keys(ptData).sort()).toEqual([
'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex', 'bbox',
'location', 'z', 'ct'
'location', 'z', 'ct', 'xPixel', 'yPixel'
].sort());
});

Expand Down Expand Up @@ -1036,7 +1036,7 @@ describe('Test geo interactions', function() {
it('should contain the correct fields', function() {
expect(Object.keys(ptData).sort()).toEqual([
'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex', 'bbox',
'location', 'z', 'ct'
'location', 'z', 'ct', 'xPixel', 'yPixel'
].sort());
});

Expand Down Expand Up @@ -1068,7 +1068,7 @@ describe('Test geo interactions', function() {
it('should contain the correct fields', function() {
expect(Object.keys(ptData).sort()).toEqual([
'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex', 'bbox',
'location', 'z', 'ct'
'location', 'z', 'ct', 'xPixel', 'yPixel'
].sort());
});

Expand Down Expand Up @@ -1792,7 +1792,7 @@ describe('Test event property of interactions on a geo plot:', function() {
expect(Object.keys(pt).sort()).toEqual([
'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex', 'bbox',
'lon', 'lat',
'location', 'text', 'marker.size'
'location', 'text', 'marker.size', 'xPixel', 'yPixel'
].sort());

expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber');
Expand Down Expand Up @@ -1896,7 +1896,7 @@ describe('Test event property of interactions on a geo plot:', function() {
expect(Object.keys(pt).sort()).toEqual([
'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex', 'bbox',
'lon', 'lat',
'location', 'text', 'marker.size'
'location', 'text', 'marker.size', 'xPixel', 'yPixel'
].sort());

expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber');
Expand Down Expand Up @@ -1937,7 +1937,7 @@ describe('Test event property of interactions on a geo plot:', function() {
expect(Object.keys(pt).sort()).toEqual([
'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex', 'bbox',
'lon', 'lat',
'location', 'text', 'marker.size'
'location', 'text', 'marker.size', 'xPixel', 'yPixel'
].sort());

expect(pt.curveNumber).toEqual(0, 'points[0].curveNumber');
Expand Down
Loading