mirror of
https://github.com/arthur-pbty/moon.git
synced 2026-06-03 15:07:31 +02:00
137 lines
5.2 KiB
TypeScript
137 lines
5.2 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { useLocale } from './LocaleProvider';
|
|
import { ts } from '@/lib/i18n';
|
|
import { LOCALES, Locale } from '@/lib/i18n';
|
|
|
|
const NAV_ITEMS = [
|
|
{ key: 'nav_home' as const, href: '#hero' },
|
|
{ key: 'nav_calendar' as const, href: '#calendar' },
|
|
{ key: 'nav_fullmoons' as const, href: '#fullmoons' },
|
|
{ key: 'nav_simulator' as const, href: '#simulator' },
|
|
{ key: 'nav_articles' as const, href: '#articles' },
|
|
{ key: 'nav_quiz' as const, href: '#quiz' },
|
|
];
|
|
|
|
export default function Navigation() {
|
|
const { locale, setLocale } = useLocale();
|
|
const [scrolled, setScrolled] = useState(false);
|
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
const [langOpen, setLangOpen] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const onScroll = () => setScrolled(window.scrollY > 50);
|
|
window.addEventListener('scroll', onScroll);
|
|
return () => window.removeEventListener('scroll', onScroll);
|
|
}, []);
|
|
|
|
return (
|
|
<>
|
|
<header
|
|
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${
|
|
scrolled ? 'bg-[rgba(10,10,26,0.95)] backdrop-blur-xl shadow-lg shadow-indigo-500/5' : 'bg-transparent'
|
|
}`}
|
|
>
|
|
<nav className="max-w-7xl mx-auto px-4 py-3 flex items-center justify-between">
|
|
<a href="#hero" className="flex items-center gap-2 text-lg font-bold text-white">
|
|
<span className="text-2xl">🌕</span>
|
|
<span className="hidden sm:inline gradient-text-brand">
|
|
Moon Phases
|
|
</span>
|
|
</a>
|
|
|
|
{/* Desktop nav */}
|
|
<div className="hidden md:flex items-center gap-1">
|
|
{NAV_ITEMS.map((item) => (
|
|
<a key={item.key} href={item.href} className="nav-link">
|
|
{ts(item.key, locale)}
|
|
</a>
|
|
))}
|
|
</div>
|
|
|
|
<div className="flex items-center gap-2">
|
|
{/* Language selector */}
|
|
<div className="relative">
|
|
<button
|
|
onClick={() => setLangOpen(!langOpen)}
|
|
className="flex items-center gap-1 px-3 py-1.5 rounded-full text-sm bg-white/5 border border-white/10 hover:border-indigo-400/50 transition-all"
|
|
aria-label="Select language"
|
|
>
|
|
{LOCALES.find(l => l.code === locale)?.flag} <span className="hidden sm:inline text-xs uppercase">{locale}</span>
|
|
</button>
|
|
|
|
{langOpen && (
|
|
<div className="absolute right-0 top-full mt-2 w-48 glass-card p-2 grid grid-cols-2 gap-1 max-h-64 overflow-y-auto">
|
|
{LOCALES.map((l) => (
|
|
<button
|
|
key={l.code}
|
|
onClick={() => { setLocale(l.code as Locale); setLangOpen(false); }}
|
|
className={`flex items-center gap-2 px-3 py-2 rounded-lg text-sm transition-all hover:bg-indigo-500/20 ${
|
|
locale === l.code ? 'bg-indigo-500/20 text-indigo-300' : 'text-white/70'
|
|
}`}
|
|
>
|
|
<span>{l.flag}</span>
|
|
<span className="truncate">{l.name}</span>
|
|
</button>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Mobile menu button */}
|
|
<button
|
|
onClick={() => setMenuOpen(!menuOpen)}
|
|
className="md:hidden p-2 rounded-lg hover:bg-white/10 transition-all"
|
|
aria-label="Menu"
|
|
>
|
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
{menuOpen ? (
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
|
) : (
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
|
)}
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</nav>
|
|
|
|
{/* Mobile menu */}
|
|
{menuOpen && (
|
|
<div className="md:hidden bg-[rgba(10,10,26,0.98)] backdrop-blur-xl border-t border-white/10 px-4 py-4">
|
|
{NAV_ITEMS.map((item) => (
|
|
<a
|
|
key={item.key}
|
|
href={item.href}
|
|
onClick={() => setMenuOpen(false)}
|
|
className="block py-3 px-4 text-white/70 hover:text-indigo-300 hover:bg-indigo-500/10 rounded-lg transition-all"
|
|
>
|
|
{ts(item.key, locale)}
|
|
</a>
|
|
))}
|
|
</div>
|
|
)}
|
|
</header>
|
|
|
|
{/* Mobile bottom nav */}
|
|
<div className="mobile-nav">
|
|
{NAV_ITEMS.slice(0, 5).map((item) => (
|
|
<a
|
|
key={item.key}
|
|
href={item.href}
|
|
className="flex flex-col items-center gap-0.5 text-xs text-white/50 hover:text-indigo-300 transition-all py-1"
|
|
>
|
|
<span className="text-base">
|
|
{item.key === 'nav_home' ? '🏠' :
|
|
item.key === 'nav_calendar' ? '📅' :
|
|
item.key === 'nav_fullmoons' ? '🌕' :
|
|
item.key === 'nav_simulator' ? '🔭' : '🌐'}
|
|
</span>
|
|
<span className="truncate max-w-15">{ts(item.key, locale)}</span>
|
|
</a>
|
|
))}
|
|
</div>
|
|
</>
|
|
);
|
|
}
|