Skip to content

Commit 223c43f

Browse files
committed
Merge pull request #473 from plotly/initial-rangeslider-range
Initial rangeslider ranges
2 parents 486b01c + 02cb4e1 commit 223c43f

File tree

10 files changed

+269
-109
lines changed

10 files changed

+269
-109
lines changed

src/components/rangeslider/attributes.js

+16
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,22 @@ module.exports = {
3030
role: 'style',
3131
description: 'Sets the border color of the range slider.'
3232
},
33+
range: {
34+
valType: 'info_array',
35+
role: 'info',
36+
items: [
37+
{valType: 'number'},
38+
{valType: 'number'}
39+
],
40+
description: [
41+
'Sets the range of the range slider.',
42+
'If not set, defaults to the full xaxis range.',
43+
'If the axis `type` is *log*, then you must take the',
44+
'log of your desired range.',
45+
'If the axis `type` is *date*, then you must convert',
46+
'the date to unix time in milliseconds.'
47+
].join(' ')
48+
},
3349
thickness: {
3450
valType: 'number',
3551
dflt: 0.15,

src/components/rangeslider/create_slider.js

+16-8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111

1212
var Plotly = require('../../plotly');
13+
var Axes = require('../../plots/cartesian/axes');
1314
var Lib = require('../../lib');
1415

1516
var svgNS = require('../../constants/xmlns_namespaces').svg;
@@ -18,7 +19,7 @@ var helpers = require('./helpers');
1819
var rangePlot = require('./range_plot');
1920

2021

21-
module.exports = function createSlider(gd, minStart, maxStart) {
22+
module.exports = function createSlider(gd) {
2223
var fullLayout = gd._fullLayout,
2324
sliderContainer = fullLayout._infolayer.selectAll('g.range-slider'),
2425
options = fullLayout.xaxis.rangeslider,
@@ -29,8 +30,8 @@ module.exports = function createSlider(gd, minStart, maxStart) {
2930
x = fullLayout.margin.l,
3031
y = fullLayout.height - height - fullLayout.margin.b;
3132

32-
minStart = minStart || 0;
33-
maxStart = maxStart || width;
33+
var minStart = 0,
34+
maxStart = width;
3435

3536
var slider = document.createElementNS(svgNS, 'g');
3637
helpers.setAttributes(slider, {
@@ -177,8 +178,8 @@ module.exports = function createSlider(gd, minStart, maxStart) {
177178
min = min || -Infinity;
178179
max = max || Infinity;
179180

180-
var rangeMin = fullLayout.xaxis.range[0],
181-
rangeMax = fullLayout.xaxis.range[1],
181+
var rangeMin = options.range[0],
182+
rangeMax = options.range[1],
182183
range = rangeMax - rangeMin,
183184
pixelMin = (min - rangeMin) / range * width,
184185
pixelMax = (max - rangeMin) / range * width;
@@ -217,9 +218,8 @@ module.exports = function createSlider(gd, minStart, maxStart) {
217218
helpers.setAttributes(grabberMin, { 'transform': 'translate(' + (min - handleWidth - 1) + ')' });
218219
helpers.setAttributes(grabberMax, { 'transform': 'translate(' + max + ')' });
219220

220-
// call to set range on plot here
221-
var rangeMin = fullLayout.xaxis.range[0],
222-
rangeMax = fullLayout.xaxis.range[1],
221+
var rangeMin = options.range[0],
222+
rangeMax = options.range[1],
223223
range = rangeMax - rangeMin,
224224
dataMin = min / width * range + rangeMin,
225225
dataMax = max / width * range + rangeMin;
@@ -236,6 +236,11 @@ module.exports = function createSlider(gd, minStart, maxStart) {
236236
}
237237

238238

239+
// Set slider range using axis autorange if necessary.
240+
if(!options.range) {
241+
options.range = Axes.getAutoRange(fullLayout.xaxis);
242+
}
243+
239244
var rangePlots = rangePlot(gd, width, height);
240245

241246
helpers.appendChildren(slider, [
@@ -248,6 +253,9 @@ module.exports = function createSlider(gd, minStart, maxStart) {
248253
grabberMax
249254
]);
250255

256+
// Set initially selected range
257+
setRange(fullLayout.xaxis.range[0], fullLayout.xaxis.range[1]);
258+
251259
sliderContainer.data([0])
252260
.enter().append(function() {
253261
options.setRange = setRange;

src/components/rangeslider/defaults.js

+14-2
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,23 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, axName, coun
2525
attributes, attr, dflt);
2626
}
2727

28-
coerce('visible');
29-
coerce('thickness');
3028
coerce('bgcolor');
3129
coerce('bordercolor');
3230
coerce('borderwidth');
31+
coerce('thickness');
32+
coerce('visible');
33+
coerce('range');
34+
35+
// Expand slider range to the axis range
36+
if(containerOut.range && !layoutOut[axName].autorange) {
37+
var outRange = containerOut.range,
38+
axRange = layoutOut[axName].range;
39+
40+
outRange[0] = Math.min(outRange[0], axRange[0]);
41+
outRange[1] = Math.max(outRange[1], axRange[1]);
42+
} else {
43+
layoutOut[axName]._needsExpand = true;
44+
}
3345

3446
if(containerOut.visible) {
3547
counterAxes.forEach(function(ax) {

src/components/rangeslider/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ module.exports = {
2020
supplyLayoutDefaults: supplyLayoutDefaults
2121
};
2222

23-
function draw(gd, minStart, maxStart) {
23+
function draw(gd) {
2424
if(!gd._fullLayout.xaxis) return;
2525

2626
var fullLayout = gd._fullLayout,
@@ -41,7 +41,7 @@ function draw(gd, minStart, maxStart) {
4141
var height = (fullLayout.height - fullLayout.margin.b - fullLayout.margin.t) * options.thickness,
4242
offsetShift = Math.floor(options.borderwidth / 2);
4343

44-
if(sliderContainer[0].length === 0 && !fullLayout._hasGL2D) createSlider(gd, minStart, maxStart);
44+
if(sliderContainer[0].length === 0 && !fullLayout._hasGL2D) createSlider(gd);
4545

4646
// Need to default to 0 for when making gl plots
4747
var bb = fullLayout.xaxis._boundingBox ?

src/components/rangeslider/range_plot.js

+9-6
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ var svgNS = require('../../constants/xmlns_namespaces').svg;
1616

1717
module.exports = function rangePlot(gd, w, h) {
1818

19-
var traces = gd._fullData,
20-
xaxis = gd._fullLayout.xaxis,
21-
yaxis = gd._fullLayout.yaxis,
22-
minX = xaxis.range[0],
23-
maxX = xaxis.range[1],
19+
var fullLayout = gd._fullLayout,
20+
traces = gd._fullData,
21+
xaxis = fullLayout.xaxis,
22+
yaxis = fullLayout.yaxis,
23+
minX = xaxis.rangeslider.range[0],
24+
maxX = xaxis.rangeslider.range[1],
2425
minY = yaxis.range[0],
2526
maxY = yaxis.range[1];
2627

@@ -62,7 +63,9 @@ module.exports = function rangePlot(gd, w, h) {
6263
var posX = w * (x[k] - minX) / (maxX - minX),
6364
posY = h * (1 - (y[k] - minY) / (maxY - minY));
6465

65-
pointPairs.push([posX, posY]);
66+
if(!isNaN(posX) && !isNaN(posY)) {
67+
pointPairs.push([posX, posY]);
68+
}
6669
}
6770

6871
// more trace type range plots can be added here

src/plot_api/plot_api.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,6 @@ Plotly.plot = function(gd, data, layout, config) {
245245

246246
function drawAxes() {
247247
// draw ticks, titles, and calculate axis scaling (._b, ._m)
248-
RangeSlider.draw(gd);
249248
return Plotly.Axes.doTicks(gd, 'redraw');
250249
}
251250

@@ -310,6 +309,7 @@ Plotly.plot = function(gd, data, layout, config) {
310309
Shapes.drawAll(gd);
311310
Plotly.Annotations.drawAll(gd);
312311
Legend.draw(gd);
312+
RangeSlider.draw(gd);
313313
RangeSelector.draw(gd);
314314
}
315315

@@ -2191,6 +2191,10 @@ Plotly.relayout = function relayout(gd, astr, val) {
21912191
docalc = true;
21922192
}
21932193

2194+
if(pleafPlus.indexOf('rangeslider') !== -1) {
2195+
docalc = true;
2196+
}
2197+
21942198
// toggling log without autorange: need to also recalculate ranges
21952199
// logical XOR (ie are we toggling log)
21962200
if(pleaf==='type' && ((parentFull.type === 'log') !== (vi === 'log'))) {

src/plots/cartesian/axes.js

+86-70
Original file line numberDiff line numberDiff line change
@@ -110,91 +110,106 @@ axes.minDtick = function(ax,newDiff,newFirst,allow) {
110110
}
111111
};
112112

113-
axes.doAutoRange = function(ax) {
114-
if(!ax._length) ax.setScale();
113+
axes.getAutoRange = function(ax) {
114+
var newRange = [];
115115

116-
if(ax.autorange && ax._min && ax._max &&
117-
ax._min.length && ax._max.length) {
118-
var minmin = ax._min[0].val,
119-
maxmax = ax._max[0].val,
120-
i;
121-
122-
for(i = 1; i < ax._min.length; i++) {
123-
if(minmin !== maxmax) break;
124-
minmin = Math.min(minmin, ax._min[i].val);
125-
}
126-
for(i = 1; i < ax._max.length; i++) {
127-
if(minmin !== maxmax) break;
128-
maxmax = Math.max(maxmax, ax._max[i].val);
129-
}
130-
131-
var j,minpt,maxpt,minbest,maxbest,dp,dv,
132-
mbest = 0,
133-
axReverse = (ax.range && ax.range[1]<ax.range[0]);
134-
// one-time setting to easily reverse the axis
135-
// when plotting from code
136-
if(ax.autorange==='reversed') {
137-
axReverse = true;
138-
ax.autorange = true;
139-
}
140-
for(i=0; i<ax._min.length; i++) {
141-
minpt = ax._min[i];
142-
for(j=0; j<ax._max.length; j++) {
143-
maxpt = ax._max[j];
144-
dv = maxpt.val-minpt.val;
145-
dp = ax._length-minpt.pad-maxpt.pad;
146-
if(dv>0 && dp>0 && dv/dp > mbest) {
147-
minbest = minpt;
148-
maxbest = maxpt;
149-
mbest = dv/dp;
150-
}
116+
var minmin = ax._min[0].val,
117+
maxmax = ax._max[0].val,
118+
i;
119+
120+
for(i = 1; i < ax._min.length; i++) {
121+
if(minmin !== maxmax) break;
122+
minmin = Math.min(minmin, ax._min[i].val);
123+
}
124+
for(i = 1; i < ax._max.length; i++) {
125+
if(minmin !== maxmax) break;
126+
maxmax = Math.max(maxmax, ax._max[i].val);
127+
}
128+
129+
var j,minpt,maxpt,minbest,maxbest,dp,dv,
130+
mbest = 0,
131+
axReverse = (ax.range && ax.range[1]<ax.range[0]);
132+
133+
// one-time setting to easily reverse the axis
134+
// when plotting from code
135+
if(ax.autorange === 'reversed') {
136+
axReverse = true;
137+
ax.autorange = true;
138+
}
139+
140+
for(i=0; i<ax._min.length; i++) {
141+
minpt = ax._min[i];
142+
for(j=0; j<ax._max.length; j++) {
143+
maxpt = ax._max[j];
144+
dv = maxpt.val-minpt.val;
145+
dp = ax._length-minpt.pad-maxpt.pad;
146+
if(dv>0 && dp>0 && dv/dp > mbest) {
147+
minbest = minpt;
148+
maxbest = maxpt;
149+
mbest = dv/dp;
151150
}
152151
}
153-
if(minmin===maxmax) {
154-
ax.range = axReverse ?
155-
[minmin+1, ax.rangemode!=='normal' ? 0 : minmin-1] :
156-
[ax.rangemode!=='normal' ? 0 : minmin-1, minmin+1];
157-
}
158-
else if(mbest) {
159-
if(ax.type==='linear' || ax.type==='-') {
160-
if(ax.rangemode==='tozero' && minbest.val>=0) {
152+
}
153+
154+
if(minmin === maxmax) {
155+
newRange = axReverse ?
156+
[minmin+1, ax.rangemode!=='normal' ? 0 : minmin-1] :
157+
[ax.rangemode!=='normal' ? 0 : minmin-1, minmin+1];
158+
}
159+
else if(mbest) {
160+
if(ax.type==='linear' || ax.type==='-') {
161+
if(ax.rangemode==='tozero' && minbest.val>=0) {
162+
minbest = {val: 0, pad: 0};
163+
}
164+
else if(ax.rangemode==='nonnegative') {
165+
if(minbest.val - mbest*minbest.pad<0) {
161166
minbest = {val: 0, pad: 0};
162167
}
163-
else if(ax.rangemode==='nonnegative') {
164-
if(minbest.val - mbest*minbest.pad<0) {
165-
minbest = {val: 0, pad: 0};
166-
}
167-
if(maxbest.val<0) {
168-
maxbest = {val: 1, pad: 0};
169-
}
168+
if(maxbest.val<0) {
169+
maxbest = {val: 1, pad: 0};
170170
}
171-
172-
// in case it changed again...
173-
mbest = (maxbest.val-minbest.val) /
174-
(ax._length-minbest.pad-maxbest.pad);
175171
}
176172

177-
ax.range = [
178-
minbest.val - mbest*minbest.pad,
179-
maxbest.val + mbest*maxbest.pad
180-
];
173+
// in case it changed again...
174+
mbest = (maxbest.val-minbest.val) /
175+
(ax._length-minbest.pad-maxbest.pad);
176+
}
181177

182-
// don't let axis have zero size
183-
if(ax.range[0]===ax.range[1]) {
184-
ax.range = [ax.range[0]-1, ax.range[0]+1];
185-
}
178+
newRange = [
179+
minbest.val - mbest*minbest.pad,
180+
maxbest.val + mbest*maxbest.pad
181+
];
186182

187-
// maintain reversal
188-
if(axReverse) {
189-
ax.range.reverse();
190-
}
183+
// don't let axis have zero size
184+
if(newRange[0] === newRange[1]) {
185+
newRange = [newRange[0]-1, newRange[0]+1];
191186
}
192187

188+
// maintain reversal
189+
if(axReverse) {
190+
newRange.reverse();
191+
}
192+
}
193+
194+
return newRange;
195+
};
196+
197+
axes.doAutoRange = function(ax) {
198+
if(!ax._length) ax.setScale();
199+
200+
// TODO do we really need this?
201+
var hasDeps = (ax._min && ax._max && ax._min.length && ax._max.length);
202+
203+
if(ax.autorange && hasDeps) {
204+
ax.range = axes.getAutoRange(ax);
205+
193206
// doAutoRange will get called on fullLayout,
194207
// but we want to report its results back to layout
195208
var axIn = ax._gd.layout[ax._name];
209+
196210
if(!axIn) ax._gd.layout[ax._name] = axIn = {};
197-
if(axIn!==ax) {
211+
212+
if(axIn !== ax) {
198213
axIn.range = ax.range.slice();
199214
axIn.autorange = ax.autorange;
200215
}
@@ -241,7 +256,8 @@ axes.saveRangeInitial = function(gd, overwrite) {
241256
// and make it a tight bound if possible
242257
var FP_SAFE = Number.MAX_VALUE/2;
243258
axes.expand = function(ax, data, options) {
244-
if(!ax.autorange || !data) return;
259+
// if(!(ax.autorange || (ax.rangeslider || {}).visible) || !data) return;
260+
if(!(ax.autorange || ax._needsExpand) || !data) return;
245261
if(!ax._min) ax._min = [];
246262
if(!ax._max) ax._max = [];
247263
if(!options) options = {};
70.6 KB
Loading

0 commit comments

Comments
 (0)