Skip to content

Commit 71e24eb

Browse files
committed
Merge remote-tracking branch 'origin/master' into ticklabeloverflow
2 parents cd256f2 + 963f362 commit 71e24eb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+1019
-99
lines changed

src/components/fx/hover.js

+25-6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ var Drawing = require('../drawing');
1414
var Color = require('../color');
1515
var dragElement = require('../dragelement');
1616
var Axes = require('../../plots/cartesian/axes');
17+
var alignPeriod = require('../../plots/cartesian/align_period');
1718
var Registry = require('../../registry');
1819

1920
var helpers = require('./helpers');
@@ -779,8 +780,9 @@ function createHoverText(hoverData, opts, gd) {
779780
var c0 = hoverData[0];
780781
var xa = c0.xa;
781782
var ya = c0.ya;
782-
var commonAttr = hovermode.charAt(0) === 'y' ? 'yLabel' : 'xLabel';
783-
var t0 = c0[commonAttr];
783+
var axLetter = hovermode.charAt(0);
784+
var v0 = c0[axLetter + 'LabelVal'];
785+
var t0 = c0[axLetter + 'Label'];
784786
var t00 = (String(t0) || '').split(' ')[0];
785787
var outerContainerBB = outerContainer.node().getBoundingClientRect();
786788
var outerTop = outerContainerBB.top;
@@ -978,8 +980,25 @@ function createHoverText(hoverData, opts, gd) {
978980

979981
function filterClosePoints(hoverData) {
980982
return hoverData.filter(function(d) {
981-
return (d.zLabelVal !== undefined) ||
982-
(d[commonAttr] || '').split(' ')[0] === t00;
983+
if(d.zLabelVal !== undefined) return true;
984+
if((d[axLetter + 'Label'] || '').split(' ')[0] === t00) return true;
985+
if(d.trace[axLetter + 'period']) {
986+
var v = d[axLetter + 'LabelVal'];
987+
var ax = d[axLetter + 'a'];
988+
var trace = {};
989+
trace[axLetter + 'period'] = d.trace[axLetter + 'period'];
990+
trace[axLetter + 'period0'] = d.trace[axLetter + 'period0'];
991+
992+
trace[axLetter + 'periodalignment'] = 'start';
993+
var start = alignPeriod(trace, ax, axLetter, [v])[0];
994+
995+
trace[axLetter + 'periodalignment'] = 'end';
996+
var end = alignPeriod(trace, ax, axLetter, [v])[0];
997+
998+
if(v0 >= start && v0 < end) return true;
999+
}
1000+
1001+
return false;
9831002
});
9841003
}
9851004

@@ -1609,11 +1628,11 @@ function cleanPoint(d, hovermode) {
16091628

16101629
// and convert the x and y label values into formatted text
16111630
if(d.xLabelVal !== undefined) {
1612-
d.xLabel = ('xLabel' in d) ? d.xLabel : Axes.hoverLabelText(d.xa, d.xLabelVal);
1631+
d.xLabel = ('xLabel' in d) ? d.xLabel : Axes.hoverLabelText(d.xa, d.xLabelVal, trace.xhoverformat);
16131632
d.xVal = d.xa.c2d(d.xLabelVal);
16141633
}
16151634
if(d.yLabelVal !== undefined) {
1616-
d.yLabel = ('yLabel' in d) ? d.yLabel : Axes.hoverLabelText(d.ya, d.yLabelVal);
1635+
d.yLabel = ('yLabel' in d) ? d.yLabel : Axes.hoverLabelText(d.ya, d.yLabelVal, trace.yhoverformat);
16171636
d.yVal = d.ya.c2d(d.yLabelVal);
16181637
}
16191638

src/plots/cartesian/autorange.js

+46-37
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ function makePadFn(fullLayout, ax, max) {
217217

218218
var zero = 0;
219219
if(!isLinked(fullLayout, ax._id)) {
220-
zero = padInsideLabelsOnAnchorAxis(ax, max);
220+
zero = padInsideLabelsOnAnchorAxis(fullLayout, ax, max);
221221
}
222222
extrappad = Math.max(zero, extrappad);
223223

@@ -236,45 +236,54 @@ function makePadFn(fullLayout, ax, max) {
236236

237237
var TEXTPAD = 3;
238238

239-
function padInsideLabelsOnAnchorAxis(ax, max) {
239+
function padInsideLabelsOnAnchorAxis(fullLayout, ax, max) {
240240
var pad = 0;
241-
var anchorAxis = ax._anchorAxis || {};
242-
if((anchorAxis.ticklabelposition || '').indexOf('inside') !== -1) {
243-
// increase padding to make more room for inside tick labels of the counter axis
244-
if((
245-
!max && (
246-
anchorAxis.side === 'left' ||
247-
anchorAxis.side === 'bottom'
248-
)
249-
) || (
250-
max && (
251-
anchorAxis.side === 'top' ||
252-
anchorAxis.side === 'right'
253-
)
254-
)) {
255-
var isX = ax._id.charAt(0) === 'x';
256-
257-
if(anchorAxis._vals) {
258-
var rad = Lib.deg2rad(anchorAxis._tickAngles[anchorAxis._id + 'tick'] || 0);
259-
var cosA = Math.abs(Math.cos(rad));
260-
var sinA = Math.abs(Math.sin(rad));
261-
262-
// use bounding boxes
263-
anchorAxis._vals.forEach(function(t) {
264-
if(t.bb) {
265-
var w = 2 * TEXTPAD + t.bb.width;
266-
var h = 2 * TEXTPAD + t.bb.height;
267-
268-
pad = Math.max(pad, isX ?
269-
Math.max(w * cosA, h * sinA) :
270-
Math.max(h * cosA, w * sinA)
271-
);
241+
242+
var isX = ax._id.charAt(0) === 'x';
243+
244+
for(var subplot in fullLayout._plots) {
245+
var plotinfo = fullLayout._plots[subplot];
246+
247+
if(ax._id !== plotinfo.xaxis._id && ax._id !== plotinfo.yaxis._id) continue;
248+
249+
var anchorAxis = (isX ? plotinfo.yaxis : plotinfo.xaxis) || {};
250+
251+
if((anchorAxis.ticklabelposition || '').indexOf('inside') !== -1) {
252+
// increase padding to make more room for inside tick labels of the counter axis
253+
if((
254+
!max && (
255+
anchorAxis.side === 'left' ||
256+
anchorAxis.side === 'bottom'
257+
)
258+
) || (
259+
max && (
260+
anchorAxis.side === 'top' ||
261+
anchorAxis.side === 'right'
262+
)
263+
)) {
264+
if(anchorAxis._vals) {
265+
var rad = Lib.deg2rad(anchorAxis._tickAngles[anchorAxis._id + 'tick'] || 0);
266+
var cosA = Math.abs(Math.cos(rad));
267+
var sinA = Math.abs(Math.sin(rad));
268+
269+
// use bounding boxes
270+
for(var i = 0; i < anchorAxis._vals.length; i++) {
271+
var t = anchorAxis._vals[i];
272+
if(t.bb) {
273+
var w = 2 * TEXTPAD + t.bb.width;
274+
var h = 2 * TEXTPAD + t.bb.height;
275+
276+
pad = Math.max(pad, isX ?
277+
Math.max(w * cosA, h * sinA) :
278+
Math.max(h * cosA, w * sinA)
279+
);
280+
}
272281
}
273-
});
274-
}
282+
}
275283

276-
if(anchorAxis.ticks === 'inside' && anchorAxis.ticklabelposition === 'inside') {
277-
pad += anchorAxis.ticklen || 0;
284+
if(anchorAxis.ticks === 'inside' && anchorAxis.ticklabelposition === 'inside') {
285+
pad += anchorAxis.ticklen || 0;
286+
}
278287
}
279288
}
280289
}

src/plots/cartesian/axes.js

+21-10
Original file line numberDiff line numberDiff line change
@@ -1361,16 +1361,23 @@ axes.tickText = function(ax, x, hover, noSuffixPrefix) {
13611361
* log axes (where negative values can't be displayed but can appear in hover text)
13621362
*
13631363
* @param {object} ax: the axis to format text for
1364-
* @param {number} val: calcdata value to format
1365-
* @param {Optional(number)} val2: a second value to display
1364+
* @param {number or array of numbers} values: calcdata value(s) to format
1365+
* @param {Optional(string)} hoverformat: trace (x|y)hoverformat to override axis.hoverformat
13661366
*
13671367
* @returns {string} `val` formatted as a string appropriate to this axis, or
1368-
* `val` and `val2` as a range (ie '<val> - <val2>') if `val2` is provided and
1369-
* it's different from `val`.
1368+
* first value and second value as a range (ie '<val1> - <val2>') if the second value is provided and
1369+
* it's different from the first value.
13701370
*/
1371-
axes.hoverLabelText = function(ax, val, val2) {
1372-
if(val2 !== BADNUM && val2 !== val) {
1373-
return axes.hoverLabelText(ax, val) + ' - ' + axes.hoverLabelText(ax, val2);
1371+
axes.hoverLabelText = function(ax, values, hoverformat) {
1372+
if(hoverformat) ax = Lib.extendFlat({}, ax, {hoverformat: hoverformat});
1373+
1374+
var val = Array.isArray(values) ? values[0] : values;
1375+
var val2 = Array.isArray(values) ? values[1] : undefined;
1376+
if(val2 !== undefined && val2 !== val) {
1377+
return (
1378+
axes.hoverLabelText(ax, val, hoverformat) + ' - ' +
1379+
axes.hoverLabelText(ax, val2, hoverformat)
1380+
);
13741381
}
13751382

13761383
var logOffScale = (ax.type === 'log' && val <= 0);
@@ -3139,8 +3146,10 @@ axes.drawLabels = function(gd, ax, opts) {
31393146
TICK_PATH,
31403147
TICK_TEXT
31413148
]).forEach(function(e) {
3142-
var isTickText = e.K === 'tick' && e.L === 'text';
3143-
if(isTickText && ax.ticklabelmode === 'period') return;
3149+
var isPeriodLabel =
3150+
e.K === 'tick' &&
3151+
e.L === 'text' &&
3152+
ax.ticklabelmode === 'period';
31443153

31453154
var sel;
31463155
if(e.K === ZERO_PATH.K) sel = opts.plotinfo.zerolinelayer.selectAll('.' + ax._id + 'zl');
@@ -3152,7 +3161,9 @@ axes.drawLabels = function(gd, ax, opts) {
31523161
if(e.L) w = w.selectAll(e.L);
31533162

31543163
w.each(function(d) {
3155-
var q = ax.l2p(d.x) + ax._offset;
3164+
var q = ax.l2p(
3165+
isPeriodLabel ? getPosX(d) : d.x
3166+
) + ax._offset;
31563167

31573168
var t = d3.select(this);
31583169
if(q < ax._visibleLabelMax && q > ax._visibleLabelMin) {

src/plots/cartesian/layout_attributes.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ var dash = require('../../components/drawing/attributes').dash;
66
var extendFlat = require('../../lib/extend').extendFlat;
77
var templatedArray = require('../../plot_api/plot_template').templatedArray;
88

9-
var FORMAT_LINK = require('../../constants/docs').FORMAT_LINK;
10-
var DATE_FORMAT_LINK = require('../../constants/docs').DATE_FORMAT_LINK;
9+
var docs = require('../../constants/docs');
10+
var FORMAT_LINK = docs.FORMAT_LINK;
11+
var DATE_FORMAT_LINK = docs.DATE_FORMAT_LINK;
12+
1113
var ONEDAY = require('../../constants/numerical').ONEDAY;
1214
var constants = require('./constants');
1315
var HOUR = constants.HOUR_PATTERN;

src/plots/gl3d/scene.js

+14-10
Original file line numberDiff line numberDiff line change
@@ -322,10 +322,14 @@ proto.render = function() {
322322
if(trace.setContourLevels) trace.setContourLevels();
323323
}
324324

325-
function formatter(axisName, val) {
326-
var axis = scene.fullSceneLayout[axisName];
325+
function formatter(axLetter, val, hoverformat) {
326+
var ax = scene.fullSceneLayout[axLetter + 'axis'];
327327

328-
return Axes.tickText(axis, axis.d2l(val), 'hover').text;
328+
if(ax.type !== 'log') {
329+
val = ax.d2l(val);
330+
}
331+
332+
return Axes.hoverLabelText(ax, val, hoverformat);
329333
}
330334

331335
var oldEventData;
@@ -337,9 +341,9 @@ proto.render = function() {
337341
var ptNumber = selection.index;
338342

339343
var labels = {
340-
xLabel: formatter('xaxis', selection.traceCoordinate[0]),
341-
yLabel: formatter('yaxis', selection.traceCoordinate[1]),
342-
zLabel: formatter('zaxis', selection.traceCoordinate[2])
344+
xLabel: formatter('x', selection.traceCoordinate[0], trace.xhoverformat),
345+
yLabel: formatter('y', selection.traceCoordinate[1], trace.yhoverformat),
346+
zLabel: formatter('z', selection.traceCoordinate[2], trace.zhoverformat)
343347
};
344348

345349
var hoverinfo = Fx.castHoverinfo(traceNow, scene.fullLayout, ptNumber);
@@ -358,17 +362,17 @@ proto.render = function() {
358362
var vectorTx = [];
359363

360364
if(trace.type === 'cone' || trace.type === 'streamtube') {
361-
labels.uLabel = formatter('xaxis', selection.traceCoordinate[3]);
365+
labels.uLabel = formatter('x', selection.traceCoordinate[3], trace.uhoverformat);
362366
if(isHoverinfoAll || hoverinfoParts.indexOf('u') !== -1) {
363367
vectorTx.push('u: ' + labels.uLabel);
364368
}
365369

366-
labels.vLabel = formatter('yaxis', selection.traceCoordinate[4]);
370+
labels.vLabel = formatter('y', selection.traceCoordinate[4], trace.vhoverformat);
367371
if(isHoverinfoAll || hoverinfoParts.indexOf('v') !== -1) {
368372
vectorTx.push('v: ' + labels.vLabel);
369373
}
370374

371-
labels.wLabel = formatter('zaxis', selection.traceCoordinate[5]);
375+
labels.wLabel = formatter('z', selection.traceCoordinate[5], trace.whoverformat);
372376
if(isHoverinfoAll || hoverinfoParts.indexOf('w') !== -1) {
373377
vectorTx.push('w: ' + labels.wLabel);
374378
}
@@ -388,7 +392,7 @@ proto.render = function() {
388392
}
389393
tx = vectorTx.join('<br>');
390394
} else if(trace.type === 'isosurface' || trace.type === 'volume') {
391-
labels.valueLabel = Axes.tickText(scene._mockAxis, scene._mockAxis.d2l(selection.traceCoordinate[3]), 'hover').text;
395+
labels.valueLabel = Axes.hoverLabelText(scene._mockAxis, scene._mockAxis.d2l(selection.traceCoordinate[3]), trace.valuehoverformat);
392396
vectorTx.push('value: ' + labels.valueLabel);
393397
if(selection.textLabel) {
394398
vectorTx.push(selection.textLabel);

src/plots/hoverformat_attributes.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
'use strict';
2+
3+
var docs = require('../constants/docs');
4+
var FORMAT_LINK = docs.FORMAT_LINK;
5+
var DATE_FORMAT_LINK = docs.DATE_FORMAT_LINK;
6+
7+
module.exports = function axisHoverFormat(x, noDates) {
8+
return {
9+
valType: 'string',
10+
dflt: '',
11+
editType: 'none',
12+
description: [
13+
'Sets the hover text formatting rule for `' + x + '`',
14+
' using d3 formatting mini-languages which are very similar to those in Python.',
15+
'See: ' + FORMAT_LINK + (
16+
noDates ?
17+
'' :
18+
' And for dates see: ' + DATE_FORMAT_LINK
19+
),
20+
'By default the values are formatted using ' + (
21+
noDates ?
22+
'generic number format' :
23+
('`' + x + 'axis.hoverformat`')
24+
) + '.',
25+
].join(' ')
26+
};
27+
};

src/plots/template_attributes.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
'use strict';
22

3-
var FORMAT_LINK = require('../constants/docs').FORMAT_LINK;
4-
var DATE_FORMAT_LINK = require('../constants/docs').DATE_FORMAT_LINK;
3+
var docs = require('../constants/docs');
4+
var FORMAT_LINK = docs.FORMAT_LINK;
5+
var DATE_FORMAT_LINK = docs.DATE_FORMAT_LINK;
56

67
var templateFormatStringDescription = [
78
'Variables are inserted using %{variable}, for example "y: %{y}".',

src/traces/bar/attributes.js

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
var scatterAttrs = require('../scatter/attributes');
4+
var axisHoverFormat = require('../../plots/hoverformat_attributes');
45
var hovertemplateAttrs = require('../../plots/template_attributes').hovertemplateAttrs;
56
var texttemplateAttrs = require('../../plots/template_attributes').texttemplateAttrs;
67
var colorScaleAttrs = require('../../components/colorscale/attributes');
@@ -104,6 +105,8 @@ module.exports = {
104105
yperiod0: scatterAttrs.yperiod0,
105106
xperiodalignment: scatterAttrs.xperiodalignment,
106107
yperiodalignment: scatterAttrs.yperiodalignment,
108+
xhoverformat: axisHoverFormat('x'),
109+
yhoverformat: axisHoverFormat('y'),
107110

108111
text: scatterAttrs.text,
109112
texttemplate: texttemplateAttrs({editType: 'plot'}, {

src/traces/bar/defaults.js

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
2424
}
2525

2626
handlePeriodDefaults(traceIn, traceOut, layout, coerce);
27+
coerce('xhoverformat');
28+
coerce('yhoverformat');
2729

2830
coerce('orientation', (traceOut.x && !traceOut.y) ? 'h' : 'v');
2931
coerce('base');

src/traces/bar/hover.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,9 @@ function hoverOnBars(pointData, xval, yval, hovermode) {
170170
var hasPeriod = di.orig_p !== undefined;
171171
pointData[posLetter + 'LabelVal'] = hasPeriod ? di.orig_p : di.p;
172172

173-
pointData.labelLabel = hoverLabelText(pa, pointData[posLetter + 'LabelVal']);
174-
pointData.valueLabel = hoverLabelText(sa, pointData[sizeLetter + 'LabelVal']);
175-
pointData.baseLabel = hoverLabelText(sa, di.b);
173+
pointData.labelLabel = hoverLabelText(pa, pointData[posLetter + 'LabelVal'], trace[posLetter + 'hoverformat']);
174+
pointData.valueLabel = hoverLabelText(sa, pointData[sizeLetter + 'LabelVal'], trace[sizeLetter + 'hoverformat']);
175+
pointData.baseLabel = hoverLabelText(sa, di.b, trace[sizeLetter + 'hoverformat']);
176176

177177
// spikelines always want "closest" distance regardless of hovermode
178178
pointData.spikeDistance = (thisBarSizeFn(di) + thisBarPositionFn(di)) / 2;

src/traces/box/attributes.js

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
var scatterAttrs = require('../scatter/attributes');
44
var barAttrs = require('../bar/attributes');
55
var colorAttrs = require('../../components/color/attributes');
6+
var axisHoverFormat = require('../../plots/hoverformat_attributes');
67
var hovertemplateAttrs = require('../../plots/template_attributes').hovertemplateAttrs;
78
var extendFlat = require('../../lib/extend').extendFlat;
89

@@ -70,6 +71,8 @@ module.exports = {
7071
yperiod0: scatterAttrs.yperiod0,
7172
xperiodalignment: scatterAttrs.xperiodalignment,
7273
yperiodalignment: scatterAttrs.yperiodalignment,
74+
xhoverformat: axisHoverFormat('x'),
75+
yhoverformat: axisHoverFormat('y'),
7376

7477
name: {
7578
valType: 'string',

src/traces/box/defaults.js

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
1717
if(traceOut.visible === false) return;
1818

1919
handlePeriodDefaults(traceIn, traceOut, layout, coerce);
20+
coerce('xhoverformat');
21+
coerce('yhoverformat');
2022

2123
var hasPreCompStats = traceOut._hasPreCompStats;
2224

0 commit comments

Comments
 (0)