Improve email suggestion handling and keyboard navigation

Enhanced the email suggestion feature by adding keyboard navigation support (`ArrowUp`, `ArrowDown`, `Enter`, `Escape`) and introducing `_selectedReceiverEmailSuggestion` to track the current selection. Updated methods to synchronize input and suggestions, pre-select matching suggestions, and reset the state when the popup is opened or closed. Improved error handling and ensured clean state management for better user experience.
This commit is contained in:
2026-07-01 17:10:54 +02:00
parent 2af8815cf6
commit 3ff3373b27

View File

@@ -281,12 +281,14 @@
<div class="sender-receiver-popup__field">
<div class="sender-receiver-popup__label">E-Mail-Adresse</div>
<DxTextBox Text="@_receiverDraftEmail"
TextChanged="OnReceiverEmailTextChangedAsync"
NullText="name@beispiel.de"
ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto"
BindValueMode="BindValueMode.OnInput"
CssClass="w-100" />
<div @onkeydown="OnReceiverEmailKeyDownAsync">
<DxTextBox Text="@_receiverDraftEmail"
TextChanged="OnReceiverEmailTextChangedAsync"
NullText="name@beispiel.de"
ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto"
BindValueMode="BindValueMode.OnInput"
CssClass="w-100" />
</div>
<div class="sender-receiver-popup__suggestions-shell">
@if (_receiverEmailSuggestions.Count > 0)
@@ -295,7 +297,7 @@
<DxListBox TData="string"
TValue="string"
Data="@_receiverEmailSuggestions"
Value="@_receiverDraftEmail"
Value="@_selectedReceiverEmailSuggestion"
ValueChanged="OnReceiverEmailSuggestionSelectedAsync"
SelectionMode="ListBoxSelectionMode.Single"
CssClass="w-100" />
@@ -355,6 +357,7 @@
bool _receiverPopupVisible;
string _receiverDraftName = string.Empty;
string _receiverDraftEmail = string.Empty;
string? _selectedReceiverEmailSuggestion;
string? _receiverPopupValidationMessage;
bool _isReceiverEmailSearchRunning;
List<string> _receiverEmailSuggestions = [];
@@ -473,6 +476,7 @@
{
_receiverDraftName = string.Empty;
_receiverDraftEmail = string.Empty;
_selectedReceiverEmailSuggestion = null;
_receiverPopupValidationMessage = null;
_receiverEmailSuggestions.Clear();
_receiverPopupVisible = true;
@@ -482,6 +486,7 @@
{
_receiverPopupVisible = false;
_receiverPopupValidationMessage = null;
_selectedReceiverEmailSuggestion = null;
_isReceiverEmailSearchRunning = false;
}
@@ -491,12 +496,56 @@
_receiverPopupValidationMessage = null;
}
async Task OnReceiverEmailKeyDownAsync(KeyboardEventArgs e)
{
if (_receiverEmailSuggestions.Count == 0)
{
if (e.Key == "Escape")
_selectedReceiverEmailSuggestion = null;
return;
}
var currentIndex = _selectedReceiverEmailSuggestion is null
? -1
: _receiverEmailSuggestions.FindIndex(email => string.Equals(email, _selectedReceiverEmailSuggestion, StringComparison.OrdinalIgnoreCase));
if (e.Key == "ArrowDown")
{
var nextIndex = currentIndex < _receiverEmailSuggestions.Count - 1 ? currentIndex + 1 : 0;
await OnReceiverEmailSuggestionSelectedAsync(_receiverEmailSuggestions[nextIndex]);
}
else if (e.Key == "ArrowUp")
{
var nextIndex = currentIndex > 0 ? currentIndex - 1 : _receiverEmailSuggestions.Count - 1;
await OnReceiverEmailSuggestionSelectedAsync(_receiverEmailSuggestions[nextIndex]);
}
else if (e.Key == "Enter")
{
var selectedValue = currentIndex >= 0 && currentIndex < _receiverEmailSuggestions.Count
? _receiverEmailSuggestions[currentIndex]
: _receiverEmailSuggestions.FirstOrDefault();
if (!string.IsNullOrWhiteSpace(selectedValue))
{
await OnReceiverEmailSuggestionSelectedAsync(selectedValue);
_receiverEmailSuggestions.Clear();
}
}
else if (e.Key == "Escape")
{
_receiverEmailSuggestions.Clear();
_selectedReceiverEmailSuggestion = null;
}
}
Task OnReceiverEmailSuggestionSelectedAsync(string? value)
{
if (string.IsNullOrWhiteSpace(value))
return Task.CompletedTask;
_receiverDraftEmail = value.Trim();
_selectedReceiverEmailSuggestion = value.Trim();
_receiverDraftEmail = _selectedReceiverEmailSuggestion;
_receiverPopupValidationMessage = null;
return Task.CompletedTask;
}
@@ -504,6 +553,7 @@
async Task OnReceiverEmailTextChangedAsync(string? value)
{
_receiverDraftEmail = value?.Trim() ?? string.Empty;
_selectedReceiverEmailSuggestion = _receiverDraftEmail;
_receiverPopupValidationMessage = null;
var searchVersion = ++_receiverEmailSearchVersion;
@@ -511,6 +561,7 @@
if (string.IsNullOrWhiteSpace(_receiverDraftEmail) || _receiverDraftEmail.Length < 2)
{
_receiverEmailSuggestions.Clear();
_selectedReceiverEmailSuggestion = null;
_isReceiverEmailSearchRunning = false;
return;
}
@@ -530,12 +581,18 @@
.OrderBy(email => email)
.Take(12)
.ToList();
_selectedReceiverEmailSuggestion = _receiverEmailSuggestions.FirstOrDefault(email =>
string.Equals(email, _receiverDraftEmail, StringComparison.OrdinalIgnoreCase));
}
catch (Exception ex)
{
Logger.LogWarning(ex, "Failed to load receiver email suggestions for {SearchTerm}", _receiverDraftEmail);
if (searchVersion == _receiverEmailSearchVersion)
{
_receiverEmailSuggestions.Clear();
_selectedReceiverEmailSuggestion = null;
}
}
finally
{