@page "/docs/{Page}"
@using Markdig
@inject HttpClient Http
@inject NavigationManager Nav
@inject IJSRuntime JS
@implements IDisposable
Docs: @Page
Docs:
@Page
@if (_isLoading)
{
loading documentation…
}
else if (_notFound)
{
document not found
}
else
{
@((MarkupString)_htmlContent!)
}
@code {
[Parameter]
public string Page { get; set; } = string.Empty;
private string? _htmlContent;
private bool _isLoading = true;
private bool _notFound;
private static readonly MarkdownPipeline Pipeline =
new MarkdownPipelineBuilder()
.UseAdvancedExtensions()
.UseAutoIdentifiers()
.Build();
private bool _pendingScroll;
protected override void OnInitialized()
{
Nav.LocationChanged += HandleLocationChanged;
}
private void HandleLocationChanged(object? sender, LocationChangedEventArgs e)
{
// Trigger re-render so OnAfterRenderAsync runs again
_pendingScroll = true;
InvokeAsync(StateHasChanged);
}
protected override async Task OnParametersSetAsync()
{
_isLoading = true;
_notFound = false;
_pendingScroll = true;
try
{
var decoded = Uri.UnescapeDataString(Page);
if (!decoded.EndsWith(".md", StringComparison.OrdinalIgnoreCase))
decoded += ".md";
var url = $"_content/Shared.Rcl/raw_docs/{decoded}";
var markdown = await Http.GetStringAsync(url);
_htmlContent = Markdown.ToHtml(markdown, Pipeline);
}
catch
{
_notFound = true;
_htmlContent = null;
}
finally
{
_isLoading = false;
}
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (_pendingScroll && !_isLoading && !_notFound)
{
_pendingScroll = false;
await ScrollToFragmentAsync();
}
}
private async Task ScrollToFragmentAsync()
{
var uri = new Uri(Nav.Uri);
var fragment = uri.Fragment;
if (!string.IsNullOrWhiteSpace(fragment))
{
var anchor = fragment.TrimStart('#');
await JS.InvokeVoidAsync("scrollToAnchor", anchor);
}
}
public void Dispose()
{
Nav.LocationChanged -= HandleLocationChanged;
}
}