Эх сурвалжийг харах

Merge pull request #225 from baiyaaaaa/update-input

add rows and autosize property
杨奕 8 жил өмнө
parent
commit
db7eb0ea8d

+ 1 - 0
CHANGELOG.md

@@ -8,6 +8,7 @@
 - 修复 TimePicker 错误的隐藏面板
 - 修复 Table Cell 的样式, #204
 - 为 Message Box 和 Dialog 添加 lockScroll 属性,用于定义是否在弹框出现时将 body 滚动锁定
+- 新增 Input textarea 类型的 rows, autosize 属性
 
 ### 1.0.0-rc.5
 

+ 7 - 3
examples/docs/zh-cn/input.md

@@ -240,6 +240,8 @@
 ```html
 <el-input
   type="textarea"
+  placeholder="请输入内容"
+  :autosize="{minRows: 2, maxRows: 5}"
   v-model="textarea">
 </el-input>
 ```
@@ -625,15 +627,17 @@
 
 | 参数          | 说明            | 类型            | 可选值                 | 默认值   |
 |-------------  |---------------- |---------------- |---------------------- |-------- |
-| type         | 同原生的 input 的 type 属性,如果为 textarea 则显示为 textarea   | string  | — | — |
+| type         | 同原生的 input 的 type 属性,另外提供 type="textarea"   | string  | — | — |
 | value         | 绑定值           | string, number  | — | — |
 | maxlength     | 最大输入长度      | number          |  —  | — |
 | minlength     | 最小输入长度      | number          | — | — |
 | placeholder   | 输入框占位文本    | string          | — | — |
 | disabled      | 禁用            | boolean         | — | false   |
-| size          | 输入框尺寸       | string          | large, small, mini  | — |
+| size          | 输入框尺寸,只在 `type!="textarea"` 时有效      | string          | large, small, mini  | — |
 | icon          | 输入框尾部图标    | string          | — | — |
-| number        | 指定model值为number类型  |  boolean | — |  false   |
+| number        | 指定 model 值为 number 类型  |  boolean | — |  false   |
+| rows          | 输入框行数,只对 `type="textarea"` 有效  |  number | — |  2   |
+| autosize      | 自适应内容高度,只对 `type="textarea"` 有效,可传入对象,如,{ minRows: 2, maxRows: 6 }  |  boolean/object | — |  false   |
 
 ### Autocomplete API
 

+ 100 - 0
packages/input/src/calcTextareaHeight.js

@@ -0,0 +1,100 @@
+let hiddenTextarea;
+
+const HIDDEN_STYLE = `
+  height:0 !important;
+  visibility:hidden !important;
+  overflow:hidden !important;
+  position:absolute !important;
+  z-index:-1000 !important;
+  top:0 !important;
+  right:0 !important
+`;
+
+const CONTEXT_STYLE = [
+  'letter-spacing',
+  'line-height',
+  'padding-top',
+  'padding-bottom',
+  'font-family',
+  'font-weight',
+  'font-size',
+  'text-rendering',
+  'text-transform',
+  'width',
+  'text-indent',
+  'padding-left',
+  'padding-right',
+  'border-width',
+  'box-sizing'
+];
+
+function calculateNodeStyling(node) {
+  const style = window.getComputedStyle(node);
+
+  const boxSizing = style.getPropertyValue('box-sizing');
+
+  const paddingSize = (
+    parseFloat(style.getPropertyValue('padding-bottom')) +
+    parseFloat(style.getPropertyValue('padding-top'))
+  );
+
+  const borderSize = (
+    parseFloat(style.getPropertyValue('border-bottom-width')) +
+    parseFloat(style.getPropertyValue('border-top-width'))
+  );
+
+  const contextStyle = CONTEXT_STYLE
+    .map(name => `${name}:${style.getPropertyValue(name)}`)
+    .join(';');
+
+  return { contextStyle, paddingSize, borderSize, boxSizing };
+}
+
+export default function calcTextareaHeight(
+  targetNode,
+  minRows = null,
+  maxRows = null
+) {
+  if (!hiddenTextarea) {
+    hiddenTextarea = document.createElement('textarea');
+    document.body.appendChild(hiddenTextarea);
+  }
+
+  let {
+    paddingSize,
+    borderSize,
+    boxSizing,
+    contextStyle
+  } = calculateNodeStyling(targetNode);
+
+  hiddenTextarea.setAttribute('style', `${contextStyle};${HIDDEN_STYLE}`);
+  hiddenTextarea.value = targetNode.value || targetNode.placeholder || '';
+
+  let height = hiddenTextarea.scrollHeight;
+
+  if (boxSizing === 'border-box') {
+    height = height + borderSize;
+  } else if (boxSizing === 'content-box') {
+    height = height - paddingSize;
+  }
+
+  hiddenTextarea.value = '';
+  let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;
+
+  if (minRows !== null) {
+    let minHeight = singleRowHeight * minRows;
+    if (boxSizing === 'border-box') {
+      minHeight = minHeight + paddingSize + borderSize;
+    }
+    height = Math.max(minHeight, height);
+  }
+  if (maxRows !== null) {
+    let maxHeight = singleRowHeight * maxRows;
+    if (boxSizing === 'border-box') {
+      maxHeight = maxHeight + paddingSize + borderSize;
+    }
+    height = Math.min(maxHeight, height);
+  }
+
+  return { height: height + 'px'};
+};

