From ab77eafdb1912def6c8bea6c8d28f3629a426636 Mon Sep 17 00:00:00 2001 From: btea <2356281422@qq.com> Date: Wed, 15 Mar 2023 09:53:49 +0800 Subject: [PATCH] feat(components): [input] add count-graphemes --- docs/en-US/component/input.md | 1 + packages/components/input/src/input.ts | 6 ++++ packages/components/input/src/input.vue | 42 ++++++++++++++++++++----- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/docs/en-US/component/input.md b/docs/en-US/component/input.md index c17eaf4304..9e169cc341 100644 --- a/docs/en-US/component/input.md +++ b/docs/en-US/component/input.md @@ -128,6 +128,7 @@ input/length-limiting | 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, only works when `type` is not 'textarea' | ^[boolean] | false | +| count-graphemes | Count graphemes of input value. If it's set, native maxlength and minlength won't be used. | ^[Function]`(value: string) => number` | — | | formatter | specifies the format of the value presented input.(only works when `type` is 'text') | ^[Function]`(value: string \| number) => string` | — | | parser | specifies the value extracted from formatter input.(only works when `type` is 'text') | ^[Function]`(value: string) => string` | — | | show-password | whether to show toggleable password input | ^[boolean] | false | diff --git a/packages/components/input/src/input.ts b/packages/components/input/src/input.ts index bd52286cae..09ab8595c3 100644 --- a/packages/components/input/src/input.ts +++ b/packages/components/input/src/input.ts @@ -166,6 +166,12 @@ export const inputProps = buildProps({ type: definePropType([Object, Array, String]), default: () => mutable({} as const), }, + /** + * @description Count graphemes of input value. If it's set, native maxlength and minlength won't be used. + */ + countGraphemes: { + type: Function, + }, } as const) export type InputProps = ExtractPropTypes diff --git a/packages/components/input/src/input.vue b/packages/components/input/src/input.vue index 1b8d1bbd6a..e631e75314 100644 --- a/packages/components/input/src/input.vue +++ b/packages/components/input/src/input.vue @@ -80,7 +80,7 @@ - {{ textLength }} / {{ attrs.maxlength }} + {{ textLength }} / {{ maxlength }} - {{ textLength }} / {{ attrs.maxlength }} + {{ textLength }} / {{ maxlength }} @@ -168,6 +168,7 @@ import { NOOP, ValidateComponentsMap, debugWarn, + isFunction, isKorean, isObject, } from '@element-plus/utils' @@ -224,9 +225,20 @@ const wrapperKls = computed(() => [ const attrs = useAttrs({ excludeKeys: computed(() => { - return Object.keys(containerAttrs.value) + const _attrs = Object.keys(containerAttrs.value) + if (props.countGraphemes) { + _attrs.push('maxlength', 'minlength') + } + return _attrs }), }) +const maxlength = computed(() => { + if (props.showWordLimit && rawAttrs.maxlength) { + return rawAttrs.maxlength + } + return 0 +}) + const { form, formItem } = useFormItem() const { inputId } = useFormItemInputId(props, { formItemContext: formItem, @@ -245,6 +257,7 @@ const isComposing = ref(false) const passwordVisible = ref(false) const countStyle = ref() const textareaCalcStyle = shallowRef(props.inputStyle) +const saveValue = ref('') const _ref = computed(() => input.value || textarea.value) @@ -287,18 +300,26 @@ const showPwdVisible = computed( const isWordLimitVisible = computed( () => props.showWordLimit && - !!attrs.value.maxlength && + !!maxlength.value && (props.type === 'text' || props.type === 'textarea') && !inputDisabled.value && !props.readonly && !props.showPassword ) -const textLength = computed(() => nativeInputValue.value.length) +const textLength = computed(() => { + if ( + props.countGraphemes && + isFunction(props.countGraphemes) && + props.showWordLimit + ) { + return props.countGraphemes(nativeInputValue.value) + } + return nativeInputValue.value.length +}) const inputExceed = computed( () => // show exceed style if length of initial value greater then maxlength - !!isWordLimitVisible.value && - textLength.value > Number(attrs.value.maxlength) + !!isWordLimitVisible.value && textLength.value > Number(maxlength.value) ) const suffixVisible = computed( () => @@ -356,6 +377,12 @@ const handleInput = async (event: Event) => { value = props.formatter(value) } + if (props.countGraphemes && isFunction(props.countGraphemes)) { + const graphemes = props.countGraphemes(value) + if (graphemes > Number(maxlength.value)) { + value = saveValue.value + } + } // should not emit input during composition // see: https://github.com/ElemeFE/element/issues/10516 if (isComposing.value) return @@ -366,6 +393,7 @@ const handleInput = async (event: Event) => { setNativeInputValue() return } + saveValue.value = value emit(UPDATE_MODEL_EVENT, value) emit('input', value)