class OnThisPage extends window.UIHandler {
|
constructor() {
|
super();
|
|
// Initialize state tracking
|
this.navOpen = false;
|
|
// Bind methods
|
this.toggleNav = this.toggleNav.bind(this);
|
|
// Bind elements first
|
this.bindElements();
|
|
|
if (this.elements.nav) {
|
if(this.elements.toggle){
|
// Bind click directly
|
this.elements.toggle.addEventListener('click', this.toggleNav);
|
|
// Bind UIHandler events for escape and outside clicks
|
this.bindEvents();
|
}
|
|
|
// Set up section observer
|
this.setupSectionObserver();
|
}
|
}
|
|
bindElements() {
|
const nav = document.querySelector('nav.on-this-page');
|
|
if (!nav) return;
|
this.elements = {
|
nav,
|
links: nav.querySelectorAll('a'),
|
sections: Array.from(nav.querySelectorAll('a'))
|
.map(a => {
|
const id = a.getAttribute('href');
|
return document.querySelector(id);
|
})
|
.filter(Boolean)
|
};
|
if(nav.querySelector('button.toggle')){
|
this.elements.toggle = nav.querySelector('button.toggle');
|
}
|
}
|
|
// Override to prevent UIHandler's component event binding
|
bindComponentEvents() {
|
// Intentionally empty
|
}
|
|
toggleNav(event) {
|
event?.preventDefault();
|
event?.stopPropagation();
|
|
const { nav, toggle } = this.elements;
|
if (!nav || !toggle) return;
|
|
// Toggle state
|
this.navOpen = !this.navOpen;
|
|
// Update DOM
|
if (this.navOpen) {
|
nav.classList.add('open');
|
toggle.setAttribute('aria-label', 'Hide Index');
|
toggle.setAttribute('aria-expanded', 'true');
|
this.bindLinkHandlers();
|
} else {
|
nav.classList.remove('open');
|
toggle.setAttribute('aria-label', 'Show Index');
|
toggle.setAttribute('aria-expanded', 'false');
|
this.cleanupLinkHandlers();
|
}
|
}
|
|
bindLinkHandlers() {
|
const { links } = this.elements;
|
links?.forEach(link => {
|
link._boundHandler = () => {
|
this.navOpen = false;
|
this.elements.nav.classList.remove('open');
|
this.elements.toggle.setAttribute('aria-label', 'Show Index');
|
this.elements.toggle.setAttribute('aria-expanded', 'false');
|
this.cleanupLinkHandlers();
|
};
|
link.addEventListener('click', link._boundHandler);
|
});
|
}
|
|
cleanupLinkHandlers() {
|
const { links } = this.elements;
|
links?.forEach(link => {
|
if (link._boundHandler) {
|
link.removeEventListener('click', link._boundHandler);
|
delete link._boundHandler;
|
}
|
});
|
}
|
|
setupSectionObserver() {
|
const { sections } = this.elements;
|
|
if (!sections?.length) return;
|
|
this.initializeObserver(
|
'sections',
|
sections,
|
{
|
rootMargin: '-50% 0% -50% 0%',
|
threshold: 0
|
},
|
(entries) => {
|
entries.forEach(entry => {
|
if (!entry.isIntersecting) return;
|
|
const id = entry.target.id;
|
const link = this.elements.nav?.querySelector(`a[href="#${id}"]`);
|
if (link) {
|
this.updateActiveClasses(link);
|
}
|
});
|
}
|
);
|
}
|
|
updateActiveClasses(activeLink) {
|
const listItem = activeLink.closest('li');
|
if (!listItem) return;
|
|
// Remove existing active and adjacent classes
|
const allItems = this.elements.nav.querySelectorAll('li');
|
allItems.forEach(item => {
|
item.classList.remove('active', 'adj');
|
});
|
|
// Add new classes
|
listItem.classList.add('active');
|
|
// Add adjacent classes
|
if (listItem.previousElementSibling) {
|
listItem.previousElementSibling.classList.add('adj');
|
}
|
if (listItem.nextElementSibling) {
|
listItem.nextElementSibling.classList.add('adj');
|
}
|
}
|
|
// Use local state for component active check
|
isComponentActive(componentKey) {
|
if (componentKey === 'nav') {
|
return this.navOpen;
|
}
|
return super.isComponentActive(componentKey);
|
}
|
|
// UIHandler event handlers
|
handleOutsideClick(event) {
|
if (this.navOpen && !this.elements.nav.contains(event.target)) {
|
this.toggleNav(event);
|
}
|
}
|
|
handleEscapeKey(event) {
|
if (event.key === 'Escape' && this.navOpen) {
|
this.toggleNav(event);
|
event.preventDefault();
|
}
|
}
|
|
cleanup() {
|
this.cleanupLinkHandlers();
|
super.cleanup();
|
}
|
}
|
|
// Initialize
|
document.addEventListener('DOMContentLoaded', () => {
|
if (document.querySelector('nav.on-this-page')) {
|
window.onThisPage = new OnThisPage();
|
}
|
});
|