• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<!--
2Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
3This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE
4The complete set of authors may be found at http://polymer.github.io/AUTHORS
5The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
6Code distributed by Google as part of the polymer project is also
7subject to an additional IP rights grant found at http://polymer.github.io/PATENTS
8-->
9
10<!--
11`paper-autogrow-textarea` is an element containing a textarea that grows in height as more
12lines of input are entered. Unless an explicit height or the `maxRows` property is set, it will
13never scroll.
14
15Example:
16
17    <paper-autogrow-textarea id="a1">
18        <textarea id="t1"></textarea>
19    </paper-autogrow-textarea>
20
21Because the `textarea`'s `value` property is not observable, if you set the `value` imperatively
22you must call `update` to notify this element the value has changed.
23
24Example:
25
26    /* using example HTML above */
27    t1.value = 'some\ntext';
28    a1.update();
29
30@group Paper Elements
31@element paper-autogrow-textarea
32@status unstable
33-->
34
35<link href="../polymer/polymer.html" rel="import">
36
37<polymer-element name="paper-autogrow-textarea" on-input="{{inputAction}}">
38<template>
39
40  <style>
41    :host {
42      display: inline-block;
43      position: relative;
44      width: 400px;
45    }
46
47    .mirror-text {
48      visibility: hidden;
49      word-wrap: break-word;
50    }
51
52    ::content textarea {
53      padding: 0;
54      margin: 0;
55      border: none;
56      outline: none;
57      resize: none;
58      /* see comments in template */
59      width: 100%;
60      height: 100%;
61    }
62
63    ::content textarea:invalid {
64      box-shadow: none;
65    }
66  </style>
67
68  <!-- the mirror sizes the input/textarea so it grows with typing -->
69  <div id="mirror" class="mirror-text" aria-hidden="true">&nbsp;</div>
70
71  <!-- size the input/textarea with a div, because the textarea has intrinsic size in ff -->
72  <div class="textarea-container" fit>
73    <content></content>
74  </div>
75
76</template>
77<script>
78
79  Polymer({
80
81    publish: {
82
83        /**
84         * The textarea that should auto grow.
85         *
86         * @attribute target
87         * @type HTMLTextAreaElement
88         * @default null
89         */
90        target: null,
91
92        /**
93         * The initial number of rows.
94         *
95         * @attribute rows
96         * @type number
97         * @default 1
98         */
99        rows: 1,
100
101        /**
102         * The maximum number of rows this element can grow to until it
103         * scrolls. 0 means no maximum.
104         *
105         * @attribute maxRows
106         * @type number
107         * @default 0
108         */
109        maxRows: 0
110    },
111
112    tokens: null,
113
114    observe: {
115      rows: 'updateCached',
116      maxRows: 'updateCached'
117    },
118
119    constrain: function(tokens) {
120      var _tokens;
121      tokens = tokens || [''];
122      // Enforce the min and max heights for a multiline input to avoid measurement
123      if (this.maxRows > 0 && tokens.length > this.maxRows) {
124        _tokens = tokens.slice(0, this.maxRows);
125      } else {
126        _tokens = tokens.slice(0);
127      }
128      while (this.rows > 0 && _tokens.length < this.rows) {
129        _tokens.push('');
130      }
131      return _tokens.join('<br>') + '&nbsp;';
132    },
133
134    valueForMirror: function(input) {
135      this.tokens = (input && input.value) ? input.value.replace(/&/gm, '&amp;').replace(/"/gm, '&quot;').replace(/'/gm, '&#39;').replace(/</gm, '&lt;').replace(/>/gm, '&gt;').split('\n') : [''];
136      return this.constrain(this.tokens);
137    },
138
139    /**
140     * Sizes this element to fit the input value. This function is automatically called
141     * when the user types in new input, but you must call this function if the value
142     * is updated imperatively.
143     *
144     * @method update
145     * @param Element The input
146     */
147    update: function(input) {
148      this.$.mirror.innerHTML = this.valueForMirror(input);
149    },
150
151    updateCached: function() {
152      this.$.mirror.innerHTML = this.constrain(this.tokens);
153    },
154
155    inputAction: function(e) {
156      this.update(e.target);
157    }
158
159  });
160
161</script>
162</polymer-element>
163