@@ -156,6 +156,7 @@ export function AriaCombobox<Value = any, Mode extends SelectionMode = 'none'>(
156156 const disabled = fieldDisabled || disabledProp ;
157157 const name = fieldName ?? nameProp ;
158158 const multiple = selectionMode === 'multiple' ;
159+ const single = selectionMode === 'single' ;
159160 const hasInputValue = inputValueProp !== undefined || defaultInputValueProp !== undefined ;
160161 const hasItems = items !== undefined ;
161162
@@ -180,18 +181,11 @@ export function AriaCombobox<Value = any, Mode extends SelectionMode = 'none'>(
180181 if ( filterProp !== undefined ) {
181182 return filterProp ;
182183 }
183- if ( selectionMode === ' single' && ! queryChangedAfterOpen ) {
184+ if ( single && ! queryChangedAfterOpen ) {
184185 return createSingleSelectionCollatorFilter ( collatorFilter , itemToStringLabel , selectedValue ) ;
185186 }
186187 return createCollatorItemFilter ( collatorFilter , itemToStringLabel ) ;
187- } , [
188- filterProp ,
189- selectionMode ,
190- selectedValue ,
191- queryChangedAfterOpen ,
192- collatorFilter ,
193- itemToStringLabel ,
194- ] ) ;
188+ } , [ filterProp , single , selectedValue , queryChangedAfterOpen , collatorFilter , itemToStringLabel ] ) ;
195189
196190 // If neither inputValue nor defaultInputValue are provided, derive it from the
197191 // selected value for single mode so the input reflects the selection on mount.
@@ -200,7 +194,7 @@ export function AriaCombobox<Value = any, Mode extends SelectionMode = 'none'>(
200194 if ( hasInputValue ) {
201195 return defaultInputValueProp ?? '' ;
202196 }
203- if ( selectionMode === ' single' ) {
197+ if ( single ) {
204198 return stringifyAsLabel ( selectedValue , itemToStringLabel ) ;
205199 }
206200 return '' ;
@@ -221,8 +215,21 @@ export function AriaCombobox<Value = any, Mode extends SelectionMode = 'none'>(
221215 state : 'open' ,
222216 } ) ;
223217
224- const query = closeQuery ?? ( inputValue === '' ? '' : String ( inputValue ) . trim ( ) ) ;
225218 const isGrouped = isGroupedItems ( items ) ;
219+ const query = closeQuery ?? ( inputValue === '' ? '' : String ( inputValue ) . trim ( ) ) ;
220+
221+ const selectedLabelString = single ? stringifyAsLabel ( selectedValue , itemToStringLabel ) : '' ;
222+
223+ const shouldBypassFiltering =
224+ single &&
225+ ! queryChangedAfterOpen &&
226+ query !== '' &&
227+ selectedLabelString !== '' &&
228+ selectedLabelString . length === query . length &&
229+ collatorFilter . contains ( selectedLabelString , query ) ;
230+
231+ const filterQuery = shouldBypassFiltering ? '' : query ;
232+ const shouldIgnoreExternalFiltering = filteredItemsProp !== undefined && shouldBypassFiltering ;
226233
227234 const flatItems : readonly any [ ] = React . useMemo ( ( ) => {
228235 if ( ! items ) {
@@ -237,7 +244,7 @@ export function AriaCombobox<Value = any, Mode extends SelectionMode = 'none'>(
237244 } , [ items , isGrouped ] ) ;
238245
239246 const filteredItems : Value [ ] | Group < Value > [ ] = React . useMemo ( ( ) => {
240- if ( filteredItemsProp ) {
247+ if ( filteredItemsProp && ! shouldIgnoreExternalFiltering ) {
241248 return filteredItemsProp as Value [ ] | Group < Value > [ ] ;
242249 }
243250
@@ -256,9 +263,9 @@ export function AriaCombobox<Value = any, Mode extends SelectionMode = 'none'>(
256263 }
257264
258265 const candidateItems =
259- query === ''
266+ filterQuery === ''
260267 ? group . items
261- : group . items . filter ( ( item ) => filter ( item , query , itemToStringLabel ) ) ;
268+ : group . items . filter ( ( item ) => filter ( item , filterQuery , itemToStringLabel ) ) ;
262269
263270 if ( candidateItems . length === 0 ) {
264271 continue ;
@@ -277,7 +284,7 @@ export function AriaCombobox<Value = any, Mode extends SelectionMode = 'none'>(
277284 return resultingGroups ;
278285 }
279286
280- if ( query === '' ) {
287+ if ( filterQuery === '' ) {
281288 return limit > - 1
282289 ? flatItems . slice ( 0 , limit )
283290 : // The cast here is done as `flatItems` is readonly.
@@ -294,13 +301,23 @@ export function AriaCombobox<Value = any, Mode extends SelectionMode = 'none'>(
294301 if ( limit > - 1 && limitedItems . length >= limit ) {
295302 break ;
296303 }
297- if ( filter ( item , query , itemToStringLabel ) ) {
304+ if ( filter ( item , filterQuery , itemToStringLabel ) ) {
298305 limitedItems . push ( item ) ;
299306 }
300307 }
301308
302309 return limitedItems ;
303- } , [ filteredItemsProp , items , isGrouped , query , limit , filter , itemToStringLabel , flatItems ] ) ;
310+ } , [
311+ filteredItemsProp ,
312+ shouldIgnoreExternalFiltering ,
313+ items ,
314+ isGrouped ,
315+ filterQuery ,
316+ limit ,
317+ filter ,
318+ itemToStringLabel ,
319+ flatItems ,
320+ ] ) ;
304321
305322 const flatFilteredItems : Value [ ] = React . useMemo ( ( ) => {
306323 if ( isGrouped ) {
@@ -511,13 +528,13 @@ export function AriaCombobox<Value = any, Mode extends SelectionMode = 'none'>(
511528 }
512529
513530 if ( ! nextOpen && queryChangedAfterOpen ) {
514- if ( selectionMode === ' single' ) {
531+ if ( single ) {
515532 setCloseQuery ( query ) ;
516533 // Avoid a flicker when closing the popup with an empty query.
517534 if ( query === '' ) {
518535 setQueryChangedAfterOpen ( false ) ;
519536 }
520- } else if ( selectionMode === ' multiple' ) {
537+ } else if ( multiple ) {
521538 if ( inline || inputInsidePopup ) {
522539 setIndices ( { activeIndex : null } ) ;
523540 } else {
@@ -548,7 +565,7 @@ export function AriaCombobox<Value = any, Mode extends SelectionMode = 'none'>(
548565
549566 const shouldFillInput =
550567 ( selectionMode === 'none' && popupRef . current && fillInputOnItemPress ) ||
551- ( selectionMode === ' single' && ! store . state . inputInsidePopup ) ;
568+ ( single && ! store . state . inputInsidePopup ) ;
552569
553570 if ( shouldFillInput ) {
554571 setInputValue (
@@ -558,7 +575,7 @@ export function AriaCombobox<Value = any, Mode extends SelectionMode = 'none'>(
558575 }
559576
560577 if (
561- selectionMode === ' single' &&
578+ single &&
562579 nextValue != null &&
563580 eventDetails . reason !== REASONS . inputChange &&
564581 queryChangedAfterOpen
@@ -650,7 +667,7 @@ export function AriaCombobox<Value = any, Mode extends SelectionMode = 'none'>(
650667 // If the user typed a filter and didn't select in multiple mode, clear the input
651668 // after close completes to avoid mid-exit flicker and start fresh on next open.
652669 if (
653- selectionMode === ' multiple' &&
670+ multiple &&
654671 inputRef . current &&
655672 inputRef . current . value !== '' &&
656673 ! hadInputClearRef . current
@@ -661,7 +678,7 @@ export function AriaCombobox<Value = any, Mode extends SelectionMode = 'none'>(
661678 // Single selection mode:
662679 // - If input is rendered inside the popup, clear it so the next open is blank
663680 // - If input is outside the popup, sync it to the selected value
664- if ( selectionMode === ' single' ) {
681+ if ( single ) {
665682 if ( store . state . inputInsidePopup ) {
666683 if ( inputRef . current && inputRef . current . value !== '' ) {
667684 setInputValue ( '' , createChangeEventDetails ( REASONS . inputClear ) ) ;
@@ -896,14 +913,14 @@ export function AriaCombobox<Value = any, Mode extends SelectionMode = 'none'>(
896913 }
897914
898915 if (
899- selectionMode === ' multiple' &&
916+ multiple &&
900917 store . state . selectedIndex !== null &&
901918 ( ! Array . isArray ( selectedValue ) || selectedValue . length === 0 )
902919 ) {
903920 setIndices ( { activeIndex : null , selectedIndex : null } ) ;
904921 }
905922
906- if ( selectionMode === ' single' && ! hasInputValue && ! inputInsidePopup ) {
923+ if ( single && ! hasInputValue && ! inputInsidePopup ) {
907924 const nextInputValue = stringifyAsLabel ( selectedValue , itemToStringLabel ) ;
908925
909926 if ( inputValue !== nextInputValue ) {
@@ -928,7 +945,7 @@ export function AriaCombobox<Value = any, Mode extends SelectionMode = 'none'>(
928945 } ) ;
929946
930947 useValueChanged ( items , ( ) => {
931- if ( selectionMode !== ' single' || hasInputValue || inputInsidePopup || queryChangedAfterOpen ) {
948+ if ( ! single || hasInputValue || inputInsidePopup || queryChangedAfterOpen ) {
932949 return ;
933950 }
934951
0 commit comments