OLD | NEW |
(Empty) | |
| 1 <!-- |
| 2 @license |
| 3 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. |
| 4 This code may only be used under the BSD style license found at http://polymer.g
ithub.io/LICENSE.txt |
| 5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt |
| 6 The complete set of contributors may be found at http://polymer.github.io/CONTRI
BUTORS.txt |
| 7 Code distributed by Google as part of the polymer project is also |
| 8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS.txt |
| 9 --> |
| 10 |
| 11 <link rel="import" href="../polymer/polymer.html"> |
| 12 <link rel="import" href="../iron-behaviors/iron-control-state.html"> |
| 13 <link rel="import" href="../iron-flex-layout/classes/iron-flex-layout.html"> |
| 14 <link rel="import" href="../iron-validatable-behavior/iron-validatable-behavior.
html"> |
| 15 |
| 16 <!-- |
| 17 `iron-autogrow-textarea` is an element containing a textarea that grows in heigh
t as more |
| 18 lines of input are entered. Unless an explicit height or the `maxRows` property
is set, it will |
| 19 never scroll. |
| 20 |
| 21 Example: |
| 22 |
| 23 <iron-autogrow-textarea id="a1"> |
| 24 <textarea id="t1"></textarea> |
| 25 </iron-autogrow-textarea> |
| 26 |
| 27 Because the `textarea`'s `value` property is not observable, you should use |
| 28 this element's `bind-value` instead for imperative updates. |
| 29 |
| 30 @group Iron Elements |
| 31 @hero hero.svg |
| 32 @demo demo/index.html |
| 33 --> |
| 34 |
| 35 <dom-module id="iron-autogrow-textarea"> |
| 36 |
| 37 <style> |
| 38 :host { |
| 39 display: inline-block; |
| 40 position: relative; |
| 41 width: 400px; |
| 42 border: 1px solid; |
| 43 padding: 2px; |
| 44 -moz-appearance: textarea; |
| 45 -webkit-appearance: textarea; |
| 46 } |
| 47 |
| 48 .mirror-text { |
| 49 visibility: hidden; |
| 50 word-wrap: break-word; |
| 51 } |
| 52 |
| 53 textarea { |
| 54 position: relative; |
| 55 outline: none; |
| 56 border: none; |
| 57 resize: none; |
| 58 background: inherit; |
| 59 /* see comments in template */ |
| 60 width: 100%; |
| 61 height: 100%; |
| 62 font-size: inherit; |
| 63 font-family: inherit; |
| 64 } |
| 65 |
| 66 ::content textarea:invalid { |
| 67 box-shadow: none; |
| 68 } |
| 69 |
| 70 </style> |
| 71 <template> |
| 72 <!-- the mirror sizes the input/textarea so it grows with typing --> |
| 73 <div id="mirror" class="mirror-text" aria-hidden="true"> </div> |
| 74 |
| 75 <!-- size the input/textarea with a div, because the textarea has intrinsic
size in ff --> |
| 76 <div class="textarea-container fit"> |
| 77 <textarea id="textarea" |
| 78 autocomplete$="[[autocomplete]]" |
| 79 autofocus$="[[autofocus]]" |
| 80 inputmode$="[[inputmode]]" |
| 81 name$="[[name]]" |
| 82 placeholder$="[[placeholder]]" |
| 83 readonly$="[[readonly]]" |
| 84 required$="[[required]]" |
| 85 rows$="[[rows]]" |
| 86 maxlength$="[[maxlength]]"></textarea> |
| 87 </div> |
| 88 </template> |
| 89 </dom-module> |
| 90 |
| 91 <script> |
| 92 |
| 93 Polymer({ |
| 94 |
| 95 is: 'iron-autogrow-textarea', |
| 96 |
| 97 behaviors: [ |
| 98 Polymer.IronValidatableBehavior, |
| 99 Polymer.IronControlState |
| 100 ], |
| 101 |
| 102 properties: { |
| 103 |
| 104 /** |
| 105 * Use this property instead of `value` for two-way data binding. |
| 106 */ |
| 107 bindValue: { |
| 108 observer: '_bindValueChanged', |
| 109 type: String |
| 110 }, |
| 111 |
| 112 /** |
| 113 * The initial number of rows. |
| 114 * |
| 115 * @attribute rows |
| 116 * @type number |
| 117 * @default 1 |
| 118 */ |
| 119 rows: { |
| 120 type: Number, |
| 121 value: 1, |
| 122 observer: '_updateCached' |
| 123 }, |
| 124 |
| 125 /** |
| 126 * The maximum number of rows this element can grow to until it |
| 127 * scrolls. 0 means no maximum. |
| 128 * |
| 129 * @attribute maxRows |
| 130 * @type number |
| 131 * @default 0 |
| 132 */ |
| 133 maxRows: { |
| 134 type: Number, |
| 135 value: 0, |
| 136 observer: '_updateCached' |
| 137 }, |
| 138 |
| 139 /** |
| 140 * Bound to the textarea's `autocomplete` attribute. |
| 141 */ |
| 142 autocomplete: { |
| 143 type: String, |
| 144 value: 'off' |
| 145 }, |
| 146 |
| 147 /** |
| 148 * Bound to the textarea's `autofocus` attribute. |
| 149 */ |
| 150 autofocus: { |
| 151 type: String, |
| 152 value: 'off' |
| 153 }, |
| 154 |
| 155 /** |
| 156 * Bound to the textarea's `inputmode` attribute. |
| 157 */ |
| 158 inputmode: { |
| 159 type: String |
| 160 }, |
| 161 |
| 162 /** |
| 163 * Bound to the textarea's `name` attribute. |
| 164 */ |
| 165 name: { |
| 166 type: String |
| 167 }, |
| 168 |
| 169 /** |
| 170 * Bound to the textarea's `placeholder` attribute. |
| 171 */ |
| 172 placeholder: { |
| 173 type: String |
| 174 }, |
| 175 |
| 176 /** |
| 177 * Bound to the textarea's `readonly` attribute. |
| 178 */ |
| 179 readonly: { |
| 180 type: String |
| 181 }, |
| 182 |
| 183 /** |
| 184 * Set to true to mark the textarea as required. |
| 185 */ |
| 186 required: { |
| 187 type: Boolean |
| 188 }, |
| 189 |
| 190 /** |
| 191 * The maximum length of the input value. |
| 192 */ |
| 193 maxlength: { |
| 194 type: Number |
| 195 } |
| 196 |
| 197 }, |
| 198 |
| 199 listeners: { |
| 200 'input': '_onInput' |
| 201 }, |
| 202 |
| 203 /** |
| 204 * Returns the underlying textarea. |
| 205 */ |
| 206 get textarea() { |
| 207 return this.$.textarea; |
| 208 }, |
| 209 |
| 210 _update: function() { |
| 211 this.$.mirror.innerHTML = this._valueForMirror(); |
| 212 |
| 213 var textarea = this.textarea; |
| 214 // If the value of the textarea was updated imperatively, then we |
| 215 // need to manually update bindValue as well. |
| 216 if (textarea && this.bindValue != textarea.value) { |
| 217 this.bindValue = textarea.value; |
| 218 } |
| 219 }, |
| 220 |
| 221 _bindValueChanged: function() { |
| 222 var textarea = this.textarea; |
| 223 if (!textarea) { |
| 224 return; |
| 225 } |
| 226 |
| 227 textarea.value = this.bindValue; |
| 228 this._update(); |
| 229 // manually notify because we don't want to notify until after setting val
ue |
| 230 this.fire('bind-value-changed', {value: this.bindValue}); |
| 231 }, |
| 232 |
| 233 _onInput: function(event) { |
| 234 this.bindValue = event.path ? event.path[0].value : event.target.value; |
| 235 this._update(); |
| 236 }, |
| 237 |
| 238 _constrain: function(tokens) { |
| 239 var _tokens; |
| 240 tokens = tokens || ['']; |
| 241 // Enforce the min and max heights for a multiline input to avoid measurem
ent |
| 242 if (this.maxRows > 0 && tokens.length > this.maxRows) { |
| 243 _tokens = tokens.slice(0, this.maxRows); |
| 244 } else { |
| 245 _tokens = tokens.slice(0); |
| 246 } |
| 247 while (this.rows > 0 && _tokens.length < this.rows) { |
| 248 _tokens.push(''); |
| 249 } |
| 250 return _tokens.join('<br>') + ' '; |
| 251 }, |
| 252 |
| 253 _valueForMirror: function() { |
| 254 var input = this.textarea; |
| 255 if (!input) { |
| 256 return; |
| 257 } |
| 258 this.tokens = (input && input.value) ? input.value.replace(/&/gm, '&')
.replace(/"/gm, '"').replace(/'/gm, ''').replace(/</gm, '<').replace
(/>/gm, '>').split('\n') : ['']; |
| 259 return this._constrain(this.tokens); |
| 260 }, |
| 261 |
| 262 _updateCached: function() { |
| 263 this.$.mirror.innerHTML = this._constrain(this.tokens); |
| 264 } |
| 265 }) |
| 266 </script> |
OLD | NEW |