OLD | NEW |
1 <!-- | 1 <!-- |
2 Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | 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.g
ithub.io/LICENSE | 3 This code may only be used under the BSD style license found at http://polymer.g
ithub.io/LICENSE |
4 The complete set of authors may be found at http://polymer.github.io/AUTHORS | 4 The complete set of authors may be found at http://polymer.github.io/AUTHORS |
5 The complete set of contributors may be found at http://polymer.github.io/CONTRI
BUTORS | 5 The complete set of contributors may be found at http://polymer.github.io/CONTRI
BUTORS |
6 Code distributed by Google as part of the polymer project is also | 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/PATEN
TS | 7 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS |
8 --> | 8 --> |
9 | 9 |
10 <!-- | 10 <!-- |
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
168 :host(.invalid) .unfocused-underline, | 168 :host(.invalid) .unfocused-underline, |
169 :host(.invalid) .focused-underline { | 169 :host(.invalid) .focused-underline { |
170 background-color: {{g.paperInput.invalidColor}}; | 170 background-color: {{g.paperInput.invalidColor}}; |
171 } | 171 } |
172 | 172 |
173 </core-style> | 173 </core-style> |
174 | 174 |
175 <polymer-element name="paper-input-decorator" layout vertical | 175 <polymer-element name="paper-input-decorator" layout vertical |
176 on-transitionEnd="{{transitionEndAction}}" on-webkitTransitionEnd="{{transitio
nEndAction}}" | 176 on-transitionEnd="{{transitionEndAction}}" on-webkitTransitionEnd="{{transitio
nEndAction}}" |
177 on-input="{{inputAction}}" | 177 on-input="{{inputAction}}" |
178 on-down="{{downAction}}"> | 178 on-down="{{downAction}}" |
| 179 on-tap="{{tapAction}}"> |
179 | 180 |
180 <template> | 181 <template> |
181 | 182 |
182 <link href="paper-input-decorator.css" rel="stylesheet"> | 183 <link href="paper-input-decorator.css" rel="stylesheet"> |
183 <core-style ref="paper-input-decorator"></core-style> | 184 <core-style ref="paper-input-decorator"></core-style> |
184 | 185 |
185 <div class="floated-label" aria-hidden="true" hidden?="{{!floatingLabel}}" i
nvisible?="{{!floatingLabelVisible || labelAnimated}}"> | 186 <div class="floated-label" aria-hidden="true" hidden?="{{!floatingLabel}}" i
nvisible?="{{!floatingLabelVisible || labelAnimated}}"> |
186 <!-- needed for floating label animation measurement --> | 187 <!-- needed for floating label animation measurement --> |
187 <span id="floatedLabelText" class="label-text">{{label}}</span> | 188 <span id="floatedLabelText" class="label-text">{{label}}</span> |
188 </div> | 189 </div> |
189 | 190 |
190 <div class="input-body" flex auto relative> | 191 <div class="input-body" flex auto relative> |
191 | 192 |
192 <div class="label" fit invisible aria-hidden="true"> | 193 <div class="label" fit invisible aria-hidden="true"> |
193 <!-- needed for floating label animation measurement --> | 194 <!-- needed for floating label animation measurement --> |
194 <span id="labelText" class="label-text" invisible?="{{!_labelVisible}}"
animated?="{{labelAnimated}}">{{label}}</span> | 195 <span id="labelText" class="label-text" invisible?="{{!_labelVisible}}"
animated?="{{labelAnimated}}">{{label}}</span> |
195 </div> | 196 </div> |
196 | 197 |
197 <content></content> | 198 <content></content> |
198 | 199 |
199 </div> | 200 </div> |
200 | 201 |
201 <div id="underline" class="underline" relative> | 202 <div id="underline" class="underline" relative> |
202 <div class="unfocused-underline" fit invisible?="{{disabled}}"></div> | 203 <div class="unfocused-underline" fit invisible?="{{disabled}}"></div> |
203 <div id="focusedUnderline" class="focused-underline" fit invisible?="{{!fo
cused}}" animated?="{{underlineAnimated}}"></div> | 204 <div id="focusedUnderline" class="focused-underline" fit invisible?="{{!un
derlineVisible}}" animated?="{{underlineAnimated}}"></div> |
204 </div> | 205 </div> |
205 | 206 |
206 <div class="error" layout horizontal center hidden?="{{!isInvalid}}"> | 207 <div class="error" layout horizontal center hidden?="{{!isInvalid}}"> |
207 <div class="error-text" flex auto role="alert" aria-hidden="{{!isInvalid}}
">{{error}}</div> | 208 <div class="error-text" flex auto role="alert" aria-hidden="{{!isInvalid}}
">{{error}}</div> |
208 <core-icon class="error-icon" icon="warning"></core-icon> | 209 <core-icon class="error-icon" icon="warning"></core-icon> |
209 </div> | 210 </div> |
210 | 211 |
211 </template> | 212 </template> |
212 | 213 |
213 <script> | 214 <script> |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
268 /** | 269 /** |
269 * Set this property to true to show the error message. | 270 * Set this property to true to show the error message. |
270 * | 271 * |
271 * @attribute isInvalid | 272 * @attribute isInvalid |
272 * @type boolean | 273 * @type boolean |
273 * @default false | 274 * @default false |
274 */ | 275 */ |
275 isInvalid: false, | 276 isInvalid: false, |
276 | 277 |
277 /** | 278 /** |
| 279 * Set this property to true to validate the input as the user types. |
| 280 * This will not validate when changing the input programmatically; call |
| 281 * `validate()` instead. |
| 282 * |
| 283 * @attribute autoValidate |
| 284 * @type boolean |
| 285 * @default false |
| 286 */ |
| 287 autoValidate: false, |
| 288 |
| 289 /** |
278 * The message to display if the input value fails validation. If this | 290 * The message to display if the input value fails validation. If this |
279 * is unset or the empty string, a default message is displayed dependin
g | 291 * is unset or the empty string, a default message is displayed dependin
g |
280 * on the type of validation error. | 292 * on the type of validation error. |
281 * | 293 * |
282 * @attribute error | 294 * @attribute error |
283 * @type string | 295 * @type string |
284 */ | 296 */ |
285 error: '', | 297 error: '', |
286 | 298 |
287 focused: {value: false, reflect: true} | 299 focused: {value: false, reflect: true} |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
356 this.$.labelText.style.transform = ''; | 368 this.$.labelText.style.transform = ''; |
357 } else { | 369 } else { |
358 this.$.labelText.style.webkitTransform = this.$.labelText.cachedTransf
orm; | 370 this.$.labelText.style.webkitTransform = this.$.labelText.cachedTransf
orm; |
359 this.$.labelText.style.transform = this.$.labelText.cachedTransform; | 371 this.$.labelText.style.transform = this.$.labelText.cachedTransform; |
360 this.input.placeholder = ''; | 372 this.input.placeholder = ''; |
361 } | 373 } |
362 | 374 |
363 return true; | 375 return true; |
364 }, | 376 }, |
365 | 377 |
| 378 animateUnderline: function(e) { |
| 379 if (this.focused) { |
| 380 var rect = this.$.underline.getBoundingClientRect(); |
| 381 var right = e.x - rect.left; |
| 382 this.$.focusedUnderline.style.mozTransformOrigin = right + 'px'; |
| 383 this.$.focusedUnderline.style.webkitTransformOrigin = right + 'px '; |
| 384 this.$.focusedUnderline.style.transformOriginX = right + 'px'; |
| 385 |
| 386 // Animations only run when the user interacts with the input |
| 387 this.underlineAnimated = true; |
| 388 } |
| 389 }, |
| 390 |
| 391 /** |
| 392 * Validate the input using HTML5 Constraints. |
| 393 * |
| 394 * @method validate |
| 395 * @return {boolean} True if the input is valid. |
| 396 */ |
| 397 validate: function() { |
| 398 this.isInvalid = !this.input.validity.valid; |
| 399 return this.input.validity.valid; |
| 400 }, |
| 401 |
366 _labelVisibleChanged: function(old) { | 402 _labelVisibleChanged: function(old) { |
367 // do not do the animation on first render | 403 // do not do the animation on first render |
368 if (old !== undefined) { | 404 if (old !== undefined) { |
369 if (!this.animateFloatingLabel()) { | 405 if (!this.animateFloatingLabel()) { |
370 this.updateInputLabel(this.input, this.label); | 406 this.updateInputLabel(this.input, this.label); |
371 } | 407 } |
372 } | 408 } |
373 }, | 409 }, |
374 | 410 |
375 labelVisibleChanged: function() { | 411 labelVisibleChanged: function() { |
376 if (this.labelVisible === 'true') { | 412 if (this.labelVisible === 'true') { |
377 this.labelVisible = true; | 413 this.labelVisible = true; |
378 } else if (this.labelVisible === 'false') { | 414 } else if (this.labelVisible === 'false') { |
379 this.labelVisible = false; | 415 this.labelVisible = false; |
380 } | 416 } |
381 }, | 417 }, |
382 | 418 |
383 labelChanged: function() { | 419 labelChanged: function() { |
384 if (this.input) { | 420 if (this.input) { |
385 this.updateInputLabel(this.input, this.label); | 421 this.updateInputLabel(this.input, this.label); |
386 } | 422 } |
387 }, | 423 }, |
388 | 424 |
389 isInvalidChanged: function() { | 425 isInvalidChanged: function() { |
390 this.classList.toggle('invalid', this.isInvalid); | 426 this.classList.toggle('invalid', this.isInvalid); |
391 }, | 427 }, |
392 | 428 |
393 focusedChanged: function() { | 429 focusedChanged: function() { |
394 this.updateLabelVisibility(this.input && this.input.value); | 430 this.updateLabelVisibility(this.input && this.input.value); |
| 431 if (this.lastEvent) { |
| 432 this.animateUnderline(this.lastEvent); |
| 433 this.lastEvent = null; |
| 434 } |
| 435 this.underlineVisible = this.focused; |
395 }, | 436 }, |
396 | 437 |
397 inputChanged: function(old) { | 438 inputChanged: function(old) { |
398 if (this.input) { | 439 if (this.input) { |
399 this.updateLabelVisibility(this.input.value); | 440 this.updateLabelVisibility(this.input.value); |
400 this.updateInputLabel(this.input, this.label); | 441 this.updateInputLabel(this.input, this.label); |
| 442 |
| 443 if (this.autoValidate) { |
| 444 this.validate(); |
| 445 } |
401 } | 446 } |
402 if (old) { | 447 if (old) { |
403 this.updateInputLabel(old, ''); | 448 this.updateInputLabel(old, ''); |
404 } | 449 } |
405 }, | 450 }, |
406 | 451 |
407 focusAction: function() { | 452 focusAction: function() { |
408 this.focused = true; | 453 this.focused = true; |
409 }, | 454 }, |
410 | 455 |
411 blurAction: function(e) { | 456 blurAction: function() { |
412 this.focused = false; | 457 this.focused = false; |
413 }, | 458 }, |
414 | 459 |
415 /** | 460 /** |
416 * Updates the label visibility based on a value. This is handled automati
cally | 461 * Updates the label visibility based on a value. This is handled automati
cally |
417 * if the user is typing, but if you imperatively set the input value you
need | 462 * if the user is typing, but if you imperatively set the input value you
need |
418 * to call this function. | 463 * to call this function. |
419 * | 464 * |
420 * @method updateLabelVisibility | 465 * @method updateLabelVisibility |
421 * @param {string} value | 466 * @param {string} value |
422 */ | 467 */ |
423 updateLabelVisibility: function(value) { | 468 updateLabelVisibility: function(value) { |
424 var v = (value !== null && value !== undefined) ? String(value) : value; | 469 var v = (value !== null && value !== undefined) ? String(value) : value; |
425 this._autoLabelVisible = (!this.focused && !v) || (!this.floatingLabel &
& !v); | 470 this._autoLabelVisible = (!this.focused && !v) || (!this.floatingLabel &
& !v); |
426 }, | 471 }, |
427 | 472 |
428 updateInputLabel: function(input, label) { | 473 updateInputLabel: function(input, label) { |
429 if (this._labelVisible) { | 474 if (this._labelVisible) { |
430 this.input.placeholder = this.label; | 475 this.input.placeholder = this.label; |
431 } else { | 476 } else { |
432 this.input.placeholder = ''; | 477 this.input.placeholder = ''; |
433 } | 478 } |
434 if (label) { | 479 if (label) { |
435 input.setAttribute('aria-label', label); | 480 input.setAttribute('aria-label', label); |
436 } else { | 481 } else { |
437 input.removeAttribute('aria-label'); | 482 input.removeAttribute('aria-label'); |
438 } | 483 } |
439 }, | 484 }, |
440 | 485 |
441 inputAction: function(e) { | 486 inputAction: function() { |
442 this.updateLabelVisibility(e.target.value); | 487 this.updateLabelVisibility(this.input.value); |
| 488 if (this.autoValidate) { |
| 489 this.validate(); |
| 490 } |
443 }, | 491 }, |
444 | 492 |
445 downAction: function(e) { | 493 downAction: function(e) { |
| 494 // eat the event and do nothing if already focused |
| 495 if (e.target !== this.input && this.focused) { |
| 496 e.preventDefault(); |
| 497 return; |
| 498 } |
| 499 // cache the event here because "down" fires before "focus" when tapping
on |
| 500 // the input and the underline animation runs on focus change |
| 501 this.lastEvent = e; |
| 502 }, |
| 503 |
| 504 tapAction: function(e) { |
446 if (this.disabled) { | 505 if (this.disabled) { |
447 return; | 506 return; |
448 } | 507 } |
449 | 508 |
450 if (this.focused) { | 509 if (this.focused) { |
451 return; | 510 return; |
452 } | 511 } |
453 | 512 |
454 if (this.input) { | 513 if (this.input) { |
455 this.input.focus(); | 514 this.input.focus(); |
456 e.preventDefault(); | 515 e.preventDefault(); |
457 } | 516 } |
458 | |
459 // The underline spills from the tap location | |
460 var rect = this.$.underline.getBoundingClientRect(); | |
461 var right = e.x - rect.left; | |
462 this.$.focusedUnderline.style.mozTransformOrigin = right + 'px'; | |
463 this.$.focusedUnderline.style.webkitTransformOrigin = right + 'px '; | |
464 this.$.focusedUnderline.style.transformOriginX = right + 'px'; | |
465 | |
466 // Animations only run when the user interacts with the input | |
467 this.underlineAnimated = true; | |
468 | |
469 // Handle interrupted animation | |
470 this.async(function() { | |
471 this.transitionEndAction(); | |
472 }, null, 250); | |
473 }, | 517 }, |
474 | 518 |
475 transitionEndAction: function() { | 519 transitionEndAction: function() { |
476 this.underlineAnimated = false; | 520 this.underlineAnimated = false; |
477 this.labelAnimated = false; | 521 this.labelAnimated = false; |
478 if (this._labelVisible) { | 522 if (this._labelVisible) { |
479 this.input.placeholder = this.label; | 523 this.input.placeholder = this.label; |
480 } | 524 } |
481 } | 525 } |
482 | 526 |
483 }); | 527 }); |
484 | 528 |
485 }()); | 529 }()); |
486 | 530 |
487 </script> | 531 </script> |
488 | 532 |
489 </polymer-element> | 533 </polymer-element> |
OLD | NEW |