Hosts UX, effective names, JS interop, domain UI

- Hosts: delete confirmation modal, shift range select, keyboard shortcuts,
  effective-name suffix tooltips, conditional modern header actions
- Align EffectiveNames on rows with scoped domain= + pending preview;
  unified hosts API uses full DomainValues from config snapshot
- Harden Blazor JS interop (safe helpers, IAsyncDisposable, disposal order)
- Effective config: domain multi-value display/editor, parsers, tests
- Misc: docs/testdata/docker script updates
This commit is contained in:
2026-04-05 14:46:17 +10:00
parent 9627926a6a
commit 2e713f0e3c
41 changed files with 1848 additions and 273 deletions
@@ -1,5 +1,6 @@
@namespace DnsmasqWebUI.Components.EffectiveConfig
@using DnsmasqWebUI.Extensions.DependencyInjection
@using DnsmasqWebUI.Extensions.Interop
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@using System.Text.Json.Serialization
@@ -57,14 +58,8 @@
{
if (firstRender)
{
try
{
_jsModule = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./js/config-option-help.js");
_moduleLoaded = true;
}
catch (InvalidOperationException) { /* prerender */ }
catch (JSDisconnectedException) { /* circuit disconnected */ }
catch (JSException) { /* import failed */ }
_jsModule = await JSRuntime.InvokeAsyncSafe<IJSObjectReference>("import", default, "./js/config-option-help.js");
_moduleLoaded = _jsModule != null;
}
if (Visible && !_scrollListenerAdded && _moduleLoaded && _jsModule != null)
@@ -93,15 +88,21 @@
public async ValueTask DisposeAsync()
{
if (_scrollListenerAdded && _jsModule != null)
try
{
if (_scrollListenerAdded && _jsModule != null)
{
_scrollListenerAdded = false;
await _jsModule.InvokeVoidAsyncSafe("removeScrollCloseListener");
}
}
finally
{
_scrollListenerAdded = false;
await _jsModule.InvokeVoidAsyncSafe("removeScrollCloseListener");
_dotNetRef?.Dispose();
_dotNetRef = null;
await _jsModule.DisposeAsyncSafe();
_jsModule = null;
}
await _jsModule.DisposeAsyncSafe();
_jsModule = null;
}
protected override async Task OnParametersSetAsync()