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