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.github.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/CONTRIBUTORS.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/PATENTS.txt
11
<link rel="import" href="../polymer/polymer.html">
12
<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
13
<link rel="import" href="../paper-styles/default-theme.html">
14
<link rel="import" href="../paper-styles/typography.html">
17
`<paper-input-container>` is a container for a `<label>`, an `<input is="iron-input">` or
18
`<iron-autogrow-textarea>` and optional add-on elements such as an error message or character
19
counter, used to implement Material Design text fields.
23
<paper-input-container>
24
<label>Your name</label>
25
<input is="iron-input">
26
</paper-input-container>
28
Do not wrap `<paper-input-container>` around elements that already include it, such as `<paper-input>`.
29
Doing so may cause events to bounce infintely between the container and its contained element.
31
### Listening for input changes
33
By default, it listens for changes on the `bind-value` attribute on its children nodes and perform
34
tasks such as auto-validating and label styling when the `bind-value` changes. You can configure
35
the attribute it listens to with the `attr-for-value` attribute.
37
### Using a custom input element
39
You can use a custom input element in a `<paper-input-container>`, for example to implement a
40
compound input field like a social security number input. The custom input element should have the
41
`paper-input-input` class, have a `notify:true` value property and optionally implements
42
`Polymer.IronValidatableBehavior` if it is validatable.
44
<paper-input-container attr-for-value="ssn-value">
45
<label>Social security number</label>
46
<ssn-input class="paper-input-input"></ssn-input>
47
</paper-input-container>
50
If you're using a `<paper-input-container>` imperatively, it's important to make sure
51
that you attach its children (the `iron-input` and the optional `label`) before you
52
attach the `<paper-input-container>` itself, so that it can be set up correctly.
56
If the `auto-validate` attribute is set, the input container will validate the input and update
57
the container styling when the input value changes.
61
Add-ons are child elements of a `<paper-input-container>` with the `add-on` attribute and
62
implements the `Polymer.PaperInputAddonBehavior` behavior. They are notified when the input value
63
or validity changes, and may implement functionality such as error messages or character counters.
64
They appear at the bottom of the input.
66
### Prefixes and suffixes
67
These are child elements of a `<paper-input-container>` with the `prefix`
68
or `suffix` attribute, and are displayed inline with the input, before or after.
70
<paper-input-container>
73
<input is="iron-input">
74
<paper-icon-button suffix icon="clear"></paper-icon-button>
75
</paper-input-container>
79
The following custom properties and mixins are available for styling:
81
Custom property | Description | Default
82
----------------|-------------|----------
83
`--paper-input-container-color` | Label and underline color when the input is not focused | `--secondary-text-color`
84
`--paper-input-container-focus-color` | Label and underline color when the input is focused | `--primary-color`
85
`--paper-input-container-invalid-color` | Label and underline color when the input is is invalid | `--error-color`
86
`--paper-input-container-input-color` | Input foreground color | `--primary-text-color`
87
`--paper-input-container` | Mixin applied to the container | `{}`
88
`--paper-input-container-disabled` | Mixin applied to the container when it's disabled | `{}`
89
`--paper-input-container-label` | Mixin applied to the label | `{}`
90
`--paper-input-container-label-focus` | Mixin applied to the label when the input is focused | `{}`
91
`--paper-input-container-label-floating` | Mixin applied to the label when floating | `{}`
92
`--paper-input-container-input` | Mixin applied to the input | `{}`
93
`--paper-input-container-underline` | Mixin applied to the underline | `{}`
94
`--paper-input-container-underline-focus` | Mixin applied to the underline when the input is focused | `{}`
95
`--paper-input-container-underline-disabled` | Mixin applied to the underline when the input is disabled | `{}`
96
`--paper-input-prefix` | Mixin applied to the input prefix | `{}`
97
`--paper-input-suffix` | Mixin applied to the input suffix | `{}`
99
This element is `display:block` by default, but you can set the `inline` attribute to make it
100
`display:inline-block`.
103
<dom-module id="paper-input-container">
110
@apply(--paper-input-container);
114
display: inline-block;
118
pointer-events: none;
121
@apply(--paper-input-container-disabled);
124
.floated-label-placeholder {
125
@apply(--paper-font-caption);
133
@apply(--layout-fit);
135
background: var(--paper-input-container-focus-color, --primary-color);
138
-webkit-transform-origin: center center;
139
transform-origin: center center;
140
-webkit-transform: scale3d(0,1,1);
141
transform: scale3d(0,1,1);
143
@apply(--paper-input-container-underline-focus);
146
.underline.is-highlighted .focused-line {
147
-webkit-transform: none;
149
-webkit-transition: -webkit-transform 0.25s;
150
transition: transform 0.25s;
152
@apply(--paper-transition-easing);
155
.underline.is-invalid .focused-line {
156
background: var(--paper-input-container-invalid-color, --error-color);
157
-webkit-transform: none;
159
-webkit-transition: -webkit-transform 0.25s;
160
transition: transform 0.25s;
162
@apply(--paper-transition-easing);
166
@apply(--layout-fit);
168
background: var(--paper-input-container-color, --secondary-text-color);
171
@apply(--paper-input-container-underline);
174
:host([disabled]) .unfocused-line {
175
border-bottom: 1px dashed;
176
border-color: var(--paper-input-container-color, --secondary-text-color);
177
background: transparent;
179
@apply(--paper-input-container-underline-disabled);
182
.label-and-input-container {
183
@apply(--layout-flex-auto);
184
@apply(--layout-relative);
191
@apply(--layout-horizontal);
192
@apply(--layout-center);
197
.input-content ::content label,
198
.input-content ::content .paper-input-label {
205
color: var(--paper-input-container-color, --secondary-text-color);
206
-webkit-transition: -webkit-transform 0.25s, width 0.25s;
207
transition: transform 0.25s, width 0.25s;
208
-webkit-transform-origin: left top;
209
transform-origin: left top;
211
@apply(--paper-font-common-nowrap);
212
@apply(--paper-font-subhead);
213
@apply(--paper-input-container-label);
214
@apply(--paper-transition-easing);
217
.input-content.label-is-floating ::content label,
218
.input-content.label-is-floating ::content .paper-input-label {
219
-webkit-transform: translateY(-75%) scale(0.75);
220
transform: translateY(-75%) scale(0.75);
222
/* Since we scale to 75/100 of the size, we actually have 100/75 of the
223
original space now available */
226
@apply(--paper-input-container-label-floating);
229
:host-context([dir="rtl"]) .input-content.label-is-floating ::content label,
230
:host-context([dir="rtl"]) .input-content.label-is-floating ::content .paper-input-label {
231
/* TODO(noms): Figure out why leaving the width at 133% before the animation
233
* it wider on the right side, not left side, as you would expect in RTL */
235
-webkit-transform-origin: right top;
236
transform-origin: right top;
239
.input-content.label-is-highlighted ::content label,
240
.input-content.label-is-highlighted ::content .paper-input-label {
241
color: var(--paper-input-container-focus-color, --primary-color);
243
@apply(--paper-input-container-label-focus);
246
.input-content.is-invalid ::content label,
247
.input-content.is-invalid ::content .paper-input-label {
248
color: var(--paper-input-container-invalid-color, --error-color);
251
.input-content.label-is-hidden ::content label,
252
.input-content.label-is-hidden ::content .paper-input-label {
256
.input-content ::content input,
257
.input-content ::content textarea,
258
.input-content ::content iron-autogrow-textarea,
259
.input-content ::content .paper-input-input {
260
position: relative; /* to make a stacking context */
266
background: transparent;
268
color: var(--paper-input-container-input-color, --primary-text-color);
269
-webkit-appearance: none;
272
@apply(--paper-font-subhead);
273
@apply(--paper-input-container-input);
277
@apply(--paper-font-subhead);
279
@apply(--paper-input-prefix);
280
@apply(--layout-flex-none);
284
@apply(--paper-font-subhead);
286
@apply(--paper-input-suffix);
287
@apply(--layout-flex-none);
290
/* Firefox sets a min-width on the input, which can cause layout issues */
291
.input-content ::content input {
295
.input-content ::content textarea {
303
.add-on-content.is-invalid ::content * {
304
color: var(--paper-input-container-invalid-color, --error-color);
307
.add-on-content.is-highlighted ::content * {
308
color: var(--paper-input-container-focus-color, --primary-color);
312
<template is="dom-if" if="[[!noLabelFloat]]">
313
<div class="floated-label-placeholder"> </div>
316
<div class$="[[_computeInputContentClass(noLabelFloat,alwaysFloatLabel,focused,invalid,_inputHasContent)]]">
317
<content select="[prefix]" id="prefix"></content>
319
<div class="label-and-input-container" id="labelAndInputContainer">
320
<content select=":not([add-on]):not([prefix]):not([suffix])"></content>
323
<content select="[suffix]"></content>
326
<div class$="[[_computeUnderlineClass(focused,invalid)]]">
327
<div class="unfocused-line"></div>
328
<div class="focused-line"></div>
331
<div class$="[[_computeAddOnContentClass(focused,invalid)]]">
332
<content id="addOnContent" select="[add-on]"></content>
339
is: 'paper-input-container',
343
* Set to true to disable the floating label. The label disappears when the input value is
352
* Set to true to always float the floating label.
360
* The attribute to listen for value changes on.
368
* Set to true to auto-validate the input value when it changes.
376
* True if the input is invalid. This property is set automatically when the input value
377
* changes if auto-validating, or when the `iron-input-validate` event is heard from a child.
380
observer: '_invalidChanged',
386
* True if the input has focus.
397
// do not set a default value here intentionally - it will be initialized lazily when a
398
// distributed child is attached, which may occur before configuration for this element
409
value: 'input,textarea,.paper-input-input'
415
return this._onFocus.bind(this);
422
return this._onBlur.bind(this);
429
return this._onInput.bind(this);
433
_boundValueChanged: {
436
return this._onValueChanged.bind(this);
442
'addon-attached': '_onAddonAttached',
443
'iron-input-validate': '_onIronInputValidate'
446
get _valueChangedEvent() {
447
return this.attrForValue + '-changed';
450
get _propertyForValue() {
451
return Polymer.CaseMap.dashToCamelCase(this.attrForValue);
454
get _inputElement() {
455
return Polymer.dom(this).querySelector(this._inputSelector);
458
get _inputElementValue() {
459
return this._inputElement[this._propertyForValue] || this._inputElement.value;
466
this.addEventListener('focus', this._boundOnFocus, true);
467
this.addEventListener('blur', this._boundOnBlur, true);
470
attached: function() {
471
if (this.attrForValue) {
472
this._inputElement.addEventListener(this._valueChangedEvent, this._boundValueChanged);
474
this.addEventListener('input', this._onInput);
477
// Only validate when attached if the input already has a value.
478
if (this._inputElementValue != '') {
479
this._handleValueAndAutoValidate(this._inputElement);
481
this._handleValue(this._inputElement);
485
_onAddonAttached: function(event) {
489
var target = event.target;
490
if (this._addons.indexOf(target) === -1) {
491
this._addons.push(target);
492
if (this.isAttached) {
493
this._handleValue(this._inputElement);
498
_onFocus: function() {
499
this._setFocused(true);
502
_onBlur: function() {
503
this._setFocused(false);
504
this._handleValueAndAutoValidate(this._inputElement);
507
_onInput: function(event) {
508
this._handleValueAndAutoValidate(event.target);
511
_onValueChanged: function(event) {
512
this._handleValueAndAutoValidate(event.target);
515
_handleValue: function(inputElement) {
516
var value = this._inputElementValue;
518
// type="number" hack needed because this.value is empty until it's valid
519
if (value || value === 0 || (inputElement.type === 'number' && !inputElement.checkValidity())) {
520
this._inputHasContent = true;
522
this._inputHasContent = false;
526
inputElement: inputElement,
528
invalid: this.invalid
532
_handleValueAndAutoValidate: function(inputElement) {
533
if (this.autoValidate) {
535
if (inputElement.validate) {
536
valid = inputElement.validate(this._inputElementValue);
538
valid = inputElement.checkValidity();
540
this.invalid = !valid;
543
// Call this last to notify the add-ons.
544
this._handleValue(inputElement);
547
_onIronInputValidate: function(event) {
548
this.invalid = this._inputElement.invalid;
551
_invalidChanged: function() {
553
this.updateAddons({invalid: this.invalid});
558
* Call this to update the state of add-ons.
559
* @param {Object} state Add-on state.
561
updateAddons: function(state) {
562
for (var addon, index = 0; addon = this._addons[index]; index++) {
567
_computeInputContentClass: function(noLabelFloat, alwaysFloatLabel, focused, invalid, _inputHasContent) {
568
var cls = 'input-content';
570
var label = this.querySelector('label');
572
if (alwaysFloatLabel || _inputHasContent) {
573
cls += ' label-is-floating';
574
// If the label is floating, ignore any offsets that may have been
575
// applied from a prefix element.
576
this.$.labelAndInputContainer.style.position = 'static';
579
cls += ' is-invalid';
580
} else if (focused) {
581
cls += " label-is-highlighted";
584
// When the label is not floating, it should overlap the input element.
586
this.$.labelAndInputContainer.style.position = 'relative';
590
if (_inputHasContent) {
591
cls += ' label-is-hidden';
597
_computeUnderlineClass: function(focused, invalid) {
598
var cls = 'underline';
600
cls += ' is-invalid';
601
} else if (focused) {
602
cls += ' is-highlighted'
607
_computeAddOnContentClass: function(focused, invalid) {
608
var cls = 'add-on-content';
610
cls += ' is-invalid';
611
} else if (focused) {
612
cls += ' is-highlighted'