Skip to content

Commit bc32ada

Browse files
authored
feat: enhancement input events (#49)
* feat: enhancement input events * docs: add jsdoc * fix: compose event not work
1 parent f00132c commit bc32ada

File tree

2 files changed

+55
-11
lines changed

2 files changed

+55
-11
lines changed

packages/sqi-web/src/input/Input.tsx

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import React, { forwardRef, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
2-
import type { FocusEvent, ReactNode } from 'react';
32
import clsx from 'clsx';
43
import { useMergeProps, useMergeState } from '@sqi-ui/hooks';
54
import { isFunction, isNumber, isObject, isString, isUndefined } from '@sqi-ui/utils';
@@ -58,14 +57,20 @@ const Input = forwardRef<InputRef, InputProps>((baseProps, ref) => {
5857
visibilityToggle,
5958
maxLength,
6059
tips,
60+
composing,
61+
onKeyDown,
6162
onFocus,
6263
onBlur,
6364
onChange,
65+
onEnter,
66+
onCompositionStart,
67+
onCompositionEnd,
6468
...restProps
6569
} = useMergeProps(baseProps, defaultProps, componentConfig?.Input);
6670

6771
const wrapperRef = useRef<HTMLDivElement>(null);
6872
const inputRef = useRef<HTMLInputElement>(null);
73+
const composingRef = useRef(false);
6974

7075
useImperativeHandle(ref, () => ({
7176
currentElement: wrapperRef.current,
@@ -75,21 +80,39 @@ const Input = forwardRef<InputRef, InputProps>((baseProps, ref) => {
7580
select: () => inputRef.current?.select(),
7681
}));
7782

78-
// =========== Input Focus ============
83+
// =========== Input Events Handler ============
7984
const [isFocused, toggleIsFocused] = useState(false);
8085

81-
const internalFocus = (e: FocusEvent<HTMLInputElement, Element>) => {
86+
const internalFocus = (e: React.FocusEvent<HTMLInputElement, Element>) => {
8287
if (disabled || readOnly) return;
8388
toggleIsFocused(true);
8489
onFocus?.(e);
8590
};
8691

87-
const internalBlur = (e: FocusEvent<HTMLInputElement, Element>) => {
92+
const internalBlur = (e: React.FocusEvent<HTMLInputElement, Element>) => {
8893
if (disabled || readOnly) return;
8994
toggleIsFocused(false);
9095
onBlur?.(e);
9196
};
9297

98+
const internalKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
99+
e.key === 'Enter' && onEnter?.(e);
100+
onKeyDown?.(e);
101+
};
102+
103+
const internalCompositionStart = (e: React.CompositionEvent<HTMLInputElement>) => {
104+
if (composing) composingRef.current = true;
105+
onCompositionStart?.(e);
106+
};
107+
108+
const internalCompositionEnd = (e: React.CompositionEvent<HTMLInputElement>) => {
109+
if (composingRef.current) {
110+
composingRef.current = false;
111+
handleChange(e);
112+
}
113+
onCompositionEnd?.(e);
114+
};
115+
93116
// =========== Input State ============
94117
const mergedMaxLength = isNumber(maxLength) ? maxLength : maxLength?.length;
95118
const mergedErrorOnly = isNumber(maxLength) ? false : maxLength?.errorOnly;
@@ -100,10 +123,10 @@ const Input = forwardRef<InputRef, InputProps>((baseProps, ref) => {
100123
const formatValue = formatValueToString(innerValue, mergedMaxLength, mergedErrorOnly);
101124
const isErrorLength = isNumber(mergedMaxLength) ? formatValue.length > mergedMaxLength : false;
102125

103-
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
104-
const { value } = e.target;
126+
const handleChange = (e: React.ChangeEvent<HTMLInputElement> | React.CompositionEvent<HTMLInputElement>) => {
127+
const value = e.currentTarget.value;
105128
setInnerValue(value);
106-
onChange?.(value, e);
129+
!composingRef.current && onChange?.(value, e);
107130
};
108131

109132
const handleClickInputWrapper = () => {
@@ -177,7 +200,7 @@ const Input = forwardRef<InputRef, InputProps>((baseProps, ref) => {
177200
// =========== Input Suffix ============
178201
const isPassword = type === 'password';
179202

180-
const suffixBaseElement: ReactNode = useMemo(() => {
203+
const suffixBaseElement: React.ReactNode = useMemo(() => {
181204
if (!isPassword) return suffix;
182205
if (isObject(visibilityToggle) && isFunction(visibilityToggle.renderIcon)) {
183206
return visibilityToggle.renderIcon(renderType === 'text');
@@ -191,7 +214,7 @@ const Input = forwardRef<InputRef, InputProps>((baseProps, ref) => {
191214

192215
// fix: rerender 会重新渲染 `InputGroupWrapper`,导致无法正常聚焦失焦
193216
const InputGroupWrapper = useMemo(() => {
194-
return function GroupWrapper({ children }: { children: ReactNode }) {
217+
return function GroupWrapper({ children }: { children: React.ReactNode }) {
195218
const hasCoreWrapper = addonBefore || addonAfter;
196219

197220
let content = children;
@@ -267,6 +290,9 @@ const Input = forwardRef<InputRef, InputProps>((baseProps, ref) => {
267290
onChange={handleChange}
268291
onFocus={internalFocus}
269292
onBlur={internalBlur}
293+
onKeyDown={internalKeyDown}
294+
onCompositionStart={internalCompositionStart}
295+
onCompositionEnd={internalCompositionEnd}
270296
/>
271297
{clearElement}
272298
{suffixElement}

packages/sqi-web/src/input/type.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import type { CompositionEvent, FormEvent, InputHTMLAttributes, MouseEvent, ReactNode } from 'react';
1+
import type {
2+
ChangeEvent,
3+
CompositionEvent,
4+
InputHTMLAttributes,
5+
KeyboardEventHandler,
6+
MouseEvent,
7+
ReactNode,
8+
} from 'react';
29
import type { LiteralUnion } from '../_util/type';
310
import type { ConfigSize } from '../config-provider';
411

@@ -130,5 +137,16 @@ export interface InputProps
130137
/**
131138
* @description 输入内容变化时触发
132139
*/
133-
onChange?: (value: string, e: FormEvent<HTMLInputElement> | MouseEvent | CompositionEvent<HTMLDivElement>) => void;
140+
onChange?: (
141+
value: string,
142+
e: ChangeEvent<HTMLInputElement> | MouseEvent | CompositionEvent<HTMLInputElement>,
143+
) => void;
144+
/**
145+
* @description 键盘回车事件
146+
*/
147+
onEnter?: KeyboardEventHandler<HTMLInputElement>;
148+
/**
149+
* @description IME 输入中文的过程中是否使用合成事件避免不必要的触发 onChange
150+
*/
151+
composing?: boolean;
134152
}

0 commit comments

Comments
 (0)