Delete chrome.mediaGalleriesPrivate because the functionality unique to it has since...
[chromium-blink-merge.git] / third_party / polymer / components / paper-input / paper-input-decorator.html
blob6a9f954fbe1ba449bafacfe7ab5d0074ee97f9a2
1 <!--
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.github.io/LICENSE
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/CONTRIBUTORS
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/PATENTS
8 -->
10 <!--
12 Material Design: <a href="http://www.google.com/design/spec/components/text-fields.html">Text fields</a>
14 `paper-input-decorator` adds Material Design input field styling and animations to an element.
16 Example:
18 <paper-input-decorator label="Your Name">
19 <input is="core-input">
20 </paper-input-decorator>
22 <paper-input-decorator floatingLabel label="Your address">
23 <textarea></textarea>
24 </paper-input-decorator>
26 Theming
27 -------
29 `paper-input-decorator` uses `core-style` for global theming. The following options are available:
31 - `CoreStyle.g.paperInput.labelColor` - The inline label, floating label, error message and error icon color when the input does not have focus.
32 - `CoreStyle.g.paperInput.focusedColor` - The floating label and the underline color when the input has focus.
33 - `CoreStyle.g.paperInput.invalidColor` - The error message, the error icon, the floating label and the underline's color when the input is invalid and has focus.
35 To add custom styling to only some elements, use these selectors:
37 paper-input-decorator /deep/ .label-text,
38 paper-input-decorator /deep/ .error {
39 /* inline label, floating label, error message and error icon color when the input is unfocused */
40 color: green;
43 paper-input-decorator /deep/ ::-webkit-input-placeholder {
44 /* platform specific rules for placeholder text */
45 color: green;
47 paper-input-decorator /deep/ ::-moz-placeholder {
48 color: green;
50 paper-input-decorator /deep/ :-ms-input-placeholder {
51 color: green;
54 paper-input-decorator /deep/ .unfocused-underline {
55 /* line color when the input is unfocused */
56 background-color: green;
59 paper-input-decorator[focused] /deep/ .floating-label .label-text {
60 /* floating label color when the input is focused */
61 color: orange;
64 paper-input-decorator /deep/ .focused-underline {
65 /* line color when the input is focused */
66 background-color: orange;
69 paper-input-decorator.invalid[focused] /deep/ .floated-label .label-text,
70 paper-input-decorator[focused] /deep/ .error {
71 /* floating label, error message nad error icon color when the input is invalid and focused */
72 color: salmon;
75 paper-input-decorator.invalid /deep/ .focused-underline {
76 /* line and color when the input is invalid and focused */
77 background-color: salmon;
80 Form submission
81 ---------------
83 You can use inputs decorated with this element in a `form` as usual.
85 Validation
86 ----------
88 Because you provide the `input` element to `paper-input-decorator`, you can use any validation library
89 or the <a href="https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation">HTML5 Constraints Validation API</a>
90 to implement validation. Set the `isInvalid` attribute when the input is validated, and provide an
91 error message in the `error` attribute.
93 Example:
95 <paper-input-decorator id="paper1" error="Value must start with a number!">
96 <input id="input1" is="core-input" pattern="^[0-9].*">
97 </paper-input-decorator>
98 <button onclick="validate()"></button>
99 <script>
100 function validate() {
101 var decorator = document.getElementById('paper1');
102 var input = document.getElementById('input1');
103 decorator.isInvalid = !input.validity.valid;
105 </script>
107 Example to validate as the user types:
109 <template is="auto-binding">
110 <paper-input-decorator id="paper2" error="Value must start with a number!" isInvalid="{{!$.input2.validity.valid}}">
111 <input id="input2" is="core-input" pattern="^[0-9].*">
112 </paper-input-decorator>
113 </template>
115 Accessibility
116 -------------
118 `paper-input-decorator` will automatically set the `aria-label` attribute on the nested input
119 to the value of `label`. Do not set the `placeholder` attribute on the nested input, as it will
120 conflict with this element.
122 @group Paper Elements
123 @element paper-input-decorator
124 @homepage github.io
126 <link href="../polymer/polymer.html" rel="import">
127 <link href="../core-icon/core-icon.html" rel="import">
128 <link href="../core-icons/core-icons.html" rel="import">
129 <link href="../core-input/core-input.html" rel="import">
130 <link href="../core-style/core-style.html" rel="import">
132 <core-style id="paper-input-decorator">
134 .label-text,
135 .error {
136 color: {{g.paperInput.labelColor}};
139 ::-webkit-input-placeholder {
140 color: {{g.paperInput.labelColor}};
143 ::-moz-placeholder {
144 color: {{g.paperInput.labelColor}};
147 :-ms-input-placeholder {
148 color: {{g.paperInput.labelColor}};
151 .unfocused-underline {
152 background-color: {{g.paperInput.labelColor}};
155 :host([focused]) .floated-label .label-text {
156 color: {{g.paperInput.focusedColor}};
159 .focused-underline {
160 background-color: {{g.paperInput.focusedColor}};
163 :host(.invalid) .floated-label .label-text,
164 .error {
165 color: {{g.paperInput.invalidColor}};
168 :host(.invalid) .unfocused-underline,
169 :host(.invalid) .focused-underline {
170 background-color: {{g.paperInput.invalidColor}};
173 </core-style>
175 <polymer-element name="paper-input-decorator" layout vertical
176 on-transitionEnd="{{transitionEndAction}}" on-webkitTransitionEnd="{{transitionEndAction}}"
177 on-input="{{inputAction}}"
178 on-down="{{downAction}}">
180 <template>
182 <link href="paper-input-decorator.css" rel="stylesheet">
183 <core-style ref="paper-input-decorator"></core-style>
185 <div class="floated-label" aria-hidden="true" hidden?="{{!floatingLabel}}" invisible?="{{!floatingLabelVisible || labelAnimated}}">
186 <!-- needed for floating label animation measurement -->
187 <span id="floatedLabelText" class="label-text">{{label}}</span>
188 </div>
190 <div class="input-body" flex auto relative>
192 <div class="label" fit invisible aria-hidden="true">
193 <!-- needed for floating label animation measurement -->
194 <span id="labelText" class="label-text" invisible?="{{!_labelVisible}}" animated?="{{labelAnimated}}">{{label}}</span>
195 </div>
197 <content></content>
199 </div>
201 <div id="underline" class="underline" relative>
202 <div class="unfocused-underline" fit invisible?="{{disabled}}"></div>
203 <div id="focusedUnderline" class="focused-underline" fit invisible?="{{!focused}}" animated?="{{underlineAnimated}}"></div>
204 </div>
206 <div class="error" layout horizontal center hidden?="{{!isInvalid}}">
207 <div class="error-text" flex auto role="alert" aria-hidden="{{!isInvalid}}">{{error}}</div>
208 <core-icon class="error-icon" icon="warning"></core-icon>
209 </div>
211 </template>
213 <script>
215 (function() {
217 var paperInput = CoreStyle.g.paperInput = CoreStyle.g.paperInput || {};
219 paperInput.labelColor = '#757575';
220 paperInput.focusedColor = '#4059a9';
221 paperInput.invalidColor = '#d34336';
223 Polymer({
225 publish: {
228 * The label for this input. It normally appears as grey text inside
229 * the text input and disappears once the user enters text.
231 * @attribute label
232 * @type string
233 * @default ''
235 label: '',
238 * If true, the label will "float" above the text input once the
239 * user enters text instead of disappearing.
241 * @attribute floatingLabel
242 * @type boolean
243 * @default false
245 floatingLabel: false,
248 * Set to true to style the element as disabled.
250 * @attribute disabled
251 * @type boolean
252 * @default false
254 disabled: {value: false, reflect: true},
257 * Use this property to override the automatic label visibility.
258 * If this property is set to `true` or `false`, the label visibility
259 * will respect this value instead of be based on whether there is
260 * a non-null value in the input.
262 * @attribute labelVisible
263 * @type boolean
264 * @default false
266 labelVisible: null,
269 * Set this property to true to show the error message.
271 * @attribute isInvalid
272 * @type boolean
273 * @default false
275 isInvalid: false,
278 * The message to display if the input value fails validation. If this
279 * is unset or the empty string, a default message is displayed depending
280 * on the type of validation error.
282 * @attribute error
283 * @type string
285 error: '',
287 focused: {value: false, reflect: true}
291 computed: {
292 floatingLabelVisible: 'floatingLabel && !_labelVisible',
293 _labelVisible: '(labelVisible === true || labelVisible === false) ? labelVisible : _autoLabelVisible'
296 ready: function() {
297 // Delegate focus/blur events
298 Polymer.addEventListener(this, 'focus', this.focusAction.bind(this), true);
299 Polymer.addEventListener(this, 'blur', this.blurAction.bind(this), true);
302 attached: function() {
303 this.input = this.querySelector('input,textarea');
305 this.mo = new MutationObserver(function() {
306 this.input = this.querySelector('input,textarea');
307 }.bind(this));
308 this.mo.observe(this, {childList: true});
311 detached: function() {
312 this.mo.disconnect();
313 this.mo = null;
316 prepareLabelTransform: function() {
317 var toRect = this.$.floatedLabelText.getBoundingClientRect();
318 var fromRect = this.$.labelText.getBoundingClientRect();
319 if (toRect.width !== 0) {
320 var sy = toRect.height / fromRect.height;
321 this.$.labelText.cachedTransform =
322 'scale3d(' + (toRect.width / fromRect.width) + ',' + sy + ',1) ' +
323 'translate3d(0,' + (toRect.top - fromRect.top) / sy + 'px,0)';
327 animateFloatingLabel: function() {
328 if (!this.floatingLabel || this.labelAnimated) {
329 return false;
332 if (!this.$.labelText.cachedTransform) {
333 this.prepareLabelTransform();
336 // If there's still no cached transform, the input is invisible so don't
337 // do the animation.
338 if (!this.$.labelText.cachedTransform) {
339 return false;
342 this.labelAnimated = true;
343 // Handle interrupted animation
344 this.async(function() {
345 this.transitionEndAction();
346 }, null, 250);
348 if (this._labelVisible) {
349 // Handle if the label started out floating
350 if (!this.$.labelText.style.webkitTransform && !this.$.labelText.style.transform) {
351 this.$.labelText.style.webkitTransform = this.$.labelText.cachedTransform;
352 this.$.labelText.style.transform = this.$.labelText.cachedTransform;
353 this.$.labelText.offsetTop;
355 this.$.labelText.style.webkitTransform = '';
356 this.$.labelText.style.transform = '';
357 } else {
358 this.$.labelText.style.webkitTransform = this.$.labelText.cachedTransform;
359 this.$.labelText.style.transform = this.$.labelText.cachedTransform;
360 this.input.placeholder = '';
363 return true;
366 _labelVisibleChanged: function(old) {
367 // do not do the animation on first render
368 if (old !== undefined) {
369 if (!this.animateFloatingLabel()) {
370 this.updateInputLabel(this.input, this.label);
375 labelVisibleChanged: function() {
376 if (this.labelVisible === 'true') {
377 this.labelVisible = true;
378 } else if (this.labelVisible === 'false') {
379 this.labelVisible = false;
383 labelChanged: function() {
384 if (this.input) {
385 this.updateInputLabel(this.input, this.label);
389 isInvalidChanged: function() {
390 this.classList.toggle('invalid', this.isInvalid);
393 focusedChanged: function() {
394 this.updateLabelVisibility(this.input && this.input.value);
397 inputChanged: function(old) {
398 if (this.input) {
399 this.updateLabelVisibility(this.input.value);
400 this.updateInputLabel(this.input, this.label);
402 if (old) {
403 this.updateInputLabel(old, '');
407 focusAction: function() {
408 this.focused = true;
411 blurAction: function(e) {
412 this.focused = false;
416 * Updates the label visibility based on a value. This is handled automatically
417 * if the user is typing, but if you imperatively set the input value you need
418 * to call this function.
420 * @method updateLabelVisibility
421 * @param {string} value
423 updateLabelVisibility: function(value) {
424 var v = (value !== null && value !== undefined) ? String(value) : value;
425 this._autoLabelVisible = (!this.focused && !v) || (!this.floatingLabel && !v);
428 updateInputLabel: function(input, label) {
429 if (this._labelVisible) {
430 this.input.placeholder = this.label;
431 } else {
432 this.input.placeholder = '';
434 if (label) {
435 input.setAttribute('aria-label', label);
436 } else {
437 input.removeAttribute('aria-label');
441 inputAction: function(e) {
442 this.updateLabelVisibility(e.target.value);
445 downAction: function(e) {
446 if (this.disabled) {
447 return;
450 if (this.focused) {
451 return;
454 if (this.input) {
455 this.input.focus();
456 e.preventDefault();
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';
466 // Animations only run when the user interacts with the input
467 this.underlineAnimated = true;
469 // Handle interrupted animation
470 this.async(function() {
471 this.transitionEndAction();
472 }, null, 250);
475 transitionEndAction: function() {
476 this.underlineAnimated = false;
477 this.labelAnimated = false;
478 if (this._labelVisible) {
479 this.input.placeholder = this.label;
485 }());
487 </script>
489 </polymer-element>