소스 검색

Input: add show-word-count attribute (#15075)

luckyCao 6 년 전
부모
커밋
88ae222c61

+ 38 - 1
examples/docs/en-US/input.md

@@ -1,4 +1,4 @@
-## Input
+## Input
 
 Input data using mouse or keyboard.
 
@@ -521,6 +521,42 @@ Search data from server-side.
 ```
 :::
 
+### Limit length
+
+:::demo `maxlength` and `minlength` are attributes of native input, they declare a limit on the number of characters a user can input. The "number of characters" is measured using JavaScript string length.Setting the `maxlength` prop for a text or textarea type of Input can limit the length of input value, allows you to show word count by setting `show-word-limit` to `true` at the same time.
+
+```html
+<el-input
+  type="text"
+  placeholder="Please input"
+  v-model="text"
+  maxlength="10"
+  show-word-limit
+>
+</el-input>
+<div style="margin: 20px 0;"></div>
+<el-input
+  type="textarea"
+  placeholder="Please input"
+  v-model="textarea"
+  maxlength="30"
+  show-word-limit
+>
+</el-input>
+
+<script>
+export default {
+  data() {
+    return {
+      text: '',
+      textarea: ''
+    }
+  }
+}
+</script>
+```
+:::
+
 ### Input Attributes
 
 | Attribute      | Description          | Type      | Accepted Values       | Default  |
@@ -529,6 +565,7 @@ Search data from server-side.
 |value / v-model| binding value | string / number| — | — |
 |maxlength| same as `maxlength` in native input | number| — | — |
 |minlength| same as `minlength` in native input | number | — | — |
+|show-word-limit | whether show word count,only works when `type` is 'text' or 'textarea' | boolean    |  —  | false |
 |placeholder| placeholder of Input| string | — | — |
 | clearable | whether to show clear button | boolean | — | false |
 | show-password | whether to show toggleable password input| boolean         | — | false |

+ 39 - 2
examples/docs/es/input.md

@@ -285,7 +285,7 @@ export default {
   <el-input
     size="small"
     placeholder="Please Input"
-    v-model="input2">
+    v-model="input3">
   </el-input>
   <el-input
     size="mini"
@@ -300,7 +300,7 @@ export default {
     return {
       input1: '',
       input2: '',
-      input2: '',
+      input3: '',
       input4: ''
     }
   }
@@ -535,6 +535,42 @@ Búsqueda de datos desde el servidor.
 
 :::
 
+### Limit length
+
+:::demo `maxlength` and `minlength` are attributes of native input, they declare a limit on the number of characters a user can input. The "number of characters" is measured using JavaScript string length.Setting the `maxlength` prop for a text or textarea type of Input can limit the length of input value, allows you to show word count by setting `show-word-limit` to `true` at the same time.
+
+```html
+<el-input
+  type="text"
+  placeholder="Please input"
+  v-model="text"
+  maxlength="10"
+  show-word-limit
+>
+</el-input>
+<div style="margin: 20px 0;"></div>
+<el-input
+  type="textarea"
+  placeholder="Please input"
+  v-model="textarea"
+  maxlength="30"
+  show-word-limit
+>
+</el-input>
+
+<script>
+export default {
+  data() {
+    return {
+      text: '',
+      textarea: ''
+    }
+  }
+}
+</script>
+```
+:::
+
 ### Input atributos
 
 | Atributo      | Descripción                                                                                                                                      | Tipo             | Valores aceptados                                                                                                                       | Por defecto |
@@ -543,6 +579,7 @@ Búsqueda de datos desde el servidor.
 | value / v-model | valor enlazado                          | boolean / string / number | —                       | —           |
 | maxlength     | igual que `maxlength` en el input nativo                                                                                                         | number           | —                                                                                                                                       | —           |
 | minlength     | igual que `minlength` en el input nativo                                                                                                         | number           | —                                                                                                                                       | —           |
+| show-word-limit | whether show word count,only works when `type` is 'text' or 'textarea' | boolean    |  —  | false |
 | placeholder   | placeholder del Input                                                                                                                            | string           | —                                                                                                                                       | —           |
 | clearable | si debe mostrar el boton de limpieza | boolean | — | false |
 | show-password | si debe mostrar la posibilidad de conmutacion de password input | boolean         | — | false |

+ 37 - 0
examples/docs/fr-FR/input.md

@@ -520,6 +520,42 @@ Vous pouvez aller chercher des infos de suggestions sur un serveur distant.
 ```
 :::
 
+### Limit length
+
+:::demo `maxlength` and `minlength` are attributes of native input, they declare a limit on the number of characters a user can input. The "number of characters" is measured using JavaScript string length.Setting the `maxlength` prop for a text or textarea type of Input can limit the length of input value, allows you to show word count by setting `show-word-limit` to `true` at the same time.
+
+```html
+<el-input
+  type="text"
+  placeholder="Please input"
+  v-model="text"
+  maxlength="10"
+  show-word-limit
+>
+</el-input>
+<div style="margin: 20px 0;"></div>
+<el-input
+  type="textarea"
+  placeholder="Please input"
+  v-model="textarea"
+  maxlength="30"
+  show-word-limit
+>
+</el-input>
+
+<script>
+export default {
+  data() {
+    return {
+      text: '',
+      textarea: ''
+    }
+  }
+}
+</script>
+```
+:::
+
 ### Attributs de l'Input
 
 | Attribut      | Description          | Type      | Valeurs acceptées       | Défaut  |
@@ -528,6 +564,7 @@ Vous pouvez aller chercher des infos de suggestions sur un serveur distant.
 | value / v-model | Variable liée. | string / number | — | — |
 | maxlength| Identique à `maxlength` dans l'input natif. | number| — | — |
 | minlength| Identique à `minlength` dans l'input natif. | number | — | — |
+| show-word-limit | whether show word count,only works when `type` is 'text' or 'textarea' | boolean    |  —  | false |
 | placeholder| Placeholder de l' Input. | string | — | — |
 | clearable | Si le bouton de reset apparaît. | boolean | — | false |
 | show-password | Si le champ doit un champ de mot de passe avec bouton de visualisation. | boolean         | — | false |

+ 36 - 0
examples/docs/zh-CN/input.md

@@ -639,6 +639,41 @@ export default {
 ```
 :::
 
+### 输入长度限制
+
+:::demo  `maxlength` 和 `minlength` 是原生属性,用来限制输入框的字符长度,其中字符长度是用 Javascript 的字符串长度统计的。对于类型为 `text` 或 `textarea` 的输入框,在使用 `maxlength` 属性限制最大输入长度的同时,可通过设置 `show-word-limit` 属性来展示字数统计。
+```html
+<el-input
+  type="text"
+  placeholder="请输入内容"
+  v-model="text"
+  maxlength="10"
+  show-word-limit
+>
+</el-input>
+<div style="margin: 20px 0;"></div>
+<el-input
+  type="textarea"
+  placeholder="请输入内容"
+  v-model="textarea"
+  maxlength="30"
+  show-word-limit
+>
+</el-input>
+
+<script>
+export default {
+  data() {
+    return {
+      text: '',
+      textarea: ''
+    }
+  }
+}
+</script>
+```
+:::
+
 ### Input Attributes
 
 | 参数          | 说明            | 类型            | 可选值                 | 默认值   |
@@ -647,6 +682,7 @@ export default {
 | value / v-model | 绑定值           | string / number  | — | — |
 | maxlength     | 原生属性,最大输入长度      | number          |  —  | — |
 | minlength     | 原生属性,最小输入长度      | number          | — | — |
+| show-word-limit | 是否显示输入字数统计,只在 `type = "text"` 或 `type = "textarea"` 时有效 | boolean    |  —  | false |
 | placeholder   | 输入框占位文本    | string          | — | — |
 | clearable     | 是否可清空        | boolean         | — | false |
 | show-password | 是否显示切换密码图标| boolean         | — | false |

+ 44 - 2
packages/input/src/input.vue

@@ -4,6 +4,7 @@
     inputSize ? 'el-input--' + inputSize : '',
     {
       'is-disabled': inputDisabled,
+      'is-exceed': inputExceed,
       'el-input-group': $slots.prepend || $slots.append,
       'el-input-group--append': $slots.append,
       'el-input-group--prepend': $slots.prepend,
@@ -48,9 +49,9 @@
       <!-- 后置内容 -->
       <span
         class="el-input__suffix"
-        v-if="$slots.suffix || suffixIcon || showClear || showPassword || validateState && needStatusIcon">
+        v-if="getSuffixVisible()">
         <span class="el-input__suffix-inner">
-          <template v-if="!showClear || !showPwdVisible">
+          <template v-if="!showClear || !showPwdVisible || !isWordLimitVisible">
             <slot name="suffix"></slot>
             <i class="el-input__icon"
               v-if="suffixIcon"
@@ -65,6 +66,11 @@
             class="el-input__icon el-icon-view el-input__clear"
             @click="handlePasswordVisible"
           ></i>
+          <span v-if="isWordLimitVisible" class="el-input__count">
+            <span class="el-input__count-inner">
+              {{ textLength }}/{{ upperLimit }}
+            </span>
+          </span>
         </span>
         <i class="el-input__icon"
           v-if="validateState"
@@ -95,6 +101,7 @@
       :aria-label="label"
     >
     </textarea>
+    <span v-if="isWordLimitVisible && type === 'textarea'" class="el-input__count">{{ textLength }}/{{ upperLimit }}</span>
   </div>
 </template>
 <script>
@@ -174,6 +181,10 @@
         type: Boolean,
         default: false
       },
+      showWordLimit: {
+        type: Boolean,
+        default: false
+      },
       tabindex: String
     },
 
@@ -218,6 +229,29 @@
           !this.inputDisabled &&
           !this.readonly &&
           (!!this.nativeInputValue || this.focused);
+      },
+      isWordLimitVisible() {
+        return this.showWordLimit &&
+          this.$attrs.maxlength &&
+          (this.type === 'text' || this.type === 'textarea') &&
+          !this.inputDisabled &&
+          !this.readonly &&
+          !this.showPassword;
+      },
+      upperLimit() {
+        return this.$attrs.maxlength;
+      },
+      textLength() {
+        if (typeof this.value === 'number') {
+          return String(this.value).length;
+        }
+
+        return (this.value || '').length;
+      },
+      inputExceed() {
+        // show exceed style if length of initial value greater then maxlength
+        return this.isWordLimitVisible &&
+          (this.textLength > this.upperLimit);
       }
     },
 
@@ -362,6 +396,14 @@
       },
       getInput() {
         return this.$refs.input || this.$refs.textarea;
+      },
+      getSuffixVisible() {
+        return this.$slots.suffix ||
+          this.suffixIcon ||
+          this.showClear ||
+          this.showPassword ||
+          this.isWordLimitVisible ||
+          (this.validateState && this.needStatusIcon);
       }
     },
 

+ 46 - 0
packages/theme-chalk/src/input.scss

@@ -2,6 +2,7 @@
 @import "common/var";
 
 @include b(textarea) {
+  position: relative;
   display: inline-block;
   width: 100%;
   vertical-align: bottom;
@@ -36,6 +37,15 @@
     }
   }
 
+  & .el-input__count {
+    color: $--color-info;
+    background: $--color-white;
+    position: absolute;
+    font-size: 12px;
+    bottom: 5px;
+    right: 10px;
+  }
+
   @include when(disabled) {
     .el-textarea__inner {
       background-color: $--input-disabled-fill;
@@ -48,6 +58,16 @@
       }
     }
   }
+
+  @include when(exceed) {
+    .el-textarea__inner {
+      border-color: $--color-danger;
+    }
+
+    .el-input__count {
+      color: $--color-danger;
+    }
+  }
 }
 
 @include b(input) {
@@ -69,6 +89,20 @@
     }
   }
 
+  & .el-input__count {
+    height: 100%;
+    display: inline-flex;
+    align-items: center;
+    color: $--color-info;
+    font-size: 12px;
+
+    .el-input__count-inner {
+      background: $--color-white;
+      display: inline-block;
+      padding: 0 5px;
+    }
+  }
+
   @include e(inner) {
     -webkit-appearance: none;
     background-color: $--input-background-color;
@@ -169,6 +203,18 @@
     }
   }
 
+  @include when(exceed) {
+    .el-input__inner {
+      border-color: $--color-danger;
+    }
+
+    .el-input__suffix {
+      .el-input__count {
+        color: $--color-danger;
+      }
+    }
+  }
+
   @include m(suffix) {
     .el-input__inner {
       padding-right: 30px;

+ 64 - 0
test/unit/specs/input.spec.js

@@ -416,4 +416,68 @@ describe('Input', () => {
     await waitImmediate();
     expect(vm.$el.querySelector('input').value).to.equal('123');
   });
+
+  it('limit input and show word count', async() => {
+    vm = createVue({
+      template: `
+        <div>
+          <el-input
+            class="test-text"
+            type="text"
+            v-model="input1"
+            maxlength="10"
+            :show-word-limit="show">
+          </el-input>
+          <el-input
+            class="test-textarea"
+            type="textarea"
+            v-model="input2"
+            maxlength="10"
+            show-word-limit>
+          </el-input>
+          <el-input
+            class="test-password"
+            type="password"
+            v-model="input3"
+            maxlength="10"
+            show-word-limit>
+          </el-input>
+          <el-input
+            class="test-initial-exceed"
+            type="text"
+            v-model="input4"
+            maxlength="2"
+            show-word-limit>
+          </el-input>
+        </div>
+      `,
+      data() {
+        return {
+          input1: '',
+          input2: '',
+          input3: '',
+          input4: 'exceed',
+          show: false
+        };
+      }
+    }, true);
+
+    const inputElm1 = vm.$el.querySelector('.test-text');
+    const inputElm2 = vm.$el.querySelector('.test-textarea');
+    const inputElm3 = vm.$el.querySelector('.test-password');
+    const inputElm4 = vm.$el.querySelector('.test-initial-exceed');
+
+    expect(inputElm1.querySelectorAll('.el-input__count').length).to.equal(0);
+    expect(inputElm2.querySelectorAll('.el-input__count').length).to.equal(1);
+    expect(inputElm3.querySelectorAll('.el-input__count').length).to.equal(0);
+    expect(inputElm4.classList.contains('is-exceed')).to.true;
+
+    vm.show = true;
+    await waitImmediate();
+    expect(inputElm1.querySelectorAll('.el-input__count').length).to.equal(1);
+
+    vm.input4 = '1';
+    await waitImmediate();
+    expect(inputElm4.classList.contains('is-exceed')).to.false;
+  });
 });

+ 9 - 0
types/input.d.ts

@@ -81,6 +81,15 @@ export declare class ElInput extends ElementUIComponent {
   /** Whether to trigger form validatio */
   validateEvent: boolean
 
+  /** Whether the input is clearable */
+  clearable: boolean
+
+  /** Whether to show password */
+  showPassword: boolean
+
+  /** Whether to show wordCount when setting maxLength */
+  showWordLimit: boolean
+
   /**
    * Focus the Input component
    */