overview / js /router.js
yjernite's picture
yjernite HF Staff
Upload 2 files
a640871 verified
raw
history blame
10.2 kB
// router.js - Simple SPA router
import { renderHomePage, getHomePageSidebar } from './pages/HomePage.js';
import { renderAreaPage, getAreaPageSidebar } from './pages/AreaPage.js';
import { renderResourcesPage, getResourcesPageSidebar } from './pages/ResourcesPage.js';
class Router {
constructor() {
this.routes = {
'/': 'home',
'/home': 'home',
'/efficiency': 'efficiency',
'/personal': 'personal',
'/rights': 'rights',
'/ecosystems': 'ecosystems',
'/about': 'resources'
};
this.currentPage = null;
this.init();
}
init() {
// Handle initial page load
this.loadPage(window.location.pathname);
// Handle browser back/forward
window.addEventListener('popstate', (e) => {
const path = window.location.pathname;
const hash = window.location.hash;
const fullUrl = path + hash;
// Use navigateToUrl to ensure proper scroll behavior
this.navigateToUrl(fullUrl);
});
// Handle hash changes for navigation
window.addEventListener('hashchange', (e) => {
const hash = window.location.hash.substring(1);
if (hash) {
// Scroll to the section after a short delay to ensure content is loaded
setTimeout(() => this.scrollToSection(hash), 100);
}
});
// Handle initial hash if present
const initialHash = window.location.hash.substring(1);
if (initialHash) {
setTimeout(() => this.scrollToSection(initialHash), 200);
}
// Initialize unified navigation handling
this.initializeNavigation();
}
async loadPage(path) {
const route = this.routes[path] || 'home';
if (this.currentPage === route) {
// Same page, no need to reload
return;
}
this.currentPage = route;
try {
switch (route) {
case 'home':
await this.loadHomePage();
break;
case 'efficiency':
case 'personal':
case 'rights':
case 'ecosystems':
await this.loadAreaPage(route);
break;
case 'resources':
await this.loadResourcesPage();
break;
default:
await this.loadHomePage();
}
} catch (error) {
console.error('Error loading page:', error);
// Fallback to home page
await this.loadHomePage();
}
return Promise.resolve();
}
async loadHomePage() {
const mainContent = document.getElementById('main-content');
const leftSidebar = document.getElementById('left-sidebar');
if (!mainContent) {
return;
}
try {
// Get page content and sidebar
const homePage = renderHomePage();
// Update content
const contentContainer = mainContent.querySelector('.max-w-4xl') || mainContent;
contentContainer.innerHTML = homePage.content;
// Update sidebar if it exists
if (leftSidebar) {
leftSidebar.innerHTML = getHomePageSidebar();
}
// Initialize page
if (homePage.init) {
homePage.init();
}
// Update navigation state
this.updateNavigation('home');
} catch (error) {
// Fallback to error content
const contentContainer = mainContent.querySelector('.max-w-4xl') || mainContent;
contentContainer.innerHTML = `
<div class="bg-white rounded-lg shadow-sm p-8">
<h1 class="text-3xl font-bold text-red-600 mb-6">Error Loading Page</h1>
<p class="text-gray-700">Sorry, there was an error loading the home page.</p>
</div>
`;
}
}
async loadAreaPage(area) {
const mainContent = document.getElementById('main-content');
const leftSidebar = document.getElementById('left-sidebar');
if (!mainContent) {
return;
}
try {
// Get area page content and sidebar
const areaPage = renderAreaPage(area);
// Update content
const contentContainer = mainContent.querySelector('.max-w-4xl') || mainContent;
contentContainer.innerHTML = areaPage.content;
// Update sidebar
if (leftSidebar) {
leftSidebar.innerHTML = getAreaPageSidebar(area);
}
// Initialize page
if (areaPage.init) {
areaPage.init();
}
} catch (error) {
console.error(`Error loading ${area} page:`, error);
// Fallback content
const contentContainer = mainContent.querySelector('.max-w-4xl') || mainContent;
contentContainer.innerHTML = `
<div class="bg-white rounded-lg shadow-sm p-8">
<h1 class="text-3xl font-bold text-red-600 mb-6">Error Loading Page</h1>
<p class="text-gray-700">Sorry, there was an error loading the ${area} page.</p>
</div>
`;
}
this.updateNavigation(area);
}
async loadResourcesPage() {
const mainContent = document.getElementById('main-content');
const leftSidebar = document.getElementById('left-sidebar');
if (!mainContent) return;
try {
// Get resources page content and sidebar
const resourcesPage = renderResourcesPage();
// Update content
const contentContainer = mainContent.querySelector('.max-w-4xl') || mainContent;
contentContainer.innerHTML = resourcesPage.content;
// Update sidebar
if (leftSidebar) {
leftSidebar.innerHTML = getResourcesPageSidebar();
}
// Initialize page
if (resourcesPage.init) {
resourcesPage.init();
}
} catch (error) {
// Fallback content
const contentContainer = mainContent.querySelector('.max-w-4xl') || mainContent;
contentContainer.innerHTML = `
<div class="bg-white rounded-lg shadow-sm p-8">
<h1 class="text-3xl font-bold text-red-600 mb-6">Error Loading Page</h1>
<p class="text-gray-700">Sorry, there was an error loading the resources page.</p>
</div>
`;
}
this.updateNavigation('resources');
}
updateNavigation(currentPage) {
// Update header navigation active states
const navLinks = document.querySelectorAll('header nav a');
navLinks.forEach(link => {
link.classList.remove('text-blue-600', 'bg-blue-50');
link.classList.add('text-gray-700');
const href = link.getAttribute('href');
if ((currentPage === 'home' && (href === '/' || href === '/home')) ||
(currentPage === 'resources' && href === '/about') ||
(currentPage !== 'home' && currentPage !== 'resources' && href === `/${currentPage}`)) {
link.classList.remove('text-gray-700');
link.classList.add('text-blue-600', 'bg-blue-50');
}
});
}
initializeNavigation() {
// Use event delegation to handle all navigation links
document.addEventListener('click', (e) => {
const link = e.target.closest('a');
if (!link) return;
const href = link.getAttribute('href');
if (!href) return;
// Handle different types of links
if (href.startsWith('/') && !href.startsWith('/js/') && !href.startsWith('/css/') && !href.startsWith('/images/')) {
// Check if this is a known SPA route
const path = href.split('#')[0];
if (this.routes[path]) {
e.preventDefault();
this.navigateToUrl(href);
}
} else if (href.startsWith('#')) {
// Same-page hash navigation
e.preventDefault();
this.scrollToSection(href.substring(1));
}
// External links are handled normally by browser
});
}
navigateToUrl(fullUrl) {
// Parse URL into path and hash
const [path, hash] = fullUrl.split('#');
// Update browser URL
window.history.pushState({}, '', fullUrl);
// Load page and then handle hash
this.loadPage(path).then(() => {
if (hash) {
setTimeout(() => this.scrollToSection(hash), 100);
} else {
// No hash - scroll to top of page with a delay to ensure content is rendered
setTimeout(() => {
window.scrollTo({ top: 0, behavior: 'smooth' });
}, 100);
}
});
}
scrollToSection(sectionId) {
const element = document.getElementById(sectionId);
if (element) {
// Scroll to element with offset to show title
const elementRect = element.getBoundingClientRect();
const absoluteElementTop = elementRect.top + window.pageYOffset;
const offset = 120; // Account for fixed header + some padding
window.scrollTo({
top: absoluteElementTop - offset,
behavior: 'smooth'
});
}
}
}
// Export router instance
export const router = new Router();