feat(toc): add folding TOC elements for more compact TOC table

This commit is contained in:
David Bailey 2025-02-13 10:45:32 +01:00
parent da25a786d1
commit 9870967093
3 changed files with 77 additions and 14 deletions

View file

@ -165,8 +165,11 @@ a:hover {
margin-right: 2rem;
}
:target {
scroll-margin-top: 6rem;
:target, html, body {
scroll-margin-top: 140px;
}
:target {
outline: 1px solid var(--highlight_1);
}
#main_content_flexbox {

View file

@ -37,7 +37,7 @@
color: var(--toc-fg) !important;
}
& .active {
& .active > a {
background-color: var(--toc-bg);
border-bottom: 1px solid var(--highlight_1);
@ -57,4 +57,11 @@
margin-bottom: 0.2em;
transition: all 1s;
}
& .toc_collapsing {
display: none;
}
& li:is(:has(.active),.active) > ol > .toc_collapsing {
display: block;
}
}

View file

@ -7,7 +7,7 @@ let tocId = "toc";
const TOC_IDENTIFIER = "toc";
const TOC_MAIN_CONTENT_ID = "#content_article"
const TOC_TOP_PIXEL_MARGIN = 300;
const TOC_TOP_PIXEL_MARGIN = 150;
class TocTracker {
known_heading_elements = [];
@ -49,14 +49,25 @@ class TocTracker {
throw Error("A `main` tag section is required to query headings from.");
}
let headings = main.querySelectorAll("h1, h2, h3, h4, h5, h6");
let headings = main.querySelectorAll("h1, h2, h3, h4, h5, h6, li strong");
headings.forEach((heading, index) => {
const heading_level = parseInt(heading.tagName.slice(-1));
let heading_level = parseInt(heading.tagName.slice(-1));
let heading_collapsed = false;
let heading_name = heading.innerHTML;
if(heading.tagName == 'STRONG') {
heading_level = -1;
heading_collapsed = true;
heading = heading.closest('li');
}
this.known_heading_elements.push({
level: heading_level,
name: heading.innerHTML,
name: heading_name,
dom: heading,
collapse: heading_collapsed
});
});
}
@ -65,6 +76,23 @@ class TocTracker {
this.known_heading_elements.sort((a, b) => {
return a.dom.getBoundingClientRect().y
- b.dom.getBoundingClientRect().y;
});
let lastHeadingLevel = 0;
this.known_heading_elements.forEach((element) => {
if(element.level == -1) {
let extra_depth = 0;
let current_dom = element.dom;
while(current_dom.tagName != 'ARTICLE') {
if((current_dom.tagName == 'OL') || (current_dom.tagName == 'UL'))
extra_depth += 1;
current_dom = current_dom.parentElement.closest('ol,ul,article');
}
element.level = lastHeadingLevel+extra_depth;
}
else
lastHeadingLevel = element.level;
});
}
@ -192,7 +220,7 @@ class TocNavBarUpdater {
let newNode = document.createElement('li');
const pathURL = location.pathname + '#' + pathItem.id + location.search;
newNode.innerHTML = "<a href=" + pathURL + "> #<sub>" + pathItem.level + '</sub>' + pathItem.name + '</a>'
newNode.innerHTML = "<a hx-boost=false href=" + pathURL + "> #<sub>" + pathItem.level + '</sub>' + pathItem.name + '</a>'
this.navbar_dom.appendChild(newNode);
this.added_navbar_elements.push(newNode);
@ -224,17 +252,37 @@ class TocSidemenu {
}
_generateSidebar() {
this.toc_tracker.known_heading_elements.forEach(element => {
let new_element = document.createElement('li');
let toc_stack = [this.sidebar_dom];
let last_added_li = null;
this.toc_tracker.known_heading_elements.forEach(element => {
while(element.level > toc_stack.length) {
if(!last_added_li) {
last_added_li = document.createElement('li');
toc_stack[toc_stack.length-1].appendChild(last_added_li);
}
let new_ol = document.createElement('ol');
last_added_li.appendChild(new_ol);
last_added_li = false;
toc_stack.push(new_ol);
}
while(element.level < toc_stack.length)
toc_stack.pop();
let new_element = document.createElement('li');
last_added_li = new_element;
const pathURL = location.pathname + '#' + element.id + location.search;
new_element.style = "padding-left: " + element.level * 0.8 + "em";
new_element.innerHTML = "<a href=" + pathURL + ">" + element.name + "</a>";
new_element.innerHTML = "<a hx-boost=false style=\"padding-left: " + element.level * 0.8 + "em\" href=" + pathURL + ">" + element.name + "</a>";
if(element.collapse)
new_element.classList.add('toc_collapsing');
this.sidebar_elements[element.id] = new_element;
this.sidebar_dom.appendChild(new_element);
toc_stack[toc_stack.length-1].appendChild(new_element);
});
}
@ -251,13 +299,18 @@ class TocSidemenu {
if(this.currently_active_entry) {
this.currently_active_entry.classList.remove('active');
}
Object.values(this.sidebar_elements).forEach((entry) => entry.classList.remove('contains_active'));
let active_entry = this.sidebar_elements[entry.dom.id];
active_entry.classList.add('active');
this.currently_active_entry = active_entry;
entry.path.forEach((path_piece) => this.sidebar_elements[path_piece.id].classList.add('contains_active'));
}
}
let tracker = new TocTracker();
let navbar_updater = new TocNavBarUpdater(tracker);
let sidebar_updater = new TocSidemenu(tracker);
let sidebar_updater = new TocSidemenu(tracker);
document.addEventListener('DOMContentLoaded', (event) => tracker.reloadHeadings());