mirror of https://github.com/ElemeFE/element
[input ]: Add formatting api
parent
2a49142965
commit
907e548cb0
|
@ -673,7 +673,29 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
:::
|
:::
|
||||||
|
### 格式化
|
||||||
|
:::demo `formatter` 和 `parser` 可配合实现格式化能力(仅`type`为text生效)。
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<el-input
|
||||||
|
v-model="input"
|
||||||
|
placeholder="Please input"
|
||||||
|
:formatter="(value) => `$ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')"
|
||||||
|
:parser="(value) => value.replace(/\$\s?|(,*)/g, '')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
input: '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
:::
|
||||||
### Input Attributes
|
### Input Attributes
|
||||||
|
|
||||||
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|
||||||
|
@ -703,8 +725,10 @@ export default {
|
||||||
| autofocus | 原生属性,自动获取焦点 | boolean | true, false | false |
|
| autofocus | 原生属性,自动获取焦点 | boolean | true, false | false |
|
||||||
| form | 原生属性 | string | — | — |
|
| form | 原生属性 | string | — | — |
|
||||||
| label | 输入框关联的label文字 | string | — | — |
|
| label | 输入框关联的label文字 | string | — | — |
|
||||||
| tabindex | 输入框的tabindex | string | - | - |
|
| tabindex | 输入框的tabindex | string | — | — |
|
||||||
| validate-event | 输入时是否触发表单的校验 | boolean | - | true |
|
| validate-event | 输入时是否触发表单的校验 | boolean | — | true |
|
||||||
|
| formatter | 指定输入值的格式。(仅当 type 是"text"时才能工作) | Function | — | — |
|
||||||
|
| parser | 指定从格式化器输入中提取的值。(仅当 type 是"text"时才起作用) | Function | — | — |
|
||||||
|
|
||||||
### Input Slots
|
### Input Slots
|
||||||
| name | 说明 |
|
| name | 说明 |
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="[
|
<div
|
||||||
type === 'textarea' ? 'el-textarea' : 'el-input',
|
:class="[
|
||||||
inputSize ? 'el-input--' + inputSize : '',
|
type === 'textarea' ? 'el-textarea' : 'el-input',
|
||||||
{
|
inputSize ? 'el-input--' + inputSize : '',
|
||||||
'is-disabled': inputDisabled,
|
{
|
||||||
'is-exceed': inputExceed,
|
'is-disabled': inputDisabled,
|
||||||
'el-input-group': $slots.prepend || $slots.append,
|
'is-exceed': inputExceed,
|
||||||
'el-input-group--append': $slots.append,
|
'el-input-group': $slots.prepend || $slots.append,
|
||||||
'el-input-group--prepend': $slots.prepend,
|
'el-input-group--append': $slots.append,
|
||||||
'el-input--prefix': $slots.prefix || prefixIcon,
|
'el-input-group--prepend': $slots.prepend,
|
||||||
'el-input--suffix': $slots.suffix || suffixIcon || clearable || showPassword
|
'el-input--prefix': $slots.prefix || prefixIcon,
|
||||||
}
|
'el-input--suffix':
|
||||||
|
$slots.suffix || suffixIcon || clearable || showPassword,
|
||||||
|
},
|
||||||
]"
|
]"
|
||||||
@mouseenter="hovering = true"
|
@mouseenter="hovering = true"
|
||||||
@mouseleave="hovering = false"
|
@mouseleave="hovering = false"
|
||||||
|
@ -25,7 +27,7 @@
|
||||||
v-if="type !== 'textarea'"
|
v-if="type !== 'textarea'"
|
||||||
class="el-input__inner"
|
class="el-input__inner"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
:type="showPassword ? (passwordVisible ? 'text': 'password') : type"
|
:type="showPassword ? (passwordVisible ? 'text' : 'password') : type"
|
||||||
:disabled="inputDisabled"
|
:disabled="inputDisabled"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:autocomplete="autoComplete || autocomplete"
|
:autocomplete="autoComplete || autocomplete"
|
||||||
|
@ -38,33 +40,28 @@
|
||||||
@blur="handleBlur"
|
@blur="handleBlur"
|
||||||
@change="handleChange"
|
@change="handleChange"
|
||||||
:aria-label="label"
|
:aria-label="label"
|
||||||
>
|
/>
|
||||||
<!-- 前置内容 -->
|
<!-- 前置内容 -->
|
||||||
<span class="el-input__prefix" v-if="$slots.prefix || prefixIcon">
|
<span class="el-input__prefix" v-if="$slots.prefix || prefixIcon">
|
||||||
<slot name="prefix"></slot>
|
<slot name="prefix"></slot>
|
||||||
<i class="el-input__icon"
|
<i class="el-input__icon" v-if="prefixIcon" :class="prefixIcon"> </i>
|
||||||
v-if="prefixIcon"
|
|
||||||
:class="prefixIcon">
|
|
||||||
</i>
|
|
||||||
</span>
|
</span>
|
||||||
<!-- 后置内容 -->
|
<!-- 后置内容 -->
|
||||||
<span
|
<span class="el-input__suffix" v-if="getSuffixVisible()">
|
||||||
class="el-input__suffix"
|
|
||||||
v-if="getSuffixVisible()">
|
|
||||||
<span class="el-input__suffix-inner">
|
<span class="el-input__suffix-inner">
|
||||||
<template v-if="!showClear || !showPwdVisible || !isWordLimitVisible">
|
<template v-if="!showClear || !showPwdVisible || !isWordLimitVisible">
|
||||||
<slot name="suffix"></slot>
|
<slot name="suffix"></slot>
|
||||||
<i class="el-input__icon"
|
<i class="el-input__icon" v-if="suffixIcon" :class="suffixIcon">
|
||||||
v-if="suffixIcon"
|
|
||||||
:class="suffixIcon">
|
|
||||||
</i>
|
</i>
|
||||||
</template>
|
</template>
|
||||||
<i v-if="showClear"
|
<i
|
||||||
|
v-if="showClear"
|
||||||
class="el-input__icon el-icon-circle-close el-input__clear"
|
class="el-input__icon el-icon-circle-close el-input__clear"
|
||||||
@mousedown.prevent
|
@mousedown.prevent
|
||||||
@click="clear"
|
@click="clear"
|
||||||
></i>
|
></i>
|
||||||
<i v-if="showPwdVisible"
|
<i
|
||||||
|
v-if="showPwdVisible"
|
||||||
class="el-input__icon el-icon-view el-input__clear"
|
class="el-input__icon el-icon-view el-input__clear"
|
||||||
@click="handlePasswordVisible"
|
@click="handlePasswordVisible"
|
||||||
></i>
|
></i>
|
||||||
|
@ -74,9 +71,11 @@
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<i class="el-input__icon"
|
<i
|
||||||
|
class="el-input__icon"
|
||||||
v-if="validateState"
|
v-if="validateState"
|
||||||
:class="['el-input__validateIcon', validateIcon]">
|
:class="['el-input__validateIcon', validateIcon]"
|
||||||
|
>
|
||||||
</i>
|
</i>
|
||||||
</span>
|
</span>
|
||||||
<!-- 后置元素 -->
|
<!-- 后置元素 -->
|
||||||
|
@ -104,337 +103,372 @@
|
||||||
:aria-label="label"
|
:aria-label="label"
|
||||||
>
|
>
|
||||||
</textarea>
|
</textarea>
|
||||||
<span v-if="isWordLimitVisible && type === 'textarea'" class="el-input__count">{{ textLength }}/{{ upperLimit }}</span>
|
<span
|
||||||
|
v-if="isWordLimitVisible && type === 'textarea'"
|
||||||
|
class="el-input__count"
|
||||||
|
>{{ textLength }}/{{ upperLimit }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import emitter from 'element-ui/src/mixins/emitter';
|
import emitter from 'element-ui/src/mixins/emitter';
|
||||||
import Migrating from 'element-ui/src/mixins/migrating';
|
import Migrating from 'element-ui/src/mixins/migrating';
|
||||||
import calcTextareaHeight from './calcTextareaHeight';
|
import merge from 'element-ui/src/utils/merge';
|
||||||
import merge from 'element-ui/src/utils/merge';
|
import { isKorean } from 'element-ui/src/utils/shared';
|
||||||
import {isKorean} from 'element-ui/src/utils/shared';
|
import calcTextareaHeight from './calcTextareaHeight';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ElInput',
|
name: 'ElInput',
|
||||||
|
|
||||||
componentName: 'ElInput',
|
componentName: 'ElInput',
|
||||||
|
|
||||||
mixins: [emitter, Migrating],
|
mixins: [emitter, Migrating],
|
||||||
|
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
|
|
||||||
inject: {
|
inject: {
|
||||||
elForm: {
|
elForm: {
|
||||||
default: ''
|
default: ''
|
||||||
},
|
},
|
||||||
elFormItem: {
|
elFormItem: {
|
||||||
default: ''
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
textareaCalcStyle: {},
|
||||||
|
hovering: false,
|
||||||
|
focused: false,
|
||||||
|
isComposing: false,
|
||||||
|
passwordVisible: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
value: [String, Number],
|
||||||
|
size: String,
|
||||||
|
resize: String,
|
||||||
|
form: String,
|
||||||
|
disabled: Boolean,
|
||||||
|
readonly: Boolean,
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'text'
|
||||||
|
},
|
||||||
|
autosize: {
|
||||||
|
type: [Boolean, Object],
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
autocomplete: {
|
||||||
|
type: String,
|
||||||
|
default: 'off'
|
||||||
|
},
|
||||||
|
/** @Deprecated in next major version */
|
||||||
|
autoComplete: {
|
||||||
|
type: String,
|
||||||
|
validator(val) {
|
||||||
|
process.env.NODE_ENV !== 'production' &&
|
||||||
|
console.warn(
|
||||||
|
"[Element Warn][Input]'auto-complete' property will be deprecated in next major version. please use 'autocomplete' instead."
|
||||||
|
);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
validateEvent: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
suffixIcon: String,
|
||||||
|
prefixIcon: String,
|
||||||
|
label: String,
|
||||||
|
clearable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
showPassword: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
showWordLimit: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
tabindex: String,
|
||||||
|
formatter: {
|
||||||
|
type: Function,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
parser: {
|
||||||
|
type: Function,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
data() {
|
computed: {
|
||||||
|
_elFormItemSize() {
|
||||||
|
return (this.elFormItem || {}).elFormItemSize;
|
||||||
|
},
|
||||||
|
validateState() {
|
||||||
|
return this.elFormItem ? this.elFormItem.validateState : '';
|
||||||
|
},
|
||||||
|
needStatusIcon() {
|
||||||
|
return this.elForm ? this.elForm.statusIcon : false;
|
||||||
|
},
|
||||||
|
validateIcon() {
|
||||||
return {
|
return {
|
||||||
textareaCalcStyle: {},
|
validating: 'el-icon-loading',
|
||||||
hovering: false,
|
success: 'el-icon-circle-check',
|
||||||
focused: false,
|
error: 'el-icon-circle-close'
|
||||||
isComposing: false,
|
}[this.validateState];
|
||||||
passwordVisible: false
|
},
|
||||||
|
textareaStyle() {
|
||||||
|
return merge({}, this.textareaCalcStyle, { resize: this.resize });
|
||||||
|
},
|
||||||
|
inputSize() {
|
||||||
|
return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
|
||||||
|
},
|
||||||
|
inputDisabled() {
|
||||||
|
return this.disabled || (this.elForm || {}).disabled;
|
||||||
|
},
|
||||||
|
nativeInputValue() {
|
||||||
|
return this.value === null || this.value === undefined
|
||||||
|
? ''
|
||||||
|
: String(this.value);
|
||||||
|
},
|
||||||
|
showClear() {
|
||||||
|
return (
|
||||||
|
this.clearable &&
|
||||||
|
!this.inputDisabled &&
|
||||||
|
!this.readonly &&
|
||||||
|
this.nativeInputValue &&
|
||||||
|
(this.focused || this.hovering)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
showPwdVisible() {
|
||||||
|
return (
|
||||||
|
this.showPassword &&
|
||||||
|
!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;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
value(val) {
|
||||||
|
this.$nextTick(this.resizeTextarea);
|
||||||
|
if (this.validateEvent) {
|
||||||
|
this.dispatch('ElFormItem', 'el.form.change', [val]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// native input value is set explicitly
|
||||||
|
// do not use v-model / :value in template
|
||||||
|
// see: https://github.com/ElemeFE/element/issues/14521
|
||||||
|
nativeInputValue() {
|
||||||
|
this.setNativeInputValue();
|
||||||
|
},
|
||||||
|
// when change between <input> and <textarea>,
|
||||||
|
// update DOM dependent value and styles
|
||||||
|
// https://github.com/ElemeFE/element/issues/14857
|
||||||
|
type() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.setNativeInputValue();
|
||||||
|
this.resizeTextarea();
|
||||||
|
this.updateIconOffset();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
focus() {
|
||||||
|
this.getInput().focus();
|
||||||
|
},
|
||||||
|
blur() {
|
||||||
|
this.getInput().blur();
|
||||||
|
},
|
||||||
|
getMigratingConfig() {
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
icon: 'icon is removed, use suffix-icon / prefix-icon instead.',
|
||||||
|
'on-icon-click': 'on-icon-click is removed.'
|
||||||
|
},
|
||||||
|
events: {
|
||||||
|
click: 'click is removed.'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
handleBlur(event) {
|
||||||
props: {
|
this.focused = false;
|
||||||
value: [String, Number],
|
this.$emit('blur', event);
|
||||||
size: String,
|
if (this.validateEvent) {
|
||||||
resize: String,
|
this.dispatch('ElFormItem', 'el.form.blur', [this.value]);
|
||||||
form: String,
|
|
||||||
disabled: Boolean,
|
|
||||||
readonly: Boolean,
|
|
||||||
type: {
|
|
||||||
type: String,
|
|
||||||
default: 'text'
|
|
||||||
},
|
|
||||||
autosize: {
|
|
||||||
type: [Boolean, Object],
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
autocomplete: {
|
|
||||||
type: String,
|
|
||||||
default: 'off'
|
|
||||||
},
|
|
||||||
/** @Deprecated in next major version */
|
|
||||||
autoComplete: {
|
|
||||||
type: String,
|
|
||||||
validator(val) {
|
|
||||||
process.env.NODE_ENV !== 'production' &&
|
|
||||||
console.warn('[Element Warn][Input]\'auto-complete\' property will be deprecated in next major version. please use \'autocomplete\' instead.');
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
validateEvent: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
suffixIcon: String,
|
|
||||||
prefixIcon: String,
|
|
||||||
label: String,
|
|
||||||
clearable: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
showPassword: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
showWordLimit: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
tabindex: String
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
_elFormItemSize() {
|
|
||||||
return (this.elFormItem || {}).elFormItemSize;
|
|
||||||
},
|
|
||||||
validateState() {
|
|
||||||
return this.elFormItem ? this.elFormItem.validateState : '';
|
|
||||||
},
|
|
||||||
needStatusIcon() {
|
|
||||||
return this.elForm ? this.elForm.statusIcon : false;
|
|
||||||
},
|
|
||||||
validateIcon() {
|
|
||||||
return {
|
|
||||||
validating: 'el-icon-loading',
|
|
||||||
success: 'el-icon-circle-check',
|
|
||||||
error: 'el-icon-circle-close'
|
|
||||||
}[this.validateState];
|
|
||||||
},
|
|
||||||
textareaStyle() {
|
|
||||||
return merge({}, this.textareaCalcStyle, { resize: this.resize });
|
|
||||||
},
|
|
||||||
inputSize() {
|
|
||||||
return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
|
|
||||||
},
|
|
||||||
inputDisabled() {
|
|
||||||
return this.disabled || (this.elForm || {}).disabled;
|
|
||||||
},
|
|
||||||
nativeInputValue() {
|
|
||||||
return this.value === null || this.value === undefined ? '' : String(this.value);
|
|
||||||
},
|
|
||||||
showClear() {
|
|
||||||
return this.clearable &&
|
|
||||||
!this.inputDisabled &&
|
|
||||||
!this.readonly &&
|
|
||||||
this.nativeInputValue &&
|
|
||||||
(this.focused || this.hovering);
|
|
||||||
},
|
|
||||||
showPwdVisible() {
|
|
||||||
return this.showPassword &&
|
|
||||||
!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);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
select() {
|
||||||
watch: {
|
this.getInput().select();
|
||||||
value(val) {
|
|
||||||
this.$nextTick(this.resizeTextarea);
|
|
||||||
if (this.validateEvent) {
|
|
||||||
this.dispatch('ElFormItem', 'el.form.change', [val]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// native input value is set explicitly
|
|
||||||
// do not use v-model / :value in template
|
|
||||||
// see: https://github.com/ElemeFE/element/issues/14521
|
|
||||||
nativeInputValue() {
|
|
||||||
this.setNativeInputValue();
|
|
||||||
},
|
|
||||||
// when change between <input> and <textarea>,
|
|
||||||
// update DOM dependent value and styles
|
|
||||||
// https://github.com/ElemeFE/element/issues/14857
|
|
||||||
type() {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.setNativeInputValue();
|
|
||||||
this.resizeTextarea();
|
|
||||||
this.updateIconOffset();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
resizeTextarea() {
|
||||||
methods: {
|
if (this.$isServer) return;
|
||||||
focus() {
|
const { autosize, type } = this;
|
||||||
this.getInput().focus();
|
if (type !== 'textarea') return;
|
||||||
},
|
if (!autosize) {
|
||||||
blur() {
|
this.textareaCalcStyle = {
|
||||||
this.getInput().blur();
|
minHeight: calcTextareaHeight(this.$refs.textarea).minHeight
|
||||||
},
|
|
||||||
getMigratingConfig() {
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
'icon': 'icon is removed, use suffix-icon / prefix-icon instead.',
|
|
||||||
'on-icon-click': 'on-icon-click is removed.'
|
|
||||||
},
|
|
||||||
events: {
|
|
||||||
'click': 'click is removed.'
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
},
|
return;
|
||||||
handleBlur(event) {
|
}
|
||||||
this.focused = false;
|
const minRows = autosize.minRows;
|
||||||
this.$emit('blur', event);
|
const maxRows = autosize.maxRows;
|
||||||
if (this.validateEvent) {
|
|
||||||
this.dispatch('ElFormItem', 'el.form.blur', [this.value]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
select() {
|
|
||||||
this.getInput().select();
|
|
||||||
},
|
|
||||||
resizeTextarea() {
|
|
||||||
if (this.$isServer) return;
|
|
||||||
const { autosize, type } = this;
|
|
||||||
if (type !== 'textarea') return;
|
|
||||||
if (!autosize) {
|
|
||||||
this.textareaCalcStyle = {
|
|
||||||
minHeight: calcTextareaHeight(this.$refs.textarea).minHeight
|
|
||||||
};
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const minRows = autosize.minRows;
|
|
||||||
const maxRows = autosize.maxRows;
|
|
||||||
|
|
||||||
this.textareaCalcStyle = calcTextareaHeight(this.$refs.textarea, minRows, maxRows);
|
this.textareaCalcStyle = calcTextareaHeight(
|
||||||
},
|
this.$refs.textarea,
|
||||||
setNativeInputValue() {
|
minRows,
|
||||||
const input = this.getInput();
|
maxRows
|
||||||
if (!input) return;
|
);
|
||||||
if (input.value === this.nativeInputValue) return;
|
},
|
||||||
input.value = this.nativeInputValue;
|
setNativeInputValue() {
|
||||||
},
|
const input = this.getInput();
|
||||||
handleFocus(event) {
|
if (!input) return;
|
||||||
this.focused = true;
|
if (input.value === this.nativeInputValue) return;
|
||||||
this.$emit('focus', event);
|
input.value = this.nativeInputValue;
|
||||||
},
|
},
|
||||||
handleCompositionStart(event) {
|
handleFocus(event) {
|
||||||
this.$emit('compositionstart', event);
|
this.focused = true;
|
||||||
this.isComposing = true;
|
this.$emit('focus', event);
|
||||||
},
|
},
|
||||||
handleCompositionUpdate(event) {
|
handleCompositionStart(event) {
|
||||||
this.$emit('compositionupdate', event);
|
this.$emit('compositionstart', event);
|
||||||
const text = event.target.value;
|
this.isComposing = true;
|
||||||
const lastCharacter = text[text.length - 1] || '';
|
},
|
||||||
this.isComposing = !isKorean(lastCharacter);
|
handleCompositionUpdate(event) {
|
||||||
},
|
this.$emit('compositionupdate', event);
|
||||||
handleCompositionEnd(event) {
|
const text = event.target.value;
|
||||||
this.$emit('compositionend', event);
|
const lastCharacter = text[text.length - 1] || '';
|
||||||
if (this.isComposing) {
|
this.isComposing = !isKorean(lastCharacter);
|
||||||
this.isComposing = false;
|
},
|
||||||
this.handleInput(event);
|
handleCompositionEnd(event) {
|
||||||
}
|
this.$emit('compositionend', event);
|
||||||
},
|
if (this.isComposing) {
|
||||||
handleInput(event) {
|
this.isComposing = false;
|
||||||
// should not emit input during composition
|
this.handleInput(event);
|
||||||
// see: https://github.com/ElemeFE/element/issues/10516
|
|
||||||
if (this.isComposing) return;
|
|
||||||
|
|
||||||
// hack for https://github.com/ElemeFE/element/issues/8548
|
|
||||||
// should remove the following line when we don't support IE
|
|
||||||
if (event.target.value === this.nativeInputValue) return;
|
|
||||||
|
|
||||||
this.$emit('input', event.target.value);
|
|
||||||
|
|
||||||
// ensure native input value is controlled
|
|
||||||
// see: https://github.com/ElemeFE/element/issues/12850
|
|
||||||
this.$nextTick(this.setNativeInputValue);
|
|
||||||
},
|
|
||||||
handleChange(event) {
|
|
||||||
this.$emit('change', event.target.value);
|
|
||||||
},
|
|
||||||
calcIconOffset(place) {
|
|
||||||
let elList = [].slice.call(this.$el.querySelectorAll(`.el-input__${place}`) || []);
|
|
||||||
if (!elList.length) return;
|
|
||||||
let el = null;
|
|
||||||
for (let i = 0; i < elList.length; i++) {
|
|
||||||
if (elList[i].parentNode === this.$el) {
|
|
||||||
el = elList[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!el) return;
|
|
||||||
const pendantMap = {
|
|
||||||
suffix: 'append',
|
|
||||||
prefix: 'prepend'
|
|
||||||
};
|
|
||||||
|
|
||||||
const pendant = pendantMap[place];
|
|
||||||
if (this.$slots[pendant]) {
|
|
||||||
el.style.transform = `translateX(${place === 'suffix' ? '-' : ''}${this.$el.querySelector(`.el-input-group__${pendant}`).offsetWidth}px)`;
|
|
||||||
} else {
|
|
||||||
el.removeAttribute('style');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updateIconOffset() {
|
|
||||||
this.calcIconOffset('prefix');
|
|
||||||
this.calcIconOffset('suffix');
|
|
||||||
},
|
|
||||||
clear() {
|
|
||||||
this.$emit('input', '');
|
|
||||||
this.$emit('change', '');
|
|
||||||
this.$emit('clear');
|
|
||||||
},
|
|
||||||
handlePasswordVisible() {
|
|
||||||
this.passwordVisible = !this.passwordVisible;
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.focus();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
handleInput(event) {
|
||||||
|
let { value, type } = event.target;
|
||||||
|
let { formatter, parser } = this;
|
||||||
|
// should not emit input during composition
|
||||||
|
// see: https://github.com/ElemeFE/element/issues/10516
|
||||||
|
if (this.isComposing) return;
|
||||||
|
// hack for https://github.com/ElemeFE/element/issues/8548
|
||||||
|
// should remove the following line when we don't support IE
|
||||||
|
if (value === this.nativeInputValue) return;
|
||||||
|
if (formatter && type === 'text') {
|
||||||
|
value = parser ? parser(value) : value;
|
||||||
|
value = formatter(value);
|
||||||
|
}
|
||||||
|
this.$emit('input', value);
|
||||||
|
|
||||||
created() {
|
// ensure native input value is controlled
|
||||||
this.$on('inputSelect', this.select);
|
// see: https://github.com/ElemeFE/element/issues/12850
|
||||||
|
this.$nextTick(this.setNativeInputValue);
|
||||||
},
|
},
|
||||||
|
handleChange(event) {
|
||||||
mounted() {
|
this.$emit('change', event.target.value);
|
||||||
this.setNativeInputValue();
|
|
||||||
this.resizeTextarea();
|
|
||||||
this.updateIconOffset();
|
|
||||||
},
|
},
|
||||||
|
calcIconOffset(place) {
|
||||||
|
let elList = [].slice.call(
|
||||||
|
this.$el.querySelectorAll(`.el-input__${place}`) || []
|
||||||
|
);
|
||||||
|
if (!elList.length) return;
|
||||||
|
let el = null;
|
||||||
|
for (let i = 0; i < elList.length; i++) {
|
||||||
|
if (elList[i].parentNode === this.$el) {
|
||||||
|
el = elList[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!el) return;
|
||||||
|
const pendantMap = {
|
||||||
|
suffix: 'append',
|
||||||
|
prefix: 'prepend'
|
||||||
|
};
|
||||||
|
|
||||||
updated() {
|
const pendant = pendantMap[place];
|
||||||
this.$nextTick(this.updateIconOffset);
|
if (this.$slots[pendant]) {
|
||||||
|
el.style.transform = `translateX(${place === 'suffix' ? '-' : ''}${
|
||||||
|
this.$el.querySelector(`.el-input-group__${pendant}`).offsetWidth
|
||||||
|
}px)`;
|
||||||
|
} else {
|
||||||
|
el.removeAttribute('style');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateIconOffset() {
|
||||||
|
this.calcIconOffset('prefix');
|
||||||
|
this.calcIconOffset('suffix');
|
||||||
|
},
|
||||||
|
clear() {
|
||||||
|
this.$emit('input', '');
|
||||||
|
this.$emit('change', '');
|
||||||
|
this.$emit('clear');
|
||||||
|
},
|
||||||
|
handlePasswordVisible() {
|
||||||
|
this.passwordVisible = !this.passwordVisible;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.focus();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
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)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.$on('inputSelect', this.select);
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.setNativeInputValue();
|
||||||
|
this.resizeTextarea();
|
||||||
|
this.updateIconOffset();
|
||||||
|
},
|
||||||
|
|
||||||
|
updated() {
|
||||||
|
this.$nextTick(this.updateIconOffset);
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -480,4 +480,30 @@ describe('Input', () => {
|
||||||
await waitImmediate();
|
await waitImmediate();
|
||||||
expect(inputElm4.classList.contains('is-exceed')).to.false;
|
expect(inputElm4.classList.contains('is-exceed')).to.false;
|
||||||
});
|
});
|
||||||
|
it('formatter Processing test', async() => {
|
||||||
|
vm = createVue({
|
||||||
|
template: `
|
||||||
|
<el-input
|
||||||
|
v-model="input"
|
||||||
|
placeholder="请输入内容"
|
||||||
|
:formatter="(value) => value + 'input'"
|
||||||
|
:parser="(value) => value.replace(/input/g, '')"
|
||||||
|
></el-input>
|
||||||
|
`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
input: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
let inputElm = vm.$el.querySelector('input');
|
||||||
|
expect(inputElm.getAttribute('placeholder')).to.equal('请输入内容');
|
||||||
|
let evt = document.createEvent('HTMLEvents');
|
||||||
|
evt = new Event('input', {'bubbles': true, 'cancelable': true});
|
||||||
|
inputElm.value = 'text';
|
||||||
|
inputElm.dispatchEvent(evt);
|
||||||
|
await waitImmediate();
|
||||||
|
expect(inputElm.value).to.equal('textinput');
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,7 +12,12 @@ export interface AutoSize {
|
||||||
/** Maximum rows to show */
|
/** Maximum rows to show */
|
||||||
maxRows: number
|
maxRows: number
|
||||||
}
|
}
|
||||||
|
export interface FormatterHandler {
|
||||||
|
/**
|
||||||
|
* @param value Current value of the text input
|
||||||
|
*/
|
||||||
|
(value: string | number): void
|
||||||
|
}
|
||||||
/** Input Component */
|
/** Input Component */
|
||||||
export declare class ElInput extends ElementUIComponent {
|
export declare class ElInput extends ElementUIComponent {
|
||||||
/** Type of input */
|
/** Type of input */
|
||||||
|
@ -90,6 +95,11 @@ export declare class ElInput extends ElementUIComponent {
|
||||||
/** Whether to show wordCount when setting maxLength */
|
/** Whether to show wordCount when setting maxLength */
|
||||||
showWordLimit: boolean
|
showWordLimit: boolean
|
||||||
|
|
||||||
|
/**Format value program */
|
||||||
|
formatter:FormatterHandler
|
||||||
|
|
||||||
|
/**Extract value program */
|
||||||
|
parser:FormatterHandler
|
||||||
/**
|
/**
|
||||||
* Focus the Input component
|
* Focus the Input component
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue