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

Output bounding box to hover event data #5512

Merged
merged 19 commits into from
Aug 26, 2021
Merged
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
1 change: 1 addition & 0 deletions draftlogs/5512_add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Provide bounding box positions in hover event data [[#5512](https://github.com/plotly/plotly.js/pull/5512)]
78 changes: 74 additions & 4 deletions src/components/fx/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,48 @@ exports.loneHover = function loneHover(hoverItems, opts) {
hoverItems = [hoverItems];
}

var gd = opts.gd;
var gTop = getTopOffset(gd);
var gLeft = getLeftOffset(gd);

var pointsData = hoverItems.map(function(hoverItem) {
var _x0 = hoverItem._x0 || hoverItem.x0 || hoverItem.x || 0;
var _x1 = hoverItem._x1 || hoverItem.x1 || hoverItem.x || 0;
var _y0 = hoverItem._y0 || hoverItem.y0 || hoverItem.y || 0;
var _y1 = hoverItem._y1 || hoverItem.y1 || hoverItem.y || 0;

var eventData = hoverItem.eventData;
if(eventData) {
var x0 = Math.min(_x0, _x1);
var x1 = Math.max(_x0, _x1);
var y0 = Math.min(_y0, _y1);
var y1 = Math.max(_y0, _y1);

var trace = hoverItem.trace;
if(Registry.traceIs(trace, 'gl3d')) {
var container = gd._fullLayout[trace.scene]._scene.container;
var dx = container.offsetLeft;
var dy = container.offsetTop;
x0 += dx;
x1 += dx;
y0 += dy;
y1 += dy;
} // TODO: handle heatmapgl

eventData.bbox = {
x0: x0 + gLeft,
x1: x1 + gLeft,
y0: y0 + gTop,
y1: y1 + gTop
};

if(opts.inOut_bbox) {
opts.inOut_bbox.push(eventData.bbox);
}
} else {
eventData = false;
}

return {
color: hoverItem.color || Color.defaultLine,
x0: hoverItem.x0 || hoverItem.x || 0,
Expand Down Expand Up @@ -158,8 +199,9 @@ exports.loneHover = function loneHover(hoverItems, opts) {
index: 0,

hovertemplate: hoverItem.hovertemplate || false,
eventData: hoverItem.eventData || false,
hovertemplateLabels: hoverItem.hovertemplateLabels || false,

eventData: eventData
};
});

Expand All @@ -174,7 +216,7 @@ exports.loneHover = function loneHover(hoverItems, opts) {
outerContainer: outerContainer3
};

var hoverLabel = createHoverText(pointsData, fullOpts, opts.gd);
var hoverLabel = createHoverText(pointsData, fullOpts, gd);

// Fix vertical overlap
var tooltipSpacing = 5;
Expand All @@ -199,8 +241,8 @@ exports.loneHover = function loneHover(hoverItems, opts) {
d.offset -= anchor;
});

var scaleX = opts.gd._fullLayout._invScaleX;
var scaleY = opts.gd._fullLayout._invScaleY;
var scaleX = gd._fullLayout._invScaleX;
var scaleY = gd._fullLayout._invScaleY;
alignHoverText(hoverLabel, fullOpts.rotateLabels, scaleX, scaleY);

return multiHover ? hoverLabel : hoverLabel.node();
Expand Down Expand Up @@ -732,6 +774,9 @@ function _hover(gd, evt, subplot, noHoverEvent) {
var oldhoverdata = gd._hoverdata;
var newhoverdata = [];

var gTop = getTopOffset(gd);
var gLeft = getLeftOffset(gd);

// pull out just the data that's useful to
// other people and send it to the event
for(itemnum = 0; itemnum < hoverData.length; itemnum++) {
Expand All @@ -746,6 +791,25 @@ function _hover(gd, evt, subplot, noHoverEvent) {
pt.hovertemplate = ht || pt.trace.hovertemplate || false;
}

if(pt.xa && pt.ya) {
var _x0 = pt.x0 + pt.xa._offset;
var _x1 = pt.x1 + pt.xa._offset;
var _y0 = pt.y0 + pt.ya._offset;
var _y1 = pt.y1 + pt.ya._offset;

var x0 = Math.min(_x0, _x1);
var x1 = Math.max(_x0, _x1);
var y0 = Math.min(_y0, _y1);
var y1 = Math.max(_y0, _y1);

eventData.bbox = {
x0: x0 + gLeft,
x1: x1 + gLeft,
y0: y0 + gTop,
y1: y1 + gTop
};
}

pt.eventData = [eventData];
newhoverdata.push(eventData);
}
Expand Down Expand Up @@ -2033,3 +2097,9 @@ function getCoord(axLetter, winningPoint, fullLayout) {

return val;
}

// Top/left hover offsets relative to graph div. As long as hover content is
// a sibling of the graph div, it will be positioned correctly relative to
// the offset parent, whatever that may be.
function getTopOffset(gd) { return gd.offsetTop + gd.clientTop; }
function getLeftOffset(gd) { return gd.offsetLeft + gd.clientLeft; }
6 changes: 5 additions & 1 deletion src/plots/gl3d/scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ proto.render = function() {
var eventData = {points: [pointData]};

if(scene.fullSceneLayout.hovermode) {
var bbox = [];
Fx.loneHover({
trace: traceNow,
x: (0.5 + 0.5 * pdata[0] / pdata[3]) * width,
Expand All @@ -442,8 +443,11 @@ proto.render = function() {
eventData: [pointData]
}, {
container: svgContainer,
gd: gd
gd: gd,
inOut_bbox: bbox
});

pointData.bbox = bbox[0];
}

if(selection.buttons && selection.distance < 5) {
Expand Down
6 changes: 6 additions & 0 deletions src/traces/icicle/draw_descendants.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ module.exports = function drawDescendants(gd, cd, entry, slices, opts) {
}

updateSlices.each(function(pt) {
// for bbox
pt._x0 = viewX(pt.x0);
pt._x1 = viewX(pt.x1);
pt._y0 = viewY(pt.y0);
pt._y1 = viewY(pt.y1);

pt._hoverX = viewX(pt.x1 - trace.tiling.pad),
pt._hoverY = hasBottom ?
viewY(pt.y1 - trace.tiling.pad / 2) :
Expand Down
1 change: 1 addition & 0 deletions src/traces/pie/event_data.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module.exports = function eventData(pt, trace) {
value: pt.v,
percent: pt.percent,
text: pt.text,
bbox: pt.bbox,

// pt.v (and pt.i below) for backward compatibility
v: pt.v
Expand Down
12 changes: 10 additions & 2 deletions src/traces/pie/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -344,9 +344,10 @@ function plotTextLines(slices, trace) {

function attachFxHandlers(sliceTop, gd, cd) {
var cd0 = cd[0];
var trace = cd0.trace;
var cx = cd0.cx;
var cy = cd0.cy;
var trace = cd0.trace;
var isFunnelArea = trace.type === 'funnelarea';

// hover state vars
// have we drawn a hover label, so it should be cleared later
Expand Down Expand Up @@ -404,11 +405,16 @@ function attachFxHandlers(sliceTop, gd, cd) {
var hoverLabel = trace2.hoverlabel;
var hoverFont = hoverLabel.font;

var bbox = [];
Fx.loneHover({
trace: trace,
x0: hoverCenterX - rInscribed * cd0.r,
x1: hoverCenterX + rInscribed * cd0.r,
y: hoverCenterY,
_x0: isFunnelArea ? cx + pt.TL[0] : hoverCenterX - rInscribed * cd0.r,
_x1: isFunnelArea ? cx + pt.TR[0] : hoverCenterX + rInscribed * cd0.r,
_y0: isFunnelArea ? cy + pt.TL[1] : hoverCenterY - rInscribed * cd0.r,
_y1: isFunnelArea ? cy + pt.BL[1] : hoverCenterY + rInscribed * cd0.r,
text: text.join('<br>'),
name: (trace2.hovertemplate || hoverinfo.indexOf('name') !== -1) ? trace2.name : undefined,
idealAlign: pt.pxmid[0] < 0 ? 'left' : 'right',
Expand All @@ -425,8 +431,10 @@ function attachFxHandlers(sliceTop, gd, cd) {
}, {
container: fullLayout2._hoverlayer.node(),
outerContainer: fullLayout2._paper.node(),
gd: gd
gd: gd,
inOut_bbox: bbox
});
pt.bbox = bbox[0];

trace._hasHoverLabel = true;
}
Expand Down
17 changes: 14 additions & 3 deletions src/traces/sunburst/fx.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ module.exports = function attachFxHandlers(sliceTop, entry, gd, cd, opts) {
var hoverinfo = Fx.castHoverinfo(traceNow, fullLayoutNow, ptNumber);
var separators = fullLayoutNow.separators;

var eventData;

if(hovertemplate || (hoverinfo && hoverinfo !== 'none' && hoverinfo !== 'skip')) {
var hoverCenterX;
var hoverCenterY;
Expand Down Expand Up @@ -125,9 +127,15 @@ module.exports = function attachFxHandlers(sliceTop, entry, gd, cd, opts) {
if(Lib.isValidTextValue(tx)) thisText.push(tx);
}

eventData = [makeEventData(pt, traceNow, opts.eventDataKeys)];

var hoverItems = {
trace: traceNow,
y: hoverCenterY,
_x0: pt._x0,
_x1: pt._x1,
_y0: pt._y0,
_y1: pt._y1,
text: thisText.join('<br>'),
name: (hovertemplate || hasFlag('name')) ? traceNow.name : undefined,
color: _cast('hoverlabel.bgcolor') || cdi.color,
Expand All @@ -139,7 +147,7 @@ module.exports = function attachFxHandlers(sliceTop, entry, gd, cd, opts) {
textAlign: _cast('hoverlabel.align'),
hovertemplate: hovertemplate,
hovertemplateLabels: hoverPt,
eventData: [makeEventData(pt, traceNow, opts.eventDataKeys)]
eventData: eventData
};

if(isSunburst) {
Expand All @@ -152,11 +160,14 @@ module.exports = function attachFxHandlers(sliceTop, entry, gd, cd, opts) {
hoverItems.idealAlign = hoverCenterX < 0 ? 'left' : 'right';
}

var bbox = [];
Fx.loneHover(hoverItems, {
container: fullLayoutNow._hoverlayer.node(),
outerContainer: fullLayoutNow._paper.node(),
gd: gd
gd: gd,
inOut_bbox: bbox
});
eventData[0].bbox = bbox[0];

trace._hasHoverLabel = true;
}
Expand All @@ -170,7 +181,7 @@ module.exports = function attachFxHandlers(sliceTop, entry, gd, cd, opts) {

trace._hasHoverEvent = true;
gd.emit('plotly_hover', {
points: [makeEventData(pt, traceNow, opts.eventDataKeys)],
points: eventData || [makeEventData(pt, traceNow, opts.eventDataKeys)],
event: d3.event
});
};
Expand Down
6 changes: 6 additions & 0 deletions src/traces/treemap/draw_ancestors.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ module.exports = function drawAncestors(gd, cd, entry, slices, opts) {
}

updateSlices.each(function(pt) {
// for bbox
pt._x0 = viewX(pt.x0);
pt._x1 = viewX(pt.x1);
pt._y0 = viewY(pt.y0);
pt._y1 = viewY(pt.y1);

pt._hoverX = viewX(pt.x1 - Math.min(width, height) / 2);
pt._hoverY = viewY(pt.y1 - height / 2);

Expand Down
6 changes: 6 additions & 0 deletions src/traces/treemap/draw_descendants.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ module.exports = function drawDescendants(gd, cd, entry, slices, opts) {
updateSlices.each(function(pt) {
var isHeader = helpers.isHeader(pt, trace);

// for bbox
pt._x0 = viewX(pt.x0);
pt._x1 = viewX(pt.x1);
pt._y0 = viewY(pt.y0);
pt._y1 = viewY(pt.y1);

pt._hoverX = viewX(pt.x1 - trace.marker.pad.r),
pt._hoverY = hasBottom ?
viewY(pt.y1 - trace.marker.pad.b / 2) :
Expand Down
11 changes: 10 additions & 1 deletion test/jasmine/tests/cartesian_interact_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2422,6 +2422,12 @@ describe('Cartesian plots with css transforms', function() {
}
};

var bbox = {
one: { x0: 20, x1: 180, y0: 273.33, y1: 273.33 },
two: { x0: 220, x1: 380, y0: 146.67, y1: 146.67 },
three: { x0: 420, x1: 580, y0: 20, y1: 20 }
};

[{
transform: 'scaleX(0.5)',
hovered: 1,
Expand All @@ -2436,12 +2442,14 @@ describe('Cartesian plots with css transforms', function() {
selected: {numPoints: 3, selectedLabels: ['one', 'two', 'three']}
}].forEach(function(t) {
var transform = t.transform;

it('hover behaves correctly after css transform: ' + transform, function(done) {
var _bboxRecordings = {};

function _hoverAndAssertEventOccurred(point, label) {
return _hover(point)
.then(function() {
expect(eventRecordings[label]).toBe(t.hovered);
expect(_bboxRecordings[label]).toEqual(bbox[label]);
})
.then(function() {
_unhover(point);
Expand All @@ -2454,6 +2462,7 @@ describe('Cartesian plots with css transforms', function() {

gd.on('plotly_hover', function(d) {
eventRecordings[d.points[0].x] = 1;
_bboxRecordings[d.points[0].x] = d.points[0].bbox;
});
})
.then(function() {_hoverAndAssertEventOccurred(points[0], xLabels[0]);})
Expand Down
Loading