+ 47 - 13
packages/input/src/input.vue

@@ -13,20 +13,20 @@
         <slot name="prepend"></slot>
       </div>
       <input
+        class="el-input__inner"
+        v-model="currentValue"
         :type="type"
         :name="name"
-        class="el-input__inner"
         :placeholder="placeholder"
-        v-model="currentValue"
         :disabled="disabled"
         :readonly="readonly"
-        @focus="$emit('onfocus', currentValue)"
-        @blur="handleBlur"
         :number="number"
         :maxlength="maxlength"
         :minlength="minlength"
         :autocomplete="autoComplete"
         ref="input"
+        @focus="$emit('onfocus', currentValue)"
+        @blur="handleBlur"
       >
       <!-- input 图标 -->
       <i class="el-input__icon" :class="[icon ? 'el-icon-' + icon : '']" v-if="icon"></i>
@@ -36,12 +36,27 @@
         <slot name="append"></slot>
       </div>
     </template>
-    <!-- 写成垂直的方式会导致 placeholder 失效, 蜜汁bug -->
-    <textarea v-else v-model="currentValue" class="el-textarea__inner" :name="name" :placeholder="placeholder" :disabled="disabled" :readonly="readonly" @focus="$emit('onfocus', currentValue)" @blur="handleBlur"></textarea>
+    <textarea
+      v-else
+      class="el-textarea__inner"
+      v-model="currentValue"
+      ref="textarea"
+      :name="name"
+      :placeholder="placeholder"
+      :disabled="disabled"
+      :style="textareaStyle"
+      :readonly="readonly"
+      :rows="rows"
+      :maxlength="maxlength"
+      :minlength="minlength"
+      @focus="$emit('onfocus', currentValue)"
+      @blur="handleBlur">
+    </textarea>
   </div>
 </template>
 <script>
   import emitter from 'main/mixins/emitter';
+  import calcTextareaHeight from './calcTextareaHeight';
 
   export default {
     name: 'ElInput',
@@ -82,6 +97,14 @@
         type: Boolean,
         default: false
       },
+      autosize: {
+        type: [Boolean, Object],
+        default: false
+      },
+      rows: {
+        type: Number,
+        default: 2
+      },
       autoComplete: {
         type: String,
         default: 'off'
@@ -98,12 +121,22 @@
 
       inputSelect() {
         this.$refs.input.select();
+      },
+      resizeTextarea() {
+        var { autosize, type } = this;
+        if (!autosize || type !== 'textarea') {
+          return;
+        }
+        const minRows = autosize ? autosize.minRows : null;
+        const maxRows = autosize ? autosize.maxRows : null;
+        this.textareaStyle = calcTextareaHeight(this.$refs.textarea, minRows, maxRows);
       }
     },
 
     data() {
       return {
-        currentValue: ''
+        currentValue: this.value,
+        textareaStyle: {}
       };
     },
 
@@ -111,6 +144,10 @@
       this.$on('inputSelect', this.inputSelect);
     },
 
+    mounted() {
+      this.resizeTextarea();
+    },
+
     computed: {
       validating() {
         return this.$parent.validating;
@@ -118,16 +155,13 @@
     },
 
     watch: {
-      'value': {
-        immediate: true,
-        handler(val) {
-          this.currentValue = val;
-        }
+      'value'(val, oldValue) {
+        this.currentValue = val;
+        this.resizeTextarea();
       },
 
       'currentValue'(val) {
         this.$emit('input', val);
-        this.$emit('onchange', val);
         this.dispatch('form-item', 'el.form.change', [val]);
       }
     }

+ 2 - 3
packages/theme-default/src/input.css

@@ -161,11 +161,10 @@
     @e inner {
       display: block;
       resize: vertical;
-      padding: 8px 5px;
-      line-height: normal;
+      padding: 5px 7px;
+      line-height: 1.5;
       box-sizing: border-box;
       width: 100%;
-      min-height: 88px;
       font-size: var(--font-size-base);
       color: var(--input-color);
       background-color: #fff;