overview / js /components /PageNavigation.js
Yacine Jernite
sticky_nav
f07b5d3
// PageNavigation.js - Reusable page navigation component
// Creates the "On this page" / breadcrumb navigation card
/**
* Renders a page navigation card
* @param {string} label - Label for the navigation (e.g., "On this page:", "Area Name:")
* @param {Array} items - Array of navigation items: { text, href, active }
* @returns {string} HTML string for the navigation card
*/
export function renderPageNavigation(label, items) {
if (!items || items.length === 0) {
return '';
}
return `
<section class="page-navigation mb-8 relative z-20 px-4 sm:px-6 lg:px-8">
<div id="nav-container" class="bg-white/85 backdrop-blur-sm shadow-md rounded-lg px-6 py-4 w-full border border-gray-200" style="max-width: min(90%, 1400px); margin: 0 auto;">
<div class="flex items-center justify-between gap-3">
<!-- Navigation items -->
<div id="nav-items" class="flex items-center gap-3 overflow-x-auto flex-1">
<span class="text-gray-600 font-semibold whitespace-nowrap text-base">${label}</span>
${items.map((item, index) => {
const activeClasses = item.active
? 'text-blue-600 font-semibold underline decoration-2 underline-offset-2'
: 'text-gray-700 hover:text-blue-600';
return `
${index > 0 ? '<span class="text-gray-300">•</span>' : ''}
<a href="${item.href}" class="px-4 py-2 text-base font-medium ${activeClasses} hover:bg-blue-50 rounded whitespace-nowrap">${item.text}</a>
`;
}).join('')}
</div>
<!-- Hamburger toggle -->
<button id="nav-toggle"
class="flex-shrink-0 p-2 rounded-lg hover:bg-gray-100"
onclick="toggleNavigation()"
aria-label="Toggle navigation menu"
aria-expanded="true">
<svg class="w-6 h-6 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
</div>
</div>
</section>
`;
}
/**
* Convenience function for home page navigation
*/
export function renderHomeNavigation() {
return renderPageNavigation('On this page:', [
{ text: 'About', href: '/#about' },
{ text: 'Recent Works', href: '/#recent-works' },
{ text: 'Research Areas', href: '/#research-areas' },
{ text: 'Team Members', href: '/#team' }
]);
}
/**
* Convenience function for area page navigation
* @param {string} areaId - Area identifier
* @param {string} areaTitle - Area display title
* @param {Array} topics - Array of topics: { id, navName }
* @param {string} currentTopicId - Current topic ID (if any)
*/
export function renderAreaNavigation(areaId, areaTitle, topics, currentTopicId = null) {
const items = [
{
text: 'Overview',
href: `/${areaId}#overview`,
active: currentTopicId === null || currentTopicId === 'overview'
},
...topics.map(topic => ({
text: topic.navName,
href: `/${areaId}#${topic.id}`,
active: topic.id === currentTopicId
}))
];
return renderPageNavigation(`${areaTitle}:`, items);
}