Skip to content

Feature: Range selectors #373

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 53 commits into from
Apr 4, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
36968ad
first pass range selector attributes
etpinard Mar 11, 2016
ab6c109
first pass range selector defaults
etpinard Mar 11, 2016
19d81d4
first pass range selector draw method
etpinard Mar 11, 2016
aa84cf0
add 'buttons' to list of non-data array in the plot-schema
etpinard Mar 11, 2016
b5e8301
ensure that range selector attributes are only coerced for date axes
etpinard Mar 14, 2016
80c75c7
pull off update object logic into seperate file:
etpinard Mar 14, 2016
f25213d
put range selector on-par with legend's
etpinard Mar 16, 2016
ef64e20
move RangeSelector.draw to just after Legend.draw
etpinard Mar 17, 2016
26494cf
make range selector border width dflt 1
etpinard Mar 17, 2016
da47cd4
tmp commit (range selector draw)
etpinard Mar 17, 2016
da71911
make range selector module more like range slider:
etpinard Mar 30, 2016
ab27021
first pass range selector attributes
etpinard Mar 11, 2016
de5e6b3
first pass range selector defaults
etpinard Mar 11, 2016
e976877
first pass range selector draw method
etpinard Mar 11, 2016
3fde2f8
add 'buttons' to list of non-data array in the plot-schema
etpinard Mar 11, 2016
121231b
ensure that range selector attributes are only coerced for date axes
etpinard Mar 14, 2016
8c839a1
pull off update object logic into seperate file:
etpinard Mar 14, 2016
48b5c50
put range selector on-par with legend's
etpinard Mar 16, 2016
4c7c99d
move RangeSelector.draw to just after Legend.draw
etpinard Mar 17, 2016
b198529
make range selector border width dflt 1
etpinard Mar 17, 2016
17c8377
tmp commit (range selector draw)
etpinard Mar 17, 2016
0d20931
make range selector module more like range slider:
etpinard Mar 30, 2016
b9e388f
better text positioning in range-select buttons
mdtusz Mar 30, 2016
a5e6aaa
Merge branch 'range-selector' of https://github.com/plotly/plotly.js …
mdtusz Mar 30, 2016
86331c7
lint in Plots.autoMargin
etpinard Mar 31, 2016
c9f76ab
add positionning logic to range selector draw step
etpinard Mar 31, 2016
574a6b3
change default yanchor to 'bottom'
etpinard Mar 31, 2016
beb239b
fixup test description
etpinard Mar 31, 2016
ec0771a
use new Date() to def specs instead of ms:
etpinard Mar 31, 2016
dd4880b
make y position 1.02 by default
etpinard Mar 31, 2016
2264bdd
add 'hour', 'minute' and 'second' button step values
etpinard Mar 31, 2016
f11aef9
Update rangeselector attributes
mdtusz Mar 31, 2016
8b4340e
don't coerce 'stepmode' and 'count' when 'step' is set to 'all'
etpinard Mar 31, 2016
053be81
handle to-date with step > 1 case
etpinard Mar 31, 2016
f49dd51
handle step 'all' case in label text
etpinard Mar 31, 2016
fb056b1
add range selector image mock
etpinard Mar 31, 2016
b2988b4
add a few range selector interaction tests
etpinard Mar 31, 2016
3a812c9
make getUpdateObject use ax._name to build update object
etpinard Mar 31, 2016
1ca424a
fixup test (to accomodate new defaults)
etpinard Mar 31, 2016
a671683
add smart default for range selector 'x' and 'y':
etpinard Mar 31, 2016
1042f4f
get range selector attributes ready for plot schema:
etpinard Apr 1, 2016
46f4a20
add comment about plotly.py in isLinkedToArray plot schema test
etpinard Apr 1, 2016
616d77d
add 'rx' and 'ry' button rect radii
etpinard Apr 1, 2016
86a3167
pass axis name to Plots.autoMargin,
etpinard Apr 1, 2016
4318f45
fix default label text generator
etpinard Apr 1, 2016
f025dc6
add is-active and is-hovered dynamic props to buttons:
etpinard Apr 1, 2016
ade535e
fixup attribute descriptions
etpinard Apr 1, 2016
49507be
change range selector and slider 'borderwidth' dflt to 0.
etpinard Apr 1, 2016
c95b45a
update range selector image test + add one more
etpinard Apr 1, 2016
6a68155
add is-active button update test
etpinard Apr 1, 2016
1275935
rename 'to date' stepmode 'todate'
etpinard Apr 4, 2016
6c3a9d7
update image mock 'to date' -> 'todate'
etpinard Apr 4, 2016
6da24e4
update default button style:
etpinard Apr 4, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/components/legend/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -402,12 +402,12 @@ function repositionLegend(gd, traces) {
else {
// approximation to height offset to center the font
// to avoid getBoundingClientRect
textY = tHeight * (0.3 + (1-tLines) / 2);
text.attr('y',textY);
tspans.attr('y',textY);
textY = tHeight * (0.3 + (1 - tLines) / 2);
text.attr('y', textY);
tspans.attr('y', textY);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you're using eslint you can use $ eslint --fix to fix all of the whitespace issues across the codebase automatically

}

tHeightFull = Math.max(tHeight*tLines, 16) + 3;
tHeightFull = Math.max(tHeight * tLines, 16) + 3;

g.attr('transform',
'translate(' + borderwidth + ',' +
Expand Down
97 changes: 97 additions & 0 deletions src/components/rangeselector/attributes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* Copyright 2012-2016, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var fontAttrs = require('../../plots/font_attributes');
var colorAttrs = require('../color/attributes');
var extendFlat = require('../../lib/extend').extendFlat;
var buttonAttrs = require('./button_attributes');

buttonAttrs = extendFlat(buttonAttrs, {
_isLinkedToArray: true,
description: [
'Sets the specifications for each buttons.',
'By default, a range selector comes with no buttons.'
].join(' ')
});

module.exports = {
visible: {
valType: 'boolean',
role: 'info',
description: [
'Determines whether or not this range selector is visible.',
'Note that range selectors are only available for x axes of',
'`type` set to or auto-typed to *date*.'
].join(' ')
},

buttons: buttonAttrs,

x: {
valType: 'number',
min: -2,
max: 3,
role: 'style',
description: 'Sets the x position (in normalized coordinates) of the range selector.'
},
xanchor: {
valType: 'enumerated',
values: ['auto', 'left', 'center', 'right'],
dflt: 'left',
role: 'info',
description: [
'Sets the range selector\'s horizontal position anchor.',
'This anchor binds the `x` position to the *left*, *center*',
'or *right* of the range selector.'
].join(' ')
},
y: {
valType: 'number',
min: -2,
max: 3,
role: 'style',
description: 'Sets the y position (in normalized coordinates) of the range selector.'
},
yanchor: {
valType: 'enumerated',
values: ['auto', 'top', 'middle', 'bottom'],
dflt: 'bottom',
role: 'info',
description: [
'Sets the range selector\'s vertical position anchor',
'This anchor binds the `y` position to the *top*, *middle*',
'or *bottom* of the range selector.'
].join(' ')
},

font: extendFlat({}, fontAttrs, {
description: 'Sets the font of the range selector button text.'
}),

bgcolor: {
valType: 'color',
dflt: colorAttrs.lightLine,
role: 'style',
description: 'Sets the background color of the range selector buttons.'
},
bordercolor: {
valType: 'color',
dflt: colorAttrs.defaultLine,
role: 'style',
description: 'Sets the color of the border enclosing the range selector.'
},
borderwidth: {
valType: 'number',
min: 0,
dflt: 0,
role: 'style',
description: 'Sets the width (in px) of the border enclosing the range selector.'
}
};
54 changes: 54 additions & 0 deletions src/components/rangeselector/button_attributes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Copyright 2012-2016, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';


module.exports = {
step: {
valType: 'enumerated',
role: 'info',
values: ['month', 'year', 'day', 'hour', 'minute', 'second', 'all'],
dflt: 'month',
description: [
'The unit of measurement that the `count` value will set the range by.'
].join(' ')
},
stepmode: {
valType: 'enumerated',
role: 'info',
values: ['backward', 'todate'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be a forwards mode?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe. HighCharts doesn't support it, so I didn't put it in the first iteration.

dflt: 'backward',
description: [
'Sets the range update mode.',
'If *backward*, the range update shifts the start of range',
'back *count* times *step* milliseconds.',
'If *todate*, the range update shifts the start of range',
'back to the first timestamp from *count* times',
'*step* milliseconds back.',
'For example, with `step` set to *year* and `count` set to *1*',
'the range update shifts the start of the range back to',
'January 01 of the current year.'
].join(' ')
},
count: {
valType: 'number',
role: 'info',
min: 0,
dflt: 1,
description: [
'Sets the number of steps to take to update the range.',
'Use with `step` to specify the update interval.'
].join(' ')
},
label: {
valType: 'string',
role: 'info',
description: 'Sets the text label to appear on the button.'
}
};
26 changes: 26 additions & 0 deletions src/components/rangeselector/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Copyright 2012-2016, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';


module.exports = {

// 'y' position pad above counter axis domain
yPad: 0.02,

// minimum button width (regardless of text size)
minButtonWidth: 30,

// buttons rect radii
rx: 3,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we plan to make this configurable later (and if people ask)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. When enough people ask.

ry: 3,

// color given to active and hovered buttons
activeColor: '#d3d3d3'
};
85 changes: 85 additions & 0 deletions src/components/rangeselector/defaults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* Copyright 2012-2016, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var Lib = require('../../lib');

var attributes = require('./attributes');
var buttonAttrs = require('./button_attributes');
var constants = require('./constants');


module.exports = function rangeSelectorDefaults(containerIn, containerOut, layout, counterAxes) {
var selectorIn = containerIn.rangeselector || {},
selectorOut = containerOut.rangeselector = {};

function coerce(attr, dflt) {
return Lib.coerce(selectorIn, selectorOut, attributes, attr, dflt);
}

var buttons = buttonsDefaults(selectorIn, selectorOut);

var visible = coerce('visible', buttons.length > 0);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment, range selector don't ship with any default buttons.

Shipping default buttons would require adding some logic based on the range of the time series, which wouldn't be too hard, but might be too magical.

Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kinda like the do-it-yourself nature of this. It's not any more complex than other configurations in plotly.js and allows far greater flexibility (especially if we allow arbitrary HTML).

if(!visible) return;

var posDflt = getPosDflt(containerOut, layout, counterAxes);
coerce('x', posDflt[0]);
coerce('y', posDflt[1]);
Lib.noneOrAll(containerIn, containerOut, ['x', 'y']);

coerce('xanchor');
coerce('yanchor');

Lib.coerceFont(coerce, 'font', layout.font);

coerce('bgcolor');
coerce('bordercolor');
coerce('borderwidth');
};

function buttonsDefaults(containerIn, containerOut) {
var buttonsIn = containerIn.buttons || [],
buttonsOut = containerOut.buttons = [];

var buttonIn, buttonOut;

function coerce(attr, dflt) {
return Lib.coerce(buttonIn, buttonOut, buttonAttrs, attr, dflt);
}

for(var i = 0; i < buttonsIn.length; i++) {
buttonIn = buttonsIn[i];
buttonOut = {};

var step = coerce('step');
if(step !== 'all') {
coerce('stepmode');
coerce('count');
}

coerce('label');

buttonsOut.push(buttonOut);
}

return buttonsOut;
}

function getPosDflt(containerOut, layout, counterAxes) {
var anchoredList = counterAxes.filter(function(ax) {
return layout[ax].anchor === containerOut._id;
});

var posY = 0;
for(var i = 0; i < anchoredList.length; i++) {
posY = Math.max(layout[anchoredList[i]].domain[1], posY);
}

return [containerOut.domain[0], posY + constants.yPad];
}
Loading