|
11 | 11 | */ |
12 | 12 |
|
13 | 13 | import {AriaTextFieldProps} from '@react-types/textfield'; |
| 14 | +import {chain, filterDOMProps, getOwnerWindow, mergeProps, useFormReset} from '@react-aria/utils'; |
14 | 15 | import {DOMAttributes, ValidationResult} from '@react-types/shared'; |
15 | | -import {filterDOMProps, getOwnerWindow, mergeProps, useFormReset} from '@react-aria/utils'; |
16 | 16 | import React, { |
17 | 17 | ChangeEvent, |
18 | 18 | HTMLAttributes, |
19 | 19 | type JSX, |
20 | 20 | LabelHTMLAttributes, |
21 | 21 | RefObject, |
| 22 | + useCallback, |
22 | 23 | useEffect, |
| 24 | + useRef, |
23 | 25 | useState |
24 | 26 | } from 'react'; |
25 | 27 | import {useControlledState} from '@react-stately/utils'; |
@@ -122,9 +124,20 @@ export function useTextField<T extends TextFieldIntrinsicElements = DefaultEleme |
122 | 124 | isRequired = false, |
123 | 125 | isReadOnly = false, |
124 | 126 | type = 'text', |
125 | | - validationBehavior = 'aria' |
| 127 | + validationBehavior = 'aria', |
| 128 | + onChange: onChangeProp |
126 | 129 | } = props; |
127 | | - let [value, setValue] = useControlledState<string>(props.value, props.defaultValue || '', props.onChange); |
| 130 | + |
| 131 | + let isComposing = useRef(false); |
| 132 | + let onChange = useCallback((val) => { |
| 133 | + if (isComposing.current) { |
| 134 | + return; |
| 135 | + } |
| 136 | + |
| 137 | + onChangeProp?.(val); |
| 138 | + }, [onChangeProp]); |
| 139 | + |
| 140 | + let [value, setValue] = useControlledState<string>(props.value, props.defaultValue || '', onChange); |
128 | 141 | let {focusableProps} = useFocusable<TextFieldHTMLElementType[T]>(props, ref); |
129 | 142 | let validationState = useFormValidationState({ |
130 | 143 | ...props, |
@@ -165,6 +178,17 @@ export function useTextField<T extends TextFieldIntrinsicElements = DefaultEleme |
165 | 178 | } |
166 | 179 | }, [ref]); |
167 | 180 |
|
| 181 | + let onCompositionStart = useCallback(() => { |
| 182 | + isComposing.current = true; |
| 183 | + }, []); |
| 184 | + |
| 185 | + let onCompositionEnd = useCallback((e) => { |
| 186 | + isComposing.current = false; |
| 187 | + if (e.data !== '') { |
| 188 | + onChangeProp?.(value); |
| 189 | + } |
| 190 | + }, [onChangeProp, value]); |
| 191 | + |
168 | 192 | return { |
169 | 193 | labelProps, |
170 | 194 | inputProps: mergeProps( |
@@ -201,8 +225,8 @@ export function useTextField<T extends TextFieldIntrinsicElements = DefaultEleme |
201 | 225 | onPaste: props.onPaste, |
202 | 226 |
|
203 | 227 | // Composition events |
204 | | - onCompositionEnd: props.onCompositionEnd, |
205 | | - onCompositionStart: props.onCompositionStart, |
| 228 | + onCompositionEnd: chain(onCompositionEnd, props.onCompositionEnd), |
| 229 | + onCompositionStart: chain(onCompositionStart, props.onCompositionStart), |
206 | 230 | onCompositionUpdate: props.onCompositionUpdate, |
207 | 231 |
|
208 | 232 | // Selection events |
|
0 commit comments