@inject IJSRuntime JS
@implements IAsyncDisposable
@if (_isRendering)
{
rendering diagram…
}
else if (_error is not null)
{
@_error
}
@code {
[Parameter] public string? Source { get; set; }
[Parameter] public string? TestId { get; set; }
///
/// The id of the host element holding the rendered SVG. Defaults to a
/// random GUID per instance; pages that need to call SVG-export JS on
/// this view should pass a stable id.
///
[Parameter] public string? Id { get; set; }
private string HostId => Id ?? _generatedId;
private readonly string _generatedId = $"rpkg-host-{Guid.NewGuid():N}";
private string? _renderedSource;
private bool _isRendering;
private string? _error;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// Re-render only when the source actually changes — Blazor calls
// OnAfterRender on every state change.
if (Source == _renderedSource) return;
_renderedSource = Source;
if (string.IsNullOrWhiteSpace(Source))
{
try
{
await JS.InvokeVoidAsync("rackpeekGraph.render", HostId, "");
}
catch
{
// Ignore — the host may not be ready / the page is unloading.
}
return;
}
_isRendering = true;
_error = null;
StateHasChanged();
try
{
await JS.InvokeVoidAsync("rackpeekGraph.render", HostId, Source);
}
catch (Exception ex)
{
_error = $"Diagram render failed: {ex.Message}";
}
finally
{
_isRendering = false;
StateHasChanged();
}
}
public async ValueTask DisposeAsync()
{
try
{
await JS.InvokeVoidAsync("rackpeekGraph.render", HostId, "");
}
catch
{
// ignore on teardown
}
}
}