Skip to content

Commit efb13b9

Browse files
committed
Merge remote-tracking branch 'origin/master' into replotting-fixes
2 parents 9fb96b8 + 7aac171 commit efb13b9

27 files changed

Lines changed: 692 additions & 157 deletions

draftlogs/7707_add.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Add `hoveranywhere` and `clickanywhere` layout attributes to emit hover and click events anywhere in the plot area, not just over traces [[#7707](https://github.com/plotly/plotly.js/pull/7707)]

draftlogs/7730_add.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Add `displayNotifier` configuration property to set the display of notifier in the top right area of the viewport [[#7730](https://github.com/plotly/plotly.js/pull/7730)], with thanks to @silversonicaxel for the contribution!

draftlogs/7731_add.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Update USA location lookup for `scattergeo` and `choropleth` traces to use both location names and abbreviations [[7731](https://github.com/plotly/plotly.js/pull/7731)]

src/components/fx/click.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ var hover = require('./hover').hover;
55

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

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

17-
function emitClick() { gd.emit('plotly_click', {points: gd._hoverdata, event: evt}); }
18+
function emitClick() {
19+
var clickData = {points: gd._hoverdata, event: evt};
20+
21+
// get coordinate values from latest hover call, if available
22+
clickData.xaxes ??= gd._hoverXAxes;
23+
clickData.yaxes ??= gd._hoverYAxes;
24+
clickData.xvals ??= gd._hoverXVals;
25+
clickData.yvals ??= gd._hoverYVals;
1826

19-
if(gd._hoverdata && evt && evt.target) {
27+
gd.emit('plotly_click', clickData);
28+
}
29+
30+
if((gd._hoverdata || fullLayout.clickanywhere) && evt && evt.target) {
31+
if(!gd._hoverdata) gd._hoverdata = [];
2032
if(annotationsDone && annotationsDone.then) {
2133
annotationsDone.then(emitClick);
2234
} else emitClick();

src/components/fx/hover.js

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,11 @@ exports.loneHover = function loneHover(hoverItems, opts) {
167167
y1: y1 + gTop
168168
};
169169

170+
// xPixel/yPixel are pixel coordinates of the hover point's center,
171+
// relative to the top-left corner of the graph div
172+
eventData.xPixel = (_x0 + _x1) / 2;
173+
eventData.yPixel = (_y0 + _y1) / 2;
174+
170175
if (opts.inOut_bbox) {
171176
opts.inOut_bbox.push(eventData.bbox);
172177
}
@@ -473,6 +478,14 @@ function _hover(gd, evt, subplot, noHoverEvent, eventTarget) {
473478
}
474479
}
475480

481+
// Save coordinate values so clickanywhere can be used without hoveranywhere
482+
if (fullLayout.clickanywhere) {
483+
gd._hoverXVals = xvalArray;
484+
gd._hoverYVals = yvalArray;
485+
gd._hoverXAxes = xaArray;
486+
gd._hoverYAxes = yaArray;
487+
}
488+
476489
// the pixel distance to beat as a matching point
477490
// in 'x' or 'y' mode this resets for each trace
478491
var distance = Infinity;
@@ -778,6 +791,18 @@ function _hover(gd, evt, subplot, noHoverEvent, eventTarget) {
778791
createSpikelines(gd, spikePoints, spikelineOpts);
779792
}
780793
}
794+
795+
if (fullLayout.hoveranywhere && !noHoverEvent && eventTarget) {
796+
var oldHoverData = gd._hoverdata;
797+
if (oldHoverData && oldHoverData.length) {
798+
gd.emit('plotly_unhover', {
799+
event: evt,
800+
points: oldHoverData
801+
});
802+
gd._hoverdata = [];
803+
}
804+
emitHover([]);
805+
}
781806
return result;
782807
}
783808

@@ -877,6 +902,9 @@ function _hover(gd, evt, subplot, noHoverEvent, eventTarget) {
877902
y0: y0 + gTop,
878903
y1: y1 + gTop
879904
};
905+
906+
eventData.xPixel = (_x0 + _x1) / 2;
907+
eventData.yPixel = (_y0 + _y1) / 2;
880908
}
881909

882910
pt.eventData = [eventData];
@@ -914,23 +942,28 @@ function _hover(gd, evt, subplot, noHoverEvent, eventTarget) {
914942
}
915943

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

919-
if (oldhoverdata) {
948+
if (oldhoverdata && _hoverChanged) {
920949
gd.emit('plotly_unhover', {
921950
event: evt,
922951
points: oldhoverdata
923952
});
924953
}
925954

926-
gd.emit('plotly_hover', {
927-
event: evt,
928-
points: gd._hoverdata,
929-
xaxes: xaArray,
930-
yaxes: yaArray,
931-
xvals: xvalArray,
932-
yvals: yvalArray
933-
});
955+
emitHover(gd._hoverdata);
956+
957+
function emitHover(points) {
958+
gd.emit('plotly_hover', {
959+
event: evt,
960+
points: points,
961+
xaxes: xaArray,
962+
yaxes: yaArray,
963+
xvals: xvalArray,
964+
yvals: yvalArray
965+
});
966+
}
934967
}
935968

936969
function hoverDataKey(d) {

src/components/fx/hovermode_defaults.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,7 @@ module.exports = function handleHoverModeDefaults(layoutIn, layoutOut) {
1313

1414
coerce('clickmode');
1515
coerce('hoversubplots');
16+
coerce('hoveranywhere');
17+
coerce('clickanywhere');
1618
return coerce('hovermode');
1719
};

src/components/fx/layout_attributes.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,28 @@ module.exports = {
9191
'when `hovermode` is set to *x*, *x unified*, *y* or *y unified*.',
9292
].join(' ')
9393
},
94+
hoveranywhere: {
95+
valType: 'boolean',
96+
dflt: false,
97+
editType: 'none',
98+
description: [
99+
'If true, `plotly_hover` events will fire for any cursor position',
100+
'within the plot area, not just over traces.',
101+
'When the cursor is not over a trace, the event will have an empty `points` array',
102+
'but will include `xvals` and `yvals` with cursor coordinates in data space.'
103+
].join(' ')
104+
},
105+
clickanywhere: {
106+
valType: 'boolean',
107+
dflt: false,
108+
editType: 'none',
109+
description: [
110+
'If true, `plotly_click` events will fire for any click position',
111+
'within the plot area, not just over traces.',
112+
'When clicking where there is no trace data, the event will have an empty `points` array',
113+
'but will include `xvals` and `yvals` with click coordinates in data space.'
114+
].join(' ')
115+
},
94116
hoverdistance: {
95117
valType: 'integer',
96118
min: -1,

src/components/legend/handle_click.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ exports.handleItemClick = function handleItemClick(g, gd, legendObj, mode) {
3131
if(mode === 'toggle' && legendObj.itemdoubleclick === 'toggleothers' &&
3232
SHOWISOLATETIP && gd.data && gd._context.showTips
3333
) {
34-
Lib.notifier(Lib._(gd, 'Double-click on legend to isolate one trace'), 'long');
34+
Lib.notifier(Lib._(gd, 'Double-click on legend to isolate one trace'), 'long', gd);
3535
SHOWISOLATETIP = false;
3636
}
3737

@@ -323,7 +323,7 @@ exports.handleTitleClick = function handleTitleClick(gd, legendObj, mode) {
323323
const item = allLegendItems[i];
324324
const inThisLegend = isInLegend(item);
325325

326-
// If item is not in this legend, skip if in toggle mode
326+
// If item is not in this legend, skip if in toggle mode
327327
// or if item is not displayed in the legend
328328
if(!inThisLegend) {
329329
const notDisplayed = (item.showlegend !== true && !item.legendgroup);

src/components/modebar/buttons.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ modeBarButtons.toImage = {
4949
var toImageButtonOptions = gd._context.toImageButtonOptions;
5050
var opts = {format: toImageButtonOptions.format || 'png'};
5151

52-
Lib.notifier(_(gd, 'Taking snapshot - this may take a few seconds'), 'long');
52+
Lib.notifier(_(gd, 'Taking snapshot - this may take a few seconds'), 'long', gd);
5353

5454
['filename', 'width', 'height', 'scale'].forEach(function(key) {
5555
if(key in toImageButtonOptions) {
@@ -58,12 +58,12 @@ modeBarButtons.toImage = {
5858
});
5959

6060
Registry.call('downloadImage', gd, opts)
61-
.then(function(filename) {
62-
Lib.notifier(_(gd, 'Snapshot succeeded') + ' - ' + filename, 'long');
63-
})
64-
.catch(function() {
65-
Lib.notifier(_(gd, 'Sorry, there was a problem downloading your snapshot!'), 'long');
66-
});
61+
.then(function(filename) {
62+
Lib.notifier(_(gd, 'Snapshot succeeded') + ' - ' + filename, 'long', gd);
63+
})
64+
.catch(function() {
65+
Lib.notifier(_(gd, 'Sorry, there was a problem downloading your snapshot!'), 'long', gd);
66+
});
6767
}
6868
};
6969

0 commit comments

Comments
 (0)