Skip to content

Commit bc3a153

Browse files
committed
PoC: Lazy loaded navigation
1 parent 3372daa commit bc3a153

File tree

12 files changed

+60
-35
lines changed

12 files changed

+60
-35
lines changed

src/Elastic.ApiExplorer/OpenApiGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ private async Task<IFileInfo> Render<T>(INavigationItem current, T page, ApiRend
288288
if (!outputFile.Directory!.Exists)
289289
outputFile.Directory.Create();
290290

291-
var navigationHtml = await navigationRenderer.RenderNavigation(current.NavigationRoot, new Uri("http://ignored.example"), ctx);
291+
var navigationHtml = await navigationRenderer.RenderNavigation(current.NavigationRoot, new Uri("http://ignored.example"), -1, ctx);
292292
renderContext = renderContext with
293293
{
294294
CurrentNavigation = current,

src/Elastic.Documentation.Site/Layout/_PagesNav.cshtml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@inherits RazorSlice<Elastic.Documentation.Site.GlobalLayoutViewModel>
22
<aside class="sidebar bg-white fixed md:sticky shadow-2xl md:shadow-none left-[100%] group-has-[#pages-nav-hamburger:checked]/body:left-0 bottom-0 md:left-auto pl-6 md:pl-2 top-[calc(var(--offset-top)+1px)] w-[80%] md:w-auto shrink-0 border-r-1 border-r-grey-20 z-40 md:z-auto">
3+
<div hx-get="@(Model.CurrentNavigationItem.Url + (Model.CurrentNavigationItem.Url.EndsWith('/') ? "index.nav.html" : "/index.nav.html"))" hx-trigger="load" hx-params="nav" hx-push-url="false" hx-swap="innerHTML" hx-target="#pages-nav"></div>
34
<nav
45
id="pages-nav"
56
class="sidebar-nav h-full">

src/Elastic.Documentation.Site/Navigation/INavigationHtmlWriter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Elastic.Documentation.Site.Navigation;
88

99
public interface INavigationHtmlWriter
1010
{
11-
Task<string> RenderNavigation(IRootNavigationItem<INavigationModel, INavigationItem> currentRootNavigation, Uri navigationSource, Cancel ctx = default);
11+
Task<string> RenderNavigation(IRootNavigationItem<INavigationModel, INavigationItem> currentRootNavigation, Uri navigationSource, int maxLevel, Cancel ctx = default);
1212

1313
async Task<string> Render(NavigationViewModel model, Cancel ctx)
1414
{

src/Elastic.Documentation.Site/Navigation/IsolatedBuildNavigationHtmlWriter.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,24 @@ namespace Elastic.Documentation.Site.Navigation;
1010
public class IsolatedBuildNavigationHtmlWriter(BuildContext context, IRootNavigationItem<INavigationModel, INavigationItem> siteRoot)
1111
: INavigationHtmlWriter
1212
{
13-
private readonly ConcurrentDictionary<string, string> _renderedNavigationCache = [];
13+
private readonly ConcurrentDictionary<(string, int), string> _renderedNavigationCache = [];
1414

15-
public async Task<string> RenderNavigation(IRootNavigationItem<INavigationModel, INavigationItem> currentRootNavigation, Uri navigationSource, Cancel ctx = default)
15+
public async Task<string> RenderNavigation(IRootNavigationItem<INavigationModel, INavigationItem> currentRootNavigation, Uri navigationSource, int maxLevel, Cancel ctx = default)
1616
{
1717
var navigation = context.Configuration.Features.PrimaryNavEnabled || currentRootNavigation.IsUsingNavigationDropdown
1818
? currentRootNavigation
1919
: siteRoot;
2020

21-
if (_renderedNavigationCache.TryGetValue(navigation.Id, out var value))
21+
if (_renderedNavigationCache.TryGetValue((navigation.Id, maxLevel), out var value))
2222
return value;
2323

24-
var model = CreateNavigationModel(navigation);
24+
var model = CreateNavigationModel(navigation, maxLevel);
2525
value = await ((INavigationHtmlWriter)this).Render(model, ctx);
26-
_renderedNavigationCache[navigation.Id] = value;
26+
_renderedNavigationCache[(navigation.Id, maxLevel)] = value;
2727
return value;
2828
}
2929

30-
private NavigationViewModel CreateNavigationModel(IRootNavigationItem<INavigationModel, INavigationItem> navigation) =>
30+
private NavigationViewModel CreateNavigationModel(IRootNavigationItem<INavigationModel, INavigationItem> navigation, int maxLevel) =>
3131
new()
3232
{
3333
Title = navigation.NavigationTitle,
@@ -36,6 +36,7 @@ private NavigationViewModel CreateNavigationModel(IRootNavigationItem<INavigatio
3636
IsPrimaryNavEnabled = context.Configuration.Features.PrimaryNavEnabled,
3737
IsUsingNavigationDropdown = context.Configuration.Features.PrimaryNavEnabled || navigation.IsUsingNavigationDropdown,
3838
IsGlobalAssemblyBuild = false,
39-
TopLevelItems = siteRoot.NavigationItems.OfType<INodeNavigationItem<INavigationModel, INavigationItem>>().ToList()
39+
TopLevelItems = siteRoot.NavigationItems.OfType<INodeNavigationItem<INavigationModel, INavigationItem>>().ToList(),
40+
MaxLevel = maxLevel
4041
};
4142
}

src/Elastic.Documentation.Site/Navigation/NavigationTreeItem.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,6 @@ public class NavigationTreeItem
1212
public required bool IsPrimaryNavEnabled { get; init; }
1313
public required bool IsGlobalAssemblyBuild { get; init; }
1414
public required string RootNavigationId { get; set; }
15+
// How many levels to render. Default is -1 (all levels)
16+
public required int MaxLevel { get; init; }
1517
}

src/Elastic.Documentation.Site/Navigation/NavigationViewModel.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,7 @@ public class NavigationViewModel
1515

1616
/// controls whether to split the navigation tree automatically
1717
public required bool IsUsingNavigationDropdown { get; init; }
18+
19+
// How many levels to render. -1 means all
20+
public required int MaxLevel { get; init; }
1821
}

src/Elastic.Documentation.Site/Navigation/_TocTree.cshtml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@using Elastic.Documentation.Site.Navigation
22
@inherits RazorSlice<Elastic.Documentation.Site.Navigation.NavigationViewModel>
3+
34
<div class="pb-20 font-body">
45
@{
56
var currentTopLevelItem = Model.TopLevelItems.FirstOrDefault(i => i.Id == Model.Tree.Id) ?? Model.Tree;
@@ -65,6 +66,7 @@
6566
Level = 0,
6667
SubTree = Model.Tree,
6768
RootNavigationId = Model.Tree.Id,
69+
MaxLevel = Model.MaxLevel
6870
}))
6971
</ul>
7072
</div>

src/Elastic.Documentation.Site/Navigation/_TocTreeNav.cshtml

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,22 @@
5757
</div>
5858
@if (g.NavigationItems.Count > 0)
5959
{
60+
// Only render children if we're within the allowed level depth
61+
// MaxLevel of -1 means render all levels
62+
bool shouldRenderChildren = Model.MaxLevel == -1 || Model.Level < (Model.MaxLevel);
6063
<ul class="w-full hidden peer-has-checked:block ml-4">
61-
@await RenderPartialAsync(_TocTreeNav.Create(new NavigationTreeItem
64+
@if (shouldRenderChildren)
6265
{
63-
IsPrimaryNavEnabled = Model.IsPrimaryNavEnabled,
64-
IsGlobalAssemblyBuild = Model.IsGlobalAssemblyBuild,
65-
Level = Model.Level + 1,
66-
SubTree = g,
67-
RootNavigationId = Model.RootNavigationId
68-
}))
66+
@await RenderPartialAsync(_TocTreeNav.Create(new NavigationTreeItem
67+
{
68+
IsPrimaryNavEnabled = Model.IsPrimaryNavEnabled,
69+
IsGlobalAssemblyBuild = Model.IsGlobalAssemblyBuild,
70+
Level = Model.Level + 1,
71+
SubTree = g,
72+
RootNavigationId = Model.RootNavigationId,
73+
MaxLevel = Model.MaxLevel
74+
}))
75+
}
6976
</ul>
7077
}
7178
</li>

