Skip to content

Commit 2ff1c85

Browse files
authored
Merge pull request #2451 from plotly/rangeslider-stacked-y
Fix rangeslider titles with stacked y axes
2 parents dfe13b5 + 44fc08b commit 2ff1c85

15 files changed

+75
-43
lines changed

src/components/rangeslider/draw.js

+30-11
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ var Plots = require('../../plots/plots');
1616
var Lib = require('../../lib');
1717
var Drawing = require('../drawing');
1818
var Color = require('../color');
19+
var Titles = require('../titles');
1920

2021
var Cartesian = require('../../plots/cartesian');
2122
var Axes = require('../../plots/cartesian/axes');
@@ -76,8 +77,7 @@ module.exports = function(gd) {
7677
// for all present range sliders
7778
rangeSliders.each(function(axisOpts) {
7879
var rangeSlider = d3.select(this),
79-
opts = axisOpts[constants.name],
80-
oppAxisOpts = fullLayout[Axes.id2name(axisOpts.anchor)];
80+
opts = axisOpts[constants.name];
8181

8282
// update range
8383
// Expand slider range to the axis range
@@ -96,11 +96,17 @@ module.exports = function(gd) {
9696

9797
// update range slider dimensions
9898

99-
var margin = fullLayout.margin,
100-
graphSize = fullLayout._size,
101-
domain = axisOpts.domain,
102-
oppDomain = oppAxisOpts.domain,
103-
tickHeight = (axisOpts._boundingBox || {}).height || 0;
99+
var margin = fullLayout.margin;
100+
var graphSize = fullLayout._size;
101+
var domain = axisOpts.domain;
102+
var tickHeight = (axisOpts._boundingBox || {}).height || 0;
103+
104+
var oppBottom = Infinity;
105+
var subplotData = Axes.getSubplots(gd, axisOpts);
106+
for(var i = 0; i < subplotData.length; i++) {
107+
var oppAxis = Axes.getFromId(gd, subplotData[i].substr(subplotData[i].indexOf('y')));
108+
oppBottom = Math.min(oppBottom, oppAxis.domain[0]);
109+
}
104110

105111
opts._id = constants.name + axisOpts._id;
106112
opts._clipId = opts._id + '-' + fullLayout._uid;
@@ -112,7 +118,7 @@ module.exports = function(gd) {
112118
var x = Math.round(margin.l + (graphSize.w * domain[0]));
113119

114120
var y = Math.round(
115-
margin.t + graphSize.h * (1 - oppDomain[0]) +
121+
graphSize.t + graphSize.h * (1 - oppBottom) +
116122
tickHeight +
117123
opts._offsetShift + constants.extraPad
118124
);
@@ -151,18 +157,31 @@ module.exports = function(gd) {
151157
// update current range
152158
setPixelRange(rangeSlider, gd, axisOpts, opts);
153159

154-
// update margins
160+
// title goes next to range slider instead of tick labels, so
161+
// just take it over and draw it from here
162+
if(axisOpts.side === 'bottom') {
163+
Titles.draw(gd, axisOpts._id + 'title', {
164+
propContainer: axisOpts,
165+
propName: axisOpts._name + '.title',
166+
placeholder: fullLayout._dfltTitle.x,
167+
attributes: {
168+
x: axisOpts._offset + axisOpts._length / 2,
169+
y: y + opts._height + opts._offsetShift + 10 + 1.5 * axisOpts.titlefont.size,
170+
'text-anchor': 'middle'
171+
}
172+
});
173+
}
155174

175+
// update margins
156176
Plots.autoMargin(gd, opts._id, {
157177
x: domain[0],
158-
y: oppDomain[0],
178+
y: oppBottom,
159179
l: 0,
160180
r: 0,
161181
t: 0,
162182
b: opts._height + margin.b + tickHeight,
163183
pad: constants.extraPad + opts._offsetShift * 2
164184
});
165-
166185
});
167186
};
168187

src/components/titles/index.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ var Color = require('../color');
2020
var svgTextUtils = require('../../lib/svg_text_utils');
2121
var interactConstants = require('../../constants/interactions');
2222

23-
var Titles = module.exports = {};
23+
module.exports = {
24+
draw: draw
25+
};
2426

2527
var numStripRE = / [XY][0-9]* /;
2628

@@ -54,7 +56,7 @@ var numStripRE = / [XY][0-9]* /;
5456
*
5557
* @return {selection} d3 selection of title container group
5658
*/
57-
Titles.draw = function(gd, titleClass, options) {
59+
function draw(gd, titleClass, options) {
5860
var cont = options.propContainer;
5961
var prop = options.propName;
6062
var placeholder = options.placeholder;
@@ -255,4 +257,4 @@ Titles.draw = function(gd, titleClass, options) {
255257
el.classed('js-placeholder', isplaceholder);
256258

257259
return group;
258-
};
260+
}

src/plots/cartesian/axes.js

+18-19
Original file line numberDiff line numberDiff line change
@@ -2008,20 +2008,23 @@ axes.doTicks = function(gd, axid, skipTitle) {
20082008
// now this only applies to regular cartesian axes; colorbars and
20092009
// others ALWAYS call doTicks with skipTitle=true so they can
20102010
// configure their own titles.
2011-
var ax = axisIds.getFromId(gd, axid),
2012-
avoidSelection = d3.select(gd).selectAll('g.' + axid + 'tick'),
2013-
avoid = {
2014-
selection: avoidSelection,
2015-
side: ax.side
2016-
},
2017-
axLetter = axid.charAt(0),
2018-
gs = gd._fullLayout._size,
2019-
offsetBase = 1.5,
2020-
fontSize = ax.titlefont.size,
2021-
transform,
2022-
counterAxis,
2023-
x,
2024-
y;
2011+
var ax = axisIds.getFromId(gd, axid);
2012+
2013+
// rangeslider takes over a bottom title so drop it here
2014+
if(ax.rangeslider && ax.rangeslider.visible && ax._boundingBox && ax.side === 'bottom') return;
2015+
2016+
var avoidSelection = d3.select(gd).selectAll('g.' + axid + 'tick');
2017+
var avoid = {
2018+
selection: avoidSelection,
2019+
side: ax.side
2020+
};
2021+
var axLetter = axid.charAt(0);
2022+
var gs = gd._fullLayout._size;
2023+
var offsetBase = 1.5;
2024+
var fontSize = ax.titlefont.size;
2025+
2026+
var transform, counterAxis, x, y;
2027+
20252028
if(avoidSelection.size()) {
20262029
var translation = Drawing.getTranslate(avoidSelection.node().parentNode);
20272030
avoid.offsetLeft = translation.x;
@@ -2037,6 +2040,7 @@ axes.doTicks = function(gd, axid, skipTitle) {
20372040
axisIds.getFromId(gd, ax.anchor);
20382041

20392042
x = ax._offset + ax._length / 2;
2043+
20402044
if(ax.side === 'top') {
20412045
y = -titleStandoff - fontSize * (ax.showticklabels ? 1 : 0);
20422046
}
@@ -2046,11 +2050,6 @@ axes.doTicks = function(gd, axid, skipTitle) {
20462050
}
20472051
y += counterAxis._offset;
20482052

2049-
if(ax.rangeslider && ax.rangeslider.visible && ax._boundingBox) {
2050-
y += (fullLayout.height - fullLayout.margin.b - fullLayout.margin.t) *
2051-
ax.rangeslider.thickness + ax._boundingBox.height;
2052-
}
2053-
20542053
if(!avoid.side) avoid.side = 'bottom';
20552054
}
20562055
else {

src/plots/cartesian/index.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,9 @@ exports.finalizeSubplots = function(layoutIn, layoutOut) {
8888
// so if there are NO subplots at all, make one from the first
8989
// x & y axes in the input layout
9090
if(!spAll.length) {
91-
var keys = Object.keys(layoutIn);
9291
xi = '';
9392
yi = '';
94-
for(i = 0; i < keys.length; i++) {
95-
var ki = keys[i];
93+
for(var ki in layoutIn) {
9694
if(constants.attrRegex.test(ki)) {
9795
var axLetter = ki.charAt(0);
9896
if(axLetter === 'x') {

src/plots/plots.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -701,11 +701,6 @@ plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLa
701701

702702
var i, j, id, ax;
703703

704-
// sort subplot lists
705-
for(var subplotType in newSubplotList) {
706-
newSubplotList[subplotType].sort(Lib.subplotSort);
707-
}
708-
709704
for(i = 0; i < ids.length; i++) {
710705
id = ids[i];
711706
var oldSubplot = oldSubplots[id];
@@ -1390,6 +1385,11 @@ plots.supplyLayoutModuleDefaults = function(layoutIn, layoutOut, fullData, trans
13901385
Cartesian.finalizeSubplots(layoutIn, layoutOut);
13911386
}
13921387

1388+
// sort subplot lists
1389+
for(var subplotType in layoutOut._subplots) {
1390+
layoutOut._subplots[subplotType].sort(Lib.subplotSort);
1391+
}
1392+
13931393
// base plot module layout defaults
13941394
for(i = 0; i < basePlotModules.length; i++) {
13951395
_module = basePlotModules[i];
Loading

test/image/baselines/range_slider.png

-7 Bytes
Loading
Loading
Loading
-8 Bytes
Loading
Loading
Loading
786 Bytes
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"data": [
3+
{"y": [1, 2, 3], "yaxis": "y2"},
4+
{"fill": "tozeroy", "y": [30, 20, 10]}
5+
],
6+
"layout": {
7+
"xaxis": {"rangeslider": {}, "title": "x"},
8+
"yaxis": {"title": "y", "domain": [0, 0.25]},
9+
"yaxis2": {"title": "y2", "domain": [ 0.3, 1]}
10+
}
11+
}

test/image/mocks/range_slider_multiple.json

+5-2
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,17 @@
2020
"xaxis": {
2121
"domain": [ 0, 0.45 ],
2222
"range": [ 1, 2 ],
23-
"rangeslider": {}
23+
"rangeslider": {},
24+
"title": "X",
25+
"side": "top"
2426
},
2527
"xaxis2": {
2628
"anchor": "y2",
2729
"domain": [ 0.55, 1 ],
2830
"rangeslider": {
2931
"range": [ -2, 4 ]
30-
}
32+
},
33+
"title": "X2"
3134
},
3235
"yaxis": {
3336
"domain": [ 0.3, 0.8 ],

0 commit comments

Comments
 (0)