Skip to content

Commit d8cff96

Browse files
authored
Merge pull request #2094 from plotly/boxpoints-hover-select
Box points hover & select
2 parents fff9c6e + a3ec75a commit d8cff96

File tree

19 files changed

+959
-386
lines changed

19 files changed

+959
-386
lines changed

src/components/fx/helpers.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ exports.p2c = function p2c(axArray, v) {
3434
};
3535

3636
exports.getDistanceFunction = function getDistanceFunction(mode, dx, dy, dxy) {
37-
if(mode === 'closest') return dxy || quadrature(dx, dy);
37+
if(mode === 'closest') return dxy || exports.quadrature(dx, dy);
3838
return mode === 'x' ? dx : dy;
3939
};
4040

@@ -77,13 +77,13 @@ exports.inbox = function inbox(v0, v1) {
7777
return Infinity;
7878
};
7979

80-
function quadrature(dx, dy) {
80+
exports.quadrature = function quadrature(dx, dy) {
8181
return function(di) {
8282
var x = dx(di),
8383
y = dy(di);
8484
return Math.sqrt(x * x + y * y);
8585
};
86-
}
86+
};
8787

8888
/** Appends values inside array attributes corresponding to given point number
8989
*

src/components/fx/hover.js

+2-24
Original file line numberDiff line numberDiff line change
@@ -1065,36 +1065,14 @@ function cleanPoint(d, hovermode) {
10651065

10661066
// and convert the x and y label values into objects
10671067
// formatted as text, with font info
1068-
var logOffScale;
10691068
if(d.xLabelVal !== undefined) {
1070-
logOffScale = (d.xa.type === 'log' && d.xLabelVal <= 0);
1071-
var xLabelObj = Axes.tickText(d.xa,
1072-
d.xa.c2l(logOffScale ? -d.xLabelVal : d.xLabelVal), 'hover');
1073-
if(logOffScale) {
1074-
if(d.xLabelVal === 0) d.xLabel = '0';
1075-
else d.xLabel = '-' + xLabelObj.text;
1076-
}
1077-
// TODO: should we do something special if the axis calendar and
1078-
// the data calendar are different? Somehow display both dates with
1079-
// their system names? Right now it will just display in the axis calendar
1080-
// but users could add the other one as text.
1081-
else d.xLabel = xLabelObj.text;
1069+
d.xLabel = ('xLabel' in d) ? d.xLabel : Axes.hoverLabelText(d.xa, d.xLabelVal);
10821070
d.xVal = d.xa.c2d(d.xLabelVal);
10831071
}
1084-
10851072
if(d.yLabelVal !== undefined) {
1086-
logOffScale = (d.ya.type === 'log' && d.yLabelVal <= 0);
1087-
var yLabelObj = Axes.tickText(d.ya,
1088-
d.ya.c2l(logOffScale ? -d.yLabelVal : d.yLabelVal), 'hover');
1089-
if(logOffScale) {
1090-
if(d.yLabelVal === 0) d.yLabel = '0';
1091-
else d.yLabel = '-' + yLabelObj.text;
1092-
}
1093-
// TODO: see above TODO
1094-
else d.yLabel = yLabelObj.text;
1073+
d.yLabel = ('yLabel' in d) ? d.yLabel : Axes.hoverLabelText(d.ya, d.yLabelVal);
10951074
d.yVal = d.ya.c2d(d.yLabelVal);
10961075
}
1097-
10981076
if(d.zLabelVal !== undefined) d.zLabel = String(d.zLabelVal);
10991077

11001078
// for box means and error bars, add the range to the label

src/components/fx/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ module.exports = {
3535
getDistanceFunction: helpers.getDistanceFunction,
3636
getClosest: helpers.getClosest,
3737
inbox: helpers.inbox,
38+
quadrature: helpers.quadrature,
3839
appendArrayPointValue: helpers.appendArrayPointValue,
3940

4041
castHoverOption: castHoverOption,

src/components/modebar/manage.js

+4
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,10 @@ function isSelectable(fullData) {
179179
if(scatterSubTypes.hasMarkers(trace) || scatterSubTypes.hasText(trace)) {
180180
selectable = true;
181181
}
182+
} else if(Registry.traceIs(trace, 'box')) {
183+
if(trace.boxpoints === 'all') {
184+
selectable = true;
185+
}
182186
}
183187
// assume that in general if the trace module has selectPoints,
184188
// then it's selectable. Scatter is an exception to this because it must

src/plots/cartesian/axes.js

+15
Original file line numberDiff line numberDiff line change
@@ -1216,6 +1216,21 @@ axes.tickText = function(ax, x, hover) {
12161216
return out;
12171217
};
12181218

1219+
axes.hoverLabelText = function(ax, val) {
1220+
var logOffScale = (ax.type === 'log' && val <= 0);
1221+
var tx = axes.tickText(ax, ax.c2l(logOffScale ? -val : val), 'hover').text;
1222+
1223+
if(logOffScale) {
1224+
return val === 0 ? '0' : '-' + tx;
1225+
}
1226+
1227+
// TODO: should we do something special if the axis calendar and
1228+
// the data calendar are different? Somehow display both dates with
1229+
// their system names? Right now it will just display in the axis calendar
1230+
// but users could add the other one as text.
1231+
return tx;
1232+
};
1233+
12191234
function tickTextObj(ax, x, text) {
12201235
var tf = ax.tickfont || {};
12211236

src/traces/bar/select.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ var DESELECTDIM = require('../../constants/interactions').DESELECTDIM;
1212

1313
module.exports = function selectPoints(searchInfo, polygon) {
1414
var cd = searchInfo.cd;
15+
var xa = searchInfo.xaxis;
16+
var ya = searchInfo.yaxis;
1517
var selection = [];
1618
var node3 = cd[0].node3;
1719
var i;
@@ -28,8 +30,8 @@ module.exports = function selectPoints(searchInfo, polygon) {
2830
if(polygon.contains(di.ct)) {
2931
selection.push({
3032
pointNumber: i,
31-
x: di.x,
32-
y: di.y
33+
x: xa.c2d(di.x),
34+
y: ya.c2d(di.y)
3335
});
3436
di.dim = 0;
3537
} else {

src/traces/box/attributes.js

+28-6
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@ var scatterAttrs = require('../scatter/attributes');
1212
var colorAttrs = require('../../components/color/attributes');
1313
var extendFlat = require('../../lib/extend').extendFlat;
1414

15-
var scatterMarkerAttrs = scatterAttrs.marker,
16-
scatterMarkerLineAttrs = scatterMarkerAttrs.line;
17-
15+
var scatterMarkerAttrs = scatterAttrs.marker;
16+
var scatterMarkerLineAttrs = scatterMarkerAttrs.line;
1817

1918
module.exports = {
2019
y: {
@@ -63,6 +62,16 @@ module.exports = {
6362
'missing and the position axis is categorical'
6463
].join(' ')
6564
},
65+
text: extendFlat({}, scatterAttrs.text, {
66+
description: [
67+
'Sets the text elements associated with each sample value.',
68+
'If a single string, the same string appears over',
69+
'all the data points.',
70+
'If an array of string, the items are mapped in order to the',
71+
'this trace\'s (x,y) coordinates.',
72+
'To be seen, trace `hoverinfo` must contain a *text* flag.'
73+
].join(' ')
74+
}),
6675
whiskerwidth: {
6776
valType: 'number',
6877
min: 0,
@@ -159,9 +168,11 @@ module.exports = {
159168
{arrayOk: false, editType: 'style'}),
160169
line: {
161170
color: extendFlat({}, scatterMarkerLineAttrs.color,
162-
{arrayOk: false, dflt: colorAttrs.defaultLine, editType: 'style'}),
171+
{arrayOk: false, dflt: colorAttrs.defaultLine, editType: 'style'}
172+
),
163173
width: extendFlat({}, scatterMarkerLineAttrs.width,
164-
{arrayOk: false, dflt: 0, editType: 'style'}),
174+
{arrayOk: false, dflt: 0, editType: 'style'}
175+
),
165176
outliercolor: {
166177
valType: 'color',
167178
role: 'style',
@@ -202,5 +213,16 @@ module.exports = {
202213
},
203214
editType: 'plot'
204215
},
205-
fillcolor: scatterAttrs.fillcolor
216+
fillcolor: scatterAttrs.fillcolor,
217+
hoveron: {
218+
valType: 'flaglist',
219+
flags: ['boxes', 'points'],
220+
dflt: 'boxes+points',
221+
role: 'info',
222+
editType: 'style',
223+
description: [
224+
'Do the hover effects highlight individual boxes ',
225+
'or sample points or both?'
226+
].join(' ')
227+
}
206228
};

0 commit comments

Comments
 (0)