mirror of
https://github.com/arthur-pbty/moon.git
synced 2026-06-06 22:43:20 +02:00
first commit
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
'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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user