mirror of
https://github.com/element-plus/element-plus.git
synced 2025-02-23 11:59:34 +08:00
refactor(components): refactor input (#3805)
* refactor(input): rename * refactor(input): with-install * refactor(input): kebab-case * refactor(components): refactor input * fix: fix test import * refactor(components): [el-input] refactor export named * refactor: improve types * refactor: improve types
This commit is contained in:
parent
1e6914d453
commit
ffe6d251c6
@ -3,7 +3,7 @@ import { mount } from '@vue/test-utils'
|
||||
import installStyle from '@element-plus/test-utils/style-plugin'
|
||||
import Checkbox from '@element-plus/components/checkbox/src/checkbox.vue'
|
||||
import CheckboxGroup from '@element-plus/components/checkbox/src/checkbox-group.vue'
|
||||
import Input from '@element-plus/components/input/src/index.vue'
|
||||
import Input from '@element-plus/components/input'
|
||||
import Form from '../src/form.vue'
|
||||
import FormItem from '../src/form-item.vue'
|
||||
import type { VueWrapper } from '@vue/test-utils'
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { nextTick, ref } from 'vue'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { sleep, defineGetter } from '@element-plus/test-utils'
|
||||
import Input from '../src/index.vue'
|
||||
import Input from '../src/input.vue'
|
||||
|
||||
const _mount = (options) =>
|
||||
mount({
|
||||
@ -84,17 +84,17 @@ describe('Input.vue', () => {
|
||||
|
||||
const elCount = wrapper.find('.el-input__count-inner')
|
||||
expect(elCount.exists()).toBe(true)
|
||||
expect(elCount.text()).toBe('3/4')
|
||||
expect(elCount.text()).toBe('3 / 4')
|
||||
|
||||
vm.inputVal = '1👌3😄'
|
||||
await sleep()
|
||||
expect(nativeInput.value).toBe('1👌3😄')
|
||||
expect(elCount.text()).toBe('4/4')
|
||||
expect(elCount.text()).toBe('4 / 4')
|
||||
|
||||
vm.inputVal = '哈哈1👌3😄'
|
||||
await sleep()
|
||||
expect(nativeInput.value).toBe('哈哈1👌3😄')
|
||||
expect(elCount.text()).toBe('6/4')
|
||||
expect(elCount.text()).toBe('6 / 4')
|
||||
expect(vm.$el.classList.contains('is-exceed')).toBe(true)
|
||||
})
|
||||
|
||||
@ -113,12 +113,12 @@ describe('Input.vue', () => {
|
||||
|
||||
const elCount = wrapper.find('.el-input__count')
|
||||
expect(elCount.exists()).toBe(true)
|
||||
expect(elCount.text()).toBe('3/4')
|
||||
expect(elCount.text()).toBe('3 / 4')
|
||||
|
||||
vm.inputVal = '哈哈1👌3😄'
|
||||
await sleep()
|
||||
expect(nativeInput.value).toBe('哈哈1👌3😄')
|
||||
expect(elCount.text()).toBe('6/4')
|
||||
expect(elCount.text()).toBe('6 / 4')
|
||||
expect(vm.$el.classList.contains('is-exceed')).toBe(true)
|
||||
})
|
||||
})
|
||||
@ -343,7 +343,7 @@ describe('Input.vue', () => {
|
||||
ref.autosize.minRows = 5
|
||||
ref.resizeTextarea()
|
||||
// Atfer this textarea min-height (style) will change
|
||||
const nowMinHeight = ref.computedTextareaStyle.minHeight
|
||||
const nowMinHeight = ref.computedTextareaStyle[1].minHeight
|
||||
expect(originMinHeight).not.toEqual(nowMinHeight)
|
||||
})
|
||||
})
|
||||
|
@ -1,13 +1,8 @@
|
||||
import Input from './src/index.vue'
|
||||
import { withInstall } from '@element-plus/utils/with-install'
|
||||
|
||||
import type { App } from 'vue'
|
||||
import type { SFCWithInstall } from '@element-plus/utils/types'
|
||||
import Input from './src/input.vue'
|
||||
|
||||
Input.install = (app: App): void => {
|
||||
app.component(Input.name, Input)
|
||||
}
|
||||
export const ElInput = withInstall(Input)
|
||||
export default ElInput
|
||||
|
||||
const _Input = Input as SFCWithInstall<typeof Input>
|
||||
|
||||
export default _Input
|
||||
export const ElInput = _Input
|
||||
export * from './src/input'
|
||||
|
@ -1,4 +1,6 @@
|
||||
let hiddenTextarea: HTMLTextAreaElement
|
||||
import { isNumber } from '@element-plus/utils/util'
|
||||
|
||||
let hiddenTextarea: HTMLTextAreaElement | undefined = undefined
|
||||
|
||||
const HIDDEN_STYLE = `
|
||||
height:0 !important;
|
||||
@ -60,10 +62,10 @@ function calculateNodeStyling(targetElement: Element): NodeStyle {
|
||||
return { contextStyle, paddingSize, borderSize, boxSizing }
|
||||
}
|
||||
|
||||
export default function calcTextareaHeight(
|
||||
targetElement: HTMLInputElement,
|
||||
export function calcTextareaHeight(
|
||||
targetElement: HTMLTextAreaElement,
|
||||
minRows = 1,
|
||||
maxRows = null
|
||||
maxRows?: number
|
||||
): TextAreaHeight {
|
||||
if (!hiddenTextarea) {
|
||||
hiddenTextarea = document.createElement('textarea')
|
||||
@ -88,7 +90,7 @@ export default function calcTextareaHeight(
|
||||
hiddenTextarea.value = ''
|
||||
const singleRowHeight = hiddenTextarea.scrollHeight - paddingSize
|
||||
|
||||
if (minRows !== null) {
|
||||
if (isNumber(minRows)) {
|
||||
let minHeight = singleRowHeight * minRows
|
||||
if (boxSizing === 'border-box') {
|
||||
minHeight = minHeight + paddingSize + borderSize
|
||||
@ -96,7 +98,7 @@ export default function calcTextareaHeight(
|
||||
height = Math.max(minHeight, height)
|
||||
result.minHeight = `${minHeight}px`
|
||||
}
|
||||
if (maxRows !== null) {
|
||||
if (isNumber(maxRows)) {
|
||||
let maxHeight = singleRowHeight * maxRows
|
||||
if (boxSizing === 'border-box') {
|
||||
maxHeight = maxHeight + paddingSize + borderSize
|
||||
@ -105,7 +107,7 @@ export default function calcTextareaHeight(
|
||||
}
|
||||
result.height = `${height}px`
|
||||
hiddenTextarea.parentNode?.removeChild(hiddenTextarea)
|
||||
hiddenTextarea = null
|
||||
hiddenTextarea = undefined
|
||||
|
||||
return result
|
||||
}
|
97
packages/components/input/src/input.ts
Normal file
97
packages/components/input/src/input.ts
Normal file
@ -0,0 +1,97 @@
|
||||
import { isString } from '@vue/shared'
|
||||
import { useFormItemProps } from '@element-plus/hooks'
|
||||
import { buildProps, definePropType, mutable } from '@element-plus/utils/props'
|
||||
import { UPDATE_MODEL_EVENT } from '@element-plus/utils/constants'
|
||||
import type { StyleValue } from '@element-plus/utils/types'
|
||||
import type { ExtractPropTypes } from 'vue'
|
||||
|
||||
type AutoSize = { minRows?: number; maxRows?: number } | boolean
|
||||
|
||||
export const inputProps = buildProps({
|
||||
...useFormItemProps,
|
||||
modelValue: {
|
||||
type: definePropType<string | number | null | undefined>(undefined),
|
||||
default: '',
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'text',
|
||||
},
|
||||
resize: {
|
||||
type: String,
|
||||
values: ['none', 'both', 'horizontal', 'vertical'],
|
||||
},
|
||||
autosize: {
|
||||
type: definePropType<AutoSize>([Boolean, Object]),
|
||||
default: false,
|
||||
},
|
||||
autocomplete: {
|
||||
type: String,
|
||||
default: 'off',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
},
|
||||
form: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showPassword: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showWordLimit: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
suffixIcon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
prefixIcon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
},
|
||||
tabindex: {
|
||||
type: [Number, String],
|
||||
},
|
||||
validateEvent: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
inputStyle: {
|
||||
type: definePropType<StyleValue>([Object, Array, String]),
|
||||
default: () => mutable({} as const),
|
||||
},
|
||||
maxlength: {
|
||||
type: [Number, String],
|
||||
},
|
||||
} as const)
|
||||
export type InputProps = ExtractPropTypes<typeof inputProps>
|
||||
|
||||
export const inputEmits = {
|
||||
[UPDATE_MODEL_EVENT]: (value: string) => isString(value),
|
||||
input: (value: string) => isString(value),
|
||||
change: (value: string) => isString(value),
|
||||
focus: (evt: FocusEvent) => evt instanceof FocusEvent,
|
||||
blur: (evt: FocusEvent) => evt instanceof FocusEvent,
|
||||
clear: () => true,
|
||||
mouseleave: (evt: MouseEvent) => evt instanceof MouseEvent,
|
||||
mouseenter: (evt: MouseEvent) => evt instanceof MouseEvent,
|
||||
keydown: (evt: KeyboardEvent) => evt instanceof KeyboardEvent,
|
||||
compositionstart: (evt: CompositionEvent) => evt instanceof CompositionEvent,
|
||||
compositionupdate: (evt: CompositionEvent) => evt instanceof CompositionEvent,
|
||||
compositionend: (evt: CompositionEvent) => evt instanceof CompositionEvent,
|
||||
}
|
||||
export type InputEmits = typeof inputEmits
|
@ -16,17 +16,18 @@
|
||||
},
|
||||
$attrs.class,
|
||||
]"
|
||||
:style="$attrs.style"
|
||||
:style="containerStyle"
|
||||
@mouseenter="onMouseEnter"
|
||||
@mouseleave="onMouseLeave"
|
||||
>
|
||||
<!-- input -->
|
||||
<template v-if="type !== 'textarea'">
|
||||
<!-- 前置元素 -->
|
||||
<!-- prepend slot -->
|
||||
<div v-if="$slots.prepend" class="el-input-group__prepend">
|
||||
<slot name="prepend"></slot>
|
||||
<slot name="prepend" />
|
||||
</div>
|
||||
|
||||
<input
|
||||
v-if="type !== 'textarea'"
|
||||
ref="input"
|
||||
class="el-input__inner"
|
||||
v-bind="attrs"
|
||||
@ -47,72 +48,78 @@
|
||||
@change="handleChange"
|
||||
@keydown="handleKeydown"
|
||||
/>
|
||||
<!-- 前置内容 -->
|
||||
|
||||
<!-- prefix slot -->
|
||||
<span v-if="$slots.prefix || prefixIcon" class="el-input__prefix">
|
||||
<slot name="prefix"></slot>
|
||||
<slot name="prefix" />
|
||||
<i v-if="prefixIcon" :class="['el-input__icon', prefixIcon]"></i>
|
||||
</span>
|
||||
<!-- 后置内容 -->
|
||||
<span v-if="getSuffixVisible()" class="el-input__suffix">
|
||||
|
||||
<!-- suffix slot -->
|
||||
<span v-if="suffixVisible" class="el-input__suffix">
|
||||
<span class="el-input__suffix-inner">
|
||||
<template v-if="!showClear || !showPwdVisible || !isWordLimitVisible">
|
||||
<slot name="suffix"></slot>
|
||||
<slot name="suffix" />
|
||||
<i v-if="suffixIcon" :class="['el-input__icon', suffixIcon]"></i>
|
||||
</template>
|
||||
|
||||
<i
|
||||
v-if="showClear"
|
||||
class="el-input__icon el-icon-circle-close el-input__clear"
|
||||
@mousedown.prevent
|
||||
@click="clear"
|
||||
></i>
|
||||
/>
|
||||
|
||||
<i
|
||||
v-if="showPwdVisible"
|
||||
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 }}/{{ maxlength }}
|
||||
{{ textLength }} / {{ maxlength }}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<i
|
||||
v-if="validateState"
|
||||
:class="['el-input__icon', 'el-input__validateIcon', validateIcon]"
|
||||
></i>
|
||||
/>
|
||||
</span>
|
||||
<!-- 后置元素 -->
|
||||
|
||||
<!-- append slot -->
|
||||
<div v-if="$slots.append" class="el-input-group__append">
|
||||
<slot name="append"></slot>
|
||||
<slot name="append" />
|
||||
</div>
|
||||
</template>
|
||||
<textarea
|
||||
v-else
|
||||
ref="textarea"
|
||||
class="el-textarea__inner"
|
||||
v-bind="attrs"
|
||||
:tabindex="tabindex"
|
||||
:disabled="inputDisabled"
|
||||
:readonly="readonly"
|
||||
:autocomplete="autocomplete"
|
||||
:style="computedTextareaStyle"
|
||||
:aria-label="label"
|
||||
:placeholder="placeholder"
|
||||
@compositionstart="handleCompositionStart"
|
||||
@compositionupdate="handleCompositionUpdate"
|
||||
@compositionend="handleCompositionEnd"
|
||||
@input="handleInput"
|
||||
@focus="handleFocus"
|
||||
@blur="handleBlur"
|
||||
@change="handleChange"
|
||||
@keydown="handleKeydown"
|
||||
>
|
||||
</textarea>
|
||||
<span
|
||||
v-if="isWordLimitVisible && type === 'textarea'"
|
||||
class="el-input__count"
|
||||
>{{ textLength }}/{{ maxlength }}</span
|
||||
>
|
||||
|
||||
<!-- textarea -->
|
||||
<template v-else>
|
||||
<textarea
|
||||
ref="textarea"
|
||||
class="el-textarea__inner"
|
||||
v-bind="attrs"
|
||||
:tabindex="tabindex"
|
||||
:disabled="inputDisabled"
|
||||
:readonly="readonly"
|
||||
:autocomplete="autocomplete"
|
||||
:style="computedTextareaStyle"
|
||||
:aria-label="label"
|
||||
:placeholder="placeholder"
|
||||
@compositionstart="handleCompositionStart"
|
||||
@compositionupdate="handleCompositionUpdate"
|
||||
@compositionend="handleCompositionEnd"
|
||||
@input="handleInput"
|
||||
@focus="handleFocus"
|
||||
@blur="handleBlur"
|
||||
@change="handleChange"
|
||||
@keydown="handleKeydown"
|
||||
/>
|
||||
<span v-if="isWordLimitVisible" class="el-input__count">
|
||||
{{ textLength }} / {{ maxlength }}
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -130,143 +137,44 @@ import {
|
||||
onUpdated,
|
||||
} from 'vue'
|
||||
import { elFormKey, elFormItemKey } from '@element-plus/tokens'
|
||||
import { useAttrs } from '@element-plus/hooks'
|
||||
import { useAttrs, useFormItem } from '@element-plus/hooks'
|
||||
import {
|
||||
UPDATE_MODEL_EVENT,
|
||||
VALIDATE_STATE_MAP,
|
||||
} from '@element-plus/utils/constants'
|
||||
import { isObject, useGlobalConfig } from '@element-plus/utils/util'
|
||||
import { isObject } from '@element-plus/utils/util'
|
||||
import isServer from '@element-plus/utils/isServer'
|
||||
import { isKorean } from '@element-plus/utils/isDef'
|
||||
import { isValidComponentSize } from '@element-plus/utils/validators'
|
||||
import calcTextareaHeight from './calcTextareaHeight'
|
||||
import { calcTextareaHeight } from './calc-textarea-height'
|
||||
import { inputProps, inputEmits } from './input'
|
||||
|
||||
import type { PropType } from 'vue'
|
||||
import type { ElFormContext, ElFormItemContext } from '@element-plus/tokens'
|
||||
import type { ComponentSize } from '@element-plus/utils/types'
|
||||
import type { StyleValue } from '@element-plus/utils/types'
|
||||
|
||||
type AutosizeProp =
|
||||
| {
|
||||
minRows?: number
|
||||
maxRows?: number
|
||||
}
|
||||
| boolean
|
||||
type TargetElement = HTMLInputElement | HTMLTextAreaElement
|
||||
|
||||
const PENDANT_MAP = {
|
||||
suffix: 'append',
|
||||
prefix: 'prepend',
|
||||
}
|
||||
} as const
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ElInput',
|
||||
|
||||
inheritAttrs: false,
|
||||
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
default: '',
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'text',
|
||||
},
|
||||
size: {
|
||||
type: String as PropType<ComponentSize>,
|
||||
validator: isValidComponentSize,
|
||||
},
|
||||
resize: {
|
||||
type: String as PropType<'none' | 'both' | 'horizontal' | 'vertical'>,
|
||||
validator: (val: string) =>
|
||||
['none', 'both', 'horizontal', 'vertical'].includes(val),
|
||||
},
|
||||
autosize: {
|
||||
type: [Boolean, Object] as PropType<AutosizeProp>,
|
||||
default: false as AutosizeProp,
|
||||
},
|
||||
autocomplete: {
|
||||
type: String,
|
||||
default: 'off',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
},
|
||||
form: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showPassword: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showWordLimit: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
suffixIcon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
prefixIcon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
},
|
||||
tabindex: {
|
||||
type: [Number, String],
|
||||
},
|
||||
validateEvent: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
inputStyle: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
maxlength: {
|
||||
type: [Number, String],
|
||||
},
|
||||
},
|
||||
props: inputProps,
|
||||
emits: inputEmits,
|
||||
|
||||
emits: [
|
||||
UPDATE_MODEL_EVENT,
|
||||
'input',
|
||||
'change',
|
||||
'focus',
|
||||
'blur',
|
||||
'clear',
|
||||
'mouseleave',
|
||||
'mouseenter',
|
||||
'keydown',
|
||||
'compositionstart',
|
||||
'compositionupdate',
|
||||
'compositionend',
|
||||
],
|
||||
|
||||
setup(props, ctx) {
|
||||
const instance = getCurrentInstance()
|
||||
setup(props, { slots, emit, attrs: rawAttrs }) {
|
||||
const instance = getCurrentInstance()!
|
||||
const attrs = useAttrs()
|
||||
const $ELEMENT = useGlobalConfig()
|
||||
|
||||
const elForm = inject(elFormKey, {} as ElFormContext)
|
||||
const elFormItem = inject(elFormItemKey, {} as ElFormItemContext)
|
||||
const elForm = inject(elFormKey, undefined)
|
||||
const elFormItem = inject(elFormItemKey, undefined)
|
||||
|
||||
const input = ref(null)
|
||||
const textarea = ref(null)
|
||||
const { size: inputSize, disabled: inputDisabled } = useFormItem({})
|
||||
|
||||
const input = ref<HTMLInputElement>()
|
||||
const textarea = ref<HTMLTextAreaElement>()
|
||||
const focused = ref(false)
|
||||
const hovering = ref(false)
|
||||
const isComposing = ref(false)
|
||||
@ -274,59 +182,50 @@ export default defineComponent({
|
||||
const _textareaCalcStyle = shallowRef(props.inputStyle)
|
||||
|
||||
const inputOrTextarea = computed(() => input.value || textarea.value)
|
||||
const inputSize = computed(
|
||||
() => props.size || elFormItem.size || $ELEMENT.size
|
||||
)
|
||||
const needStatusIcon = computed(() => elForm.statusIcon)
|
||||
const validateState = computed(() => elFormItem.validateState || '')
|
||||
const needStatusIcon = computed(() => elForm?.statusIcon ?? false)
|
||||
const validateState = computed(() => elFormItem?.validateState || '')
|
||||
const validateIcon = computed(() => VALIDATE_STATE_MAP[validateState.value])
|
||||
const computedTextareaStyle = computed(() => ({
|
||||
...props.inputStyle,
|
||||
..._textareaCalcStyle.value,
|
||||
resize: props.resize,
|
||||
}))
|
||||
const inputDisabled = computed(() => props.disabled || elForm.disabled)
|
||||
const containerStyle = computed(() => rawAttrs.style as StyleValue)
|
||||
const computedTextareaStyle = computed<StyleValue>(() => [
|
||||
props.inputStyle,
|
||||
_textareaCalcStyle.value,
|
||||
{ resize: props.resize },
|
||||
])
|
||||
const nativeInputValue = computed(() =>
|
||||
props.modelValue === null || props.modelValue === undefined
|
||||
? ''
|
||||
: String(props.modelValue)
|
||||
)
|
||||
const showClear = computed(() => {
|
||||
return (
|
||||
const showClear = computed(
|
||||
() =>
|
||||
props.clearable &&
|
||||
!inputDisabled.value &&
|
||||
!props.readonly &&
|
||||
nativeInputValue.value &&
|
||||
!!nativeInputValue.value &&
|
||||
(focused.value || hovering.value)
|
||||
)
|
||||
})
|
||||
const showPwdVisible = computed(() => {
|
||||
return (
|
||||
)
|
||||
const showPwdVisible = computed(
|
||||
() =>
|
||||
props.showPassword &&
|
||||
!inputDisabled.value &&
|
||||
!props.readonly &&
|
||||
(!!nativeInputValue.value || focused.value)
|
||||
)
|
||||
})
|
||||
const isWordLimitVisible = computed(() => {
|
||||
return (
|
||||
)
|
||||
const isWordLimitVisible = computed(
|
||||
() =>
|
||||
props.showWordLimit &&
|
||||
props.maxlength &&
|
||||
!!props.maxlength &&
|
||||
(props.type === 'text' || props.type === 'textarea') &&
|
||||
!inputDisabled.value &&
|
||||
!props.readonly &&
|
||||
!props.showPassword
|
||||
)
|
||||
})
|
||||
const textLength = computed(() => {
|
||||
return Array.from(nativeInputValue.value).length
|
||||
})
|
||||
const inputExceed = computed(() => {
|
||||
// show exceed style if length of initial value greater then maxlength
|
||||
return (
|
||||
isWordLimitVisible.value && textLength.value > Number(props.maxlength)
|
||||
)
|
||||
})
|
||||
)
|
||||
const textLength = computed(() => Array.from(nativeInputValue.value).length)
|
||||
const inputExceed = computed(
|
||||
() =>
|
||||
// show exceed style if length of initial value greater then maxlength
|
||||
!!isWordLimitVisible.value && textLength.value > Number(props.maxlength)
|
||||
)
|
||||
|
||||
const resizeTextarea = () => {
|
||||
const { type, autosize } = props
|
||||
@ -337,11 +236,11 @@ export default defineComponent({
|
||||
const minRows = isObject(autosize) ? autosize.minRows : undefined
|
||||
const maxRows = isObject(autosize) ? autosize.maxRows : undefined
|
||||
_textareaCalcStyle.value = {
|
||||
...calcTextareaHeight(textarea.value, minRows, maxRows),
|
||||
...calcTextareaHeight(textarea.value!, minRows, maxRows),
|
||||
}
|
||||
} else {
|
||||
_textareaCalcStyle.value = {
|
||||
minHeight: calcTextareaHeight(textarea.value).minHeight,
|
||||
minHeight: calcTextareaHeight(textarea.value!).minHeight,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -352,8 +251,9 @@ export default defineComponent({
|
||||
input.value = nativeInputValue.value
|
||||
}
|
||||
|
||||
const calcIconOffset = (place) => {
|
||||
const calcIconOffset = (place: 'prefix' | 'suffix') => {
|
||||
const { el } = instance.vnode
|
||||
if (!el) return
|
||||
const elList: HTMLSpanElement[] = Array.from(
|
||||
el.querySelectorAll(`.el-input__${place}`)
|
||||
)
|
||||
@ -363,7 +263,7 @@ export default defineComponent({
|
||||
|
||||
const pendant = PENDANT_MAP[place]
|
||||
|
||||
if (ctx.slots[pendant]) {
|
||||
if (slots[pendant]) {
|
||||
target.style.transform = `translateX(${place === 'suffix' ? '-' : ''}${
|
||||
el.querySelector(`.el-input-group__${pendant}`).offsetWidth
|
||||
}px)`
|
||||
@ -377,8 +277,8 @@ export default defineComponent({
|
||||
calcIconOffset('suffix')
|
||||
}
|
||||
|
||||
const handleInput = (event) => {
|
||||
let { value } = event.target
|
||||
const handleInput = (event: Event) => {
|
||||
let { value } = event.target as TargetElement
|
||||
|
||||
// should not emit input during composition
|
||||
// see: https://github.com/ElemeFE/element/issues/10516
|
||||
@ -397,60 +297,60 @@ export default defineComponent({
|
||||
value = Array.from(value).slice(0, Number(sliceIndex)).join('')
|
||||
}
|
||||
|
||||
ctx.emit(UPDATE_MODEL_EVENT, value)
|
||||
ctx.emit('input', value)
|
||||
emit(UPDATE_MODEL_EVENT, value)
|
||||
emit('input', value)
|
||||
|
||||
// ensure native input value is controlled
|
||||
// see: https://github.com/ElemeFE/element/issues/12850
|
||||
nextTick(setNativeInputValue)
|
||||
}
|
||||
|
||||
const handleChange = (event) => {
|
||||
ctx.emit('change', event.target.value)
|
||||
const handleChange = (event: Event) => {
|
||||
emit('change', (event.target as TargetElement).value)
|
||||
}
|
||||
|
||||
const focus = () => {
|
||||
// see: https://github.com/ElemeFE/element/issues/18573
|
||||
nextTick(() => {
|
||||
inputOrTextarea.value.focus()
|
||||
inputOrTextarea.value?.focus()
|
||||
})
|
||||
}
|
||||
|
||||
const blur = () => {
|
||||
inputOrTextarea.value.blur()
|
||||
inputOrTextarea.value?.blur()
|
||||
}
|
||||
|
||||
const handleFocus = (event) => {
|
||||
const handleFocus = (event: FocusEvent) => {
|
||||
focused.value = true
|
||||
ctx.emit('focus', event)
|
||||
emit('focus', event)
|
||||
}
|
||||
|
||||
const handleBlur = (event) => {
|
||||
const handleBlur = (event: FocusEvent) => {
|
||||
focused.value = false
|
||||
ctx.emit('blur', event)
|
||||
emit('blur', event)
|
||||
if (props.validateEvent) {
|
||||
elFormItem.validate?.('blur')
|
||||
elFormItem?.validate?.('blur')
|
||||
}
|
||||
}
|
||||
|
||||
const select = () => {
|
||||
inputOrTextarea.value.select()
|
||||
inputOrTextarea.value?.select()
|
||||
}
|
||||
|
||||
const handleCompositionStart = (event: CompositionEvent) => {
|
||||
ctx.emit('compositionstart', event)
|
||||
emit('compositionstart', event)
|
||||
isComposing.value = true
|
||||
}
|
||||
|
||||
const handleCompositionUpdate = (event: CompositionEvent) => {
|
||||
ctx.emit('compositionupdate', event)
|
||||
emit('compositionupdate', event)
|
||||
const text = (event.target as HTMLInputElement)?.value
|
||||
const lastCharacter = text[text.length - 1] || ''
|
||||
isComposing.value = !isKorean(lastCharacter)
|
||||
}
|
||||
|
||||
const handleCompositionEnd = (event: CompositionEvent) => {
|
||||
ctx.emit('compositionend', event)
|
||||
emit('compositionend', event)
|
||||
if (isComposing.value) {
|
||||
isComposing.value = false
|
||||
handleInput(event)
|
||||
@ -458,10 +358,10 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
ctx.emit(UPDATE_MODEL_EVENT, '')
|
||||
ctx.emit('change', '')
|
||||
ctx.emit('clear')
|
||||
ctx.emit('input', '')
|
||||
emit(UPDATE_MODEL_EVENT, '')
|
||||
emit('change', '')
|
||||
emit('clear')
|
||||
emit('input', '')
|
||||
}
|
||||
|
||||
const handlePasswordVisible = () => {
|
||||
@ -469,23 +369,22 @@ export default defineComponent({
|
||||
focus()
|
||||
}
|
||||
|
||||
const getSuffixVisible = () => {
|
||||
return (
|
||||
ctx.slots.suffix ||
|
||||
props.suffixIcon ||
|
||||
const suffixVisible = computed(
|
||||
() =>
|
||||
!!slots.suffix ||
|
||||
!!props.suffixIcon ||
|
||||
showClear.value ||
|
||||
props.showPassword ||
|
||||
isWordLimitVisible.value ||
|
||||
(validateState.value && needStatusIcon.value)
|
||||
)
|
||||
}
|
||||
(!!validateState.value && needStatusIcon.value)
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
nextTick(resizeTextarea)
|
||||
if (props.validateEvent) {
|
||||
elFormItem.validate?.('change')
|
||||
elFormItem?.validate?.('change')
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -493,9 +392,7 @@ export default defineComponent({
|
||||
// native input value is set explicitly
|
||||
// do not use v-model / :value in template
|
||||
// see: https://github.com/ElemeFE/element/issues/14521
|
||||
watch(nativeInputValue, () => {
|
||||
setNativeInputValue()
|
||||
})
|
||||
watch(nativeInputValue, () => setNativeInputValue())
|
||||
|
||||
// when change between <input> and <textarea>,
|
||||
// update DOM dependent value and styles
|
||||
@ -521,18 +418,18 @@ export default defineComponent({
|
||||
nextTick(updateIconOffset)
|
||||
})
|
||||
|
||||
const onMouseLeave = (e) => {
|
||||
const onMouseLeave = (evt: MouseEvent) => {
|
||||
hovering.value = false
|
||||
ctx.emit('mouseleave', e)
|
||||
emit('mouseleave', evt)
|
||||
}
|
||||
|
||||
const onMouseEnter = (e) => {
|
||||
const onMouseEnter = (evt: MouseEvent) => {
|
||||
hovering.value = true
|
||||
ctx.emit('mouseenter', e)
|
||||
emit('mouseenter', evt)
|
||||
}
|
||||
|
||||
const handleKeydown = (e) => {
|
||||
ctx.emit('keydown', e)
|
||||
const handleKeydown = (evt: KeyboardEvent) => {
|
||||
emit('keydown', evt)
|
||||
}
|
||||
|
||||
return {
|
||||
@ -542,8 +439,8 @@ export default defineComponent({
|
||||
inputSize,
|
||||
validateState,
|
||||
validateIcon,
|
||||
containerStyle,
|
||||
computedTextareaStyle,
|
||||
resizeTextarea,
|
||||
inputDisabled,
|
||||
showClear,
|
||||
showPwdVisible,
|
||||
@ -553,6 +450,9 @@ export default defineComponent({
|
||||
inputExceed,
|
||||
passwordVisible,
|
||||
inputOrTextarea,
|
||||
suffixVisible,
|
||||
|
||||
resizeTextarea,
|
||||
handleInput,
|
||||
handleChange,
|
||||
handleFocus,
|
||||
@ -565,7 +465,6 @@ export default defineComponent({
|
||||
select,
|
||||
focus,
|
||||
blur,
|
||||
getSuffixVisible,
|
||||
onMouseLeave,
|
||||
onMouseEnter,
|
||||
handleKeydown,
|
@ -140,7 +140,11 @@ export function buildProp<
|
||||
: undefined
|
||||
|
||||
return {
|
||||
type: (type as any)?.[wrapperKey] || type,
|
||||
type:
|
||||
typeof type === 'object' &&
|
||||
Object.getOwnPropertySymbols(type).includes(wrapperKey)
|
||||
? type[wrapperKey]
|
||||
: type,
|
||||
required: !!required,
|
||||
default: defaultValue,
|
||||
validator: _validator,
|
||||
|
Loading…
Reference in New Issue
Block a user