<!-- Copyright (c) 2014 The Polymer Project Authors. All rights reserved. This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt --> <!-- `paper-slider` allows user to select a value from a range of values by moving the slider thumb. The interactive nature of the slider makes it a great choice for settings that reflect intensity levels, such as volume, brightness, or color saturation. Example: <paper-slider></paper-slider> Use `min` and `max` to specify the slider range. Default is 0 to 100. Example: <paper-slider min="10" max="200" value="110"></paper-slider> Styling slider: To change the slider progress bar color: paper-slider::shadow #sliderBar::shadow #activeProgress { background-color: #0f9d58; } To change the slider knob color: paper-slider::shadow #sliderKnobInner { background-color: #0f9d58; } To change the slider pin color: paper-slider::shadow #sliderKnobInner::before { background-color: #0f9d58; } To change the slider pin's value: paper-slider::shadow #sliderKnobInner::after { color: #0f9d58 } To change the slider secondary progress bar color: paper-slider::shadow #sliderBar::shadow #secondaryProgress { background-color: #0f9d58; } @group Paper Elements @element paper-slider @extends core-range @homepage github.io --> <link rel="import" href="../paper-progress/paper-progress.html"> <link rel="import" href="../paper-input/paper-input.html"> <polymer-element name="paper-slider" extends="core-range" attributes="snaps pin disabled secondaryProgress editable immediateValue"> <template> <link rel="stylesheet" href="paper-slider.css"> <div id="sliderContainer" on-keydown="{{keydown}}"> <paper-progress id="sliderBar" aria-hidden="true" min="{{min}}" max="{{max}}" value="{{immediateValue}}" secondaryProgress="{{secondaryProgress}}" on-down="{{bardown}}" on-up="{{resetKnob}}" on-trackstart="{{trackStart}}" on-trackx="{{trackx}}" on-trackend="{{trackEnd}}"></paper-progress> <template if="{{snaps && !disabled}}"> <div class="slider-markers" horizontal layout> <template repeat="{{markers}}"> <div flex class="slider-marker"></div> </template> </div> </template> <div id="sliderKnob" class="{{ {pin : pin, snaps : snaps} | tokenList }}" on-down="{{expandKnob}}" on-up="{{resetKnob}}" on-trackstart="{{trackStart}}" on-trackx="{{trackx}}" on-trackend="{{trackEnd}}" on-transitionend="{{knobTransitionEnd}}" role="slider" aria-valuenow="{{value}}" aria-valuemin="{{min}}" aria-valuemax="{{max}}" aria-valuetext="{{value}}" tabindex="0" center-justified center horizontal layout> <div id="sliderKnobInner" value="{{immediateValue}}"></div> </div> </div> <template if="{{editable}}"> <paper-input id="input" class="slider-input" value="{{immediateValue}}" validate="^[-+]?[0-9]*\.?[0-9]+$" disabled?="{{disabled}}" on-change="{{inputChange}}"></paper-input> </template> </template> <script> Polymer('paper-slider', { /** * Fired when the slider's value changes. * * @event change */ /** * Fired when the slider's value changes due to manual interaction. * * Changes to the slider's value due to changes in an underlying * bound variable will not trigger this event. * * @event manual-change */ /** * If true, the slider thumb snaps to tick marks evenly spaced based * on the `step` property value. * * @attribute snaps * @type boolean * @default false */ snaps: false, /** * If true, a pin with numeric value label is shown when the slider thumb * is pressed. Use for settings for which users need to know the exact * value of the setting. * * @attribute pin * @type boolean * @default false */ pin: false, /** * If true, this slider is disabled. A disabled slider cannot be tapped * or dragged to change the slider value. * * @attribute disabled * @type boolean * @default false */ disabled: false, /** * The number that represents the current secondary progress. * * @attribute secondaryProgress * @type number * @default 0 */ secondaryProgress: 0, /** * If true, an input is shown and user can use it to set the slider value. * * @attribute editable * @type boolean * @default false */ editable: false, /** * The immediate value of the slider. This value is updated while the user * is dragging the slider. * * @attribute immediateValue * @type number * @default 0 */ observe: { 'min max step snaps': 'update' }, ready: function() { this.update(); }, update: function() { this.positionKnob(this.calcRatio(this.value)); this.updateMarkers(); }, valueChanged: function() { this.update(); this.fire('change'); }, expandKnob: function() { this.$.sliderKnob.classList.add('expand'); }, resetKnob: function() { this.expandJob && this.expandJob.stop(); this.$.sliderKnob.classList.remove('expand'); }, positionKnob: function(ratio) { this._ratio = ratio; this.immediateValue = this.calcStep(this.calcKnobPosition()) || 0; if (this.snaps) { this._ratio = this.calcRatio(this.immediateValue); } this.$.sliderKnob.style.left = this._ratio * 100 + '%'; }, immediateValueChanged: function() { this.$.sliderKnob.classList.toggle('ring', this.immediateValue <= this.min); }, inputChange: function() { this.value = this.$.input.value; this.fire('manual-change'); }, calcKnobPosition: function() { return (this.max - this.min) * this._ratio + this.min; }, measureWidth: function() { this._w = this.$.sliderBar.offsetWidth; }, trackStart: function(e) { this.measureWidth(); this._x = this._ratio * this._w; this._startx = this._x || 0; this._minx = - this._startx; this._maxx = this._w - this._startx; this.$.sliderKnob.classList.add('dragging'); e.preventTap(); }, trackx: function(e) { var x = Math.min(this._maxx, Math.max(this._minx, e.dx)); this._x = this._startx + x; this._ratio = this._x / this._w; this.immediateValue = this.calcStep(this.calcKnobPosition()) || 0; var s = this.$.sliderKnob.style; s.transform = s.webkitTransform = 'translate3d(' + (this.snaps ? (this.calcRatio(this.immediateValue) * this._w) - this._startx : x) + 'px, 0, 0)'; }, trackEnd: function() { var s = this.$.sliderKnob.style; s.transform = s.webkitTransform = ''; this.$.sliderKnob.classList.remove('dragging'); this.resetKnob(); this.value = this.immediateValue; this.fire('manual-change'); }, bardown: function(e) { this.measureWidth(); this.$.sliderKnob.classList.add('transiting'); var rect = this.$.sliderBar.getBoundingClientRect(); this.positionKnob((e.x - rect.left) / this._w); this.value = this.calcStep(this.calcKnobPosition()); this.expandJob = this.job(this.expandJob, this.expandKnob, 60); this.fire('manual-change'); }, knobTransitionEnd: function() { this.$.sliderKnob.classList.remove('transiting'); }, updateMarkers: function() { this.markers = [], l = (this.max - this.min) / this.step; for (var i = 0; i < l; i++) { this.markers.push(''); } }, increment: function() { this.value = this.clampValue(this.value + this.step); }, decrement: function() { this.value = this.clampValue(this.value - this.step); }, keydown: function(e) { if (this.disabled) { return; } var c = e.keyCode; if (c === 37) { this.decrement(); this.fire('manual-change'); } else if (c === 39) { this.increment(); this.fire('manual-change'); } } }); </script> </polymer-element>