Browse Source

Refactor TOC heading handling with Heading record

Introduces a Heading record to replace tuple usage for TOC headings, improving code readability and maintainability. Updates related logic in TocGeneratorService to use the new Heading type and adds constants for heading levels.
pull/23666/head
Ahmet Çelik 5 months ago
parent
commit
33b6f0f57d
  1. 32
      modules/docs/src/Volo.Docs.Web/TableOfContents/TocGeneratorService.cs

32
modules/docs/src/Volo.Docs.Web/TableOfContents/TocGeneratorService.cs

@ -11,6 +11,7 @@ namespace Volo.Docs.TableOfContents;
public class TocGeneratorService : ITocGeneratorService, ITransientDependency
{
private readonly HashSet<string> _generatedIds = [];
public record Heading(int Level, string Text, string Id);
public (string TocHtml, string ProcessedContent) GenerateTocAndProcessHeadings(string content)
{
@ -20,7 +21,7 @@ public class TocGeneratorService : ITocGeneratorService, ITransientDependency
}
_generatedIds.Clear();
var tocHeadings = new List<(int Level, string Text, string Id)>();
var tocHeadings = new List<Heading>();
var doc = new HtmlDocument();
doc.LoadHtml(content);
@ -50,7 +51,7 @@ public class TocGeneratorService : ITocGeneratorService, ITransientDependency
var level = int.Parse(node.Name.Substring(1));
if (level == 2 || level == 3)
{
tocHeadings.Add((level, node.InnerText.Trim(), id));
tocHeadings.Add(new Heading(level, node.InnerText.Trim(), id));
}
}
}
@ -91,13 +92,16 @@ public class TocGeneratorService : ITocGeneratorService, ITransientDependency
return finalId;
}
private static string BuildTocHtml(List<(int Level, string Text, string Id)> headings)
private static string BuildTocHtml(List<Heading> headings)
{
if (headings == null || headings.Count == 0)
{
return string.Empty;
}
const int H2Level = 2;
const int H3Level = 3;
var tocBuilder = new StringBuilder();
tocBuilder.Append("<ul class=\"nav nav-pills flex-column\">");
@ -107,47 +111,47 @@ public class TocGeneratorService : ITocGeneratorService, ITransientDependency
foreach (var (index, heading) in headings.Select((h, i) => (i, h)))
{
var isLastItem = index == headings.Count - 1;
var nextHeading = isLastItem ? default : headings[index + 1];
var hasChildren = nextHeading.Level == 3 && heading.Level == 2;
var nextHeading = isLastItem ? null : headings[index + 1];
var hasChildren = nextHeading?.Level == H3Level && heading.Level == H2Level;
if (heading.Level < currentLevel)
{
tocBuilder.Append("</ul></li>");
}
else if (!isFirstH2 && heading.Level == 2 && currentLevel == 2)
else if (heading.Level == currentLevel && heading.Level == H2Level && !isFirstH2)
{
tocBuilder.Append("</li>");
}
if (heading.Level == 2)
if (heading.Level == H2Level)
{
var liClass = hasChildren ? "nav-item toc-item-has-children" : "nav-item";
tocBuilder.Append($"<li class=\"{liClass}\"><a class=\"nav-link\" href=\"#{heading.Id}\">{heading.Text}</a>");
isFirstH2 = false;
}
else if (heading.Level == 3)
else if (heading.Level == H3Level)
{
if (currentLevel != 3)
if (currentLevel < H3Level)
{
tocBuilder.Append("<ul class=\"nav nav-pills flex-column\">");
}
tocBuilder.Append($"<li class=\"nav-item\"><a class=\"nav-link\" href=\"#{heading.Id}\">{heading.Text}</a></li>");
}
currentLevel = heading.Level;
}
if (currentLevel == 3)
if (currentLevel == H3Level)
{
tocBuilder.Append("</ul></li>");
tocBuilder.Append("</ul></li>");
}
else if (currentLevel == 2 && !isFirstH2)
else if (currentLevel == H2Level)
{
tocBuilder.Append("</li>");
}
tocBuilder.Append("</ul>");
tocBuilder.Append("</ul>");
return tocBuilder.ToString();
}

Loading…
Cancel
Save