@@ -455,11 +455,11 @@ protected async Task SyncMultiselectionValues(bool singleToMultiselection)
455455 {
456456 SelectedValues = new HashSet < T > ( ) { Value } ;
457457 }
458-
458+
459459 }
460460 await SelectedValuesChanged . InvokeAsync ( _selectedValues ) ;
461461 }
462- else
462+ else
463463 {
464464 await SetValueAsync ( SelectedValues . LastOrDefault ( ) , false ) ;
465465 _searchString = Converter . Set ( SelectedValues . LastOrDefault ( ) ) ;
@@ -652,7 +652,7 @@ protected override void OnInitialized()
652652 // TODO: Check this line again
653653 SetValueAsync ( SelectedValues . FirstOrDefault ( ) ) . AndForget ( ) ;
654654 }
655-
655+
656656 }
657657
658658 bool _oldShowCheckbox = true ;
@@ -873,8 +873,6 @@ protected internal async Task SearchBoxHandleKeyDown(KeyboardEventArgs obj)
873873 case "ArrowDown" :
874874 case "Home" :
875875 case "End" :
876- HandleKeyDown ( obj ) ;
877- break ;
878876 case "Enter" :
879877 case "NumpadEnter" :
880878 HandleKeyDown ( obj ) ;
@@ -885,7 +883,7 @@ protected internal async Task SearchBoxHandleKeyDown(KeyboardEventArgs obj)
885883 StateHasChanged ( ) ;
886884 break ;
887885 }
888-
886+
889887 }
890888
891889 protected internal void SearchBoxHandleKeyUp ( KeyboardEventArgs obj )
@@ -901,17 +899,38 @@ protected internal async void HandleKeyUp(KeyboardEventArgs obj)
901899
902900 protected internal async Task HandleOnBlur ( FocusEventArgs obj )
903901 {
904- if ( Strict )
902+ if ( ! MultiSelection )
905903 {
906- var item = Items . FirstOrDefault ( x => Converter . Set ( x . Value ) . Equals ( _searchString , StringComparison . OrdinalIgnoreCase ) ) ;
907- if ( item is not null )
908- await ToggleOption ( item , true ) ;
904+ if ( Strict )
905+ {
906+ // Check if the user-provided search string is an exact (case-insensitive) match against an item in the collection.
907+ var item = Items . FirstOrDefault ( x => Converter . Set ( x . Value ) . Equals ( _searchString , StringComparison . OrdinalIgnoreCase ) ) ;
908+ if ( item is not null )
909+ await ToggleOption ( item , true ) ;
910+
911+ // No item equals the user-provided search string.
912+ else
913+ {
914+ // Restore the previous selected item, if any.
915+ if ( Value is not null )
916+ {
917+ item = Items . FirstOrDefault ( x => x . Value . Equals ( Value ) ) ;
918+ if ( item is not null )
919+ await ToggleOption ( item , true ) ;
920+
921+ // Remove non-matching search string.
922+ else
923+ await Clear ( ) ;
924+ }
925+
926+ // There was no previous selected item. Remove non-matching search string.
927+ else
928+ await Clear ( ) ;
929+ }
930+ }
909931 else
910- await Clear ( ) ;
932+ await UpdateComboBoxValueAsync ( Converter . Get ( _searchString ) , updateText : true , updateSearchString : true ) ;
911933 }
912- else
913- await UpdateComboBoxValueAsync ( Converter . Get ( _searchString ) , updateText : true , updateSearchString : true ) ;
914-
915934 await OnBlurredAsync ( obj ) ;
916935 }
917936
@@ -956,9 +975,7 @@ public async Task ToggleMenu()
956975 else
957976 {
958977 await OpenMenu ( ) ;
959-
960978 }
961-
962979 }
963980
964981 public async Task OpenMenu ( )
@@ -968,18 +985,22 @@ public async Task OpenMenu()
968985 _isOpen = true ;
969986 UpdateIcon ( ) ;
970987 StateHasChanged ( ) ;
988+
971989 if ( Editable )
972990 {
973- if ( MultiSelection == true && SearchBoxAutoFocus == true )
991+ if ( MultiSelection )
974992 {
975- await Task . Delay ( 1 ) ;
976- await _searchbox . SelectAsync ( ) ;
993+ if ( SearchBoxAutoFocus )
994+ {
995+ await Task . Delay ( 1 ) ;
996+ await _searchbox . SelectAsync ( ) ;
997+ }
977998 }
978999 else
979- {
9801000 await _inputReference . SelectAsync ( ) ;
981- }
9821001 }
1002+
1003+ // Disable escape propagation: if ComboBox menu is open, only the ComboBox popover should close and underlying components should not handle escape key.
9831004 await _keyInterceptor . UpdateKey ( new ( ) { Key = "Escape" , StopDown = "Key+none" } ) ;
9841005
9851006 if ( MultiSelection )
@@ -1002,6 +1023,7 @@ public async Task CloseMenu()
10021023 DeactiveAllItems ( ) ;
10031024 StateHasChanged ( ) ;
10041025
1026+ // Enable escape propagation: The ComboBox popover was closed, no underlying components are allowed to handle escape key.
10051027 await _keyInterceptor . UpdateKey ( new ( ) { Key = "Escape" , StopDown = "none" } ) ;
10061028
10071029 await OnClose . InvokeAsync ( ) ;
@@ -1050,7 +1072,7 @@ protected internal async Task ToggleOption(MudComboBoxItem<T> item, bool selecte
10501072 SelectedValues = SelectedValues . Append ( item . Value ) ;
10511073 await SelectedValuesChanged . InvokeAsync ( _selectedValues ) ;
10521074 _allSelected = GetAllSelectedState ( ) ;
1053-
1075+
10541076 //await Task.Delay(1);
10551077 }
10561078 item . Selected = true ;
@@ -1086,10 +1108,10 @@ public override async Task ForceUpdate()
10861108 }
10871109 }
10881110
1089- public async Task BeginValidatePublic ( )
1090- {
1091- await BeginValidateAsync ( ) ;
1092- }
1111+ // public async Task BeginValidatePublic()
1112+ // {
1113+ // await BeginValidateAsync();
1114+ // }
10931115
10941116 protected internal bool ? Add ( MudComboBoxItem < T > item )
10951117 {
@@ -1118,7 +1140,7 @@ public async Task BeginValidatePublic()
11181140 /// <summary>
11191141 /// Extra handler for clearing selection.
11201142 /// </summary>
1121- protected async ValueTask ClearButtonClickHandlerAsync ( MouseEventArgs e )
1143+ protected async Task ClearButtonClickHandlerAsync ( MouseEventArgs e )
11221144 {
11231145 await UpdateComboBoxValueAsync ( default ) ;
11241146 _searchString = null ;
@@ -1273,22 +1295,22 @@ protected int GetActiveProperItemIndex()
12731295 return - 1 ;
12741296 }
12751297
1276- protected T GetActiveItemValue ( )
1277- {
1278- if ( _lastActivatedItem == null )
1279- {
1280- return Items . FirstOrDefault ( x => x . Active == true ) . Value ;
1281- }
1282- else
1283- {
1284- return _lastActivatedItem . Value ;
1285- }
1286- }
1298+ // protected T GetActiveItemValue()
1299+ // {
1300+ // if (_lastActivatedItem == null)
1301+ // {
1302+ // return Items.FirstOrDefault(x => x.Active == true).Value;
1303+ // }
1304+ // else
1305+ // {
1306+ // return _lastActivatedItem.Value;
1307+ // }
1308+ // }
12871309
1288- protected internal void UpdateLastActivatedItem ( T value )
1289- {
1290- _lastActivatedItem = Items . FirstOrDefault ( x => value == null ? x . Value == null : Comparer != null ? Comparer . Equals ( value , x . Value ) : value . Equals ( x . Value ) ) ;
1291- }
1310+ // protected internal void UpdateLastActivatedItem(T value)
1311+ // {
1312+ // _lastActivatedItem = Items.FirstOrDefault(x => value == null ? x.Value == null : Comparer != null ? Comparer.Equals(value, x.Value) : value.Equals(x.Value));
1313+ // }
12921314
12931315 protected void DeactiveAllItems ( )
12941316 {
@@ -1365,21 +1387,23 @@ public async Task ActivateFirstItem(string startsWith = null)
13651387 public async Task ActivateAdjacentItem ( int changeCount )
13661388 {
13671389 if ( Items == null || Items . Count == 0 )
1368- {
13691390 return ;
1370- }
13711391
13721392 var properItems = GetEnabledAndEligibleItems ( ) ;
13731393 int index = GetActiveProperItemIndex ( ) ;
1374- if ( index + changeCount >= properItems . Count || 0 > index + changeCount )
1394+ if ( properItems . Count == 1 && index == 0 )
1395+ {
1396+ properItems [ 0 ] . SetActive ( true ) ;
1397+ _lastActivatedItem = properItems [ 0 ] ;
1398+ }
1399+ else if ( index + changeCount >= properItems . Count || 0 > index + changeCount )
13751400 {
13761401 return ;
13771402 }
1378-
1403+
13791404 DeactiveAllItems ( ) ;
13801405 properItems [ index + changeCount ] . SetActive ( true ) ;
13811406 _lastActivatedItem = properItems [ index + changeCount ] ;
1382-
13831407 await ScrollToMiddleAsync ( Items [ index + changeCount ] ) ;
13841408 }
13851409
@@ -1399,30 +1423,25 @@ public async Task ActivateLastItem()
13991423 }
14001424 _lastActivatedItem = item ;
14011425 }
1402-
1426+
14031427 #endregion
14041428
14051429 protected internal List < MudComboBoxItem < T > > GetEnabledAndEligibleItems ( ) => Items . Where ( x => ! x . Disabled && x . Eligible ) . ToList ( ) ;
1406-
14071430
14081431 protected bool HasEligibleItems ( )
14091432 {
1410- return Items . Any ( x => x . Eligible == true ) ;
1433+ foreach ( var item in CollectionsMarshal . AsSpan ( Items ) )
1434+ {
1435+ if ( item . Eligible )
1436+ return true ;
1437+ }
1438+ return false ;
14111439 }
14121440
14131441 protected internal string GetSearchString ( ) => _searchString ;
14141442
1415- protected Typo GetTypo ( )
1416- {
1417- if ( Dense == Dense . Slim || Dense == Dense . Superslim )
1418- {
1419- return Typo . body2 ;
1420- }
1421-
1422- return Typo . body1 ;
1423- }
1443+ protected Typo GetTypo ( ) => Dense == Dense . Slim || Dense == Dense . Superslim ? Typo . body2 : Typo . body1 ;
14241444
1425- protected internal ValueTask ScrollToMiddleAsync ( MudComboBoxItem < T > item ) =>
1426- item is not null ? ScrollManagerExtended . ScrollToMiddleAsync ( _popoverId , item . ItemId ) : ValueTask . CompletedTask ;
1445+ protected internal ValueTask ScrollToMiddleAsync ( MudComboBoxItem < T > item ) => item is not null ? ScrollManagerExtended . ScrollToMiddleAsync ( _popoverId , item . ItemId ) : ValueTask . CompletedTask ;
14271446 }
14281447}
0 commit comments