Commit 8df3390e4160f5d4d1e67e4aeaf14135252e8619
1 parent
1902505a
update calcTextareaHeight
Showing
2 changed files
with
265 additions
and
103 deletions
Show diff stats
examples/routers/input.vue
... | ... | @@ -80,64 +80,67 @@ |
80 | 80 | <!--<br>--> |
81 | 81 | <!--</div>--> |
82 | 82 | |
83 | - <div> | |
84 | - <Input | |
85 | - v-model="value" | |
86 | - size="small" | |
87 | - prefix="ios-contact" | |
88 | - suffix="ios-search" | |
89 | - placeholder="Enter something..." | |
90 | - style="width: 300px"></Input> | |
91 | - <br> | |
92 | - <Input | |
93 | - v-model="value" | |
94 | - prefix="ios-contact" | |
95 | - suffix="ios-search" | |
96 | - placeholder="Enter something..." | |
97 | - style="width: 300px"></Input> | |
98 | - <br> | |
99 | - <Input | |
100 | - v-model="value" | |
101 | - size="large" | |
102 | - prefix="ios-contact" | |
103 | - suffix="ios-search" | |
104 | - placeholder="Enter something..." | |
105 | - style="width: 300px"></Input> | |
106 | - <br><br> | |
107 | - <Input | |
108 | - v-model="value" | |
109 | - size="small" | |
110 | - icon="ios-search" | |
111 | - placeholder="Enter something..." | |
112 | - style="width: 300px"></Input> | |
113 | - <br> | |
114 | - <Input | |
115 | - v-model="value" | |
116 | - icon="ios-search" | |
117 | - placeholder="Enter something..." | |
118 | - style="width: 300px"></Input> | |
119 | - <br> | |
120 | - <Input | |
121 | - v-model="value" | |
122 | - size="large" | |
123 | - icon="ios-search" | |
124 | - placeholder="Enter something..." | |
125 | - style="width: 300px"></Input> | |
126 | - <br><br><br> | |
127 | - <Input v-model="value" placeholder="Enter something..." style="width: 300px"> | |
128 | - <Icon type="ios-alarm-outline" slot="suffix" /> | |
129 | - <Icon type="ios-aperture" slot="prefix" /> | |
130 | - </Input> | |
131 | - <br><br><br><br> | |
132 | - <Input v-model="value" search enter-button style="width: 300px" @on-search="hs" size="small" /> | |
133 | - <br> | |
134 | - <Input v-model="value" search enter-button style="width: 300px" @on-search="hs" /> | |
135 | - <br> | |
136 | - <Input v-model="value" search enter-button style="width: 300px" @on-search="hs" size="large" /> | |
137 | - <br><br> | |
138 | - <Input v-model="value" search style="width: 300px" @on-search="hs" /> | |
139 | - <br><br> | |
140 | - <Input v-model="value" search enter-button="Search" style="width: 300px" @on-search="hs" /> | |
83 | + <!--<div>--> | |
84 | + <!--<Input--> | |
85 | + <!--v-model="value"--> | |
86 | + <!--size="small"--> | |
87 | + <!--prefix="ios-contact"--> | |
88 | + <!--suffix="ios-search"--> | |
89 | + <!--placeholder="Enter something..."--> | |
90 | + <!--style="width: 300px"></Input>--> | |
91 | + <!--<br>--> | |
92 | + <!--<Input--> | |
93 | + <!--v-model="value"--> | |
94 | + <!--prefix="ios-contact"--> | |
95 | + <!--suffix="ios-search"--> | |
96 | + <!--placeholder="Enter something..."--> | |
97 | + <!--style="width: 300px"></Input>--> | |
98 | + <!--<br>--> | |
99 | + <!--<Input--> | |
100 | + <!--v-model="value"--> | |
101 | + <!--size="large"--> | |
102 | + <!--prefix="ios-contact"--> | |
103 | + <!--suffix="ios-search"--> | |
104 | + <!--placeholder="Enter something..."--> | |
105 | + <!--style="width: 300px"></Input>--> | |
106 | + <!--<br><br>--> | |
107 | + <!--<Input--> | |
108 | + <!--v-model="value"--> | |
109 | + <!--size="small"--> | |
110 | + <!--icon="ios-search"--> | |
111 | + <!--placeholder="Enter something..."--> | |
112 | + <!--style="width: 300px"></Input>--> | |
113 | + <!--<br>--> | |
114 | + <!--<Input--> | |
115 | + <!--v-model="value"--> | |
116 | + <!--icon="ios-search"--> | |
117 | + <!--placeholder="Enter something..."--> | |
118 | + <!--style="width: 300px"></Input>--> | |
119 | + <!--<br>--> | |
120 | + <!--<Input--> | |
121 | + <!--v-model="value"--> | |
122 | + <!--size="large"--> | |
123 | + <!--icon="ios-search"--> | |
124 | + <!--placeholder="Enter something..."--> | |
125 | + <!--style="width: 300px"></Input>--> | |
126 | + <!--<br><br><br>--> | |
127 | + <!--<Input v-model="value" placeholder="Enter something..." style="width: 300px">--> | |
128 | + <!--<Icon type="ios-alarm-outline" slot="suffix" />--> | |
129 | + <!--<Icon type="ios-aperture" slot="prefix" />--> | |
130 | + <!--</Input>--> | |
131 | + <!--<br><br><br><br>--> | |
132 | + <!--<Input v-model="value" search enter-button style="width: 300px" @on-search="hs" size="small" />--> | |
133 | + <!--<br>--> | |
134 | + <!--<Input v-model="value" search enter-button style="width: 300px" @on-search="hs" />--> | |
135 | + <!--<br>--> | |
136 | + <!--<Input v-model="value" search enter-button style="width: 300px" @on-search="hs" size="large" />--> | |
137 | + <!--<br><br>--> | |
138 | + <!--<Input v-model="value" search style="width: 300px" @on-search="hs" />--> | |
139 | + <!--<br><br>--> | |
140 | + <!--<Input v-model="value" search enter-button="Search" style="width: 300px" @on-search="hs" />--> | |
141 | + <!--</div>--> | |
142 | + <div style="width: 200px"> | |
143 | + <Input v-model="value7" type="textarea" :autosize="true" placeholder="Enter something..."></Input> | |
141 | 144 | </div> |
142 | 145 | </template> |
143 | 146 | <script> |
... | ... | @@ -150,7 +153,8 @@ |
150 | 153 | value13: '', |
151 | 154 | select1: 'http', |
152 | 155 | select2: 'com', |
153 | - select3: 'day' | |
156 | + select3: 'day', | |
157 | + value7: `` | |
154 | 158 | } |
155 | 159 | }, |
156 | 160 | methods: { | ... | ... |
src/utils/calcTextareaHeight.js
1 | 1 | // Thanks to |
2 | 2 | // https://github.com/andreypopp/react-textarea-autosize/ |
3 | -// https://github.com/ElemeFE/element/blob/master/packages/input/src/calcTextareaHeight.js | |
4 | 3 | |
5 | -let hiddenTextarea; | |
4 | +// let hiddenTextarea; | |
5 | +// | |
6 | +// const HIDDEN_STYLE = ` | |
7 | +// height:0 !important; | |
8 | +// min-height:0 !important; | |
9 | +// max-height:none !important; | |
10 | +// visibility:hidden !important; | |
11 | +// overflow:hidden !important; | |
12 | +// position:absolute !important; | |
13 | +// z-index:-1000 !important; | |
14 | +// top:0 !important; | |
15 | +// right:0 !important | |
16 | +// `; | |
17 | +// | |
18 | +// const CONTEXT_STYLE = [ | |
19 | +// 'letter-spacing', | |
20 | +// 'line-height', | |
21 | +// 'padding-top', | |
22 | +// 'padding-bottom', | |
23 | +// 'font-family', | |
24 | +// 'font-weight', | |
25 | +// 'font-size', | |
26 | +// 'text-rendering', | |
27 | +// 'text-transform', | |
28 | +// 'width', | |
29 | +// 'text-indent', | |
30 | +// 'padding-left', | |
31 | +// 'padding-right', | |
32 | +// 'border-width', | |
33 | +// 'box-sizing' | |
34 | +// ]; | |
35 | +// | |
36 | +// function calculateNodeStyling(node) { | |
37 | +// const style = window.getComputedStyle(node); | |
38 | +// | |
39 | +// const boxSizing = style.getPropertyValue('box-sizing'); | |
40 | +// | |
41 | +// const paddingSize = ( | |
42 | +// parseFloat(style.getPropertyValue('padding-bottom')) + | |
43 | +// parseFloat(style.getPropertyValue('padding-top')) | |
44 | +// ); | |
45 | +// | |
46 | +// const borderSize = ( | |
47 | +// parseFloat(style.getPropertyValue('border-bottom-width')) + | |
48 | +// parseFloat(style.getPropertyValue('border-top-width')) | |
49 | +// ); | |
50 | +// | |
51 | +// const contextStyle = CONTEXT_STYLE | |
52 | +// .map(name => `${name}:${style.getPropertyValue(name)}`) | |
53 | +// .join(';'); | |
54 | +// | |
55 | +// return {contextStyle, paddingSize, borderSize, boxSizing}; | |
56 | +// } | |
57 | +// | |
58 | +// export default function calcTextareaHeight(targetNode, minRows = null, maxRows = null) { | |
59 | +// if (!hiddenTextarea) { | |
60 | +// hiddenTextarea = document.createElement('textarea'); | |
61 | +// document.body.appendChild(hiddenTextarea); | |
62 | +// } | |
63 | +// | |
64 | +// let { | |
65 | +// paddingSize, | |
66 | +// borderSize, | |
67 | +// boxSizing, | |
68 | +// contextStyle | |
69 | +// } = calculateNodeStyling(targetNode); | |
70 | +// | |
71 | +// hiddenTextarea.setAttribute('style', `${contextStyle};${HIDDEN_STYLE}`); | |
72 | +// hiddenTextarea.value = targetNode.value || targetNode.placeholder || ''; | |
73 | +// | |
74 | +// let height = hiddenTextarea.scrollHeight; | |
75 | +// let minHeight = -Infinity; | |
76 | +// let maxHeight = Infinity; | |
77 | +// let overflowY; | |
78 | +// | |
79 | +// if (boxSizing === 'border-box') { | |
80 | +// height = height + borderSize; | |
81 | +// } else if (boxSizing === 'content-box') { | |
82 | +// height = height - paddingSize; | |
83 | +// } | |
84 | +// | |
85 | +// hiddenTextarea.value = ''; | |
86 | +// let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize; | |
87 | +// | |
88 | +// if (minRows !== null) { | |
89 | +// minHeight = singleRowHeight * minRows; | |
90 | +// if (boxSizing === 'border-box') { | |
91 | +// minHeight = minHeight + paddingSize + borderSize; | |
92 | +// } | |
93 | +// height = Math.max(minHeight, height); | |
94 | +// } | |
95 | +// if (maxRows !== null) { | |
96 | +// maxHeight = singleRowHeight * maxRows; | |
97 | +// if (boxSizing === 'border-box') { | |
98 | +// maxHeight = maxHeight + paddingSize + borderSize; | |
99 | +// } | |
100 | +// overflowY = height > maxHeight ? '' : 'hidden'; | |
101 | +// height = Math.min(maxHeight, height); | |
102 | +// } | |
103 | +// | |
104 | +// if (!maxRows) { | |
105 | +// overflowY = 'hidden'; | |
106 | +// } | |
107 | +// | |
108 | +// return { | |
109 | +// height: `${height}px`, | |
110 | +// minHeight: `${minHeight}px`, | |
111 | +// maxHeight: `${maxHeight}px`, | |
112 | +// overflowY | |
113 | +// }; | |
114 | +// } | |
6 | 115 | |
7 | -const HIDDEN_STYLE = ` | |
8 | - height:0 !important; | |
9 | - min-height:0 !important; | |
10 | - max-height:none !important; | |
11 | - visibility:hidden !important; | |
12 | - overflow:hidden !important; | |
13 | - position:absolute !important; | |
14 | - z-index:-1000 !important; | |
15 | - top:0 !important; | |
16 | - right:0 !important | |
116 | +const HIDDEN_TEXTAREA_STYLE = ` | |
117 | + min-height:0 !important; | |
118 | + max-height:none !important; | |
119 | + height:0 !important; | |
120 | + visibility:hidden !important; | |
121 | + overflow:hidden !important; | |
122 | + position:absolute !important; | |
123 | + z-index:-1000 !important; | |
124 | + top:0 !important; | |
125 | + right:0 !important | |
17 | 126 | `; |
18 | 127 | |
19 | -const CONTEXT_STYLE = [ | |
128 | +const SIZING_STYLE = [ | |
20 | 129 | 'letter-spacing', |
21 | 130 | 'line-height', |
22 | 131 | 'padding-top', |
... | ... | @@ -31,13 +140,29 @@ const CONTEXT_STYLE = [ |
31 | 140 | 'padding-left', |
32 | 141 | 'padding-right', |
33 | 142 | 'border-width', |
34 | - 'box-sizing' | |
143 | + 'box-sizing', | |
35 | 144 | ]; |
36 | 145 | |
37 | -function calculateNodeStyling(node) { | |
146 | +let computedStyleCache = {}; | |
147 | +let hiddenTextarea; | |
148 | + | |
149 | +function calculateNodeStyling(node, useCache = false) { | |
150 | + const nodeRef = ( | |
151 | + node.getAttribute('id') || | |
152 | + node.getAttribute('data-reactid') || | |
153 | + node.getAttribute('name')); | |
154 | + | |
155 | + if (useCache && computedStyleCache[nodeRef]) { | |
156 | + return computedStyleCache[nodeRef]; | |
157 | + } | |
158 | + | |
38 | 159 | const style = window.getComputedStyle(node); |
39 | 160 | |
40 | - const boxSizing = style.getPropertyValue('box-sizing'); | |
161 | + const boxSizing = ( | |
162 | + style.getPropertyValue('box-sizing') || | |
163 | + style.getPropertyValue('-moz-box-sizing') || | |
164 | + style.getPropertyValue('-webkit-box-sizing') | |
165 | + ); | |
41 | 166 | |
42 | 167 | const paddingSize = ( |
43 | 168 | parseFloat(style.getPropertyValue('padding-bottom')) + |
... | ... | @@ -49,60 +174,93 @@ function calculateNodeStyling(node) { |
49 | 174 | parseFloat(style.getPropertyValue('border-top-width')) |
50 | 175 | ); |
51 | 176 | |
52 | - const contextStyle = CONTEXT_STYLE | |
177 | + const sizingStyle = SIZING_STYLE | |
53 | 178 | .map(name => `${name}:${style.getPropertyValue(name)}`) |
54 | 179 | .join(';'); |
55 | 180 | |
56 | - return {contextStyle, paddingSize, borderSize, boxSizing}; | |
181 | + const nodeInfo = { | |
182 | + sizingStyle, | |
183 | + paddingSize, | |
184 | + borderSize, | |
185 | + boxSizing, | |
186 | + }; | |
187 | + | |
188 | + if (useCache && nodeRef) { | |
189 | + computedStyleCache[nodeRef] = nodeInfo; | |
190 | + } | |
191 | + | |
192 | + return nodeInfo; | |
57 | 193 | } |
58 | 194 | |
59 | -export default function calcTextareaHeight(targetNode, minRows = null, maxRows = null) { | |
195 | +export default function calcTextareaHeight(uiTextNode, minRows = null, maxRows = null, useCache = false) { | |
60 | 196 | if (!hiddenTextarea) { |
61 | 197 | hiddenTextarea = document.createElement('textarea'); |
62 | 198 | document.body.appendChild(hiddenTextarea); |
63 | 199 | } |
64 | 200 | |
201 | + // Fix wrap="off" issue | |
202 | + // https://github.com/ant-design/ant-design/issues/6577 | |
203 | + if (uiTextNode.getAttribute('wrap')) { | |
204 | + hiddenTextarea.setAttribute('wrap', uiTextNode.getAttribute('wrap')); | |
205 | + } else { | |
206 | + hiddenTextarea.removeAttribute('wrap'); | |
207 | + } | |
208 | + | |
209 | + // Copy all CSS properties that have an impact on the height of the content in | |
210 | + // the textbox | |
65 | 211 | let { |
66 | - paddingSize, | |
67 | - borderSize, | |
68 | - boxSizing, | |
69 | - contextStyle | |
70 | - } = calculateNodeStyling(targetNode); | |
212 | + paddingSize, borderSize, | |
213 | + boxSizing, sizingStyle, | |
214 | + } = calculateNodeStyling(uiTextNode, useCache); | |
71 | 215 | |
72 | - hiddenTextarea.setAttribute('style', `${contextStyle};${HIDDEN_STYLE}`); | |
73 | - hiddenTextarea.value = targetNode.value || targetNode.placeholder || ''; | |
216 | + // Need to have the overflow attribute to hide the scrollbar otherwise | |
217 | + // text-lines will not calculated properly as the shadow will technically be | |
218 | + // narrower for content | |
219 | + hiddenTextarea.setAttribute('style', `${sizingStyle};${HIDDEN_TEXTAREA_STYLE}`); | |
220 | + hiddenTextarea.value = uiTextNode.value || uiTextNode.placeholder || ''; | |
74 | 221 | |
222 | + let minHeight = Number.MIN_SAFE_INTEGER; | |
223 | + let maxHeight = Number.MAX_SAFE_INTEGER; | |
75 | 224 | let height = hiddenTextarea.scrollHeight; |
76 | - let minHeight = -Infinity; | |
77 | - let maxHeight = Infinity; | |
225 | + let overflowY; | |
78 | 226 | |
79 | 227 | if (boxSizing === 'border-box') { |
228 | + // border-box: add border, since height = content + padding + border | |
80 | 229 | height = height + borderSize; |
81 | 230 | } else if (boxSizing === 'content-box') { |
231 | + // remove padding, since height = content | |
82 | 232 | height = height - paddingSize; |
83 | 233 | } |
84 | 234 | |
85 | - hiddenTextarea.value = ''; | |
86 | - let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize; | |
87 | - | |
88 | - if (minRows !== null) { | |
89 | - minHeight = singleRowHeight * minRows; | |
90 | - if (boxSizing === 'border-box') { | |
91 | - minHeight = minHeight + paddingSize + borderSize; | |
235 | + if (minRows !== null || maxRows !== null) { | |
236 | + // measure height of a textarea with a single row | |
237 | + hiddenTextarea.value = ' '; | |
238 | + let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize; | |
239 | + if (minRows !== null) { | |
240 | + minHeight = singleRowHeight * minRows; | |
241 | + if (boxSizing === 'border-box') { | |
242 | + minHeight = minHeight + paddingSize + borderSize; | |
243 | + } | |
244 | + height = Math.max(minHeight, height); | |
92 | 245 | } |
93 | - height = Math.max(minHeight, height); | |
94 | - } | |
95 | - if (maxRows !== null) { | |
96 | - maxHeight = singleRowHeight * maxRows; | |
97 | - if (boxSizing === 'border-box') { | |
98 | - maxHeight = maxHeight + paddingSize + borderSize; | |
246 | + if (maxRows !== null) { | |
247 | + maxHeight = singleRowHeight * maxRows; | |
248 | + if (boxSizing === 'border-box') { | |
249 | + maxHeight = maxHeight + paddingSize + borderSize; | |
250 | + } | |
251 | + overflowY = height > maxHeight ? '' : 'hidden'; | |
252 | + height = Math.min(maxHeight, height); | |
99 | 253 | } |
100 | - height = Math.min(maxHeight, height); | |
254 | + } | |
255 | + // Remove scroll bar flash when autosize without maxRows | |
256 | + if (!maxRows) { | |
257 | + overflowY = 'hidden'; | |
101 | 258 | } |
102 | 259 | |
103 | 260 | return { |
104 | 261 | height: `${height}px`, |
105 | 262 | minHeight: `${minHeight}px`, |
106 | - maxHeight: `${maxHeight}px` | |
263 | + maxHeight: `${maxHeight}px`, | |
264 | + overflowY | |
107 | 265 | }; |
108 | -} | |
109 | 266 | \ No newline at end of file |
267 | +} | ... | ... |