Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 27 additions & 31 deletions lib/rdoc/generator/template/aliki/js/aliki.js
Original file line number Diff line number Diff line change
Expand Up @@ -305,28 +305,25 @@ function hookTocActiveHighlighting() {

if (targetHeadings.length === 0) return;

const observerOptions = {
root: null,
rootMargin: '0% 0px -35% 0px',
threshold: 0
};

const intersectingHeadings = new Set();
let activeLink = null;
const update = () => {
const firstIntersectingHeading = targetHeadings.find((heading) => {
return intersectingHeadings.has(heading);
});
if (!firstIntersectingHeading) return;
const correspondingLink = document.querySelector(`.toc-link[data-target="${firstIntersectingHeading.id}"]`);
if (!correspondingLink) return;

// Remove active class from all links
tocLinks.forEach((link) => {
link.classList.remove('active');
// Prefer the first heading in the upper 65% of the viewport. With no
// heading there, such as deep inside a long method list, the current
// section is the last heading above the viewport.
const viewportLimit = window.innerHeight * 0.65;
const currentHeading = targetHeadings.find((heading) => {
const rect = heading.getBoundingClientRect();
return rect.bottom > 0 && rect.top < viewportLimit;
}) || targetHeadings.findLast((heading) => {
return heading.getBoundingClientRect().top < 0;
});
if (!currentHeading) return;
const correspondingLink = document.querySelector(`.toc-link[data-target="${currentHeading.id}"]`);
if (!correspondingLink || correspondingLink === activeLink) return;

// Add active class to current link
activeLink?.classList.remove('active');
correspondingLink.classList.add('active');
activeLink = correspondingLink;

// Scroll link into view if needed
const tocNav = document.querySelector('#toc-nav');
Expand All @@ -339,21 +336,20 @@ function hookTocActiveHighlighting() {
}
}
};
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
intersectingHeadings.add(entry.target);
} else {
intersectingHeadings.delete(entry.target);
}

let updateScheduled = false;
const scheduleUpdate = () => {
if (updateScheduled) return;
updateScheduled = true;
requestAnimationFrame(() => {
updateScheduled = false;
update();
});
update();
}, observerOptions);
};
window.addEventListener('scroll', scheduleUpdate, { passive: true });
window.addEventListener('resize', scheduleUpdate);

// Observe all headings that have corresponding TOC links
targetHeadings.forEach((heading) => {
observer.observe(heading);
});
update();
}

function setHeadingScrollHandler(heading, link) {
Expand Down
Loading