Browse Source

fix: Fixed the problem that when maxLength and getValueLength are set… (#2859)

* fix: Fixed the problem that when maxLength and getValueLength are set at the same time in the Input component, the Chinese input will be truncated before the input is completed

* fix: Fixed the problem that when maxLength and getValueLength are set at the same time in the Input component, the Chinese input will be truncated before the input is completed

* chore: remove example

* fix: Fixed the problem that when maxLength and getValueLength are set at the same time in the TextArea component, the chinese input will be truncated before the input is completed
YyumeiZhang 4 months ago
parent
commit
b56fa2b1dc

+ 43 - 8
packages/semi-foundation/input/foundation.ts

@@ -36,6 +36,7 @@ class InputFoundation extends BaseFoundation<InputAdapter> {
     }
 
     _timer: number | null;
+    compositionEnter: boolean = false;
 
     constructor(adapter: InputAdapter) {
         super({ ...InputFoundation.inputDefaultAdapter, ...adapter });
@@ -55,28 +56,62 @@ class InputFoundation extends BaseFoundation<InputAdapter> {
     }
 
     handleChange(value: any, e: any) {
-        const { maxLength, minLength, getValueLength } = this._adapter.getProps();
         let nextValue = value;
-        if (maxLength && isFunction(getValueLength)) {
-            nextValue = this.handleVisibleMaxLength(value);
+        if (!this.compositionEnter) {
+            nextValue = this.getNextValue(nextValue);
+        }
+        this.changeInput(nextValue, e);
+    }
+
+    getNextValue = (value: any) => {
+        const { maxLength, minLength, getValueLength } = this._adapter.getProps();
+        if (!isFunction(getValueLength)) {
+            return value;
+        }
+        if (maxLength) {
+            return this.handleVisibleMaxLength(value);
         }
-        if (minLength && isFunction(getValueLength)) {
-            this.handleVisibleMinLength(nextValue);
+        if (minLength) {
+            this.handleVisibleMinLength(value);
         }
+        return value;
+    }
+
+    changeInput = (value: any, e: any) => {
         if (this._isControlledComponent()) {
             /**
              * If it is a controlled component, directly notify the caller of the modified value.
              * Truncate the input value from the input box if the input value exceeds the maximum length limit.
              *  Even in controlled components, characters that exceed the length limit cannot be entered through the input box.
              */
-            this._adapter.notifyChange(nextValue, e);
+            this._adapter.notifyChange(value, e);
         } else {
-            this._adapter.setValue(nextValue);
-            this._adapter.notifyChange(nextValue, e);
+            this._adapter.setValue(value);
+            this._adapter.notifyChange(value, e);
             // this.checkAllowClear(value);
         }
     }
 
+    handleCompositionStart = () => {
+        this.compositionEnter = true;
+    }
+
+    handleCompositionEnd = (e: any) => {
+        const value = e.target.value;
+        this.compositionEnter = false;
+        const { getValueLength, maxLength, minLength } = this.getProps();
+        if (!isFunction(getValueLength)) {
+            return;
+        }
+        if (maxLength) {
+            const nextValue = this.handleVisibleMaxLength(value);
+            nextValue !== value && this.changeInput(nextValue, e);
+        }
+        if (minLength) {
+            this.handleVisibleMinLength(value);
+        } 
+    }
+
     /**
      * Modify minLength to trigger browser check for minimum length
      * Controlled mode is not checked

+ 45 - 8
packages/semi-foundation/input/textareaFoundation.ts

@@ -42,6 +42,8 @@ export default class TextAreaFoundation extends BaseFoundation<TextAreaAdapter>
         };
     }
 
+    compositionEnter: boolean = false;
+
     constructor(adapter: TextAreaAdapter) {
         super({
             ...TextAreaFoundation.textAreaDefaultAdapter,
@@ -58,18 +60,53 @@ export default class TextAreaFoundation extends BaseFoundation<TextAreaAdapter>
     handleChange(value: string, e: any) {
         const { maxLength, minLength, getValueLength } = this._adapter.getProps();
         let nextValue = value;
-        if (maxLength && isFunction(getValueLength)) {
-            nextValue = this.handleVisibleMaxLength(value);
-        }
-        if (minLength && isFunction(getValueLength)) {
-            this.handleVisibleMinLength(nextValue);
+        if (!this.compositionEnter) {
+            nextValue = this.getNextValue(nextValue);
         }
+        this._changeValue(nextValue, e);
+    }
+
+    _changeValue = (value: any, e: any) => {
         if (this._isControlledComponent()) {
-            this._adapter.notifyChange(nextValue, e);
+            this._adapter.notifyChange(value, e);
         } else {
-            this._adapter.setValue(nextValue);
-            this._adapter.notifyChange(nextValue, e);
+            this._adapter.setValue(value);
+            this._adapter.notifyChange(value, e);
+        }
+    }
+
+    getNextValue = (value: any) => {
+        const { maxLength, minLength, getValueLength } = this._adapter.getProps();
+        if (!isFunction(getValueLength)) {
+            return value;
+        }
+        if (maxLength) {
+            return this.handleVisibleMaxLength(value);
+        }
+        if (minLength) {
+            this.handleVisibleMinLength(value);
+        }
+        return value;
+    }
+
+    handleCompositionStart = () => {
+        this.compositionEnter = true;
+    }
+
+    handleCompositionEnd = (e: any) => {
+        this.compositionEnter = false;
+        const { getValueLength, maxLength, minLength } = this.getProps();
+        if (!isFunction(getValueLength)) {
+            return;
+        }
+        const value = e.target.value;
+        if (maxLength) {
+            const nextValue = this.handleVisibleMaxLength(value);
+            nextValue !== value && this._changeValue(nextValue, e);
         }
+        if (minLength) {
+            this.handleVisibleMinLength(value);
+        } 
     }
 
     /**

+ 0 - 1
packages/semi-ui/input/_story/input.stories.jsx

@@ -1067,4 +1067,3 @@ export const FixTextAreaAutoFocus = () => {
     </div>
   )
 };
-

+ 2 - 0
packages/semi-ui/input/index.tsx

@@ -503,6 +503,8 @@ class Input extends BaseComponent<InputProps, InputState> {
             onKeyUp: e => this.foundation.handleKeyUp(e),
             onKeyDown: e => this.foundation.handleKeyDown(e),
             onKeyPress: e => this.foundation.handleKeyPress(e),
+            onCompositionStart: this.foundation.handleCompositionStart,
+            onCompositionEnd: this.foundation.handleCompositionEnd,
             value: inputValue,
         };
         if (!isFunction(getValueLength)) {

+ 2 - 0
packages/semi-ui/input/textarea.tsx

@@ -301,6 +301,8 @@ class TextArea extends BaseComponent<TextAreaProps, TextAreaState> {
             onBlur: (e: React.FocusEvent<HTMLTextAreaElement>) => this.foundation.handleBlur(e.nativeEvent),
             onKeyDown: (e: React.KeyboardEvent<HTMLTextAreaElement>) => this.foundation.handleKeyDown(e),
             value: value === null || value === undefined ? '' : value,
+            onCompositionStart: this.foundation.handleCompositionStart,
+            onCompositionEnd: this.foundation.handleCompositionEnd,
         };
         if (!isFunction(getValueLength)) {
             (itemProps as any).maxLength = maxLength;