Skip to content

Commit e341edd

Browse files
authored
Merge pull request #2247 from apalchys/crossline
Add spikelines
2 parents d20d344 + f56413a commit e341edd

File tree

11 files changed

+788
-131
lines changed

11 files changed

+788
-131
lines changed

src/components/fx/hover.js

+271-86
Large diffs are not rendered by default.

src/components/fx/layout_attributes.js

+22-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,28 @@ module.exports = {
3838
editType: 'modebar',
3939
description: 'Determines the mode of hover interactions.'
4040
},
41-
41+
hoverdistance: {
42+
valType: 'integer',
43+
min: -1,
44+
dflt: 20,
45+
role: 'info',
46+
editType: 'none',
47+
description: [
48+
'Sets the default distance (in pixels) to look for data',
49+
'to add hover labels (-1 means no cutoff, 0 means no looking for data)'
50+
].join(' ')
51+
},
52+
spikedistance: {
53+
valType: 'integer',
54+
min: -1,
55+
dflt: 20,
56+
role: 'info',
57+
editType: 'none',
58+
description: [
59+
'Sets the default distance (in pixels) to look for data to draw',
60+
'spikelines to (-1 means no cutoff, 0 means no looking for data).'
61+
].join(' ')
62+
},
4263
hoverlabel: {
4364
bgcolor: {
4465
valType: 'color',

src/components/fx/layout_defaults.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
2727
}
2828
else hovermodeDflt = 'closest';
2929

30-
coerce('hovermode', hovermodeDflt);
30+
var hoverMode = coerce('hovermode', hovermodeDflt);
31+
if(hoverMode) {
32+
coerce('hoverdistance');
33+
coerce('spikedistance');
34+
}
3135

3236
// if only mapbox or geo subplots is present on graph,
3337
// reset 'zoom' dragmode to 'pan' until 'zoom' is implemented,

src/components/modebar/buttons.js

+9-14
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ function handleCartesian(gd, ev) {
181181
var fullLayout = gd._fullLayout;
182182
var aobj = {};
183183
var axList = axisIds.list(gd, null, true);
184-
var allEnabled = 'on';
184+
var allSpikesEnabled = 'on';
185185

186186
var ax, i;
187187

@@ -209,8 +209,8 @@ function handleCartesian(gd, ev) {
209209
}
210210
if(ax._showSpikeInitial !== undefined) {
211211
aobj[axName + '.showspikes'] = ax._showSpikeInitial;
212-
if(allEnabled === 'on' && !ax._showSpikeInitial) {
213-
allEnabled = 'off';
212+
if(allSpikesEnabled === 'on' && !ax._showSpikeInitial) {
213+
allSpikesEnabled = 'off';
214214
}
215215
}
216216
}
@@ -230,24 +230,21 @@ function handleCartesian(gd, ev) {
230230
}
231231
}
232232
}
233-
fullLayout._cartesianSpikesEnabled = allEnabled;
233+
fullLayout._cartesianSpikesEnabled = allSpikesEnabled;
234234
}
235235
else {
236236
// if ALL traces have orientation 'h', 'hovermode': 'x' otherwise: 'y'
237237
if(astr === 'hovermode' && (val === 'x' || val === 'y')) {
238238
val = fullLayout._isHoriz ? 'y' : 'x';
239239
button.setAttribute('data-val', val);
240-
if(val !== 'closest') {
241-
fullLayout._cartesianSpikesEnabled = 'off';
242-
}
243240
} else if(astr === 'hovermode' && val === 'closest') {
244241
for(i = 0; i < axList.length; i++) {
245242
ax = axList[i];
246-
if(allEnabled === 'on' && !ax.showspikes) {
247-
allEnabled = 'off';
243+
if(allSpikesEnabled === 'on' && !ax.showspikes) {
244+
allSpikesEnabled = 'off';
248245
}
249246
}
250-
fullLayout._cartesianSpikesEnabled = allEnabled;
247+
fullLayout._cartesianSpikesEnabled = allSpikesEnabled;
251248
}
252249

253250
aobj[astr] = val;
@@ -551,12 +548,10 @@ modeBarButtons.toggleSpikelines = {
551548
click: function(gd) {
552549
var fullLayout = gd._fullLayout;
553550

554-
fullLayout._cartesianSpikesEnabled = fullLayout.hovermode === 'closest' ?
555-
(fullLayout._cartesianSpikesEnabled === 'on' ? 'off' : 'on') : 'on';
551+
fullLayout._cartesianSpikesEnabled = fullLayout._cartesianSpikesEnabled === 'on' ? 'off' : 'on';
556552

557553
var aobj = setSpikelineVisibility(gd);
558554

559-
aobj.hovermode = 'closest';
560555
Plotly.relayout(gd, aobj);
561556
}
562557
};
@@ -571,7 +566,7 @@ function setSpikelineVisibility(gd) {
571566
for(var i = 0; i < axList.length; i++) {
572567
ax = axList[i];
573568
axName = ax._name;
574-
aobj[axName + '.showspikes'] = fullLayout._cartesianSpikesEnabled === 'on' ? true : false;
569+
aobj[axName + '.showspikes'] = fullLayout._cartesianSpikesEnabled === 'on' ? true : ax._showSpikeInitial;
575570
}
576571

577572
return aobj;

src/plots/cartesian/axes.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ axes.saveRangeInitial = function(gd, overwrite) {
398398
axes.saveShowSpikeInitial = function(gd, overwrite) {
399399
var axList = axes.list(gd, '', true),
400400
hasOneAxisChanged = false,
401-
allEnabled = 'on';
401+
allSpikesEnabled = 'on';
402402

403403
for(var i = 0; i < axList.length; i++) {
404404
var ax = axList[i];
@@ -415,11 +415,11 @@ axes.saveShowSpikeInitial = function(gd, overwrite) {
415415
hasOneAxisChanged = true;
416416
}
417417

418-
if(allEnabled === 'on' && !ax.showspikes) {
419-
allEnabled = 'off';
418+
if(allSpikesEnabled === 'on' && !ax.showspikes) {
419+
allSpikesEnabled = 'off';
420420
}
421421
}
422-
gd._fullLayout._cartesianSpikesEnabled = allEnabled;
422+
gd._fullLayout._cartesianSpikesEnabled = allSpikesEnabled;
423423
return hasOneAxisChanged;
424424
};
425425

@@ -2168,7 +2168,7 @@ axes.doTicks = function(gd, axid, skipTitle) {
21682168
top: pos,
21692169
bottom: pos,
21702170
left: ax._offset,
2171-
rigth: ax._offset + ax._length,
2171+
right: ax._offset + ax._length,
21722172
width: ax._length,
21732173
height: 0
21742174
};

src/plots/cartesian/layout_attributes.js

+8
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,14 @@ module.exports = {
387387
'plotted on'
388388
].join(' ')
389389
},
390+
spikesnap: {
391+
valType: 'enumerated',
392+
values: ['data', 'cursor'],
393+
dflt: 'data',
394+
role: 'style',
395+
editType: 'none',
396+
description: 'Determines whether spikelines are stuck to the cursor or to the closest datapoints.'
397+
},
390398
tickfont: fontAttrs({
391399
editType: 'ticks',
392400
description: 'Sets the tick font.'

src/plots/cartesian/layout_defaults.js

+17-6
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
8989
return Lib.coerce(axLayoutIn, axLayoutOut, layoutAttributes, attr, dflt);
9090
}
9191

92+
function coerce2(attr, dflt) {
93+
return Lib.coerce2(axLayoutIn, axLayoutOut, layoutAttributes, attr, dflt);
94+
}
95+
9296
function getCounterAxes(axLetter) {
9397
return (axLetter === 'x') ? yIds : xIds;
9498
}
@@ -139,12 +143,19 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
139143

140144
handleAxisDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions, layoutOut);
141145

142-
var showSpikes = coerce('showspikes');
143-
if(showSpikes) {
144-
coerce('spikecolor');
145-
coerce('spikethickness');
146-
coerce('spikedash');
147-
coerce('spikemode');
146+
var spikecolor = coerce2('spikecolor'),
147+
spikethickness = coerce2('spikethickness'),
148+
spikedash = coerce2('spikedash'),
149+
spikemode = coerce2('spikemode'),
150+
spikesnap = coerce2('spikesnap'),
151+
showSpikes = coerce('showspikes', !!spikecolor || !!spikethickness || !!spikedash || !!spikemode || !!spikesnap);
152+
153+
if(!showSpikes) {
154+
delete axLayoutOut.spikecolor;
155+
delete axLayoutOut.spikethickness;
156+
delete axLayoutOut.spikedash;
157+
delete axLayoutOut.spikemode;
158+
delete axLayoutOut.spikesnap;
148159
}
149160

150161
var positioningOptions = {

src/traces/scatter/hover.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
7979

8080
y0: yc - rad,
8181
y1: yc + rad,
82-
yLabelVal: di.y
82+
yLabelVal: di.y,
83+
84+
kink: Math.max(minRad, di.mrc || 0)
8385
});
8486

8587
fillHoverText(di, trace, pointData);

0 commit comments

Comments
 (0)