Effective config: empty multi-value UX, clickable changes count

- Multi-value: show editable badge next to (none); clicking activates edit and opens add row
- List editor: when empty and add allowed, do not show (none) row; auto-focus new-item input on open and when clicking add item
- Toolbar: make 'X change(s)' clickable to open save dialog (keyboard accessible)
This commit is contained in:
2026-03-02 22:13:07 +10:00
parent 3b22e4fb7d
commit 87179f0195
4 changed files with 59 additions and 3 deletions
@@ -38,6 +38,14 @@
margin-left: auto; margin-left: auto;
} }
::deep .ec-toolbar-changes-link {
cursor: pointer;
}
::deep .ec-toolbar-changes-link:hover {
text-decoration: underline;
}
.ec-sections { .ec-sections {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -22,7 +22,13 @@
</button> </button>
@if (PendingChangesCount > 0) @if (PendingChangesCount > 0)
{ {
<span class="text-muted small">@PendingChangesCount change(s)</span> <span class="text-muted small ec-toolbar-changes-link" role="button" tabindex="0"
@onclick="OnOpenSaveModal"
@onkeydown="OnChangesLinkKeyDown"
title="Open save dialog"
aria-label="Open save dialog">
@PendingChangesCount change(s)
</span>
} }
} }
else else
@@ -59,4 +65,12 @@
{ {
await SearchTermChanged.InvokeAsync(e.Value?.ToString() ?? ""); await SearchTermChanged.InvokeAsync(e.Value?.ToString() ?? "");
} }
private async Task OnChangesLinkKeyDown(KeyboardEventArgs e)
{
if (e.Key == "Enter" || e.Key == " ")
{
await OnOpenSaveModal.InvokeAsync();
}
}
} }
@@ -1,6 +1,8 @@
@namespace DnsmasqWebUI.Components.EffectiveConfig @namespace DnsmasqWebUI.Components.EffectiveConfig
@using DnsmasqWebUI.Models.Dnsmasq @using DnsmasqWebUI.Models.Dnsmasq
@using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig @using DnsmasqWebUI.Models.Dnsmasq.EffectiveConfig
@using Microsoft.AspNetCore.Components.Web
@inject IJSRuntime JSRuntime
<span class="ec-field-value ec-field-value-multi ec-multi-list-editor" @onclick:stopPropagation="true"> <span class="ec-field-value ec-field-value-multi ec-multi-list-editor" @onclick:stopPropagation="true">
<ol class="ec-field-value-list"> <ol class="ec-field-value-list">
@@ -53,7 +55,7 @@
</li> </li>
} }
} }
else else if (!AllowAddManagedValue)
{ {
<li class="ec-multi-list-row ec-multi-list-empty-hint"> <li class="ec-multi-list-row ec-multi-list-empty-hint">
<span class="text-muted">(none)</span> <span class="text-muted">(none)</span>
@@ -65,7 +67,7 @@
<li class="ec-multi-list-row ec-multi-list-add-item" @key="AddItemKey"> <li class="ec-multi-list-row ec-multi-list-add-item" @key="AddItemKey">
@if (_addingNew) @if (_addingNew)
{ {
<input class="form-control form-control-sm ec-multi-list-input" <input @ref="_addNewInputRef" class="form-control form-control-sm ec-multi-list-input"
placeholder="New value" placeholder="New value"
@bind="_draftValue" @bind:event="oninput" /> @bind="_draftValue" @bind:event="oninput" />
<span class="status-badge-inline"> <span class="status-badge-inline">
@@ -102,6 +104,35 @@
private int? _editingIndex; private int? _editingIndex;
private bool _addingNew; private bool _addingNew;
private string _draftValue = ""; private string _draftValue = "";
private bool _didAutoOpenAdd;
private bool _shouldFocusAddInput;
private ElementReference _addNewInputRef;
protected override void OnParametersSet()
{
if (!_didAutoOpenAdd && (SourceItems?.Count ?? 0) == 0 && AllowAddManagedValue)
{
_didAutoOpenAdd = true;
_addingNew = true;
_shouldFocusAddInput = true;
}
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (_shouldFocusAddInput && _addingNew)
{
_shouldFocusAddInput = false;
try
{
await _addNewInputRef.FocusAsync();
}
catch
{
// Focus can fail if the element is not yet in the DOM (e.g. prerender)
}
}
}
private void BeginEdit(int index, string current) private void BeginEdit(int index, string current)
{ {
@@ -115,6 +146,7 @@
_editingIndex = null; _editingIndex = null;
_addingNew = true; _addingNew = true;
_draftValue = ""; _draftValue = "";
_shouldFocusAddInput = true;
} }
private void CancelInline() private void CancelInline()
@@ -34,6 +34,8 @@
else else
{ {
<span class="text-muted">(none)</span> <span class="text-muted">(none)</span>
<OptionStateBadge OptionName="@Label" ShowEditableBadge="true" EditableAsButton="true"
FieldKey="@FieldKey" IsActiveEditor="false" IsInteractive="true" />
} }
</span> </span>
} }