Home | History | Annotate | Download | only in paper-slider
      1 <!--
      2 Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
      3 This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
      4 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
      5 The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
      6 Code distributed by Google as part of the polymer project is also
      7 subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
      8 -->
      9 
     10 <!--
     11 `paper-slider` allows user to select a value from a range of values by
     12 moving the slider thumb.  The interactive nature of the slider makes it a 
     13 great choice for settings that reflect intensity levels, such as volume, 
     14 brightness, or color saturation.
     15 
     16 Example:
     17 
     18     <paper-slider></paper-slider>
     19 
     20 Use `min` and `max` to specify the slider range.  Default is 0 to 100.
     21 
     22 Example:
     23 
     24     <paper-slider min="10" max="200" value="110"></paper-slider>
     25 
     26 Styling slider:
     27 
     28 To change the slider progress bar color:
     29 
     30     paper-slider::shadow #sliderBar::shadow #activeProgress {
     31       background-color: #0f9d58;
     32     }
     33 
     34 To change the slider knob color:
     35 
     36     paper-slider::shadow #sliderKnobInner {
     37       background-color: #0f9d58;
     38     }
     39 
     40 To change the slider pin color:
     41 
     42     paper-slider::shadow #sliderKnobInner::before {
     43       background-color: #0f9d58;
     44     }
     45 
     46 To change the slider pin's value:
     47 
     48     paper-slider::shadow #sliderKnobInner::after {
     49       color: #0f9d58
     50     }
     51 
     52 To change the slider secondary progress bar color:
     53 
     54     paper-slider::shadow #sliderBar::shadow #secondaryProgress {
     55       background-color: #0f9d58;
     56     }
     57 
     58 @group Paper Elements
     59 @element paper-slider
     60 @extends core-range
     61 @homepage github.io
     62 -->
     63 
     64 <link rel="import" href="../paper-progress/paper-progress.html">
     65 <link rel="import" href="../paper-input/paper-input.html">
     66 
     67 <polymer-element name="paper-slider" extends="core-range" attributes="snaps pin disabled secondaryProgress editable immediateValue">
     68 <template>
     69 
     70   <link rel="stylesheet" href="paper-slider.css">
     71 
     72   <div id="sliderContainer" on-keydown="{{keydown}}">
     73   
     74     <paper-progress id="sliderBar" aria-hidden="true" min="{{min}}" max="{{max}}" value="{{immediateValue}}" secondaryProgress="{{secondaryProgress}}"
     75         on-down="{{bardown}}" on-up="{{resetKnob}}" 
     76         on-trackstart="{{trackStart}}" on-trackx="{{trackx}}" on-trackend="{{trackEnd}}"></paper-progress>
     77     
     78     <template if="{{snaps && !disabled}}">
     79       <div class="slider-markers" horizontal layout>
     80         <template repeat="{{markers}}">
     81           <div flex class="slider-marker"></div>
     82         </template>
     83       </div>
     84     </template>
     85     
     86     <div id="sliderKnob" class="{{ {pin : pin, snaps : snaps} | tokenList }}"
     87         on-down="{{expandKnob}}" on-up="{{resetKnob}}" 
     88         on-trackstart="{{trackStart}}" on-trackx="{{trackx}}" on-trackend="{{trackEnd}}"
     89         on-transitionend="{{knobTransitionEnd}}"
     90         role="slider" aria-valuenow="{{value}}" aria-valuemin="{{min}}" aria-valuemax="{{max}}"
     91         aria-valuetext="{{value}}" tabindex="0"
     92         center-justified center horizontal layout>
     93         
     94       <div id="sliderKnobInner" value="{{immediateValue}}"></div>
     95       
     96     </div>
     97     
     98   </div>
     99   
    100   <template if="{{editable}}">
    101     <paper-input id="input" class="slider-input" value="{{immediateValue}}" validate="^[-+]?[0-9]*\.?[0-9]+$" disabled?="{{disabled}}" on-change="{{inputChange}}"></paper-input>
    102   </template>
    103 
    104 </template>
    105 <script>
    106 
    107   Polymer('paper-slider', {
    108     
    109     /**
    110      * Fired when the slider's value changes.
    111      *
    112      * @event change
    113      */
    114 
    115     /**
    116      * Fired when the slider's value changes due to manual interaction.
    117      *
    118      * Changes to the slider's value due to changes in an underlying
    119      * bound variable will not trigger this event.
    120      *
    121      * @event manual-change
    122      */
    123      
    124     /**
    125      * If true, the slider thumb snaps to tick marks evenly spaced based
    126      * on the `step` property value.
    127      *
    128      * @attribute snaps
    129      * @type boolean
    130      * @default false
    131      */
    132     snaps: false,
    133     
    134     /**
    135      * If true, a pin with numeric value label is shown when the slider thumb 
    136      * is pressed.  Use for settings for which users need to know the exact 
    137      * value of the setting.
    138      *
    139      * @attribute pin
    140      * @type boolean
    141      * @default false
    142      */
    143     pin: false,
    144     
    145     /**
    146      * If true, this slider is disabled.  A disabled slider cannot be tapped
    147      * or dragged to change the slider value.
    148      *
    149      * @attribute disabled
    150      * @type boolean
    151      * @default false
    152      */
    153     disabled: false,
    154     
    155     /**
    156      * The number that represents the current secondary progress.
    157      *
    158      * @attribute secondaryProgress
    159      * @type number
    160      * @default 0
    161      */
    162     secondaryProgress: 0,
    163     
    164     /**
    165      * If true, an input is shown and user can use it to set the slider value.
    166      *
    167      * @attribute editable
    168      * @type boolean
    169      * @default false
    170      */
    171     editable: false,
    172     
    173     /**
    174      * The immediate value of the slider.  This value is updated while the user
    175      * is dragging the slider.
    176      *
    177      * @attribute immediateValue
    178      * @type number
    179      * @default 0
    180      */
    181     
    182     observe: {
    183       'min max step snaps': 'update'
    184     },
    185     
    186     ready: function() {
    187       this.update();
    188     },
    189     
    190     update: function() {
    191       this.positionKnob(this.calcRatio(this.value));
    192       this.updateMarkers();
    193     },
    194     
    195     valueChanged: function() {
    196       this.update();
    197       this.fire('change');
    198     },
    199     
    200     expandKnob: function() {
    201       this.$.sliderKnob.classList.add('expand');
    202     },
    203     
    204     resetKnob: function() {
    205       this.expandJob && this.expandJob.stop();
    206       this.$.sliderKnob.classList.remove('expand');
    207     },
    208     
    209     positionKnob: function(ratio) {
    210       this._ratio = ratio;
    211       this.immediateValue = this.calcStep(this.calcKnobPosition()) || 0;
    212       if (this.snaps) {
    213         this._ratio = this.calcRatio(this.immediateValue);
    214       }
    215       this.$.sliderKnob.style.left = this._ratio * 100 + '%';
    216     },
    217     
    218     immediateValueChanged: function() {
    219       this.$.sliderKnob.classList.toggle('ring', this.immediateValue <= this.min);
    220     },
    221     
    222     inputChange: function() {
    223       this.value = this.$.input.value;
    224       this.fire('manual-change');
    225     },
    226     
    227     calcKnobPosition: function() {
    228       return (this.max - this.min) * this._ratio + this.min;
    229     },
    230     
    231     measureWidth: function() {
    232       this._w = this.$.sliderBar.offsetWidth;
    233     },
    234     
    235     trackStart: function(e) {
    236       this.measureWidth();
    237       this._x = this._ratio * this._w;
    238       this._startx = this._x || 0;
    239       this._minx = - this._startx;
    240       this._maxx = this._w - this._startx;
    241       this.$.sliderKnob.classList.add('dragging');
    242       e.preventTap();
    243     },
    244 
    245     trackx: function(e) {
    246       var x = Math.min(this._maxx, Math.max(this._minx, e.dx));
    247       this._x = this._startx + x;
    248       this._ratio = this._x / this._w;
    249       this.immediateValue = this.calcStep(this.calcKnobPosition()) || 0;
    250       var s =  this.$.sliderKnob.style;
    251       s.transform = s.webkitTransform = 'translate3d(' + (this.snaps ? 
    252           (this.calcRatio(this.immediateValue) * this._w) - this._startx : x) + 'px, 0, 0)';
    253     },
    254     
    255     trackEnd: function() {
    256       var s =  this.$.sliderKnob.style;
    257       s.transform = s.webkitTransform = '';
    258       this.$.sliderKnob.classList.remove('dragging');
    259       this.resetKnob();
    260       this.value = this.immediateValue;
    261       this.fire('manual-change');
    262     },
    263     
    264     bardown: function(e) {
    265       this.measureWidth();
    266       this.$.sliderKnob.classList.add('transiting');
    267       var rect = this.$.sliderBar.getBoundingClientRect();
    268       this.positionKnob((e.x - rect.left) / this._w);
    269       this.value = this.calcStep(this.calcKnobPosition());
    270       this.expandJob = this.job(this.expandJob, this.expandKnob, 60);
    271       this.fire('manual-change');
    272     },
    273     
    274     knobTransitionEnd: function() {
    275       this.$.sliderKnob.classList.remove('transiting');
    276     },
    277     
    278     updateMarkers: function() {
    279       this.markers = [], l = (this.max - this.min) / this.step;
    280       for (var i = 0; i < l; i++) {
    281         this.markers.push('');
    282       }
    283     },
    284     
    285     increment: function() {
    286       this.value = this.clampValue(this.value + this.step);
    287     },
    288     
    289     decrement: function() {
    290       this.value = this.clampValue(this.value - this.step);
    291     },
    292     
    293     keydown: function(e) {
    294       if (this.disabled) {
    295         return;
    296       }
    297       var c = e.keyCode;
    298       if (c === 37) {
    299         this.decrement();
    300         this.fire('manual-change');
    301       } else if (c === 39) {
    302         this.increment();
    303         this.fire('manual-change');
    304       }
    305     }
    306 
    307   });
    308 
    309 </script>
    310 </polymer-element>
    311