|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function initializeStickyNavigation(navElementId) { |
|
|
const navElement = document.getElementById(navElementId); |
|
|
if (!navElement) { |
|
|
console.warn(`Navigation element #${navElementId} not found`); |
|
|
return; |
|
|
} |
|
|
|
|
|
const navSection = navElement.closest('section.page-navigation'); |
|
|
if (!navSection) { |
|
|
console.warn('Navigation section wrapper not found'); |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
const headerHeight = parseInt(getComputedStyle(document.documentElement) |
|
|
.getPropertyValue('--header-height')) || 100; |
|
|
const stickyThreshold = navSection.offsetTop - headerHeight; |
|
|
|
|
|
|
|
|
let scrollTimeout; |
|
|
const handleScroll = () => { |
|
|
const scrollPosition = window.pageYOffset || document.documentElement.scrollTop; |
|
|
navSection.classList.toggle('sticky-nav', scrollPosition >= stickyThreshold); |
|
|
}; |
|
|
|
|
|
window.addEventListener('scroll', () => { |
|
|
if (scrollTimeout) cancelAnimationFrame(scrollTimeout); |
|
|
scrollTimeout = requestAnimationFrame(handleScroll); |
|
|
}); |
|
|
|
|
|
handleScroll(); |
|
|
|
|
|
|
|
|
initializeActiveSectionTracking(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function initializeActiveSectionTracking() { |
|
|
|
|
|
const navLinks = document.querySelectorAll('.page-navigation a[href^="/#"], .page-navigation a[href*="#"]'); |
|
|
if (navLinks.length === 0) return; |
|
|
|
|
|
|
|
|
const sectionIds = Array.from(navLinks) |
|
|
.map(link => { |
|
|
const href = link.getAttribute('href'); |
|
|
const hashIndex = href.indexOf('#'); |
|
|
return hashIndex >= 0 ? href.substring(hashIndex + 1) : null; |
|
|
}) |
|
|
.filter(id => id); |
|
|
|
|
|
if (sectionIds.length === 0) return; |
|
|
|
|
|
|
|
|
const sections = sectionIds |
|
|
.map(id => document.getElementById(id)) |
|
|
.filter(section => section); |
|
|
|
|
|
if (sections.length === 0) return; |
|
|
|
|
|
|
|
|
const observerOptions = { |
|
|
root: null, |
|
|
rootMargin: '-20% 0px -60% 0px', |
|
|
threshold: [0, 0.1, 0.2, 0.5, 0.8, 1] |
|
|
}; |
|
|
|
|
|
let activeSection = null; |
|
|
|
|
|
const observer = new IntersectionObserver((entries) => { |
|
|
|
|
|
let mostVisible = null; |
|
|
let maxRatio = 0; |
|
|
|
|
|
entries.forEach(entry => { |
|
|
if (entry.isIntersecting && entry.intersectionRatio > maxRatio) { |
|
|
maxRatio = entry.intersectionRatio; |
|
|
mostVisible = entry.target; |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
if (mostVisible && mostVisible !== activeSection) { |
|
|
activeSection = mostVisible; |
|
|
updateActiveNavItem(mostVisible.id); |
|
|
} |
|
|
}, observerOptions); |
|
|
|
|
|
|
|
|
sections.forEach(section => observer.observe(section)); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function updateActiveNavItem(sectionId) { |
|
|
const navLinks = document.querySelectorAll('.page-navigation a'); |
|
|
|
|
|
navLinks.forEach(link => { |
|
|
const href = link.getAttribute('href'); |
|
|
const linkSectionId = href.substring(href.indexOf('#') + 1); |
|
|
|
|
|
if (linkSectionId === sectionId) { |
|
|
|
|
|
link.classList.add('text-blue-600', 'font-semibold'); |
|
|
link.classList.add('underline', 'decoration-2', 'underline-offset-2'); |
|
|
link.classList.remove('text-gray-700'); |
|
|
} else { |
|
|
|
|
|
link.classList.remove('text-blue-600', 'font-semibold'); |
|
|
link.classList.remove('underline', 'decoration-2', 'underline-offset-2'); |
|
|
link.classList.add('text-gray-700'); |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
window.toggleNavigation = function() { |
|
|
const navItems = document.getElementById('nav-items'); |
|
|
const navToggle = document.getElementById('nav-toggle'); |
|
|
const navContainer = document.getElementById('nav-container'); |
|
|
|
|
|
if (!navItems || !navToggle) return; |
|
|
|
|
|
const isCollapsed = navItems.classList.contains('nav-collapsed'); |
|
|
|
|
|
|
|
|
navItems.classList.toggle('nav-collapsed', !isCollapsed); |
|
|
navToggle.setAttribute('aria-expanded', isCollapsed ? 'true' : 'false'); |
|
|
navContainer?.classList.toggle('nav-container-collapsed', !isCollapsed); |
|
|
}; |
|
|
|
|
|
|