Skip to content

Commit 2037f09

Browse files
reakaleekMpdreamz
andauthored
Optimize lazy load navigation (#1514)
* Optimize lazy load navigation * Only render hx node if the navigationFilename is not empty * Fix typo in comment * Fix concurrent access to dictionary * Run prettier * Fix empty navigation * refactor: Use static empty NavigationRenderResult object * Update src/Elastic.Documentation.Site/Navigation/INavigationHtmlWriter.cs Co-authored-by: Martijn Laarman <[email protected]> --------- Co-authored-by: Martijn Laarman <[email protected]>
1 parent 9ca7223 commit 2037f09

File tree

17 files changed

+115
-46
lines changed

17 files changed

+115
-46
lines changed

src/Elastic.ApiExplorer/ApiViewModel.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public GlobalLayoutViewModel CreateGlobalLayoutModel() =>
3737
Previous = null,
3838
Next = null,
3939
NavigationHtml = NavigationHtml,
40+
NavigationFileName = string.Empty,
4041
UrlPathPrefix = BuildContext.UrlPathPrefix,
4142
AllowIndexing = BuildContext.AllowIndexing,
4243
CanonicalBaseUrl = BuildContext.CanonicalBaseUrl,

src/Elastic.ApiExplorer/OpenApiGenerator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,11 +288,11 @@ 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"), INavigationHtmlWriter.AllLevels, ctx);
291+
var navigationRenderResult = await navigationRenderer.RenderNavigation(current.NavigationRoot, new Uri("http://ignored.example"), INavigationHtmlWriter.AllLevels, ctx);
292292
renderContext = renderContext with
293293
{
294294
CurrentNavigation = current,
295-
NavigationHtml = navigationHtml
295+
NavigationHtml = navigationRenderResult.Html
296296
};
297297
await using var stream = _writeFileSystem.FileStream.New(outputFile.FullName, FileMode.OpenOrCreate);
298298
await page.RenderAsync(stream, renderContext, ctx);

src/Elastic.Documentation.Site/Assets/main.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,23 @@ import { $, $$ } from 'select-dom'
1414
import { UAParser } from 'ua-parser-js'
1515

1616
const { getOS } = new UAParser()
17+
const isLazyLoadNavigationEnabled =
18+
$('meta[property="docs:feature:lazy-load-navigation"]')?.content === 'true'
1719

1820
document.addEventListener('htmx:load', function (event) {
19-
console.log('htmx:load')
20-
console.log(event.detail)
2121
initTocNav()
2222
initHighlight()
2323
initCopyButton()
2424
initTabs()
25-
initNav()
25+
26+
// We do this so that the navigation is not initialized twice
27+
if (isLazyLoadNavigationEnabled) {
28+
if (event.detail.elt.id === 'nav-tree') {
29+
initNav()
30+
}
31+
} else {
32+
initNav()
33+
}
2634
initSmoothScroll()
2735
openDetailsWithAnchor()
2836
initDismissibleBanner()

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,4 @@
3636
{
3737
<meta property="og:url" content="@Model.CanonicalUrl" />
3838
}
39+
<meta property="docs:feature:lazy-load-navigation" content="@Model.Features.LazyLoadNavigation" />

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
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">
33

4-
@if (Model.Features.LazyLoadNavigation)
4+
@if (Model.Features.LazyLoadNavigation && !string.IsNullOrEmpty(Model.NavigationFileName))
55
{
6-
<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>
6+
<div class="hidden" hx-get="@(Model.Link(Model.NavigationFileName))" hx-trigger="load" hx-params="nav" hx-push-url="false" hx-swap="innerHTML" hx-target="#pages-nav"></div>
77
}
88
<nav
99
id="pages-nav"

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,23 @@ public interface INavigationHtmlWriter
1010
{
1111
const int AllLevels = -1;
1212

13-
Task<string> RenderNavigation(IRootNavigationItem<INavigationModel, INavigationItem> currentRootNavigation, Uri navigationSource, int maxLevel, Cancel ctx = default);
13+
Task<NavigationRenderResult> RenderNavigation(IRootNavigationItem<INavigationModel, INavigationItem> currentRootNavigation, Uri navigationSource,
14+
int maxLevel, Cancel ctx = default);
1415

1516
async Task<string> Render(NavigationViewModel model, Cancel ctx)
1617
{
1718
var slice = _TocTree.Create(model);
1819
return await slice.RenderAsync(cancellationToken: ctx);
1920
}
2021
}
22+
public record NavigationRenderResult
23+
{
24+
public static NavigationRenderResult Empty { get; } = new()
25+
{
26+
Html = string.Empty,
27+
Id = "empty-navigation" // random id
28+
};
29+
30+
public required string Html { get; init; }
31+
public required string Id { get; init; }
32+
}

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

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System.Collections.Concurrent;
66
using Elastic.Documentation.Configuration;
7+
using Elastic.Documentation.Extensions;
78

89
namespace Elastic.Documentation.Site.Navigation;
910

@@ -12,19 +13,29 @@ public class IsolatedBuildNavigationHtmlWriter(BuildContext context, IRootNaviga
1213
{
1314
private readonly ConcurrentDictionary<(string, int), string> _renderedNavigationCache = [];
1415

15-
public async Task<string> RenderNavigation(IRootNavigationItem<INavigationModel, INavigationItem> currentRootNavigation, Uri navigationSource, int maxLevel, Cancel ctx = default)
16+
public async Task<NavigationRenderResult> RenderNavigation(IRootNavigationItem<INavigationModel, INavigationItem> currentRootNavigation,
17+
Uri navigationSource, int maxLevel, Cancel ctx = default)
1618
{
1719
var navigation = context.Configuration.Features.PrimaryNavEnabled || currentRootNavigation.IsUsingNavigationDropdown
1820
? currentRootNavigation
1921
: siteRoot;
20-
22+
var id = ShortId.Create($"{(navigation.Id, maxLevel).GetHashCode()}");
2123
if (_renderedNavigationCache.TryGetValue((navigation.Id, maxLevel), out var value))
22-
return value;
23-
24+
{
25+
return new NavigationRenderResult
26+
{
27+
Html = value,
28+
Id = id
29+
};
30+
}
2431
var model = CreateNavigationModel(navigation, maxLevel);
2532
value = await ((INavigationHtmlWriter)this).Render(model, ctx);
2633
_renderedNavigationCache[(navigation.Id, maxLevel)] = value;
27-
return value;
34+
return new NavigationRenderResult
35+
{
36+
Html = value,
37+
Id = id
38+
};
2839
}
2940

3041
private NavigationViewModel CreateNavigationModel(IRootNavigationItem<INavigationModel, INavigationItem> navigation, int maxLevel) =>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
@using Elastic.Documentation.Site.Navigation
22
@inherits RazorSlice<Elastic.Documentation.Site.Navigation.NavigationViewModel>
33

4-
<div class="pb-20 font-body">
4+
<div class="pb-20 font-body" id="nav-tree">
55
@{
66
var currentTopLevelItem = Model.TopLevelItems.FirstOrDefault(i => i.Id == Model.Tree.Id) ?? Model.Tree;
77
}

src/Elastic.Documentation.Site/_ViewModels.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public record GlobalLayoutViewModel
2727
public required INavigationItem? Next { get; init; }
2828

2929
public required string NavigationHtml { get; init; }
30+
public required string NavigationFileName { get; init; }
3031
public required string? UrlPathPrefix { get; init; }
3132
public required Uri? CanonicalBaseUrl { get; init; }
3233
public string? CanonicalUrl => CanonicalBaseUrl is not null ?

src/Elastic.Markdown/Exporters/DocumentationFileExporter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public class DocumentationFileExporter(IFileSystem readFileSystem, IFileSystem w
6666
public override async ValueTask ProcessFile(ProcessingFileContext context, Cancel ctx)
6767
{
6868
if (context.File is MarkdownFile markdown)
69-
context.MarkdownDocument = await context.HtmlWriter.WriteAsync(context.OutputFile, markdown, context.ConversionCollector, ctx);
69+
context.MarkdownDocument = await context.HtmlWriter.WriteAsync(context.BuildContext.DocumentationOutputDirectory, context.OutputFile, markdown, context.ConversionCollector, ctx);
7070
else
7171
{
7272
if (context.OutputFile.Directory is { Exists: false })

0 commit comments

Comments
 (0)