Commit 8df3390e4160f5d4d1e67e4aeaf14135252e8619

Authored by 梁灏
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 +}
... ...