| OLD | NEW | 
| (Empty) |  | 
 |    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.g
     ithub.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/CONTRI
     BUTORS | 
 |    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/PATEN
     TS | 
 |    8 --> | 
 |    9  | 
 |   10 <!-- | 
 |   11  | 
 |   12 Material Design: <a href="http://www.google.com/design/spec/components/text-fiel
     ds.html">Text fields</a> | 
 |   13  | 
 |   14 `paper-input-decorator` adds Material Design input field styling and animations 
     to an element. | 
 |   15  | 
 |   16 Example: | 
 |   17  | 
 |   18     <paper-input-decorator label="Your Name"> | 
 |   19         <input is="core-input"> | 
 |   20     </paper-input-decorator> | 
 |   21  | 
 |   22     <paper-input-decorator floatingLabel label="Your address"> | 
 |   23         <textarea></textarea> | 
 |   24     </paper-input-decorator> | 
 |   25  | 
 |   26 Theming | 
 |   27 ------- | 
 |   28  | 
 |   29 `paper-input-decorator` uses `core-style` for global theming. The following opti
     ons are available: | 
 |   30  | 
 |   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 c
     olor 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 focu
     s. | 
 |   34  | 
 |   35 To add custom styling to only some elements, use these selectors: | 
 |   36  | 
 |   37     paper-input-decorator /deep/ .label-text, | 
 |   38     paper-input-decorator /deep/ .error { | 
 |   39         /* inline label,  floating label, error message and error icon color whe
     n the input is unfocused */ | 
 |   40         color: green; | 
 |   41     } | 
 |   42  | 
 |   43     paper-input-decorator /deep/ ::-webkit-input-placeholder { | 
 |   44         /* platform specific rules for placeholder text */ | 
 |   45         color: green; | 
 |   46     } | 
 |   47     paper-input-decorator /deep/ ::-moz-placeholder { | 
 |   48         color: green; | 
 |   49     } | 
 |   50     paper-input-decorator /deep/ :-ms-input-placeholder { | 
 |   51         color: green; | 
 |   52     } | 
 |   53  | 
 |   54     paper-input-decorator /deep/ .unfocused-underline { | 
 |   55         /* line color when the input is unfocused */ | 
 |   56         background-color: green; | 
 |   57     } | 
 |   58  | 
 |   59     paper-input-decorator[focused] /deep/ .floating-label .label-text { | 
 |   60         /* floating label color when the input is focused */ | 
 |   61         color: orange; | 
 |   62     } | 
 |   63  | 
 |   64     paper-input-decorator /deep/ .focused-underline { | 
 |   65         /* line color when the input is focused */ | 
 |   66         background-color: orange; | 
 |   67     } | 
 |   68  | 
 |   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; | 
 |   73     } | 
 |   74  | 
 |   75     paper-input-decorator.invalid /deep/ .focused-underline { | 
 |   76         /* line and color when the input is invalid and focused */ | 
 |   77         background-color: salmon; | 
 |   78     } | 
 |   79  | 
 |   80 Form submission | 
 |   81 --------------- | 
 |   82  | 
 |   83 You can use inputs decorated with this element in a `form` as usual. | 
 |   84  | 
 |   85 Validation | 
 |   86 ---------- | 
 |   87  | 
 |   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/Co
     nstraint_validation">HTML5 Constraints Validation API</a> | 
 |   90 to implement validation. Set the `isInvalid` attribute when the input is validat
     ed, and provide an | 
 |   91 error message in the `error` attribute. | 
 |   92  | 
 |   93 Example: | 
 |   94  | 
 |   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; | 
 |  104         } | 
 |  105     </script> | 
 |  106  | 
 |  107 Example to validate as the user types: | 
 |  108  | 
 |  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> | 
 |  114  | 
 |  115 Accessibility | 
 |  116 ------------- | 
 |  117  | 
 |  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 in
     put, as it will | 
 |  120 conflict with this element. | 
 |  121  | 
 |  122 @group Paper Elements | 
 |  123 @element paper-input-decorator | 
 |  124 @homepage github.io | 
 |  125 --> | 
 |  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"> | 
 |  131  | 
 |  132 <core-style id="paper-input-decorator"> | 
 |  133  | 
 |  134 .label-text, | 
 |  135 .error { | 
 |  136   color: {{g.paperInput.labelColor}}; | 
 |  137 } | 
 |  138  | 
 |  139 ::-webkit-input-placeholder { | 
 |  140   color: {{g.paperInput.labelColor}}; | 
 |  141 } | 
 |  142  | 
 |  143 ::-moz-placeholder { | 
 |  144   color: {{g.paperInput.labelColor}}; | 
 |  145 } | 
 |  146  | 
 |  147 :-ms-input-placeholder { | 
 |  148   color: {{g.paperInput.labelColor}}; | 
 |  149 } | 
 |  150  | 
 |  151 .unfocused-underline { | 
 |  152   background-color: {{g.paperInput.labelColor}}; | 
 |  153 } | 
 |  154  | 
 |  155 :host([focused]) .floated-label .label-text { | 
 |  156   color: {{g.paperInput.focusedColor}}; | 
 |  157 } | 
 |  158  | 
 |  159 .focused-underline { | 
 |  160   background-color: {{g.paperInput.focusedColor}}; | 
 |  161 } | 
 |  162  | 
 |  163 :host(.invalid) .floated-label .label-text, | 
 |  164 .error { | 
 |  165   color: {{g.paperInput.invalidColor}}; | 
 |  166 } | 
 |  167  | 
 |  168 :host(.invalid) .unfocused-underline, | 
 |  169 :host(.invalid) .focused-underline { | 
 |  170   background-color: {{g.paperInput.invalidColor}}; | 
 |  171 } | 
 |  172  | 
 |  173 </core-style> | 
 |  174  | 
 |  175 <polymer-element name="paper-input-decorator" layout vertical | 
 |  176   on-transitionEnd="{{transitionEndAction}}" on-webkitTransitionEnd="{{transitio
     nEndAction}}" | 
 |  177   on-input="{{inputAction}}" | 
 |  178   on-down="{{downAction}}"> | 
 |  179  | 
 |  180   <template> | 
 |  181  | 
 |  182     <link href="paper-input-decorator.css" rel="stylesheet"> | 
 |  183     <core-style ref="paper-input-decorator"></core-style> | 
 |  184  | 
 |  185     <div class="floated-label" aria-hidden="true" hidden?="{{!floatingLabel}}" i
     nvisible?="{{!floatingLabelVisible || labelAnimated}}"> | 
 |  186       <!-- needed for floating label animation measurement --> | 
 |  187       <span id="floatedLabelText" class="label-text">{{label}}</span> | 
 |  188     </div> | 
 |  189  | 
 |  190     <div class="input-body" flex auto relative> | 
 |  191  | 
 |  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> | 
 |  196  | 
 |  197       <content></content> | 
 |  198  | 
 |  199     </div> | 
 |  200  | 
 |  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?="{{!fo
     cused}}" animated?="{{underlineAnimated}}"></div> | 
 |  204     </div> | 
 |  205  | 
 |  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> | 
 |  210  | 
 |  211   </template> | 
 |  212  | 
 |  213   <script> | 
 |  214  | 
 |  215   (function() { | 
 |  216  | 
 |  217     var paperInput = CoreStyle.g.paperInput = CoreStyle.g.paperInput || {}; | 
 |  218  | 
 |  219     paperInput.labelColor = '#757575'; | 
 |  220     paperInput.focusedColor = '#4059a9'; | 
 |  221     paperInput.invalidColor = '#d34336'; | 
 |  222  | 
 |  223     Polymer({ | 
 |  224  | 
 |  225       publish: { | 
 |  226  | 
 |  227         /** | 
 |  228          * The label for this input. It normally appears as grey text inside | 
 |  229          * the text input and disappears once the user enters text. | 
 |  230          * | 
 |  231          * @attribute label | 
 |  232          * @type string | 
 |  233          * @default '' | 
 |  234          */ | 
 |  235         label: '', | 
 |  236  | 
 |  237         /** | 
 |  238          * If true, the label will "float" above the text input once the | 
 |  239          * user enters text instead of disappearing. | 
 |  240          * | 
 |  241          * @attribute floatingLabel | 
 |  242          * @type boolean | 
 |  243          * @default false | 
 |  244          */ | 
 |  245         floatingLabel: false, | 
 |  246  | 
 |  247         /** | 
 |  248          * Set to true to style the element as disabled. | 
 |  249          * | 
 |  250          * @attribute disabled | 
 |  251          * @type boolean | 
 |  252          * @default false | 
 |  253          */ | 
 |  254         disabled: {value: false, reflect: true}, | 
 |  255  | 
 |  256         /** | 
 |  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. | 
 |  261          * | 
 |  262          * @attribute labelVisible | 
 |  263          * @type boolean | 
 |  264          * @default false | 
 |  265          */ | 
 |  266         labelVisible: null, | 
 |  267  | 
 |  268         /** | 
 |  269          * Set this property to true to show the error message. | 
 |  270          * | 
 |  271          * @attribute isInvalid | 
 |  272          * @type boolean | 
 |  273          * @default false | 
 |  274          */ | 
 |  275         isInvalid: false, | 
 |  276  | 
 |  277         /** | 
 |  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 dependin
     g | 
 |  280          * on the type of validation error. | 
 |  281          * | 
 |  282          * @attribute error | 
 |  283          * @type string | 
 |  284          */ | 
 |  285         error: '', | 
 |  286  | 
 |  287         focused: {value: false, reflect: true} | 
 |  288  | 
 |  289       }, | 
 |  290  | 
 |  291       computed: { | 
 |  292         floatingLabelVisible: 'floatingLabel && !_labelVisible', | 
 |  293         _labelVisible: '(labelVisible === true || labelVisible === false) ? labe
     lVisible : _autoLabelVisible' | 
 |  294       }, | 
 |  295  | 
 |  296       ready: function() { | 
 |  297         // Delegate focus/blur events | 
 |  298         Polymer.addEventListener(this, 'focus', this.focusAction.bind(this), tru
     e); | 
 |  299         Polymer.addEventListener(this, 'blur', this.blurAction.bind(this), true)
     ; | 
 |  300       }, | 
 |  301  | 
 |  302       attached: function() { | 
 |  303         this.input = this.querySelector('input,textarea'); | 
 |  304  | 
 |  305         this.mo = new MutationObserver(function() { | 
 |  306           this.input = this.querySelector('input,textarea'); | 
 |  307         }.bind(this)); | 
 |  308         this.mo.observe(this, {childList: true}); | 
 |  309       }, | 
 |  310  | 
 |  311       detached: function() { | 
 |  312         this.mo.disconnect(); | 
 |  313         this.mo = null; | 
 |  314       }, | 
 |  315  | 
 |  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)'; | 
 |  324         } | 
 |  325       }, | 
 |  326  | 
 |  327       animateFloatingLabel: function() { | 
 |  328         if (!this.floatingLabel || this.labelAnimated) { | 
 |  329           return false; | 
 |  330         } | 
 |  331  | 
 |  332         if (!this.$.labelText.cachedTransform) { | 
 |  333           this.prepareLabelTransform(); | 
 |  334         } | 
 |  335  | 
 |  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; | 
 |  340         } | 
 |  341  | 
 |  342         this.labelAnimated = true; | 
 |  343         // Handle interrupted animation | 
 |  344         this.async(function() { | 
 |  345           this.transitionEndAction(); | 
 |  346         }, null, 250); | 
 |  347  | 
 |  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.cachedTran
     sform; | 
 |  352             this.$.labelText.style.transform = this.$.labelText.cachedTransform; | 
 |  353             this.$.labelText.offsetTop; | 
 |  354           } | 
 |  355           this.$.labelText.style.webkitTransform = ''; | 
 |  356           this.$.labelText.style.transform = ''; | 
 |  357         } else { | 
 |  358           this.$.labelText.style.webkitTransform = this.$.labelText.cachedTransf
     orm; | 
 |  359           this.$.labelText.style.transform = this.$.labelText.cachedTransform; | 
 |  360           this.input.placeholder = ''; | 
 |  361         } | 
 |  362  | 
 |  363         return true; | 
 |  364       }, | 
 |  365  | 
 |  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); | 
 |  371           } | 
 |  372         } | 
 |  373       }, | 
 |  374  | 
 |  375       labelVisibleChanged: function() { | 
 |  376         if (this.labelVisible === 'true') { | 
 |  377           this.labelVisible = true; | 
 |  378         } else if (this.labelVisible === 'false') { | 
 |  379           this.labelVisible = false; | 
 |  380         } | 
 |  381       }, | 
 |  382  | 
 |  383       labelChanged: function() { | 
 |  384         if (this.input) { | 
 |  385           this.updateInputLabel(this.input, this.label); | 
 |  386         } | 
 |  387       }, | 
 |  388  | 
 |  389       isInvalidChanged: function() { | 
 |  390         this.classList.toggle('invalid', this.isInvalid); | 
 |  391       }, | 
 |  392  | 
 |  393       focusedChanged: function() { | 
 |  394         this.updateLabelVisibility(this.input && this.input.value); | 
 |  395       }, | 
 |  396  | 
 |  397       inputChanged: function(old) { | 
 |  398         if (this.input) { | 
 |  399           this.updateLabelVisibility(this.input.value); | 
 |  400           this.updateInputLabel(this.input, this.label); | 
 |  401         } | 
 |  402         if (old) { | 
 |  403           this.updateInputLabel(old, ''); | 
 |  404         } | 
 |  405       }, | 
 |  406  | 
 |  407       focusAction: function() { | 
 |  408         this.focused = true; | 
 |  409       }, | 
 |  410  | 
 |  411       blurAction: function(e) { | 
 |  412         this.focused = false; | 
 |  413       }, | 
 |  414  | 
 |  415       /** | 
 |  416        * Updates the label visibility based on a value. This is handled automati
     cally | 
 |  417        * if the user is typing, but if you imperatively set the input value you 
     need | 
 |  418        * to call this function. | 
 |  419        * | 
 |  420        * @method updateLabelVisibility | 
 |  421        * @param {string} value | 
 |  422        */ | 
 |  423       updateLabelVisibility: function(value) { | 
 |  424         var v = (value !== null && value !== undefined) ? String(value) : value; | 
 |  425         this._autoLabelVisible = (!this.focused && !v) || (!this.floatingLabel &
     & !v); | 
 |  426       }, | 
 |  427  | 
 |  428       updateInputLabel: function(input, label) { | 
 |  429         if (this._labelVisible) { | 
 |  430           this.input.placeholder = this.label; | 
 |  431         } else { | 
 |  432           this.input.placeholder = ''; | 
 |  433         } | 
 |  434         if (label) { | 
 |  435           input.setAttribute('aria-label', label); | 
 |  436         } else { | 
 |  437           input.removeAttribute('aria-label'); | 
 |  438         } | 
 |  439       }, | 
 |  440  | 
 |  441       inputAction: function(e) { | 
 |  442         this.updateLabelVisibility(e.target.value); | 
 |  443       }, | 
 |  444  | 
 |  445       downAction: function(e) { | 
 |  446         if (this.disabled) { | 
 |  447           return; | 
 |  448         } | 
 |  449  | 
 |  450         if (this.focused) { | 
 |  451           return; | 
 |  452         } | 
 |  453  | 
 |  454         if (this.input) { | 
 |  455           this.input.focus(); | 
 |  456           e.preventDefault(); | 
 |  457         } | 
 |  458  | 
 |  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'; | 
 |  465  | 
 |  466         // Animations only run when the user interacts with the input | 
 |  467         this.underlineAnimated = true; | 
 |  468  | 
 |  469         // Handle interrupted animation | 
 |  470         this.async(function() { | 
 |  471           this.transitionEndAction(); | 
 |  472         }, null, 250); | 
 |  473       }, | 
 |  474  | 
 |  475       transitionEndAction: function() { | 
 |  476         this.underlineAnimated = false; | 
 |  477         this.labelAnimated = false; | 
 |  478         if (this._labelVisible) { | 
 |  479           this.input.placeholder = this.label; | 
 |  480         } | 
 |  481       } | 
 |  482  | 
 |  483     }); | 
 |  484  | 
 |  485   }()); | 
 |  486  | 
 |  487   </script> | 
 |  488  | 
 |  489 </polymer-element> | 
| OLD | NEW |