aboutsummaryrefslogtreecommitdiff
path: root/catapult/third_party/polymer/components/paper-input/paper-input-container.html
diff options
context:
space:
mode:
Diffstat (limited to 'catapult/third_party/polymer/components/paper-input/paper-input-container.html')
-rw-r--r--catapult/third_party/polymer/components/paper-input/paper-input-container.html653
1 files changed, 653 insertions, 0 deletions
diff --git a/catapult/third_party/polymer/components/paper-input/paper-input-container.html b/catapult/third_party/polymer/components/paper-input/paper-input-container.html
new file mode 100644
index 00000000..2ef06efa
--- /dev/null
+++ b/catapult/third_party/polymer/components/paper-input/paper-input-container.html
@@ -0,0 +1,653 @@
+<!--
+@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 `<input is="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>Your name</label>
+ <input is="iron-input">
+ </paper-input-container>
+
+Do not wrap `<paper-input-container>` around elements that already include it, such as `<paper-input>`.
+Doing so may cause events to bounce infintely 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>Social security number</label>
+ <ssn-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 prefix>$</div>
+ <label>Total</label>
+ <input is="iron-input">
+ <paper-icon-button 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-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;
+
+ @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;
+ }
+
+ .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, --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, --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, --secondary-text-color);
+
+ @apply(--paper-input-container-underline);
+ }
+
+ :host([disabled]) .unfocused-line {
+ border-bottom: 1px dashed;
+ border-color: var(--paper-input-container-color, --secondary-text-color);
+
+ @apply(--paper-input-container-underline-disabled);
+ }
+
+ .label-and-input-container {
+ @apply(--layout-flex-auto);
+ @apply(--layout-relative);
+
+ width: 100%;
+ max-width: 100%;
+ }
+
+ .input-content {
+ @apply(--layout-horizontal);
+ @apply(--layout-center);
+
+ position: relative;
+ }
+
+ .input-content ::content label,
+ .input-content ::content .paper-input-label {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ width: 100%;
+ font: inherit;
+ color: var(--paper-input-container-color, --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;
+
+ @apply(--paper-font-common-nowrap);
+ @apply(--paper-font-subhead);
+ @apply(--paper-input-container-label);
+ @apply(--paper-transition-easing);
+ }
+
+ .input-content.label-is-floating ::content label,
+ .input-content.label-is-floating ::content .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-context([dir="rtl"]) .input-content.label-is-floating ::content label,
+ :host-context([dir="rtl"]) .input-content.label-is-floating ::content .paper-input-label {
+ /* TODO(noms): Figure out why leaving the width at 133% before the animation
+ * actually makes
+ * it wider on the right side, not left side, as you would expect in RTL */
+ width: 100%;
+ -webkit-transform-origin: right top;
+ transform-origin: right top;
+ }
+
+ .input-content.label-is-highlighted ::content label,
+ .input-content.label-is-highlighted ::content .paper-input-label {
+ color: var(--paper-input-container-focus-color, --primary-color);
+
+ @apply(--paper-input-container-label-focus);
+ }
+
+ .input-content.is-invalid ::content label,
+ .input-content.is-invalid ::content .paper-input-label {
+ color: var(--paper-input-container-invalid-color, --error-color);
+ }
+
+ .input-content.label-is-hidden ::content label,
+ .input-content.label-is-hidden ::content .paper-input-label {
+ visibility: hidden;
+ }
+
+ .input-content ::content input,
+ .input-content ::content textarea,
+ .input-content ::content iron-autogrow-textarea,
+ .input-content ::content .paper-input-input {
+ 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, --primary-text-color);
+ -webkit-appearance: none;
+ text-align: inherit;
+ vertical-align: bottom;
+
+ @apply(--paper-font-subhead);
+ @apply(--paper-input-container-input);
+ }
+
+ .input-content.focused ::content input,
+ .input-content.focused ::content textarea,
+ .input-content.focused ::content iron-autogrow-textarea,
+ .input-content.focused ::content .paper-input-input {
+ @apply(--paper-input-container-input-focus);
+ }
+
+ .input-content.is-invalid ::content input,
+ .input-content.is-invalid ::content textarea,
+ .input-content.is-invalid ::content iron-autogrow-textarea,
+ .input-content.is-invalid ::content .paper-input-input {
+ @apply(--paper-input-container-input-invalid);
+ }
+
+ .input-content ::content input::-webkit-outer-spin-button,
+ .input-content ::content input::-webkit-inner-spin-button {
+ @apply(--paper-input-container-input-webkit-spinner);
+ }
+
+ ::content [prefix] {
+ @apply(--paper-font-subhead);
+
+ @apply(--paper-input-prefix);
+ @apply(--layout-flex-none);
+ }
+
+ ::content [suffix] {
+ @apply(--paper-font-subhead);
+
+ @apply(--paper-input-suffix);
+ @apply(--layout-flex-none);
+ }
+
+ /* Firefox sets a min-width on the input, which can cause layout issues */
+ .input-content ::content input {
+ min-width: 0;
+ }
+
+ .input-content ::content textarea {
+ resize: none;
+ }
+
+ .add-on-content {
+ position: relative;
+ }
+
+ .add-on-content.is-invalid ::content * {
+ color: var(--paper-input-container-invalid-color, --error-color);
+ }
+
+ .add-on-content.is-highlighted ::content * {
+ color: var(--paper-input-container-focus-color, --primary-color);
+ }
+ </style>
+
+ <template is="dom-if" if="[[!noLabelFloat]]">
+ <div class="floated-label-placeholder" aria-hidden="true">&nbsp;</div>
+ </template>
+
+ <div class$="[[_computeInputContentClass(noLabelFloat,alwaysFloatLabel,focused,invalid,_inputHasContent)]]">
+ <content select="[prefix]" id="prefix"></content>
+
+ <div class="label-and-input-container" id="labelAndInputContainer">
+ <content select=":not([add-on]):not([prefix]):not([suffix])"></content>
+ </div>
+
+ <content select="[suffix]"></content>
+ </div>
+
+ <div class$="[[_computeUnderlineClass(focused,invalid)]]">
+ <div class="unfocused-line"></div>
+ <div class="focused-line"></div>
+ </div>
+
+ <div class$="[[_computeAddOnContentClass(focused,invalid)]]">
+ <content id="addOnContent" select="[add-on]"></content>
+ </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,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._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) {
+ 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) {
+ 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>