Fix Input.TextArea cut text logic when maxLength configured.
							parent
							
								
									ab26180635
								
							
						
					
					
						commit
						d929217752
					
				|  | @ -33,6 +33,7 @@ export default defineComponent({ | |||
|     focused: PropTypes.looseBool, | ||||
|     bordered: PropTypes.looseBool.def(true), | ||||
|     triggerFocus: { type: Function as PropType<() => void> }, | ||||
|     hidden: Boolean, | ||||
|   }, | ||||
|   setup(props, { slots, attrs }) { | ||||
|     const containerRef = ref(); | ||||
|  | @ -91,6 +92,7 @@ export default defineComponent({ | |||
|         direction, | ||||
|         readonly, | ||||
|         bordered, | ||||
|         hidden, | ||||
|         addonAfter = slots.addonAfter, | ||||
|         addonBefore = slots.addonBefore, | ||||
|       } = props; | ||||
|  | @ -121,6 +123,7 @@ export default defineComponent({ | |||
|           class={affixWrapperCls} | ||||
|           style={attrs.style} | ||||
|           onMouseup={onInputMouseUp} | ||||
|           hidden={hidden} | ||||
|         > | ||||
|           {prefixNode} | ||||
|           {cloneElement(element, { | ||||
|  | @ -139,6 +142,7 @@ export default defineComponent({ | |||
|         addonAfter = slots.addonAfter?.(), | ||||
|         size, | ||||
|         direction, | ||||
|         hidden, | ||||
|       } = props; | ||||
|       // Not wrap when there is not addons | ||||
|       if (!hasAddon({ addonBefore, addonAfter })) { | ||||
|  | @ -169,7 +173,7 @@ export default defineComponent({ | |||
|       // Need another wrapper for changing display:table to display:inline-block | ||||
|       // and put style prop in wrapper | ||||
|       return ( | ||||
|         <span class={mergedGroupClassName} style={attrs.style}> | ||||
|         <span class={mergedGroupClassName} style={attrs.style} hidden={hidden}> | ||||
|           <span class={mergedWrapperClassName}> | ||||
|             {addonBeforeNode} | ||||
|             {cloneElement(labeledElement, { style: null })} | ||||
|  | @ -185,6 +189,7 @@ export default defineComponent({ | |||
|         allowClear, | ||||
|         direction, | ||||
|         bordered, | ||||
|         hidden, | ||||
|         addonAfter = slots.addonAfter, | ||||
|         addonBefore = slots.addonBefore, | ||||
|       } = props; | ||||
|  | @ -204,7 +209,7 @@ export default defineComponent({ | |||
|         }, | ||||
|       ); | ||||
|       return ( | ||||
|         <span class={affixWrapperCls} style={attrs.style}> | ||||
|         <span class={affixWrapperCls} style={attrs.style} hidden={hidden}> | ||||
|           {cloneElement(element, { | ||||
|             style: null, | ||||
|             value, | ||||
|  |  | |||
|  | @ -24,6 +24,26 @@ function fixEmojiLength(value: string, maxLength: number) { | |||
|   return [...(value || '')].slice(0, maxLength).join(''); | ||||
| } | ||||
| 
 | ||||
| function setTriggerValue( | ||||
|   isCursorInEnd: boolean, | ||||
|   preValue: string, | ||||
|   triggerValue: string, | ||||
|   maxLength: number, | ||||
| ) { | ||||
|   let newTriggerValue = triggerValue; | ||||
|   if (isCursorInEnd) { | ||||
|     // 光标在尾部,直接截断 | ||||
|     newTriggerValue = fixEmojiLength(triggerValue, maxLength!); | ||||
|   } else if ( | ||||
|     [...(preValue || '')].length < triggerValue.length && | ||||
|     [...(triggerValue || '')].length > maxLength! | ||||
|   ) { | ||||
|     // 光标在中间,如果最后的值超过最大值,则采用原先的值 | ||||
|     newTriggerValue = preValue; | ||||
|   } | ||||
|   return newTriggerValue; | ||||
| } | ||||
| 
 | ||||
| export default defineComponent({ | ||||
|   name: 'ATextarea', | ||||
|   inheritAttrs: false, | ||||
|  | @ -40,6 +60,40 @@ export default defineComponent({ | |||
|     // Max length value | ||||
|     const hasMaxLength = computed(() => Number(props.maxlength) > 0); | ||||
|     const compositing = ref(false); | ||||
| 
 | ||||
|     const oldCompositionValueRef = ref<string>(); | ||||
|     const oldSelectionStartRef = ref<number>(0); | ||||
|     const onInternalCompositionStart = (e: CompositionEvent) => { | ||||
|       compositing.value = true; | ||||
|       // 拼音输入前保存一份旧值 | ||||
|       oldCompositionValueRef.value = mergedValue.value as string; | ||||
|       // 保存旧的光标位置 | ||||
|       oldSelectionStartRef.value = (e.currentTarget as any).selectionStart; | ||||
|       emit('compositionstart', e); | ||||
|     }; | ||||
| 
 | ||||
|     const onInternalCompositionEnd = (e: CompositionEvent) => { | ||||
|       compositing.value = false; | ||||
|       let triggerValue = (e.currentTarget as any).value; | ||||
|       if (hasMaxLength.value) { | ||||
|         const isCursorInEnd = | ||||
|           oldSelectionStartRef.value >= props.maxlength + 1 || | ||||
|           oldSelectionStartRef.value === oldCompositionValueRef.value?.length; | ||||
|         triggerValue = setTriggerValue( | ||||
|           isCursorInEnd, | ||||
|           oldCompositionValueRef.value as string, | ||||
|           triggerValue, | ||||
|           props.maxlength, | ||||
|         ); | ||||
|       } | ||||
|       // Patch composition onChange when value changed | ||||
|       if (triggerValue !== mergedValue.value) { | ||||
|         setValue(triggerValue); | ||||
|         resolveOnChange(e.currentTarget as any, e, triggerChange, triggerValue); | ||||
|       } | ||||
| 
 | ||||
|       emit('compositionend', e); | ||||
|     }; | ||||
|     const instance = getCurrentInstance(); | ||||
|     watch( | ||||
|       () => props.value, | ||||
|  | @ -103,12 +157,24 @@ export default defineComponent({ | |||
|     }; | ||||
| 
 | ||||
|     const handleChange = (e: Event) => { | ||||
|       const { value, composing } = e.target as any; | ||||
|       compositing.value = (e as any).isComposing || composing; | ||||
|       if ((compositing.value && props.lazy) || stateValue.value === value) return; | ||||
|       let triggerValue = (e.currentTarget as any).value; | ||||
|       const { composing } = e.target as any; | ||||
|       let triggerValue = (e.target as any).value; | ||||
|       compositing.value = !!((e as any).isComposing || composing); | ||||
|       if ((compositing.value && props.lazy) || stateValue.value === triggerValue) return; | ||||
| 
 | ||||
|       if (hasMaxLength.value) { | ||||
|         triggerValue = fixEmojiLength(triggerValue, props.maxlength!); | ||||
|         // 1. 复制粘贴超过maxlength的情况 2.未超过maxlength的情况 | ||||
|         const target = e.target as any; | ||||
|         const isCursorInEnd = | ||||
|           target.selectionStart >= props.maxlength! + 1 || | ||||
|           target.selectionStart === triggerValue.length || | ||||
|           !target.selectionStart; | ||||
|         triggerValue = setTriggerValue( | ||||
|           isCursorInEnd, | ||||
|           mergedValue.value as string, | ||||
|           triggerValue, | ||||
|           props.maxlength!, | ||||
|         ); | ||||
|       } | ||||
|       resolveOnChange(e.currentTarget as any, e, triggerChange, triggerValue); | ||||
|       setValue(triggerValue); | ||||
|  | @ -132,6 +198,8 @@ export default defineComponent({ | |||
|         onChange: handleChange, | ||||
|         onBlur, | ||||
|         onKeydown: handleKeyDown, | ||||
|         onCompositionstart: onInternalCompositionStart, | ||||
|         onCompositionend: onInternalCompositionEnd, | ||||
|       }; | ||||
|       if (props.valueModifiers?.lazy) { | ||||
|         delete resizeProps.onInput; | ||||
|  | @ -172,7 +240,7 @@ export default defineComponent({ | |||
|       mergedValue.value = val; | ||||
|     }); | ||||
|     return () => { | ||||
|       const { maxlength, bordered = true } = props; | ||||
|       const { maxlength, bordered = true, hidden } = props; | ||||
|       const { style, class: customClass } = attrs; | ||||
| 
 | ||||
|       const inputProps: any = { | ||||
|  | @ -204,6 +272,7 @@ export default defineComponent({ | |||
|         } | ||||
|         textareaNode = ( | ||||
|           <div | ||||
|             hidden={hidden} | ||||
|             class={classNames( | ||||
|               `${prefixCls.value}-textarea`, | ||||
|               { | ||||
|  |  | |||
|  | @ -17,6 +17,9 @@ For multi-line input. | |||
| </docs> | ||||
| <template> | ||||
|   <a-textarea v-model:value="value" placeholder="Basic usage" :rows="4" /> | ||||
|   <br /> | ||||
|   <br /> | ||||
|   <a-textarea :rows="4" placeholder="maxLength is 6" :maxlength="6" /> | ||||
| </template> | ||||
| <script lang="ts"> | ||||
| import { defineComponent, ref } from 'vue'; | ||||
|  |  | |||
|  | @ -72,6 +72,7 @@ const inputProps = { | |||
|   onInput: PropTypes.func, | ||||
|   'onUpdate:value': PropTypes.func, | ||||
|   valueModifiers: Object, | ||||
|   hidden: Boolean, | ||||
| }; | ||||
| export default inputProps; | ||||
| export type InputProps = Partial<ExtractPropTypes<typeof inputProps>>; | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 tangjinzhou
						tangjinzhou