From ee04be2b41d63283d4933cbe775095cfb376c837 Mon Sep 17 00:00:00 2001 From: ankitsinghal0880 <34663555+ankitsinghal0880@users.noreply.github.com> Date: Tue, 16 Jan 2018 21:08:33 -0500 Subject: [PATCH] Change for adding a mixing to style input wrapper I have a requirement to display paper-dropdown-menu as a standard dropdown and not in material design. I need to do it through stylesheet as few of our customer still want to have old look/feel and we do not want to have custom coding instead trying to change the look/feel using styles. I am able to display paper-input like legacy elements easily by using paper-input-container styles but no luck with dropdown. --- paper-input-container.html | 1365 ++++++++++++++++++------------------ 1 file changed, 683 insertions(+), 682 deletions(-) diff --git a/paper-input-container.html b/paper-input-container.html index adf1c966..a600428c 100644 --- a/paper-input-container.html +++ b/paper-input-container.html @@ -1,682 +1,683 @@ -<!-- -@license -Copyright (c) 2015 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt ---> - -<link rel="import" href="../polymer/polymer.html"> -<link rel="import" href="../iron-flex-layout/iron-flex-layout.html"> -<link rel="import" href="../paper-styles/default-theme.html"> -<link rel="import" href="../paper-styles/typography.html"> - -<!-- -`<paper-input-container>` is a container for a `<label>`, an `<iron-input>` or -`<textarea>` and optional add-on elements such as an error message or character -counter, used to implement Material Design text fields. - -For example: - - <paper-input-container> - <label slot="label">Your name</label> - <iron-input slot="input"> - <input> - </iron-input> - // In Polymer 1.0, you would use `<input is="iron-input" slot="input">` instead of the above. - </paper-input-container> - -You can style the nested `<input>` however you want; if you want it to look like a -Material Design input, you can style it with the --paper-input-container-shared-input-style mixin. - -Do not wrap `<paper-input-container>` around elements that already include it, such as `<paper-input>`. -Doing so may cause events to bounce infinitely between the container and its contained element. - -### Listening for input changes - -By default, it listens for changes on the `bind-value` attribute on its children nodes and perform -tasks such as auto-validating and label styling when the `bind-value` changes. You can configure -the attribute it listens to with the `attr-for-value` attribute. - -### Using a custom input element - -You can use a custom input element in a `<paper-input-container>`, for example to implement a -compound input field like a social security number input. The custom input element should have the -`paper-input-input` class, have a `notify:true` value property and optionally implements -`Polymer.IronValidatableBehavior` if it is validatable. - - <paper-input-container attr-for-value="ssn-value"> - <label slot="label">Social security number</label> - <ssn-input slot="input" class="paper-input-input"></ssn-input> - </paper-input-container> - - -If you're using a `<paper-input-container>` imperatively, it's important to make sure -that you attach its children (the `iron-input` and the optional `label`) before you -attach the `<paper-input-container>` itself, so that it can be set up correctly. - -### Validation - -If the `auto-validate` attribute is set, the input container will validate the input and update -the container styling when the input value changes. - -### Add-ons - -Add-ons are child elements of a `<paper-input-container>` with the `add-on` attribute and -implements the `Polymer.PaperInputAddonBehavior` behavior. They are notified when the input value -or validity changes, and may implement functionality such as error messages or character counters. -They appear at the bottom of the input. - -### Prefixes and suffixes -These are child elements of a `<paper-input-container>` with the `prefix` -or `suffix` attribute, and are displayed inline with the input, before or after. - - <paper-input-container> - <div slot="prefix">$</div> - <label slot="label">Total</label> - <iron-input slot="input"> - <input> - </iron-input> - // In Polymer 1.0, you would use `<input is="iron-input" slot="input">` instead of the above. - <paper-icon-button slot="suffix" icon="clear"></paper-icon-button> - </paper-input-container> - -### Styling - -The following custom properties and mixins are available for styling: - -Custom property | Description | Default -----------------|-------------|---------- -`--paper-input-container-color` | Label and underline color when the input is not focused | `--secondary-text-color` -`--paper-input-container-focus-color` | Label and underline color when the input is focused | `--primary-color` -`--paper-input-container-invalid-color` | Label and underline color when the input is is invalid | `--error-color` -`--paper-input-container-input-color` | Input foreground color | `--primary-text-color` -`--paper-input-container` | Mixin applied to the container | `{}` -`--paper-input-container-disabled` | Mixin applied to the container when it's disabled | `{}` -`--paper-input-container-label` | Mixin applied to the label | `{}` -`--paper-input-container-label-focus` | Mixin applied to the label when the input is focused | `{}` -`--paper-input-container-label-floating` | Mixin applied to the label when floating | `{}` -`--paper-input-container-input` | Mixin applied to the input | `{}` -`--paper-input-container-input-disabled` | Mixin applied to the input when the component is disabled | `{}` -`--paper-input-container-input-focus` | Mixin applied to the input when focused | `{}` -`--paper-input-container-input-invalid` | Mixin applied to the input when invalid | `{}` -`--paper-input-container-input-webkit-spinner` | Mixin applied to the webkit spinner | `{}` -`--paper-input-container-input-webkit-clear` | Mixin applied to the webkit clear button | `{}` -`--paper-input-container-ms-clear` | Mixin applied to the Internet Explorer clear button | `{}` -`--paper-input-container-underline` | Mixin applied to the underline | `{}` -`--paper-input-container-underline-focus` | Mixin applied to the underline when the input is focused | `{}` -`--paper-input-container-underline-disabled` | Mixin applied to the underline when the input is disabled | `{}` -`--paper-input-prefix` | Mixin applied to the input prefix | `{}` -`--paper-input-suffix` | Mixin applied to the input suffix | `{}` - -This element is `display:block` by default, but you can set the `inline` attribute to make it -`display:inline-block`. ---> - -<dom-module id="paper-input-container"> - <template> - <style> - :host { - display: block; - padding: 8px 0; - - --paper-input-container-shared-input-style: { - position: relative; /* to make a stacking context */ - outline: none; - box-shadow: none; - padding: 0; - width: 100%; - max-width: 100%; - background: transparent; - border: none; - color: var(--paper-input-container-input-color, var(--primary-text-color)); - -webkit-appearance: none; - text-align: inherit; - vertical-align: bottom; - - @apply --paper-font-subhead; - }; - - @apply --paper-input-container; - } - - :host([inline]) { - display: inline-block; - } - - :host([disabled]) { - pointer-events: none; - opacity: 0.33; - - @apply --paper-input-container-disabled; - } - - :host([hidden]) { - display: none !important; - } - - [hidden] { - display: none !important; - } - - .floated-label-placeholder { - @apply --paper-font-caption; - } - - .underline { - height: 2px; - position: relative; - } - - .focused-line { - @apply --layout-fit; - border-bottom: 2px solid var(--paper-input-container-focus-color, var(--primary-color)); - - -webkit-transform-origin: center center; - transform-origin: center center; - -webkit-transform: scale3d(0,1,1); - transform: scale3d(0,1,1); - - @apply --paper-input-container-underline-focus; - } - - .underline.is-highlighted .focused-line { - -webkit-transform: none; - transform: none; - -webkit-transition: -webkit-transform 0.25s; - transition: transform 0.25s; - - @apply --paper-transition-easing; - } - - .underline.is-invalid .focused-line { - border-color: var(--paper-input-container-invalid-color, var(--error-color)); - -webkit-transform: none; - transform: none; - -webkit-transition: -webkit-transform 0.25s; - transition: transform 0.25s; - - @apply --paper-transition-easing; - } - - .unfocused-line { - @apply --layout-fit; - border-bottom: 1px solid var(--paper-input-container-color, var(--secondary-text-color)); - @apply --paper-input-container-underline; - } - - :host([disabled]) .unfocused-line { - border-bottom: 1px dashed; - border-color: var(--paper-input-container-color, var(--secondary-text-color)); - @apply --paper-input-container-underline-disabled; - } - - .input-wrapper { - @apply --layout-horizontal; - @apply --layout-center; - position: relative; - } - - .input-content { - @apply --layout-flex-auto; - @apply --layout-relative; - max-width: 100%; - } - - .input-content ::slotted(label), - .input-content ::slotted(.paper-input-label) { - position: absolute; - top: 0; - left: 0; - width: 100%; - font: inherit; - color: var(--paper-input-container-color, var(--secondary-text-color)); - -webkit-transition: -webkit-transform 0.25s, width 0.25s; - transition: transform 0.25s, width 0.25s; - -webkit-transform-origin: left top; - transform-origin: left top; - /* Fix for safari not focusing 0-height date/time inputs with -webkit-apperance: none; */ - min-height: 1px; - - @apply --paper-font-common-nowrap; - @apply --paper-font-subhead; - @apply --paper-input-container-label; - @apply --paper-transition-easing; - } - - .input-content.label-is-floating ::slotted(label), - .input-content.label-is-floating ::slotted(.paper-input-label) { - -webkit-transform: translateY(-75%) scale(0.75); - transform: translateY(-75%) scale(0.75); - - /* Since we scale to 75/100 of the size, we actually have 100/75 of the - original space now available */ - width: 133%; - - @apply --paper-input-container-label-floating; - } - - :host(:dir(rtl)) .input-content.label-is-floating ::slotted(label), - :host(:dir(rtl)) .input-content.label-is-floating ::slotted(.paper-input-label) { - right: 0; - left: auto; - -webkit-transform-origin: right top; - transform-origin: right top; - } - - .input-content.label-is-highlighted ::slotted(label), - .input-content.label-is-highlighted ::slotted(.paper-input-label) { - color: var(--paper-input-container-focus-color, var(--primary-color)); - - @apply --paper-input-container-label-focus; - } - - .input-content.is-invalid ::slotted(label), - .input-content.is-invalid ::slotted(.paper-input-label) { - color: var(--paper-input-container-invalid-color, var(--error-color)); - } - - .input-content.label-is-hidden ::slotted(label), - .input-content.label-is-hidden ::slotted(.paper-input-label) { - visibility: hidden; - } - - .input-content ::slotted(iron-input) { - @apply --paper-input-container-shared-input-style; - } - - .input-content ::slotted(input), - .input-content ::slotted(textarea), - .input-content ::slotted(iron-autogrow-textarea), - .input-content ::slotted(.paper-input-input) { - @apply --paper-input-container-shared-input-style; - @apply --paper-input-container-input; - } - - .input-content ::slotted(input)::-webkit-outer-spin-button, - .input-content ::slotted(input)::-webkit-inner-spin-button { - @apply --paper-input-container-input-webkit-spinner; - } - - .input-content.focused ::slotted(input), - .input-content.focused ::slotted(textarea), - .input-content.focused ::slotted(iron-autogrow-textarea), - .input-content.focused ::slotted(.paper-input-input) { - @apply --paper-input-container-input-focus; - } - - .input-content.is-invalid ::slotted(input), - .input-content.is-invalid ::slotted(textarea), - .input-content.is-invalid ::slotted(iron-autogrow-textarea), - .input-content.is-invalid ::slotted(.paper-input-input) { - @apply --paper-input-container-input-invalid; - } - - .prefix ::slotted(*) { - display: inline-block; - @apply --paper-font-subhead; - @apply --layout-flex-none; - @apply --paper-input-prefix; - } - - .suffix ::slotted(*) { - display: inline-block; - @apply --paper-font-subhead; - @apply --layout-flex-none; - - @apply --paper-input-suffix; - } - - /* Firefox sets a min-width on the input, which can cause layout issues */ - .input-content ::slotted(input) { - min-width: 0; - } - - .input-content ::slotted(textarea) { - resize: none; - } - - .add-on-content { - position: relative; - } - - .add-on-content.is-invalid ::slotted(*) { - color: var(--paper-input-container-invalid-color, var(--error-color)); - } - - .add-on-content.is-highlighted ::slotted(*) { - color: var(--paper-input-container-focus-color, var(--primary-color)); - } - </style> - - <div class="floated-label-placeholder" aria-hidden="true" hidden="[[noLabelFloat]]"> </div> - - <div class="input-wrapper"> - <span class="prefix"><slot name="prefix"></slot></span> - - <div class$="[[_computeInputContentClass(noLabelFloat,alwaysFloatLabel,focused,invalid,_inputHasContent)]]" id="labelAndInputContainer"> - <slot name="label"></slot> - <slot name="input"></slot> - </div> - - <span class="suffix"><slot name="suffix"></slot></span> - </div> - - <div class$="[[_computeUnderlineClass(focused,invalid)]]"> - <div class="unfocused-line"></div> - <div class="focused-line"></div> - </div> - - <div class$="[[_computeAddOnContentClass(focused,invalid)]]"> - <slot name="add-on"></slot> - </div> - </template> -</dom-module> - -<script> - Polymer({ - is: 'paper-input-container', - - properties: { - /** - * Set to true to disable the floating label. The label disappears when the input value is - * not null. - */ - noLabelFloat: { - type: Boolean, - value: false - }, - - /** - * Set to true to always float the floating label. - */ - alwaysFloatLabel: { - type: Boolean, - value: false - }, - - /** - * The attribute to listen for value changes on. - */ - attrForValue: { - type: String, - value: 'bind-value' - }, - - /** - * Set to true to auto-validate the input value when it changes. - */ - autoValidate: { - type: Boolean, - value: false - }, - - /** - * True if the input is invalid. This property is set automatically when the input value - * changes if auto-validating, or when the `iron-input-validate` event is heard from a child. - */ - invalid: { - observer: '_invalidChanged', - type: Boolean, - value: false - }, - - /** - * True if the input has focus. - */ - focused: { - readOnly: true, - type: Boolean, - value: false, - notify: true - }, - - _addons: { - type: Array - // do not set a default value here intentionally - it will be initialized lazily when a - // distributed child is attached, which may occur before configuration for this element - // in polyfill. - }, - - _inputHasContent: { - type: Boolean, - value: false - }, - - _inputSelector: { - type: String, - value: 'input,iron-input,textarea,.paper-input-input' - }, - - _boundOnFocus: { - type: Function, - value: function() { - return this._onFocus.bind(this); - } - }, - - _boundOnBlur: { - type: Function, - value: function() { - return this._onBlur.bind(this); - } - }, - - _boundOnInput: { - type: Function, - value: function() { - return this._onInput.bind(this); - } - }, - - _boundValueChanged: { - type: Function, - value: function() { - return this._onValueChanged.bind(this); - } - } - }, - - listeners: { - 'addon-attached': '_onAddonAttached', - 'iron-input-validate': '_onIronInputValidate' - }, - - get _valueChangedEvent() { - return this.attrForValue + '-changed'; - }, - - get _propertyForValue() { - return Polymer.CaseMap.dashToCamelCase(this.attrForValue); - }, - - get _inputElement() { - return Polymer.dom(this).querySelector(this._inputSelector); - }, - - get _inputElementValue() { - return this._inputElement[this._propertyForValue] || this._inputElement.value; - }, - - ready: function() { - if (!this._addons) { - this._addons = []; - } - this.addEventListener('focus', this._boundOnFocus, true); - this.addEventListener('blur', this._boundOnBlur, true); - }, - - attached: function() { - if (this.attrForValue) { - this._inputElement.addEventListener(this._valueChangedEvent, this._boundValueChanged); - } else { - this.addEventListener('input', this._onInput); - } - - // Only validate when attached if the input already has a value. - if (this._inputElementValue && this._inputElementValue != '') { - this._handleValueAndAutoValidate(this._inputElement); - } else { - this._handleValue(this._inputElement); - } - }, - - _onAddonAttached: function(event) { - if (!this._addons) { - this._addons = []; - } - var target = event.target; - if (this._addons.indexOf(target) === -1) { - this._addons.push(target); - if (this.isAttached) { - this._handleValue(this._inputElement); - } - } - }, - - _onFocus: function() { - this._setFocused(true); - }, - - _onBlur: function() { - this._setFocused(false); - this._handleValueAndAutoValidate(this._inputElement); - }, - - _onInput: function(event) { - this._handleValueAndAutoValidate(event.target); - }, - - _onValueChanged: function(event) { - var input = event.target; - - // Problem: if the input is required but has no text entered, we should - // only validate it on blur (so that an empty form doesn't come up red - // immediately; however, in this handler, we don't know whether this is - // the booting up value or a value in the future. I am assuming that the - // case we care about manifests itself when the value is undefined. - // If this causes future problems, we need to do something like track whether - // the iron-input-ready event has fired, and this handler came before that. - - if (input.value === undefined) { - return; - } - - this._handleValueAndAutoValidate(event.target); - }, - - _handleValue: function(inputElement) { - var value = this._inputElementValue; - - // type="number" hack needed because this.value is empty until it's valid - if (value || value === 0 || (inputElement.type === 'number' && !inputElement.checkValidity())) { - this._inputHasContent = true; - } else { - this._inputHasContent = false; - } - - this.updateAddons({ - inputElement: inputElement, - value: value, - invalid: this.invalid - }); - }, - - _handleValueAndAutoValidate: function(inputElement) { - if (this.autoValidate && inputElement) { - var valid; - - if (inputElement.validate) { - valid = inputElement.validate(this._inputElementValue); - } else { - valid = inputElement.checkValidity(); - } - this.invalid = !valid; - } - - // Call this last to notify the add-ons. - this._handleValue(inputElement); - }, - - _onIronInputValidate: function(event) { - this.invalid = this._inputElement.invalid; - }, - - _invalidChanged: function() { - if (this._addons) { - this.updateAddons({invalid: this.invalid}); - } - }, - - /** - * Call this to update the state of add-ons. - * @param {Object} state Add-on state. - */ - updateAddons: function(state) { - for (var addon, index = 0; addon = this._addons[index]; index++) { - addon.update(state); - } - }, - - _computeInputContentClass: function(noLabelFloat, alwaysFloatLabel, focused, invalid, _inputHasContent) { - var cls = 'input-content'; - if (!noLabelFloat) { - var label = this.querySelector('label'); - - if (alwaysFloatLabel || _inputHasContent) { - cls += ' label-is-floating'; - // If the label is floating, ignore any offsets that may have been - // applied from a prefix element. - this.$.labelAndInputContainer.style.position = 'static'; - - if (invalid) { - cls += ' is-invalid'; - } else if (focused) { - cls += " label-is-highlighted"; - } - } else { - // When the label is not floating, it should overlap the input element. - if (label) { - this.$.labelAndInputContainer.style.position = 'relative'; - } - if (invalid) { - cls += ' is-invalid'; - } - } - } else { - if (_inputHasContent) { - cls += ' label-is-hidden'; - } - if (invalid) { - cls += ' is-invalid'; - } - } - if (focused) { - cls += ' focused'; - } - return cls; - }, - - _computeUnderlineClass: function(focused, invalid) { - var cls = 'underline'; - if (invalid) { - cls += ' is-invalid'; - } else if (focused) { - cls += ' is-highlighted' - } - return cls; - }, - - _computeAddOnContentClass: function(focused, invalid) { - var cls = 'add-on-content'; - if (invalid) { - cls += ' is-invalid'; - } else if (focused) { - cls += ' is-highlighted' - } - return cls; - } - }); -</script> +<!-- +@license +Copyright (c) 2015 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +--> + +<link rel="import" href="../polymer/polymer.html"> +<link rel="import" href="../iron-flex-layout/iron-flex-layout.html"> +<link rel="import" href="../paper-styles/default-theme.html"> +<link rel="import" href="../paper-styles/typography.html"> + +<!-- +`<paper-input-container>` is a container for a `<label>`, an `<iron-input>` or +`<textarea>` and optional add-on elements such as an error message or character +counter, used to implement Material Design text fields. + +For example: + + <paper-input-container> + <label slot="label">Your name</label> + <iron-input slot="input"> + <input> + </iron-input> + // In Polymer 1.0, you would use `<input is="iron-input" slot="input">` instead of the above. + </paper-input-container> + +You can style the nested `<input>` however you want; if you want it to look like a +Material Design input, you can style it with the --paper-input-container-shared-input-style mixin. + +Do not wrap `<paper-input-container>` around elements that already include it, such as `<paper-input>`. +Doing so may cause events to bounce infinitely between the container and its contained element. + +### Listening for input changes + +By default, it listens for changes on the `bind-value` attribute on its children nodes and perform +tasks such as auto-validating and label styling when the `bind-value` changes. You can configure +the attribute it listens to with the `attr-for-value` attribute. + +### Using a custom input element + +You can use a custom input element in a `<paper-input-container>`, for example to implement a +compound input field like a social security number input. The custom input element should have the +`paper-input-input` class, have a `notify:true` value property and optionally implements +`Polymer.IronValidatableBehavior` if it is validatable. + + <paper-input-container attr-for-value="ssn-value"> + <label slot="label">Social security number</label> + <ssn-input slot="input" class="paper-input-input"></ssn-input> + </paper-input-container> + + +If you're using a `<paper-input-container>` imperatively, it's important to make sure +that you attach its children (the `iron-input` and the optional `label`) before you +attach the `<paper-input-container>` itself, so that it can be set up correctly. + +### Validation + +If the `auto-validate` attribute is set, the input container will validate the input and update +the container styling when the input value changes. + +### Add-ons + +Add-ons are child elements of a `<paper-input-container>` with the `add-on` attribute and +implements the `Polymer.PaperInputAddonBehavior` behavior. They are notified when the input value +or validity changes, and may implement functionality such as error messages or character counters. +They appear at the bottom of the input. + +### Prefixes and suffixes +These are child elements of a `<paper-input-container>` with the `prefix` +or `suffix` attribute, and are displayed inline with the input, before or after. + + <paper-input-container> + <div slot="prefix">$</div> + <label slot="label">Total</label> + <iron-input slot="input"> + <input> + </iron-input> + // In Polymer 1.0, you would use `<input is="iron-input" slot="input">` instead of the above. + <paper-icon-button slot="suffix" icon="clear"></paper-icon-button> + </paper-input-container> + +### Styling + +The following custom properties and mixins are available for styling: + +Custom property | Description | Default +----------------|-------------|---------- +`--paper-input-container-color` | Label and underline color when the input is not focused | `--secondary-text-color` +`--paper-input-container-focus-color` | Label and underline color when the input is focused | `--primary-color` +`--paper-input-container-invalid-color` | Label and underline color when the input is is invalid | `--error-color` +`--paper-input-container-input-color` | Input foreground color | `--primary-text-color` +`--paper-input-container` | Mixin applied to the container | `{}` +`--paper-input-container-disabled` | Mixin applied to the container when it's disabled | `{}` +`--paper-input-container-label` | Mixin applied to the label | `{}` +`--paper-input-container-label-focus` | Mixin applied to the label when the input is focused | `{}` +`--paper-input-container-label-floating` | Mixin applied to the label when floating | `{}` +`--paper-input-container-input` | Mixin applied to the input | `{}` +`--paper-input-container-input-disabled` | Mixin applied to the input when the component is disabled | `{}` +`--paper-input-container-input-focus` | Mixin applied to the input when focused | `{}` +`--paper-input-container-input-invalid` | Mixin applied to the input when invalid | `{}` +`--paper-input-container-input-webkit-spinner` | Mixin applied to the webkit spinner | `{}` +`--paper-input-container-input-webkit-clear` | Mixin applied to the webkit clear button | `{}` +`--paper-input-container-ms-clear` | Mixin applied to the Internet Explorer clear button | `{}` +`--paper-input-container-underline` | Mixin applied to the underline | `{}` +`--paper-input-container-underline-focus` | Mixin applied to the underline when the input is focused | `{}` +`--paper-input-container-underline-disabled` | Mixin applied to the underline when the input is disabled | `{}` +`--paper-input-prefix` | Mixin applied to the input prefix | `{}` +`--paper-input-suffix` | Mixin applied to the input suffix | `{}` + +This element is `display:block` by default, but you can set the `inline` attribute to make it +`display:inline-block`. +--> + +<dom-module id="paper-input-container"> + <template> + <style> + :host { + display: block; + padding: 8px 0; + + --paper-input-container-shared-input-style: { + position: relative; /* to make a stacking context */ + outline: none; + box-shadow: none; + padding: 0; + width: 100%; + max-width: 100%; + background: transparent; + border: none; + color: var(--paper-input-container-input-color, var(--primary-text-color)); + -webkit-appearance: none; + text-align: inherit; + vertical-align: bottom; + + @apply --paper-font-subhead; + }; + + @apply --paper-input-container; + } + + :host([inline]) { + display: inline-block; + } + + :host([disabled]) { + pointer-events: none; + opacity: 0.33; + + @apply --paper-input-container-disabled; + } + + :host([hidden]) { + display: none !important; + } + + [hidden] { + display: none !important; + } + + .floated-label-placeholder { + @apply --paper-font-caption; + } + + .underline { + height: 2px; + position: relative; + } + + .focused-line { + @apply --layout-fit; + border-bottom: 2px solid var(--paper-input-container-focus-color, var(--primary-color)); + + -webkit-transform-origin: center center; + transform-origin: center center; + -webkit-transform: scale3d(0,1,1); + transform: scale3d(0,1,1); + + @apply --paper-input-container-underline-focus; + } + + .underline.is-highlighted .focused-line { + -webkit-transform: none; + transform: none; + -webkit-transition: -webkit-transform 0.25s; + transition: transform 0.25s; + + @apply --paper-transition-easing; + } + + .underline.is-invalid .focused-line { + border-color: var(--paper-input-container-invalid-color, var(--error-color)); + -webkit-transform: none; + transform: none; + -webkit-transition: -webkit-transform 0.25s; + transition: transform 0.25s; + + @apply --paper-transition-easing; + } + + .unfocused-line { + @apply --layout-fit; + border-bottom: 1px solid var(--paper-input-container-color, var(--secondary-text-color)); + @apply --paper-input-container-underline; + } + + :host([disabled]) .unfocused-line { + border-bottom: 1px dashed; + border-color: var(--paper-input-container-color, var(--secondary-text-color)); + @apply --paper-input-container-underline-disabled; + } + + .input-wrapper { + @apply --layout-horizontal; + @apply --layout-center; + @apply --paper-input-container-input-wrapper; + position: relative; + } + + .input-content { + @apply --layout-flex-auto; + @apply --layout-relative; + max-width: 100%; + } + + .input-content ::slotted(label), + .input-content ::slotted(.paper-input-label) { + position: absolute; + top: 0; + left: 0; + width: 100%; + font: inherit; + color: var(--paper-input-container-color, var(--secondary-text-color)); + -webkit-transition: -webkit-transform 0.25s, width 0.25s; + transition: transform 0.25s, width 0.25s; + -webkit-transform-origin: left top; + transform-origin: left top; + /* Fix for safari not focusing 0-height date/time inputs with -webkit-apperance: none; */ + min-height: 1px; + + @apply --paper-font-common-nowrap; + @apply --paper-font-subhead; + @apply --paper-input-container-label; + @apply --paper-transition-easing; + } + + .input-content.label-is-floating ::slotted(label), + .input-content.label-is-floating ::slotted(.paper-input-label) { + -webkit-transform: translateY(-75%) scale(0.75); + transform: translateY(-75%) scale(0.75); + + /* Since we scale to 75/100 of the size, we actually have 100/75 of the + original space now available */ + width: 133%; + + @apply --paper-input-container-label-floating; + } + + :host(:dir(rtl)) .input-content.label-is-floating ::slotted(label), + :host(:dir(rtl)) .input-content.label-is-floating ::slotted(.paper-input-label) { + right: 0; + left: auto; + -webkit-transform-origin: right top; + transform-origin: right top; + } + + .input-content.label-is-highlighted ::slotted(label), + .input-content.label-is-highlighted ::slotted(.paper-input-label) { + color: var(--paper-input-container-focus-color, var(--primary-color)); + + @apply --paper-input-container-label-focus; + } + + .input-content.is-invalid ::slotted(label), + .input-content.is-invalid ::slotted(.paper-input-label) { + color: var(--paper-input-container-invalid-color, var(--error-color)); + } + + .input-content.label-is-hidden ::slotted(label), + .input-content.label-is-hidden ::slotted(.paper-input-label) { + visibility: hidden; + } + + .input-content ::slotted(iron-input) { + @apply --paper-input-container-shared-input-style; + } + + .input-content ::slotted(input), + .input-content ::slotted(textarea), + .input-content ::slotted(iron-autogrow-textarea), + .input-content ::slotted(.paper-input-input) { + @apply --paper-input-container-shared-input-style; + @apply --paper-input-container-input; + } + + .input-content ::slotted(input)::-webkit-outer-spin-button, + .input-content ::slotted(input)::-webkit-inner-spin-button { + @apply --paper-input-container-input-webkit-spinner; + } + + .input-content.focused ::slotted(input), + .input-content.focused ::slotted(textarea), + .input-content.focused ::slotted(iron-autogrow-textarea), + .input-content.focused ::slotted(.paper-input-input) { + @apply --paper-input-container-input-focus; + } + + .input-content.is-invalid ::slotted(input), + .input-content.is-invalid ::slotted(textarea), + .input-content.is-invalid ::slotted(iron-autogrow-textarea), + .input-content.is-invalid ::slotted(.paper-input-input) { + @apply --paper-input-container-input-invalid; + } + + .prefix ::slotted(*) { + display: inline-block; + @apply --paper-font-subhead; + @apply --layout-flex-none; + @apply --paper-input-prefix; + } + + .suffix ::slotted(*) { + display: inline-block; + @apply --paper-font-subhead; + @apply --layout-flex-none; + + @apply --paper-input-suffix; + } + + /* Firefox sets a min-width on the input, which can cause layout issues */ + .input-content ::slotted(input) { + min-width: 0; + } + + .input-content ::slotted(textarea) { + resize: none; + } + + .add-on-content { + position: relative; + } + + .add-on-content.is-invalid ::slotted(*) { + color: var(--paper-input-container-invalid-color, var(--error-color)); + } + + .add-on-content.is-highlighted ::slotted(*) { + color: var(--paper-input-container-focus-color, var(--primary-color)); + } + </style> + + <div class="floated-label-placeholder" aria-hidden="true" hidden="[[noLabelFloat]]"> </div> + + <div class="input-wrapper"> + <span class="prefix"><slot name="prefix"></slot></span> + + <div class$="[[_computeInputContentClass(noLabelFloat,alwaysFloatLabel,focused,invalid,_inputHasContent)]]" id="labelAndInputContainer"> + <slot name="label"></slot> + <slot name="input"></slot> + </div> + + <span class="suffix"><slot name="suffix"></slot></span> + </div> + + <div class$="[[_computeUnderlineClass(focused,invalid)]]"> + <div class="unfocused-line"></div> + <div class="focused-line"></div> + </div> + + <div class$="[[_computeAddOnContentClass(focused,invalid)]]"> + <slot name="add-on"></slot> + </div> + </template> +</dom-module> + +<script> + Polymer({ + is: 'paper-input-container', + + properties: { + /** + * Set to true to disable the floating label. The label disappears when the input value is + * not null. + */ + noLabelFloat: { + type: Boolean, + value: false + }, + + /** + * Set to true to always float the floating label. + */ + alwaysFloatLabel: { + type: Boolean, + value: false + }, + + /** + * The attribute to listen for value changes on. + */ + attrForValue: { + type: String, + value: 'bind-value' + }, + + /** + * Set to true to auto-validate the input value when it changes. + */ + autoValidate: { + type: Boolean, + value: false + }, + + /** + * True if the input is invalid. This property is set automatically when the input value + * changes if auto-validating, or when the `iron-input-validate` event is heard from a child. + */ + invalid: { + observer: '_invalidChanged', + type: Boolean, + value: false + }, + + /** + * True if the input has focus. + */ + focused: { + readOnly: true, + type: Boolean, + value: false, + notify: true + }, + + _addons: { + type: Array + // do not set a default value here intentionally - it will be initialized lazily when a + // distributed child is attached, which may occur before configuration for this element + // in polyfill. + }, + + _inputHasContent: { + type: Boolean, + value: false + }, + + _inputSelector: { + type: String, + value: 'input,iron-input,textarea,.paper-input-input' + }, + + _boundOnFocus: { + type: Function, + value: function() { + return this._onFocus.bind(this); + } + }, + + _boundOnBlur: { + type: Function, + value: function() { + return this._onBlur.bind(this); + } + }, + + _boundOnInput: { + type: Function, + value: function() { + return this._onInput.bind(this); + } + }, + + _boundValueChanged: { + type: Function, + value: function() { + return this._onValueChanged.bind(this); + } + } + }, + + listeners: { + 'addon-attached': '_onAddonAttached', + 'iron-input-validate': '_onIronInputValidate' + }, + + get _valueChangedEvent() { + return this.attrForValue + '-changed'; + }, + + get _propertyForValue() { + return Polymer.CaseMap.dashToCamelCase(this.attrForValue); + }, + + get _inputElement() { + return Polymer.dom(this).querySelector(this._inputSelector); + }, + + get _inputElementValue() { + return this._inputElement[this._propertyForValue] || this._inputElement.value; + }, + + ready: function() { + if (!this._addons) { + this._addons = []; + } + this.addEventListener('focus', this._boundOnFocus, true); + this.addEventListener('blur', this._boundOnBlur, true); + }, + + attached: function() { + if (this.attrForValue) { + this._inputElement.addEventListener(this._valueChangedEvent, this._boundValueChanged); + } else { + this.addEventListener('input', this._onInput); + } + + // Only validate when attached if the input already has a value. + if (this._inputElementValue && this._inputElementValue != '') { + this._handleValueAndAutoValidate(this._inputElement); + } else { + this._handleValue(this._inputElement); + } + }, + + _onAddonAttached: function(event) { + if (!this._addons) { + this._addons = []; + } + var target = event.target; + if (this._addons.indexOf(target) === -1) { + this._addons.push(target); + if (this.isAttached) { + this._handleValue(this._inputElement); + } + } + }, + + _onFocus: function() { + this._setFocused(true); + }, + + _onBlur: function() { + this._setFocused(false); + this._handleValueAndAutoValidate(this._inputElement); + }, + + _onInput: function(event) { + this._handleValueAndAutoValidate(event.target); + }, + + _onValueChanged: function(event) { + var input = event.target; + + // Problem: if the input is required but has no text entered, we should + // only validate it on blur (so that an empty form doesn't come up red + // immediately; however, in this handler, we don't know whether this is + // the booting up value or a value in the future. I am assuming that the + // case we care about manifests itself when the value is undefined. + // If this causes future problems, we need to do something like track whether + // the iron-input-ready event has fired, and this handler came before that. + + if (input.value === undefined) { + return; + } + + this._handleValueAndAutoValidate(event.target); + }, + + _handleValue: function(inputElement) { + var value = this._inputElementValue; + + // type="number" hack needed because this.value is empty until it's valid + if (value || value === 0 || (inputElement.type === 'number' && !inputElement.checkValidity())) { + this._inputHasContent = true; + } else { + this._inputHasContent = false; + } + + this.updateAddons({ + inputElement: inputElement, + value: value, + invalid: this.invalid + }); + }, + + _handleValueAndAutoValidate: function(inputElement) { + if (this.autoValidate && inputElement) { + var valid; + + if (inputElement.validate) { + valid = inputElement.validate(this._inputElementValue); + } else { + valid = inputElement.checkValidity(); + } + this.invalid = !valid; + } + + // Call this last to notify the add-ons. + this._handleValue(inputElement); + }, + + _onIronInputValidate: function(event) { + this.invalid = this._inputElement.invalid; + }, + + _invalidChanged: function() { + if (this._addons) { + this.updateAddons({invalid: this.invalid}); + } + }, + + /** + * Call this to update the state of add-ons. + * @param {Object} state Add-on state. + */ + updateAddons: function(state) { + for (var addon, index = 0; addon = this._addons[index]; index++) { + addon.update(state); + } + }, + + _computeInputContentClass: function(noLabelFloat, alwaysFloatLabel, focused, invalid, _inputHasContent) { + var cls = 'input-content'; + if (!noLabelFloat) { + var label = this.querySelector('label'); + + if (alwaysFloatLabel || _inputHasContent) { + cls += ' label-is-floating'; + // If the label is floating, ignore any offsets that may have been + // applied from a prefix element. + this.$.labelAndInputContainer.style.position = 'static'; + + if (invalid) { + cls += ' is-invalid'; + } else if (focused) { + cls += " label-is-highlighted"; + } + } else { + // When the label is not floating, it should overlap the input element. + if (label) { + this.$.labelAndInputContainer.style.position = 'relative'; + } + if (invalid) { + cls += ' is-invalid'; + } + } + } else { + if (_inputHasContent) { + cls += ' label-is-hidden'; + } + if (invalid) { + cls += ' is-invalid'; + } + } + if (focused) { + cls += ' focused'; + } + return cls; + }, + + _computeUnderlineClass: function(focused, invalid) { + var cls = 'underline'; + if (invalid) { + cls += ' is-invalid'; + } else if (focused) { + cls += ' is-highlighted' + } + return cls; + }, + + _computeAddOnContentClass: function(focused, invalid) { + var cls = 'add-on-content'; + if (invalid) { + cls += ' is-invalid'; + } else if (focused) { + cls += ' is-highlighted' + } + return cls; + } + }); +</script>