Skip to content

Commit b168dfc

Browse files
committed
Docs Nav & Toc Sections
1 parent d7033ec commit b168dfc

55 files changed

Lines changed: 3446 additions & 56 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/content/fundamentals/index.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,16 @@ group: fundamentals
1212

1313

1414

15-
\# 🧠 Fundamentals
15+
\# 🧠 Fundamental Overview
1616

1717

1818

1919
This section explains how UltimateAuth works internally.
2020

2121

2222

23+
\## Section Order
24+
2325
If you are new, follow this order:
2426

2527

docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@
1818
</ItemGroup>
1919

2020
<Target Name="BuildDocs" BeforeTargets="Build">
21-
<Exec
22-
WorkingDirectory="$(MSBuildProjectDirectory)"
23-
Command="dotnet run --project &quot;$(MSBuildProjectDirectory)\..\..\..\..\src\utilities\CodeBeam.UltimateAuth.DocsBuilder\CodeBeam.UltimateAuth.DocsBuilder.csproj&quot; --solutionDir &quot;$(MSBuildProjectDirectory)\..\..\..\..\&quot;"
24-
ConsoleToMSBuild="true" />
21+
<Exec WorkingDirectory="$(MSBuildProjectDirectory)" Command="dotnet run --project &quot;$(MSBuildProjectDirectory)\..\..\..\..\src\utilities\CodeBeam.UltimateAuth.DocsBuilder\CodeBeam.UltimateAuth.DocsBuilder.csproj&quot; --solutionDir &quot;$(MSBuildProjectDirectory)\..\..\..\..\&quot;" ConsoleToMSBuild="true" />
2522
</Target>
2623

2724
</Project>

docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/DocsLayout.razor

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,9 @@
1010
<MudMainContent Class="docs-main">
1111
@Body
1212
</MudMainContent>
13-
</MudLayout>
13+
14+
<MudDrawer Open="true" Elevation="0" Class="pa-2 docs-toc-container" Style="width:260px; background: transparent" Anchor="Anchor.End">
15+
<DocsToc />
16+
<MudPageContentNavigation SectionClassSelector="mud-scrollspy-section" />
17+
</MudDrawer>
18+
</MudLayout>

docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@using CodeBeam.UltimateAuth.Docs.Wasm.Client.Brand
2+
@using CodeBeam.UltimateAuth.Docs.Wasm.Client.Pages
23
@inherits LayoutComponentBase
34

45
@inject NavigationManager Nav
@@ -39,7 +40,9 @@
3940
<MudDrawer @bind-Open="_drawerOpen" Breakpoint="Breakpoint.None" ClipMode="DrawerClipMode.Always" Variant="DrawerVariant.Temporary">
4041
<MudNavMenu>
4142
<MudNavLink Href="/#uauth" Icon="@Icons.Material.Filled.Home">Home</MudNavLink>
42-
<MudNavLink Href="/docs" Icon="@Icons.Material.Filled.Book">Docs</MudNavLink>
43+
<MudNavGroup Icon="@Icons.Material.Filled.Book" Title="Docs" Expanded="false">
44+
<DocsSidebar Inline="true" />
45+
</MudNavGroup>
4346
<MudNavLink Href="/samples" Icon="@Icons.Material.Filled.Apps">Samples</MudNavLink>
4447
<MudNavLink Href="/#donate" Icon="@Icons.Material.Filled.VolunteerActivism">Donate</MudNavLink>
4548
</MudNavMenu>

docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsContent.razor

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
@using System.Net.Http.Json
2+
@inject DocsPageState PageState
23
@inject HttpClient Http
4+
@inject NavigationManager Nav
5+
@inject IJSRuntime JS
36

47
@if (_loading || !RendererInfo.IsInteractive)
58
{
@@ -24,6 +27,44 @@ else if (_doc is not null)
2427
<MudDivider Class="mb-4" />
2528

2629
<div class="docs-content" @ref="_contentRef">@((MarkupString)_doc.Html)</div>
30+
31+
<MudDivider Class="my-6" />
32+
33+
<MudGrid>
34+
<MudItem xs="6" Class="d-flex justify-start">
35+
@if (_prev != null)
36+
{
37+
<MudPaper Class="pa-3 nav-card" Style="background: transparent" Outlined="true">
38+
<MudStack Spacing="1">
39+
<MudText Typo="Typo.caption" Class="text-secondary">
40+
Previous
41+
</MudText>
42+
43+
<MudButton Href="@($"/docs/{_prev.Slug}")" Variant="Variant.Text" StartIcon="@Icons.Material.Filled.ArrowBack">
44+
@_prev.Title
45+
</MudButton>
46+
</MudStack>
47+
</MudPaper>
48+
}
49+
</MudItem>
50+
51+
<MudItem xs="6" Class="d-flex justify-end">
52+
@if (_next != null)
53+
{
54+
<MudPaper Class="pa-3 nav-card" Style="background: transparent" Outlined="true">
55+
<MudStack Spacing="1" AlignItems="AlignItems.End">
56+
<MudText Typo="Typo.caption" Class="text-secondary">
57+
Next
58+
</MudText>
59+
60+
<MudButton Href="@($"/docs/{_next.Slug}")" Variant="Variant.Text" EndIcon="@Icons.Material.Filled.ArrowForward">
61+
@_next.Title
62+
</MudButton>
63+
</MudStack>
64+
</MudPaper>
65+
}
66+
</MudItem>
67+
</MudGrid>
2768
</MudContainer>
2869
}
2970

