| OLD | NEW |
| (Empty) | |
| 1 <!-- |
| 2 @license |
| 3 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. |
| 4 This code may only be used under the BSD style license found at http://polymer.g
ithub.io/LICENSE |
| 5 The complete set of authors may be found at http://polymer.github.io/AUTHORS |
| 6 The complete set of contributors may be found at http://polymer.github.io/CONTRI
BUTORS |
| 7 Code distributed by Google as part of the polymer project is also |
| 8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS |
| 9 --> |
| 10 |
| 11 <link rel="import" href="../polymer/polymer.html"> |
| 12 <link rel="import" href="../paper-styles/paper-styles.html"> |
| 13 <link rel="import" href="../paper-progress/paper-progress.html"> |
| 14 <link rel="import" href="../paper-input/paper-input.html"> |
| 15 <link rel="import" href="../paper-behaviors/paper-inky-focus-behavior.html"> |
| 16 <link rel="import" href="../paper-ripple/paper-ripple.html"> |
| 17 <link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html
"> |
| 18 <link rel="import" href="../iron-form-element-behavior/iron-form-element-behavio
r.html"> |
| 19 |
| 20 <!-- |
| 21 `paper-slider` allows user to select a value from a range of values by |
| 22 moving the slider thumb. The interactive nature of the slider makes it a |
| 23 great choice for settings that reflect intensity levels, such as volume, |
| 24 brightness, or color saturation. |
| 25 |
| 26 Example: |
| 27 |
| 28 <paper-slider></paper-slider> |
| 29 |
| 30 Use `min` and `max` to specify the slider range. Default is 0 to 100. |
| 31 |
| 32 Example: |
| 33 |
| 34 <paper-slider min="10" max="200" value="110"></paper-slider> |
| 35 |
| 36 Styling slider: |
| 37 |
| 38 To change the slider progress bar color: |
| 39 |
| 40 paper-slider { |
| 41 --paper-slider-active-color: #0f9d58; |
| 42 } |
| 43 |
| 44 To change the slider knob color: |
| 45 |
| 46 paper-slider { |
| 47 --paper-slider-knob-color: #0f9d58; |
| 48 } |
| 49 |
| 50 To change the slider pin color: |
| 51 |
| 52 paper-slider { |
| 53 --paper-slider-pin-color: #0f9d58; |
| 54 } |
| 55 |
| 56 To change the slider pin's font color: |
| 57 |
| 58 paper-slider { |
| 59 --paper-slider-pin-font-color: #0f9d58; |
| 60 } |
| 61 |
| 62 To change the slider secondary progress bar color: |
| 63 |
| 64 paper-slider { |
| 65 --paper-slider-secondary-color: #0f9d58; |
| 66 } |
| 67 |
| 68 To change the slider disabled active color: |
| 69 |
| 70 paper-slider { |
| 71 --paper-slider-disabled-active-color: #ccc; |
| 72 } |
| 73 |
| 74 To change the slider disabled secondary progress bar color: |
| 75 |
| 76 paper-slider { |
| 77 --paper-slider-disabled-secondary-color: #ccc; |
| 78 } |
| 79 |
| 80 @group Paper Elements |
| 81 @element paper-slider |
| 82 @demo demo/index.html |
| 83 @hero hero.svg |
| 84 --> |
| 85 |
| 86 <dom-module id="paper-slider"> |
| 87 |
| 88 <link rel="import" type="css" href="paper-slider.css"> |
| 89 |
| 90 <template> |
| 91 <div id="sliderContainer" |
| 92 class$="[[_getClassNames(disabled, pin, snaps, immediateValue, min, expand
, dragging, transiting, editable)]]"> |
| 93 |
| 94 <div class="bar-container"> |
| 95 <paper-progress |
| 96 id="sliderBar" |
| 97 aria-hidden="true" |
| 98 min="[[min]]" |
| 99 max="[[max]]" |
| 100 step="[[step]]" |
| 101 value="[[immediateValue]]" |
| 102 secondary-progress="[[secondaryProgress]]" |
| 103 on-down="_bardown" |
| 104 on-up="_resetKnob" |
| 105 on-track="_onTrack"> |
| 106 </paper-progress> |
| 107 </div> |
| 108 |
| 109 <template is="dom-if" if="[[snaps]]"> |
| 110 <div class="slider-markers horizontal layout"> |
| 111 <template is="dom-repeat" items="[[markers]]"> |
| 112 <div class="slider-marker flex"></div> |
| 113 </template> |
| 114 </div> |
| 115 </template> |
| 116 |
| 117 <div id="sliderKnob" |
| 118 class="center-justified center horizontal layout" |
| 119 on-down="_knobdown" |
| 120 on-up="_resetKnob" |
| 121 on-track="_onTrack" |
| 122 on-transitionend="_knobTransitionEnd"> |
| 123 <paper-ripple id="ink" class="circle" center></paper-ripple> |
| 124 <div id="sliderKnobInner" value$="[[immediateValue]]"></div> |
| 125 </div> |
| 126 </div> |
| 127 |
| 128 <template is="dom-if" if="[[editable]]"> |
| 129 <paper-input |
| 130 id="input" |
| 131 class="slider-input" |
| 132 disabled$="[[disabled]]" |
| 133 on-change="_inputChange"> |
| 134 </paper-input> |
| 135 </template> |
| 136 </template> |
| 137 |
| 138 </dom-module> |
| 139 |
| 140 <script> |
| 141 /** |
| 142 * Fired when the slider's value changes. |
| 143 * |
| 144 * @event value-change |
| 145 */ |
| 146 |
| 147 /** |
| 148 * Fired when the slider's immediateValue changes. |
| 149 * |
| 150 * @event immediate-value-change |
| 151 */ |
| 152 |
| 153 /** |
| 154 * Fired when the slider's value changes due to user interaction. |
| 155 * |
| 156 * Changes to the slider's value due to changes in an underlying |
| 157 * bound variable will not trigger this event. |
| 158 * |
| 159 * @event change |
| 160 */ |
| 161 |
| 162 Polymer({ |
| 163 is: 'paper-slider', |
| 164 |
| 165 behaviors: [ |
| 166 Polymer.IronRangeBehavior, |
| 167 Polymer.IronA11yKeysBehavior, |
| 168 Polymer.IronFormElementBehavior, |
| 169 Polymer.PaperInkyFocusBehavior |
| 170 ], |
| 171 |
| 172 properties: { |
| 173 /** |
| 174 * If true, the slider thumb snaps to tick marks evenly spaced based |
| 175 * on the `step` property value. |
| 176 */ |
| 177 snaps: { |
| 178 type: Boolean, |
| 179 value: false, |
| 180 notify: true |
| 181 }, |
| 182 |
| 183 /** |
| 184 * If true, a pin with numeric value label is shown when the slider thumb |
| 185 * is pressed. Use for settings for which users need to know the exact |
| 186 * value of the setting. |
| 187 */ |
| 188 pin: { |
| 189 type: Boolean, |
| 190 value: false, |
| 191 notify: true |
| 192 }, |
| 193 |
| 194 /** |
| 195 * The number that represents the current secondary progress. |
| 196 */ |
| 197 secondaryProgress: { |
| 198 type: Number, |
| 199 value: 0, |
| 200 notify: true, |
| 201 observer: '_secondaryProgressChanged' |
| 202 }, |
| 203 |
| 204 /** |
| 205 * If true, an input is shown and user can use it to set the slider value. |
| 206 */ |
| 207 editable: { |
| 208 type: Boolean, |
| 209 value: false |
| 210 }, |
| 211 |
| 212 /** |
| 213 * The immediate value of the slider. This value is updated while the use
r |
| 214 * is dragging the slider. |
| 215 */ |
| 216 immediateValue: { |
| 217 type: Number, |
| 218 value: 0, |
| 219 readOnly: true |
| 220 }, |
| 221 |
| 222 /** |
| 223 * The maximum number of markers |
| 224 */ |
| 225 maxMarkers: { |
| 226 type: Number, |
| 227 value: 0, |
| 228 notify: true, |
| 229 observer: '_maxMarkersChanged' |
| 230 }, |
| 231 |
| 232 /** |
| 233 * If true, the knob is expanded |
| 234 */ |
| 235 expand: { |
| 236 type: Boolean, |
| 237 value: false, |
| 238 readOnly: true |
| 239 }, |
| 240 |
| 241 /** |
| 242 * True when the user is dragging the slider. |
| 243 */ |
| 244 dragging: { |
| 245 type: Boolean, |
| 246 value: false, |
| 247 readOnly: true |
| 248 }, |
| 249 |
| 250 transiting: { |
| 251 type: Boolean, |
| 252 value: false, |
| 253 readOnly: true |
| 254 }, |
| 255 |
| 256 markers: { |
| 257 type: Array, |
| 258 readOnly: true, |
| 259 value: [] |
| 260 }, |
| 261 }, |
| 262 |
| 263 observers: [ |
| 264 '_updateKnob(value, min, max, snaps, step)', |
| 265 '_minChanged(min)', |
| 266 '_maxChanged(max)', |
| 267 '_valueChanged(value)', |
| 268 '_immediateValueChanged(immediateValue)' |
| 269 ], |
| 270 |
| 271 hostAttributes: { |
| 272 role: 'slider', |
| 273 tabindex: 0 |
| 274 }, |
| 275 |
| 276 keyBindings: { |
| 277 'left down pagedown home': '_decrementKey', |
| 278 'right up pageup end': '_incrementKey' |
| 279 }, |
| 280 |
| 281 ready: function() { |
| 282 // issue polymer/polymer#1305 |
| 283 |
| 284 this.async(function() { |
| 285 this._updateKnob(this.value); |
| 286 this._updateInputValue(); |
| 287 }, 1); |
| 288 }, |
| 289 |
| 290 /** |
| 291 * Increases value by `step` but not above `max`. |
| 292 * @method increment |
| 293 */ |
| 294 increment: function() { |
| 295 this.value = this._clampValue(this.value + this.step); |
| 296 }, |
| 297 |
| 298 /** |
| 299 * Decreases value by `step` but not below `min`. |
| 300 * @method decrement |
| 301 */ |
| 302 decrement: function() { |
| 303 this.value = this._clampValue(this.value - this.step); |
| 304 }, |
| 305 |
| 306 _updateKnob: function(value) { |
| 307 this._positionKnob(this._calcRatio(value)); |
| 308 }, |
| 309 |
| 310 _minChanged: function() { |
| 311 this.setAttribute('aria-valuemin', this.min); |
| 312 }, |
| 313 |
| 314 _maxChanged: function() { |
| 315 this.setAttribute('aria-valuemax', this.max); |
| 316 }, |
| 317 |
| 318 _valueChanged: function() { |
| 319 this.setAttribute('aria-valuenow', this.value); |
| 320 this.fire('value-change'); |
| 321 }, |
| 322 |
| 323 _immediateValueChanged: function() { |
| 324 if (this.dragging) { |
| 325 this.fire('immediate-value-change'); |
| 326 } else { |
| 327 this.value = this.immediateValue; |
| 328 } |
| 329 this._updateInputValue(); |
| 330 }, |
| 331 |
| 332 _secondaryProgressChanged: function() { |
| 333 this.secondaryProgress = this._clampValue(this.secondaryProgress); |
| 334 }, |
| 335 |
| 336 _updateInputValue: function() { |
| 337 if (this.editable) { |
| 338 this.$$('#input').value = this.immediateValue.toString(); |
| 339 } |
| 340 }, |
| 341 |
| 342 _expandKnob: function() { |
| 343 this.$.ink.holdDown = false; |
| 344 this._setExpand(true); |
| 345 }, |
| 346 |
| 347 _resetKnob: function() { |
| 348 this.cancelDebouncer('expandKnob'); |
| 349 this._setExpand(false); |
| 350 this.$.ink.hidden = true; |
| 351 }, |
| 352 |
| 353 _positionKnob: function(ratio) { |
| 354 this._setImmediateValue(this._calcStep(this._calcKnobPosition(ratio))); |
| 355 this._setRatio(this._calcRatio(this.immediateValue)); |
| 356 |
| 357 this.$.sliderKnob.style.left = (this.ratio * 100) + '%'; |
| 358 }, |
| 359 |
| 360 _inputChange: function() { |
| 361 this.value = this.$$('#input').value; |
| 362 this.fire('change'); |
| 363 }, |
| 364 |
| 365 _calcKnobPosition: function(ratio) { |
| 366 return (this.max - this.min) * ratio + this.min; |
| 367 }, |
| 368 |
| 369 _onTrack: function(event) { |
| 370 switch (event.detail.state) { |
| 371 case 'start': |
| 372 this._trackStart(event); |
| 373 break; |
| 374 case 'track': |
| 375 this._trackX(event); |
| 376 break; |
| 377 case 'end': |
| 378 this._trackEnd(); |
| 379 break; |
| 380 } |
| 381 }, |
| 382 |
| 383 _trackStart: function(event) { |
| 384 this._w = this.$.sliderBar.offsetWidth; |
| 385 this._x = this.ratio * this._w; |
| 386 this._startx = this._x || 0; |
| 387 this._minx = - this._startx; |
| 388 this._maxx = this._w - this._startx; |
| 389 this.$.sliderKnob.classList.add('dragging'); |
| 390 |
| 391 this._setDragging(true); |
| 392 }, |
| 393 |
| 394 _trackX: function(e) { |
| 395 if (!this.dragging) { |
| 396 this._trackStart(e); |
| 397 } |
| 398 |
| 399 var dx = Math.min(this._maxx, Math.max(this._minx, e.detail.dx)); |
| 400 this._x = this._startx + dx; |
| 401 |
| 402 var immediateValue = this._calcStep(this._calcKnobPosition(this._x / this.
_w)); |
| 403 this._setImmediateValue(immediateValue); |
| 404 |
| 405 // update knob's position |
| 406 var translateX = ((this._calcRatio(immediateValue) * this._w) - this._star
tx); |
| 407 this.translate3d(translateX + 'px', 0, 0, this.$.sliderKnob); |
| 408 }, |
| 409 |
| 410 _trackEnd: function() { |
| 411 var s = this.$.sliderKnob.style; |
| 412 |
| 413 this.$.sliderKnob.classList.remove('dragging'); |
| 414 this._setDragging(false); |
| 415 this._resetKnob(); |
| 416 this.value = this.immediateValue; |
| 417 |
| 418 s.transform = s.webkitTransform = ''; |
| 419 |
| 420 this.fire('change'); |
| 421 }, |
| 422 |
| 423 _knobdown: function(event) { |
| 424 this._expandKnob(); |
| 425 |
| 426 // cancel selection |
| 427 event.detail.sourceEvent.preventDefault(); |
| 428 |
| 429 // set the focus manually because we will called prevent default |
| 430 this.focus(); |
| 431 }, |
| 432 |
| 433 _bardown: function(event) { |
| 434 this.$.ink.hidden = true; |
| 435 |
| 436 event.preventDefault(); |
| 437 |
| 438 this._w = this.$.sliderBar.offsetWidth; |
| 439 var rect = this.$.sliderBar.getBoundingClientRect(); |
| 440 var ratio = (event.detail.x - rect.left) / this._w; |
| 441 var prevRatio = this.ratio; |
| 442 |
| 443 this._setTransiting(true); |
| 444 |
| 445 this._positionKnob(ratio); |
| 446 |
| 447 this.debounce('expandKnob', this._expandKnob, 60); |
| 448 |
| 449 // if the ratio doesn't change, sliderKnob's animation won't start |
| 450 // and `_knobTransitionEnd` won't be called |
| 451 // Therefore, we need to manually update the `transiting` state |
| 452 |
| 453 if (prevRatio === this.ratio) { |
| 454 this._setTransiting(false); |
| 455 } |
| 456 |
| 457 this.async(function() { |
| 458 this.fire('change'); |
| 459 }); |
| 460 |
| 461 // cancel selection |
| 462 event.detail.sourceEvent.preventDefault(); |
| 463 }, |
| 464 |
| 465 _knobTransitionEnd: function(event) { |
| 466 if (event.target === this.$.sliderKnob) { |
| 467 this._setTransiting(false); |
| 468 } |
| 469 }, |
| 470 |
| 471 _maxMarkersChanged: function(maxMarkers) { |
| 472 var l = (this.max - this.min) / this.step; |
| 473 if (!this.snaps && l > maxMarkers) { |
| 474 this._setMarkers([]); |
| 475 } else { |
| 476 this._setMarkers(new Array(l)); |
| 477 } |
| 478 }, |
| 479 |
| 480 _getClassNames: function() { |
| 481 var classes = {}; |
| 482 |
| 483 classes.disabled = this.disabled; |
| 484 classes.pin = this.pin; |
| 485 classes.snaps = this.snaps; |
| 486 classes.ring = this.immediateValue <= this.min; |
| 487 classes.expand = this.expand; |
| 488 classes.dragging = this.dragging; |
| 489 classes.transiting = this.transiting; |
| 490 classes.editable = this.editable; |
| 491 |
| 492 return Object.keys(classes).filter( |
| 493 function(className) { |
| 494 return classes[className]; |
| 495 }).join(' '); |
| 496 }, |
| 497 |
| 498 _incrementKey: function(event) { |
| 499 if (event.detail.key === 'end') { |
| 500 this.value = this.max; |
| 501 } else { |
| 502 this.increment(); |
| 503 } |
| 504 this.fire('change'); |
| 505 }, |
| 506 |
| 507 _decrementKey: function(event) { |
| 508 if (event.detail.key === 'home') { |
| 509 this.value = this.min; |
| 510 } else { |
| 511 this.decrement(); |
| 512 } |
| 513 this.fire('change'); |
| 514 } |
| 515 }) |
| 516 </script> |
| OLD | NEW |