Skip to content

Commit ccf1a76

Browse files
committed
update histograms to include input point numbers in events and smart ranges in hover labels
1 parent 6dc9904 commit ccf1a76

File tree

6 files changed

+340
-84
lines changed

6 files changed

+340
-84
lines changed

src/components/fx/hover.js

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,14 +1063,13 @@ function cleanPoint(d, hovermode) {
10631063
d.y0 = Lib.constrain(d.y0, 0, d.ya._length);
10641064
d.y1 = Lib.constrain(d.y1, 0, d.ya._length);
10651065

1066-
// and convert the x and y label values into objects
1067-
// formatted as text, with font info
1066+
// and convert the x and y label values into formatted text
10681067
if(d.xLabelVal !== undefined) {
1069-
d.xLabel = ('xLabel' in d) ? d.xLabel : Axes.hoverLabelText(d.xa, d.xLabelVal);
1068+
d.xLabel = ('xLabel' in d) ? d.xLabel : getDimText(d, 'x');
10701069
d.xVal = d.xa.c2d(d.xLabelVal);
10711070
}
10721071
if(d.yLabelVal !== undefined) {
1073-
d.yLabel = ('yLabel' in d) ? d.yLabel : Axes.hoverLabelText(d.ya, d.yLabelVal);
1072+
d.yLabel = ('yLabel' in d) ? d.yLabel : getDimText(d, 'y');
10741073
d.yVal = d.ya.c2d(d.yLabelVal);
10751074
}
10761075
if(d.zLabelVal !== undefined) d.zLabel = String(d.zLabelVal);
@@ -1113,6 +1112,24 @@ function cleanPoint(d, hovermode) {
11131112
return d;
11141113
}
11151114

1115+
// get text for either a value range (val0 .. val1) if it exists,
1116+
// or single value (val) if the range does not exist
1117+
function getDimText(d, axLetter) {
1118+
var val = d[axLetter + 'LabelVal'];
1119+
var val0 = d[axLetter + 'LabelVal0'];
1120+
var val1 = d[axLetter + 'LabelVal1'];
1121+
var ax = d[axLetter + 'a'];
1122+
1123+
if(val0 !== undefined && val1 !== undefined) {
1124+
var text0 = Axes.hoverLabelText(ax, val0);
1125+
var text1 = Axes.hoverLabelText(ax, val1);
1126+
1127+
if(text0 === text1) return text0;
1128+
return text0 + ' - ' + text1;
1129+
}
1130+
return Axes.hoverLabelText(ax, val);
1131+
}
1132+
11161133
function createSpikelines(hoverData, opts) {
11171134
var hovermode = opts.hovermode;
11181135
var container = opts.container;

src/traces/bar/hover.js

Lines changed: 54 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -18,37 +18,13 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
1818
var cd = pointData.cd;
1919
var trace = cd[0].trace;
2020
var t = cd[0].t;
21-
var xa = pointData.xa;
22-
var ya = pointData.ya;
2321

24-
var posVal, thisBarMinPos, thisBarMaxPos, minPos, maxPos, dx, dy;
22+
var posVal, sizeVal, posLetter, sizeLetter, dx, dy;
2523

26-
var positionFn = function(di) {
27-
return Fx.inbox(minPos(di) - posVal, maxPos(di) - posVal);
28-
};
29-
30-
if(trace.orientation === 'h') {
31-
posVal = yval;
32-
thisBarMinPos = function(di) { return di.y - di.w / 2; };
33-
thisBarMaxPos = function(di) { return di.y + di.w / 2; };
34-
dx = function(di) {
35-
// add a gradient so hovering near the end of a
36-
// bar makes it a little closer match
37-
return Fx.inbox(di.b - xval, di.x - xval) + (di.x - xval) / (di.x - di.b);
38-
};
39-
dy = positionFn;
40-
}
41-
else {
42-
posVal = xval;
43-
thisBarMinPos = function(di) { return di.x - di.w / 2; };
44-
thisBarMaxPos = function(di) { return di.x + di.w / 2; };
45-
dy = function(di) {
46-
return Fx.inbox(di.b - yval, di.y - yval) + (di.y - yval) / (di.y - di.b);
47-
};
48-
dx = positionFn;
49-
}
24+
function thisBarMinPos(di) { return di[posLetter] - di.w / 2; }
25+
function thisBarMaxPos(di) { return di[posLetter] + di.w / 2; }
5026

51-
minPos = (hovermode === 'closest') ?
27+
var minPos = (hovermode === 'closest') ?
5228
thisBarMinPos :
5329
function(di) {
5430
/*
@@ -60,44 +36,74 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
6036
return Math.min(thisBarMinPos(di), di.p - t.bargroupwidth / 2);
6137
};
6238

63-
maxPos = (hovermode === 'closest') ?
39+
var maxPos = (hovermode === 'closest') ?
6440
thisBarMaxPos :
6541
function(di) {
6642
return Math.max(thisBarMaxPos(di), di.p + t.bargroupwidth / 2);
6743
};
6844

45+
function positionFn(di) {
46+
return Fx.inbox(minPos(di) - posVal, maxPos(di) - posVal);
47+
}
48+
49+
function sizeFn(di) {
50+
// add a gradient so hovering near the end of a
51+
// bar makes it a little closer match
52+
return Fx.inbox(di.b - sizeVal, di[sizeLetter] - sizeVal) +
53+
(di[sizeLetter] - sizeVal) / (di[sizeLetter] - di.b);
54+
}
55+
56+
if(trace.orientation === 'h') {
57+
posVal = yval;
58+
sizeVal = xval;
59+
posLetter = 'y';
60+
sizeLetter = 'x';
61+
dx = sizeFn;
62+
dy = positionFn;
63+
}
64+
else {
65+
posVal = xval;
66+
sizeVal = yval;
67+
posLetter = 'x';
68+
sizeLetter = 'y';
69+
dy = sizeFn;
70+
dx = positionFn;
71+
}
72+
73+
var pa = pointData[posLetter + 'a'];
74+
var sa = pointData[sizeLetter + 'a'];
75+
6976
var distfn = Fx.getDistanceFunction(hovermode, dx, dy);
7077
Fx.getClosest(cd, distfn, pointData);
7178

7279
// skip the rest (for this trace) if we didn't find a close point
7380
if(pointData.index === false) return;
7481

7582
// the closest data point
76-
var index = pointData.index,
77-
di = cd[index],
78-
mc = di.mcc || trace.marker.color,
79-
mlc = di.mlcc || trace.marker.line.color,
80-
mlw = di.mlw || trace.marker.line.width;
83+
var index = pointData.index;
84+
var di = cd[index];
85+
var mc = di.mcc || trace.marker.color;
86+
var mlc = di.mlcc || trace.marker.line.color;
87+
var mlw = di.mlw || trace.marker.line.width;
88+
8189
if(Color.opacity(mc)) pointData.color = mc;
8290
else if(Color.opacity(mlc) && mlw) pointData.color = mlc;
8391

8492
var size = (trace.base) ? di.b + di.s : di.s;
85-
if(trace.orientation === 'h') {
86-
pointData.x0 = pointData.x1 = xa.c2p(di.x, true);
87-
pointData.xLabelVal = size;
88-
89-
pointData.y0 = ya.c2p(minPos(di), true);
90-
pointData.y1 = ya.c2p(maxPos(di), true);
91-
pointData.yLabelVal = di.p;
93+
pointData[sizeLetter + '0'] = pointData[sizeLetter + '1'] = sa.c2p(di[sizeLetter], true);
94+
pointData[sizeLetter + 'LabelVal'] = size;
95+
96+
pointData[posLetter + '0'] = pa.c2p(minPos(di), true);
97+
pointData[posLetter + '1'] = pa.c2p(maxPos(di), true);
98+
pointData[posLetter + 'LabelVal'] = di.p;
99+
// for histograms
100+
if(di.p0 !== undefined && di.p1 !== undefined) {
101+
pointData[posLetter + 'LabelVal0'] = di.p0;
102+
pointData[posLetter + 'LabelVal1'] = di.p1;
92103
}
93-
else {
94-
pointData.y0 = pointData.y1 = ya.c2p(di.y, true);
95-
pointData.yLabelVal = size;
96104

97-
pointData.x0 = xa.c2p(minPos(di), true);
98-
pointData.x1 = xa.c2p(maxPos(di), true);
99-
pointData.xLabelVal = di.p;
100-
}
105+
// for histograms
106+
if(di.pts) pointData.pts = di.pts;
101107

102108
fillHoverText(di, trace, pointData);
103109
ErrorBars.hoverInfo(di, trace, pointData);

0 commit comments

Comments
 (0)