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=
"../paper-styles/paper-styles.html">
15 `<paper-input-container>` is a container for a `<label>`, an `<input is="iron-input">` or
16 `<iron-autogrow-textarea>` and optional add-on elements such as an error message or character
17 counter, used to implement Material Design text fields.
21 <paper-input-container>
22 <label>Your name</label>
23 <input is="iron-input">
24 </paper-input-container>
26 ### Listening for input changes
28 By default, it listens for changes on the `bind-value` attribute on its children nodes and perform
29 tasks such as auto-validating and label styling when the `bind-value` changes. You can configure
30 the attribute it listens to with the `attr-for-value` attribute.
32 ### Using a custom input element
34 You can use a custom input element in a `<paper-input-container>`, for example to implement a
35 compound input field like a social security number input. The custom input element should have the
36 `paper-input-input` class, have a `notify:true` value property and optionally implements
37 `Polymer.IronValidatableBehavior` if it is validatble.
39 <paper-input-container attr-for-value="ssn-value">
40 <label>Social security number</label>
41 <ssn-input class="paper-input-input"></ssn-input>
42 </paper-input-container>
46 If the `auto-validate` attribute is set, the input container will validate the input and update
47 the container styling when the input value changes.
51 Add-ons are child elements of a `<paper-input-container>` with the `add-on` attribute and
52 implements the `Polymer.PaperInputAddonBehavior` behavior. They are notified when the input value
53 or validity changes, and may implement functionality such as error messages or character counters.
54 They appear at the bottom of the input.
58 The following custom properties and mixins are available for styling:
60 Custom property | Description | Default
61 ----------------|-------------|----------
62 `--paper-input-container-color` | Label and underline color when the input is not focused | `--secondary-text-color`
63 `--paper-input-container-focus-color` | Label and underline color when the input is focused | `--default-primary-color`
64 `--paper-input-container-invalid-color` | Label and underline color when the input is focused | `--google-red-500`
65 `--paper-input-container-input-color` | Input foreground color | `--primary-text-color`
66 `--paper-input-container` | Mixin applied to the container | `{}`
67 `--paper-input-container-label` | Mixin applied to the label | `{}`
68 `--paper-input-container-label-focus` | Mixin applied to the label when the input is focused | `{}`
69 `--paper-input-container-input` | Mixin applied to the input | `{}`
70 `--paper-input-container-underline` | Mixin applied to the underline | `{}`
71 `--paper-input-container-underline-focus` | Mixin applied to the underline when the input is focued | `{}`
72 `--paper-input-container-underline-disabled` | Mixin applied to the underline when the input is disabled | `{}`
74 This element is `display:block` by default, but you can set the `inline` attribute to make it
75 `display:inline-block`.
77 <dom-module id=
"paper-input-container">
85 @apply(--paper-input-container);
89 display: inline-block;
97 .floated-label-placeholder {
98 @apply(--paper-font-caption);
108 -webkit-transform-origin: center center;
109 transform-origin: center center;
110 -webkit-transform: scale3d(
0,
1,
1);
111 transform: scale3d(
0,
1,
1);
113 background: var(--paper-input-container-focus-color, --default-primary-color);
115 @apply(--paper-input-container-underline-focus);
118 .underline.is-highlighted .focused-line {
119 -webkit-transform: none;
121 -webkit-transition: -webkit-transform
0.25s;
122 transition: transform
0.25s;
124 @apply(--paper-transition-easing);
127 .underline.is-invalid .focused-line {
128 background: var(--paper-input-container-invalid-color, --google-red-
500);
130 -webkit-transform: none;
132 -webkit-transition: -webkit-transform
0.25s;
133 transition: transform
0.25s;
135 @apply(--paper-transition-easing);
140 background: var(--paper-input-container-color, --secondary-text-color);
142 @apply(--paper-input-container-underline);
145 :host([disabled]) .unfocused-line {
146 border-bottom:
1px dashed;
147 border-color: var(--paper-input-container-color, --secondary-text-color);
148 background: transparent;
150 @apply(--paper-input-container-underline-disabled);
157 .input-content ::content label,
158 .input-content ::content .paper-input-label {
164 color: var(--paper-input-container-color, --secondary-text-color);
166 @apply(--paper-font-subhead);
167 @apply(--paper-input-container-label);
170 .input-content.label-is-floating ::content label,
171 .input-content.label-is-floating ::content .paper-input-label {
172 -webkit-transform: translate3d(
0, -
75%,
0) scale(
0.75);
173 transform: translate3d(
0, -
75%,
0) scale(
0.75);
174 -webkit-transform-origin: left top;
175 transform-origin: left top;
176 -webkit-transition: -webkit-transform
0.25s;
177 transition: transform
0.25s;
179 @apply(--paper-transition-easing);
182 .input-content.label-is-highlighted ::content label,
183 .input-content.label-is-highlighted ::content .paper-input-label {
184 color: var(--paper-input-container-focus-color, --default-primary-color);
186 @apply(--paper-input-container-label-focus);
189 .input-content.is-invalid ::content label,
190 .input-content.is-invalid ::content .paper-input-label {
191 color: var(--paper-input-container-invalid-color, --google-red-
500);
194 .input-content.label-is-hidden ::content label,
195 .input-content.label-is-hidden ::content .paper-input-label {
199 .input-content ::content input,
200 .input-content ::content textarea,
201 .input-content ::content iron-autogrow-textarea,
202 .input-content ::content .paper-input-input {
203 position: relative; /* to make a stacking context */
208 background: transparent;
210 color: var(--paper-input-container-input-color, --primary-text-color);
212 @apply(--paper-font-subhead);
213 @apply(--paper-input-container-input);
216 /* Firefox sets a min-width on the input, which can cause layout issues */
217 .input-content ::content input {
221 .input-content ::content textarea {
225 .add-on-content.is-invalid ::content * {
226 color: var(--paper-input-container-invalid-color, --google-red-
500);
229 .add-on-content.is-highlighted ::content * {
230 color: var(--paper-input-container-focus-color, --default-primary-color);
237 <template is=
"dom-if" if=
"[[!noLabelFloat]]">
238 <div class=
"floated-label-placeholder"> </div>
241 <div class$=
"[[_computeInputContentClass(noLabelFloat,alwaysFloatLabel,focused,invalid,_inputHasContent)]]">
242 <content select=
":not([add-on])"></content>
245 <div class$=
"[[_computeUnderlineClass(focused,invalid)]]">
246 <div class=
"unfocused-line fit"></div>
247 <div class=
"focused-line fit"></div>
250 <div class$=
"[[_computeAddOnContentClass(focused,invalid)]]">
251 <content id=
"addOnContent" select=
"[add-on]"></content>
263 is
: 'paper-input-container',
268 * Set to true to disable the floating label. The label disappears when the input value is
277 * Set to true to always float the floating label.
285 * The attribute to listen for value changes on.
293 * Set to true to auto-validate the input value when it changes.
301 * True if the input is invalid. This property is set automatically when the input value
302 * changes if auto-validating, or when the `iron-input-valid` event is heard from a child.
305 observer
: '_invalidChanged',
311 * True if the input has focus.
321 // do not set a default value here intentionally - it will be initialized lazily when a
322 // distributed child is attached, which may occur before configuration for this element
333 value
: 'input,textarea,.paper-input-input'
339 return this._onFocus
.bind(this);
346 return this._onBlur
.bind(this);
353 return this._onInput
.bind(this);
357 _boundValueChanged
: {
360 return this._onValueChanged
.bind(this);
367 'addon-attached': '_onAddonAttached',
368 'iron-input-validate': '_onIronInputValidate'
371 get _valueChangedEvent() {
372 return this.attrForValue
+ '-changed';
375 get _propertyForValue() {
376 return Polymer
.CaseMap
.dashToCamelCase(this.attrForValue
);
379 get _inputElement() {
380 return Polymer
.dom(this).querySelector(this._inputSelector
);
387 this.addEventListener('focus', this._boundOnFocus
, true);
388 this.addEventListener('blur', this._boundOnBlur
, true);
389 if (this.attrForValue
) {
390 this._inputElement
.addEventListener(this._valueChangedEvent
, this._boundValueChanged
);
392 this.addEventListener('input', this._onInput
);
396 attached: function() {
397 this._handleValue(this._inputElement
);
400 _onAddonAttached: function(event
) {
404 var target
= event
.target
;
405 if (this._addons
.indexOf(target
) === -1) {
406 this._addons
.push(target
);
407 if (this.isAttached
) {
408 this._handleValue(this._inputElement
);
413 _onFocus: function() {
414 this._setFocused(true);
417 _onBlur: function() {
418 this._setFocused(false);
421 _onInput: function(event
) {
422 this._handleValue(event
.target
);
425 _onValueChanged: function(event
) {
426 this._handleValue(event
.target
);
429 _handleValue: function(inputElement
) {
430 var value
= inputElement
[this._propertyForValue
] || inputElement
.value
;
432 if (this.autoValidate
) {
434 if (inputElement
.validate
) {
435 valid
= inputElement
.validate(value
);
437 valid
= inputElement
.checkValidity();
439 this.invalid
= !valid
;
442 // type="number" hack needed because this.value is empty until it's valid
443 if (value
|| (inputElement
.type
=== 'number' && !inputElement
.checkValidity())) {
444 this._inputHasContent
= true;
446 this._inputHasContent
= false;
450 inputElement
: inputElement
,
452 invalid
: this.invalid
456 _onIronInputValidate: function(event
) {
457 this.invalid
= this._inputElement
.invalid
;
460 _invalidChanged: function() {
462 this.updateAddons({invalid
: this.invalid
});
467 * Call this to update the state of add-ons.
468 * @param {Object} state Add-on state.
470 updateAddons: function(state
) {
471 for (var addon
, index
= 0; addon
= this._addons
[index
]; index
++) {
476 _computeInputContentClass: function(noLabelFloat
, alwaysFloatLabel
, focused
, invalid
, _inputHasContent
) {
477 var cls
= 'input-content';
479 if (alwaysFloatLabel
|| _inputHasContent
) {
480 cls
+= ' label-is-floating';
482 cls
+= ' is-invalid';
483 } else if (focused
) {
484 cls
+= " label-is-highlighted";
488 if (_inputHasContent
) {
489 cls
+= ' label-is-hidden';
495 _computeUnderlineClass: function(focused
, invalid
) {
496 var cls
= 'underline';
498 cls
+= ' is-invalid';
499 } else if (focused
) {
500 cls
+= ' is-highlighted'
505 _computeAddOnContentClass: function(focused
, invalid
) {
506 var cls
= 'add-on-content';
508 cls
+= ' is-invalid';
509 } else if (focused
) {
510 cls
+= ' is-highlighted'