diff --git a/src/Input.tsx b/src/Input.tsx index abce2a9..5b7aa82 100644 --- a/src/Input.tsx +++ b/src/Input.tsx @@ -98,18 +98,31 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => { | React.CompositionEvent<HTMLInputElement>, currentValue: string, ) => { - let cutValue = currentValue; + setValue(currentValue); + if (inputRef.current) { + resolveOnChange(inputRef.current, e, onChange, currentValue); + } + }; + React.useEffect(() => { + if (selection) { + inputRef.current?.setSelectionRange(...selection); + } + }, [selection]); + + const onInternalFormatter = (currentValue: string) => { + let cutValue = currentValue; + let isExceed = false; if ( !compositionRef.current && countConfig.exceedFormatter && countConfig.max && countConfig.strategy(currentValue) > countConfig.max ) { - cutValue = countConfig.exceedFormatter(currentValue, { - max: countConfig.max, + isExceed = true; + cutValue = countConfig.exceedFormatter!(currentValue, { + max: countConfig.max!, }); - if (currentValue !== cutValue) { setSelection([ inputRef.current?.selectionStart || 0, @@ -117,28 +130,25 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => { ]); } } - setValue(cutValue); - - if (inputRef.current) { - resolveOnChange(inputRef.current, e, onChange, cutValue); - } + return { + cutValue, + isExceed, + }; }; - React.useEffect(() => { - if (selection) { - inputRef.current?.setSelectionRange(...selection); - } - }, [selection]); - const onInternalChange: React.ChangeEventHandler<HTMLInputElement> = (e) => { - triggerChange(e, e.target.value); + const { cutValue } = onInternalFormatter(e.target.value); + triggerChange(e, cutValue); }; const onInternalCompositionEnd = ( e: React.CompositionEvent<HTMLInputElement>, ) => { compositionRef.current = false; - triggerChange(e, e.currentTarget.value); + const { cutValue, isExceed } = onInternalFormatter(e.currentTarget.value); + if (isExceed) { + triggerChange(e, cutValue); + } onCompositionEnd?.(e); }; diff --git a/tests/count.test.tsx b/tests/count.test.tsx index a6c22e4..ccee16e 100644 --- a/tests/count.test.tsx +++ b/tests/count.test.tsx @@ -109,6 +109,45 @@ describe('Input.Count', () => { expect(setSelectionRange).toHaveBeenCalledWith(2, 2); }); + it('input using the input method should trigger onChange once', () => { + const onChange = jest.fn(); + const { container } = render(<Input onChange={onChange} />); + const input = container.querySelector('input')!; + fireEvent.compositionStart(input); + fireEvent.compositionUpdate(input, { data: '你' }); + fireEvent.compositionEnd(input, { data: '你好' }); + fireEvent.input(input, { target: { value: '你好' } }); + expect(onChange).toHaveBeenCalledTimes(1); + }); + + it('using the input method to enter the cropped content should trigger onChange twice', () => { + const onChange = jest.fn(); + const onCompositionEnd = jest.fn(); + const { container } = render( + <Input + count={{ + show: true, + max: 3, + exceedFormatter: (val, { max }) => + getSegments(val) + .filter((seg) => seg.index + seg.segment.length <= max) + .map((seg) => seg.segment) + .join(''), + }} + onChange={onChange} + onCompositionEnd={onCompositionEnd} + />, + ); + const input = container.querySelector('input')!; + fireEvent.compositionStart(input); + fireEvent.compositionUpdate(input, { target: { value: '你' } }); + fireEvent.compositionUpdate(input, { target: { value: '你好' } }); + fireEvent.compositionUpdate(input, { target: { value: '你好世' } }); + fireEvent.compositionUpdate(input, { target: { value: '你好世界' } }); + fireEvent.compositionEnd(input, { target: { value: '你好世界' } }); + expect(input?.value).toEqual('你好世'); + }); + describe('cls', () => { it('raw', () => { const { container } = render(