@ -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 & & ! isFirstH 2)
{
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 & & ! isFirst H2)
else if ( currentLevel = = H2Level )
{
tocBuilder . Append ( "</li>" ) ;
}
tocBuilder . Append ( "</ul>" ) ;
tocBuilder . Append ( "</ul>" ) ;
return tocBuilder . ToString ( ) ;
}