@@ -41,6 +82,9 @@ else if (_doc is not null)
4182
_loading = true;
4283
_notFound = false;
4384
_doc = null;
85+
_prev = null;
86+
_next = null;
87+
PageState.Clear();
4488

4589
var slug = string.IsNullOrWhiteSpace(Slug)
4690
? "index"
@@ -51,7 +95,15 @@ else if (_doc is not null)
5195
_doc = await Http.GetFromJsonAsync<DocPage>($"docs/{slug}.json");
5296

5397
if (_doc == null)
98+
{
5499
_notFound = true;
100+
}
101+
else
102+
{
103+
PageState.SetPage(slug, _doc.Title, _doc.Headings);
104+
await EnsureNavLoaded();
105+
ComputePrevNext();
106+
}
55107
}
56108
catch
57109
{
@@ -74,5 +126,72 @@ else if (_doc is not null)
74126
public string Slug { get; set; } = "";
75127
public string Title { get; set; } = "";
76128
public string Html { get; set; } = "";
129+
public List<DocsHeadingItem> Headings { get; set; } = [];
130+
}
131+
132+
private List<DocNavItem> _nav = [];
133+
private List<DocNavItem>? _navCache;
134+
private DocNavItem? _prev;
135+
private DocNavItem? _next;
136+
137+
private class DocNavItem
138+
{
139+
public string Title { get; set; } = "";
140+
public string Slug { get; set; } = "";
77141
}
78-
}
142+
143+
private async Task EnsureNavLoaded()
144+
{
145+
if (_navCache is not null)
146+
return;
147+
148+
_navCache = await Http.GetFromJsonAsync<List<DocNavItem>>("docs/docs-index.json") ?? [];
149+
}
150+
151+
private void ComputePrevNext()
152+
{
153+
_prev = null;
154+
_next = null;
155+
156+
if (_navCache is null)
157+
return;
158+
159+
var currentPath = Nav.ToBaseRelativePath(Nav.Uri);
160+
161+
var currentSlug = currentPath.StartsWith("docs/")
162+
? currentPath.Substring(5)
163+
: currentPath;
164+
165+
currentSlug = currentSlug.Trim('/');
166+
167+
var index = _navCache.FindIndex(x =>
168+
x.Slug.Trim('/').Equals(currentSlug, StringComparison.OrdinalIgnoreCase));
169+
170+
if (index > 0)
171+
_prev = _navCache[index - 1];
172+
173+
if (index >= 0 && index < _navCache.Count - 1)
174+
_next = _navCache[index + 1];
175+
}
176+
177+
private async Task LoadNavAsync()
178+
{
179+
_nav = await Http.GetFromJsonAsync<List<DocNavItem>>("docs/docs-index.json") ?? [];
180+
181+
var currentPath = Nav.ToBaseRelativePath(Nav.Uri);
182+
183+
var currentSlug = currentPath.StartsWith("docs/")
184+
? currentPath.Substring(5)
185+
: currentPath;
186+
187+
var index = _nav.FindIndex(x => x.Slug == currentSlug);
188+
189+
Console.WriteLine($"Slug: {currentSlug}, Index: {index}");
190+
191+
if (index > 0)
192+
_prev = _nav[index - 1];
193+
194+
if (index >= 0 && index < _nav.Count - 1)
195+
_next = _nav[index + 1];
196+
}
197+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
namespace CodeBeam.UltimateAuth.Docs.Wasm.Client.Pages;
2+
3+
public sealed class DocsPageState
4+
{
5+
public string? CurrentSlug { get; private set; }
6+
public string? CurrentTitle { get; private set; }
7+
public IReadOnlyList<DocsHeadingItem> Headings { get; private set; } = [];
8+
9+
public string? ActiveHeadingId { get; private set; }
10+
11+
public event Action? Changed;
12+
13+
public void SetPage(string? slug, string? title, IReadOnlyList<DocsHeadingItem> headings)
14+
{
15+
CurrentSlug = slug;
16+
CurrentTitle = title;
17+
Headings = headings;
18+
ActiveHeadingId = headings.Count > 0 ? headings[0].Id : null;
19+
Changed?.Invoke();
20+
}
21+
22+
public void SetActiveHeading(string? id)
23+
{
24+
if (string.Equals(ActiveHeadingId, id, StringComparison.Ordinal))
25+
return;
26+
27+
ActiveHeadingId = id;
28+
Changed?.Invoke();
29+
}
30+
31+
public void Clear()
32+
{
33+
CurrentSlug = null;
34+
CurrentTitle = null;
35+
Headings = [];
36+
ActiveHeadingId = null;
37+
Changed?.Invoke();
38+
}
39+
}
40+
41+
public sealed class DocsHeadingItem
42+
{
43+
public string Id { get; set; } = "";
44+
public string Text { get; set; } = "";
45+
public int Level { get; set; }
46+
}

docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/DocsSidebar.razor

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22
@inject NavigationManager Nav
33

44
<div>
5-
<MudText Typo="Typo.h6" Class="mb-2 px-2">
6-
Documentation
7-
</MudText>
5+
@if (!Inline)
6+
{
7+
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
8+
<MudText Typo="Typo.h6" Class="mb-2 px-2">UAuth Docs</MudText>
9+
<MudIconButton Icon="@Icons.Material.Filled.CloseFullscreen" Color="Color.Primary" OnClick="@(() => _expanded = !_expanded)" />
10+
</MudStack>
11+
}
812

913
<MudDivider Class="mb-2" />
1014

@@ -15,9 +19,10 @@
1519
else
1620
{
1721
<MudNavMenu Color="Color.Primary">
22+
<MudNavLink Match="NavLinkMatch.All" Href="/docs">Overview</MudNavLink>
1823
@foreach (var group in _groups)
1924
{
20-
<MudNavGroup Title="@FormatGroup(group.Key)" Expanded="true">
25+
<MudNavGroup Title="@FormatGroup(group.Key)" Expanded="_expanded">
2126
@foreach (var item in group.Value)
2227
{
2328
<MudNavLink Match="NavLinkMatch.Prefix" Href="@($"/docs/{item.Slug}")">@item.Title</MudNavLink>
@@ -31,8 +36,12 @@
3136
@code {
3237
private Dictionary<string, List<DocIndexItem>>? _groups;
3338
private DocIndexItem? _selectedValue;
39+
private bool _expanded = true;
40+
41+
[Parameter]
42+
public bool Inline { get; set; }
3443

35-
protected override async Task OnAfterRenderAsync(bool firstRender)
44+
protected override async Task OnAfterRenderAsync(bool firstRender)
3645
{
3746
if (!firstRender)
3847
return;
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
@inject DocsPageState PageState
2+
@inject NavigationManager Nav
3+
@inject IJSRuntime JS
4+
5+
@if (PageState.Headings.Count == 0)
6+
{
7+
return;
8+
}
9+
10+
<MudPaper Class="pa-3" Style="background: transparent" Outlined="true" Elevation="0">
11+
<MudText Typo="Typo.subtitle2" Class="mb-2">On this page</MudText>
12+
13+
<MudNavMenu Dense="true">
14+
@foreach (var item in PageState.Headings)
15+
{
16+
<MudNavLink Class="@GetClass(item)" Style="@($"padding-left:{item.Level * 12}px")"
17+
OnClick="@(() => OnClick(item.Id))" Match="NavLinkMatch.Prefix">
18+
@item.Text
19+
</MudNavLink>
20+
}
21+
</MudNavMenu>
22+
</MudPaper>
23+
24+
@code {
25+
private DotNetObjectReference<DocsToc>? _ref;
26+
27+
protected override void OnInitialized()
28+
{
29+
PageState.Changed += OnStateChanged;
30+
}
31+
32+
protected override async Task OnAfterRenderAsync(bool firstRender)
33+
{
34+
Console.WriteLine($"OnAfterRender - Headings: {PageState.Headings.Count}");
35+
36+
if (_ref == null && PageState.Headings.Count > 0)
37+
{
38+
Console.WriteLine("Starting ScrollSpy");
39+
40+
_ref = DotNetObjectReference.Create(this);
41+
await JS.InvokeVoidAsync("uaDocsScrollSpy.start", _ref);
42+
}
43+
}
44+
45+
private void OnStateChanged()
46+
{
47+
InvokeAsync(StateHasChanged);
48+
}
49+
50+
private string GetHref(string id)
51+
{
52+
return $"{Nav.Uri.Split('#')[0]}#{id}";
53+
}
54+
55+
[JSInvokable]
56+
public Task SetActiveHeading(string id)
57+
{
58+
PageState.SetActiveHeading(id);
59+
return Task.CompletedTask;
60+
}
61+
62+
private async Task OnClick(string id)
63+
{
64+
PageState.SetActiveHeading(id);
65+
await JS.InvokeVoidAsync("uaDocsScrollSpy.scrollTo", id);
66+
}
67+
68+
private string GetClass(DocsHeadingItem item)
69+
=> item.Id == PageState.ActiveHeadingId ? "active toc-active" : "";
70+
71+
private Task ScrollTo(string id)
72+
=> JS.InvokeVoidAsync("uaDocsScrollSpy.scrollTo", id).AsTask();
73+
74+
public void Dispose()
75+
{
76+
PageState.Changed -= OnStateChanged;
77+
}
78+
}

docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Program.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using CodeBeam.UltimateAuth.Docs.Wasm.Client.Pages;
12
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
23
using MudBlazor.Services;
34
using MudExtensions.Services;
@@ -7,6 +8,8 @@
78
builder.Services.AddMudServices();
89
builder.Services.AddMudExtensions();
910

11+
builder.Services.AddScoped<DocsPageState>();
12+
1013
builder.Services.AddScoped(sp =>
1114
new HttpClient
1215
{

0 commit comments

Comments
 (0)