@@ -61,6 +61,9 @@ export const ComboboxInput = React.forwardRef(function ComboboxInput(
6161
6262 const disabled = fieldDisabled || comboboxDisabled || disabledProp ;
6363
64+ const [ composingValue , setComposingValue ] = React . useState < string | null > ( null ) ;
65+ const isComposingRef = React . useRef ( false ) ;
66+
6467 const setInputElement = useEventCallback ( ( element ) => {
6568 store . apply ( {
6669 inputElement : element ,
@@ -145,7 +148,7 @@ export const ComboboxInput = React.forwardRef(function ComboboxInput(
145148 triggerProps ,
146149 {
147150 type : 'text' ,
148- value : componentProps . value ?? inputValue ,
151+ value : componentProps . value ?? composingValue ?? inputValue ,
149152 'aria-readonly' : readOnly || undefined ,
150153 'aria-labelledby' : labelId ,
151154 disabled,
@@ -163,7 +166,62 @@ export const ComboboxInput = React.forwardRef(function ComboboxInput(
163166 fieldControlValidation ?. commitValidation ( valueToValidate ) ;
164167 }
165168 } ,
169+ onCompositionStart ( event ) {
170+ isComposingRef . current = true ;
171+ setComposingValue ( event . currentTarget . value ) ;
172+ } ,
173+ onCompositionEnd ( event ) {
174+ isComposingRef . current = false ;
175+ const next = event . currentTarget . value ;
176+ setComposingValue ( null ) ;
177+ store . state . setInputValue (
178+ next ,
179+ createBaseUIEventDetails ( 'input-change' , event . nativeEvent ) ,
180+ ) ;
181+ } ,
166182 onChange ( event : React . ChangeEvent < HTMLInputElement > ) {
183+ // During IME composition, avoid propagating controlled updates to preserve
184+ // its state.
185+ if ( isComposingRef . current ) {
186+ const nextVal = event . currentTarget . value ;
187+ setComposingValue ( nextVal ) ;
188+
189+ if ( nextVal === '' && ! openOnInputClick && ! hasPositionerParent ) {
190+ store . state . setOpen (
191+ false ,
192+ createBaseUIEventDetails ( 'input-clear' , event . nativeEvent ) ,
193+ ) ;
194+ }
195+
196+ if ( ! readOnly && ! disabled ) {
197+ const trimmed = nextVal . trim ( ) ;
198+ if ( trimmed !== '' ) {
199+ store . state . setOpen ( true , createBaseUIEventDetails ( 'none' , event . nativeEvent ) ) ;
200+ if ( ! ( selectionMode === 'none' && autoHighlight ) ) {
201+ store . state . setIndices ( {
202+ activeIndex : null ,
203+ selectedIndex : null ,
204+ type : store . state . keyboardActiveRef . current ? 'keyboard' : 'pointer' ,
205+ } ) ;
206+ }
207+ }
208+ }
209+
210+ if (
211+ open &&
212+ store . state . activeIndex !== null &&
213+ ! ( selectionMode === 'none' && autoHighlight && nextVal . trim ( ) !== '' )
214+ ) {
215+ store . state . setIndices ( {
216+ activeIndex : null ,
217+ selectedIndex : null ,
218+ type : store . state . keyboardActiveRef . current ? 'keyboard' : 'pointer' ,
219+ } ) ;
220+ }
221+
222+ return ;
223+ }
224+
167225 store . state . setInputValue (
168226 event . currentTarget . value ,
169227 createBaseUIEventDetails ( 'input-change' , event . nativeEvent ) ,
0 commit comments