Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 2k
Feature range slider#336
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
Uh oh!
There was an error while loading. Please reload this page.
Feature range slider #336
Changes from all commits
4c28de7866d1d2d2973f3eb619ef854442db0c16cd2666162c8d87d0598d69f3ecdcdcfb863f65857574028b46e4143a658cfd258fe5142609af762f381299149348c0ebc89480571f3c4d19ad242458d4f4ffeb665c061c0083280a63f2a771546c47a6829ab9ba7225071ae37e93d500c07324d238e921d6bdc0e9bc907fd37ae0b32a3688e855bdf58e62358243d6bb10c00fd5c37288c4a3767a82167674b62deed2e7ead4615214897771b113fb919082dd5e8e83375d2cb509976736bbbc613c6cc55bf02cbe2c26271589cFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| /** | ||
| * 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 colorAttributes = require('../color/attributes'); | ||
| module.exports = { | ||
| bgcolor: { | ||
| valType: 'color', | ||
| dflt: colorAttributes.background, | ||
| role: 'style', | ||
| description: 'Sets the background color of the range slider.' | ||
| }, | ||
| bordercolor: { | ||
| valType: 'color', | ||
| dflt: colorAttributes.defaultLine, | ||
| role: 'style', | ||
| description: 'Sets the border color of the range slider.' | ||
| }, | ||
| borderwidth: { | ||
| valType: 'integer', | ||
| dflt: 0, | ||
| role: 'style', | ||
| description: 'Sets the border color of the range slider.' | ||
| }, | ||
| thickness: { | ||
| valType: 'number', | ||
| dflt: 0.15, | ||
| min: 0, | ||
| max: 1, | ||
| role: 'style', | ||
| description: [ | ||
| 'The height of the range slider as a fraction of the', | ||
| 'total plot area height.' | ||
| ].join(' ') | ||
| }, | ||
| visible: { | ||
| valType: 'boolean', | ||
| dflt: true, | ||
| role: 'info', | ||
| description: [ | ||
| 'Determines whether or not the range slider will be visible.', | ||
| 'If visible, perpendicular axes will be set to `fixedrange`' | ||
| ].join(' ') | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,248 @@ | ||
| /** | ||
| * 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 Plotly = require('../../plotly'); | ||
| var Lib = require('../../lib'); | ||
| var svgNS = require('../../constants/xmlns_namespaces').svg; | ||
| var helpers = require('./helpers'); | ||
| var rangePlot = require('./range_plot'); | ||
| module.exports = function createSlider(gd, minStart, maxStart) { | ||
| var fullLayout = gd._fullLayout, | ||
| sliderContainer = fullLayout._infolayer.selectAll('g.range-slider'), | ||
| options = fullLayout.xaxis.rangeslider, | ||
| width = fullLayout._size.w, | ||
| height = (fullLayout.height - fullLayout.margin.b - fullLayout.margin.t) * options.thickness, | ||
| handleWidth = 2, | ||
| offsetShift = Math.floor(options.borderwidth / 2), | ||
| x = fullLayout.margin.l, | ||
| y = fullLayout.height - height - fullLayout.margin.b; | ||
| minStart = minStart || 0; | ||
| maxStart = maxStart || width; | ||
| var slider = document.createElementNS(svgNS, 'g'); | ||
| helpers.setAttributes(slider, { | ||
| 'class': 'range-slider', | ||
| 'data-min': minStart, | ||
| 'data-max': maxStart, | ||
| 'pointer-events': 'all', | ||
| 'transform': 'translate(' + x + ',' + y + ')' | ||
| }); | ||
| var sliderBg = document.createElementNS(svgNS, 'rect'), | ||
| borderCorrect = options.borderwidth % 2 === 0 ? options.borderwidth : options.borderwidth - 1; | ||
| helpers.setAttributes(sliderBg, { | ||
| 'fill': options.bgcolor, | ||
| 'stroke': options.bordercolor, | ||
| 'stroke-width': options.borderwidth, | ||
| 'height': height + borderCorrect, | ||
| 'width': width + borderCorrect, | ||
| 'transform': 'translate(-' + offsetShift + ', -' + offsetShift + ')', | ||
| 'shape-rendering': 'crispEdges' | ||
| }); | ||
| var maskMin = document.createElementNS(svgNS, 'rect'); | ||
| helpers.setAttributes(maskMin, { | ||
| 'x': 0, | ||
| 'width': minStart, | ||
| 'height': height, | ||
| 'fill': 'rgba(0,0,0,0.4)' | ||
| }); | ||
| var maskMax = document.createElementNS(svgNS, 'rect'); | ||
| helpers.setAttributes(maskMax, { | ||
| 'x': maxStart, | ||
| 'width': width - maxStart, | ||
| 'height': height, | ||
| 'fill': 'rgba(0,0,0,0.4)' | ||
| }); | ||
| var grabberMin = document.createElementNS(svgNS, 'g'), | ||
| grabAreaMin = document.createElementNS(svgNS, 'rect'), | ||
| handleMin = document.createElementNS(svgNS, 'rect'); | ||
| helpers.setAttributes(grabberMin, { 'transform': 'translate(' + (minStart - handleWidth - 1) + ')' }); | ||
| helpers.setAttributes(grabAreaMin, { | ||
| 'width': 10, | ||
| 'height': height, | ||
| 'x': -6, | ||
| 'fill': 'transparent', | ||
| 'cursor': 'col-resize' | ||
| }); | ||
| helpers.setAttributes(handleMin, { | ||
| 'width': handleWidth, | ||
| 'height': height / 2, | ||
| 'y': height / 4, | ||
| 'rx': 1, | ||
| 'fill': 'white', | ||
| 'stroke': '#666', | ||
| 'shape-rendering': 'crispEdges' | ||
| }); | ||
| helpers.appendChildren(grabberMin, [handleMin, grabAreaMin]); | ||
| var grabberMax = document.createElementNS(svgNS, 'g'), | ||
| grabAreaMax = document.createElementNS(svgNS, 'rect'), | ||
| handleMax = document.createElementNS(svgNS, 'rect'); | ||
| helpers.setAttributes(grabberMax, { 'transform': 'translate(' + maxStart + ')' }); | ||
| helpers.setAttributes(grabAreaMax, { | ||
| 'width': 10, | ||
| 'height': height, | ||
| 'x': -2, | ||
| 'fill': 'transparent', | ||
| 'cursor': 'col-resize' | ||
| }); | ||
| helpers.setAttributes(handleMax, { | ||
| 'width': handleWidth, | ||
| 'height': height / 2, | ||
| 'y': height / 4, | ||
| 'rx': 1, | ||
| 'fill': 'white', | ||
| 'stroke': '#666', | ||
| 'shape-rendering': 'crispEdges' | ||
| }); | ||
| helpers.appendChildren(grabberMax, [handleMax, grabAreaMax]); | ||
| var slideBox = document.createElementNS(svgNS, 'rect'); | ||
| helpers.setAttributes(slideBox, { | ||
| 'x': minStart, | ||
| 'width': maxStart - minStart, | ||
| 'height': height, | ||
| 'cursor': 'ew-resize', | ||
| 'fill': 'transparent' | ||
| }); | ||
| slider.addEventListener('mousedown', function(event) { | ||
| var target = event.target, | ||
| startX = event.clientX, | ||
| offsetX = startX - slider.getBoundingClientRect().left, | ||
| minVal = slider.getAttribute('data-min'), | ||
| maxVal = slider.getAttribute('data-max'); | ||
| window.addEventListener('mousemove', mouseMove); | ||
| window.addEventListener('mouseup', mouseUp); | ||
| function mouseMove(e) { | ||
| var delta = +e.clientX - startX; | ||
| switch(target) { | ||
| case slideBox: | ||
| slider.style.cursor = 'ew-resize'; | ||
| setPixelRange(+maxVal + delta, +minVal + delta); | ||
| break; | ||
| case grabAreaMin: | ||
| slider.style.cursor = 'col-resize'; | ||
| setPixelRange(+minVal + delta, +maxVal); | ||
| break; | ||
| case grabAreaMax: | ||
| slider.style.cursor = 'col-resize'; | ||
| setPixelRange(+minVal, +maxVal + delta); | ||
| break; | ||
| default: | ||
| slider.style.cursor = 'ew-resize'; | ||
| setPixelRange(offsetX, offsetX + delta); | ||
| break; | ||
| } | ||
| } | ||
| function mouseUp() { | ||
| window.removeEventListener('mousemove', mouseMove); | ||
| window.removeEventListener('mouseup', mouseUp); | ||
| slider.style.cursor = 'auto'; | ||
| } | ||
| }); | ||
| function setRange(min, max) { | ||
| min = min || -Infinity; | ||
| max = max || Infinity; | ||
| var rangeMin = fullLayout.xaxis.range[0], | ||
| rangeMax = fullLayout.xaxis.range[1], | ||
| range = rangeMax - rangeMin, | ||
| pixelMin = (min - rangeMin) / range * width, | ||
| pixelMax = (max - rangeMin) / range * width; | ||
| setPixelRange(pixelMin, pixelMax); | ||
| } | ||
| function setPixelRange(min, max) { | ||
| min = Lib.constrain(min, 0, width); | ||
| max = Lib.constrain(max, 0, width); | ||
| if(max < min) { | ||
| var temp = max; | ||
| max = min; | ||
| min = temp; | ||
| } | ||
| helpers.setAttributes(slider, { | ||
| 'data-min': min, | ||
| 'data-max': max | ||
| }); | ||
| helpers.setAttributes(slideBox, { | ||
| 'x': min, | ||
| 'width': max - min | ||
| }); | ||
| helpers.setAttributes(maskMin, { 'width': min }); | ||
| helpers.setAttributes(maskMax, { | ||
| 'x': max, | ||
| 'width': width - max | ||
| }); | ||
| helpers.setAttributes(grabberMin, { 'transform': 'translate(' + (min - handleWidth - 1) + ')' }); | ||
| helpers.setAttributes(grabberMax, { 'transform': 'translate(' + max + ')' }); | ||
| // call to set range on plot here | ||
| var rangeMin = fullLayout.xaxis.range[0], | ||
| rangeMax = fullLayout.xaxis.range[1], | ||
| range = rangeMax - rangeMin, | ||
| dataMin = min / width * range + rangeMin, | ||
| dataMax = max / width * range + rangeMin; | ||
| Plotly.relayout(gd, 'xaxis.range', [dataMin, dataMax]); | ||
| } | ||
| var rangePlots = rangePlot(gd, width, height); | ||
| helpers.appendChildren(slider, [ | ||
| sliderBg, | ||
| rangePlots, | ||
| maskMin, | ||
| maskMax, | ||
| slideBox, | ||
| grabberMin, | ||
| grabberMax | ||
| ]); | ||
| sliderContainer.data([0]) | ||
| .enter().append(function() { | ||
| options.setRange = setRange; | ||
| return slider; | ||
| }); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| /** | ||
| * 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'); | ||
| module.exports = { | ||
| 'linear': function(val) { return val; }, | ||
| 'log': function(val) { return Math.log(val)/Math.log(10); }, | ||
| 'date': function(val) { return Lib.dateTime2ms(val); }, | ||
| 'category': function(_, i) { return i; } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| /** | ||
| * 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'); | ||
| module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, axName, counterAxes) { | ||
| if(!layoutIn[axName].rangeslider) return; | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should be more strict here. ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did this so that we could say Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see your point. But at the moment, container attribute objects are either objects or undefined and nothing else. | ||
| var containerIn = typeof layoutIn[axName].rangeslider === 'object' ? | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah never mind. if | ||
| layoutIn[axName].rangeslider : {}, | ||
| containerOut = layoutOut[axName].rangeslider = {}; | ||
| function coerce(attr, dflt) { | ||
| return Lib.coerce(containerIn, containerOut, | ||
| attributes, attr, dflt); | ||
| } | ||
| coerce('visible'); | ||
| coerce('thickness'); | ||
| coerce('bgcolor'); | ||
| coerce('bordercolor'); | ||
| coerce('borderwidth'); | ||
| if(containerOut.visible) { | ||
| counterAxes.forEach(function(ax) { | ||
| var opposing = layoutOut[ax] || {}; | ||
| opposing.fixedrange = true; | ||
| layoutOut[ax] = opposing; | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 | ||
| }); | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| /** | ||
| * 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'; | ||
| exports.setAttributes = function setAttributes(el, attributes) { | ||
| for(var key in attributes) { | ||
| el.setAttribute(key, attributes[key]); | ||
| } | ||
| }; | ||
| exports.appendChildren = function appendChildren(el, children) { | ||
| for(var i = 0; i < children.length; i++) { | ||
| if(children[i]) { | ||
| el.appendChild(children[i]); | ||
| } | ||
| } | ||
| }; |
Uh oh!
There was an error while loading. Please reload this page.

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should probably be swapped out for the
ax._2_methods, but I couldn't wrap my head around their usage and which one(s) are needed for this use case (i.e. raw data -> numeric relative coordinate value for plotting).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like you're looking for
ax.d2c