src/Elastic.Markdown/DocumentationGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ private async Task GenerateDocumentationState(Cancel ctx)
334334
await DocumentationSet.OutputDirectory.FileSystem.File.WriteAllBytesAsync(stateFile.FullName, bytes, ctx);
335335
}
336336

337-
public async Task<string?> RenderLayout(MarkdownFile markdown, Cancel ctx)
337+
public async Task<(string, string)> RenderLayout(MarkdownFile markdown, Cancel ctx)
338338
{
339339
await DocumentationSet.Tree.Resolve(ctx);
340340
return await HtmlWriter.RenderLayout(markdown, ctx);

src/Elastic.Markdown/HtmlWriter.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,19 @@ public string Render(string markdown, IFileInfo? source)
4444
return MarkdownFile.CreateHtml(parsed);
4545
}
4646

47-
public async Task<string> RenderLayout(MarkdownFile markdown, Cancel ctx = default)
47+
public async Task<(string, string)> RenderLayout(MarkdownFile markdown, Cancel ctx = default)
4848
{
4949
var document = await markdown.ParseFullAsync(ctx);
5050
return await RenderLayout(markdown, document, ctx);
5151
}
5252

53-
private async Task<string> RenderLayout(MarkdownFile markdown, MarkdownDocument document, Cancel ctx = default)
53+
private async Task<(string, string)> RenderLayout(MarkdownFile markdown, MarkdownDocument document, Cancel ctx = default)
5454
{
5555
var html = MarkdownFile.CreateHtml(document);
5656
await DocumentationSet.Tree.Resolve(ctx);
5757

58-
var navigationHtml = await NavigationHtmlWriter.RenderNavigation(markdown.NavigationRoot, markdown.NavigationSource, ctx);
58+
var fullNavigationHtml = await NavigationHtmlWriter.RenderNavigation(markdown.NavigationRoot, markdown.NavigationSource, -1, ctx);
59+
var miniNavigationHtml = await NavigationHtmlWriter.RenderNavigation(markdown.NavigationRoot, markdown.NavigationSource, 1, ctx);
5960

6061
var current = PositionalNavigation.GetCurrent(markdown);
6162
var previous = PositionalNavigation.GetPrevious(markdown);
@@ -115,7 +116,7 @@ private async Task<string> RenderLayout(MarkdownFile markdown, MarkdownDocument
115116
PreviousDocument = previous,
116117
NextDocument = next,
117118
Parents = parents,
118-
NavigationHtml = navigationHtml,
119+
NavigationHtml = miniNavigationHtml,
119120
UrlPathPrefix = markdown.UrlPathPrefix,
120121
AppliesTo = markdown.YamlFrontMatter?.AppliesTo,
121122
GithubEditUrl = editUrl,
@@ -132,7 +133,7 @@ private async Task<string> RenderLayout(MarkdownFile markdown, MarkdownDocument
132133
Products = allProducts,
133134
VersionsConfig = DocumentationSet.Context.VersionsConfig
134135
});
135-
return await slice.RenderAsync(cancellationToken: ctx);
136+
return (fullNavigationHtml, await slice.RenderAsync(cancellationToken: ctx));
136137
}
137138

138139
public async Task<MarkdownDocument> WriteAsync(IFileInfo outputFile, MarkdownFile markdown, IConversionCollector? collector, Cancel ctx = default)
@@ -160,8 +161,9 @@ public async Task<MarkdownDocument> WriteAsync(IFileInfo outputFile, MarkdownFil
160161
var document = await markdown.ParseFullAsync(ctx);
161162

162163
var rendered = await RenderLayout(markdown, document, ctx);
163-
collector?.Collect(markdown, document, rendered);
164-
await writeFileSystem.File.WriteAllTextAsync(path, rendered, ctx);
164+
collector?.Collect(markdown, document, rendered.Item2);
165+
await writeFileSystem.File.WriteAllTextAsync(path, rendered.Item2, ctx);
166+
await writeFileSystem.File.WriteAllTextAsync(path.Replace(".html", ".nav.html"), rendered.Item1, ctx);
165167
return document;
166168
}
167169

0 commit comments

Comments
 (0)