119 lines
5.1 KiB
React
119 lines
5.1 KiB
React
import { useEffect, useState } from 'react';
|
||
import { Icons, MoonMark, getBootstrap } from './atoms.jsx';
|
||
|
||
export default function Nav() {
|
||
const [scrolled, setScrolled] = useState(false);
|
||
const [open, setOpen] = useState(false);
|
||
|
||
useEffect(() => {
|
||
const onScroll = () => setScrolled(window.scrollY > 40);
|
||
window.addEventListener('scroll', onScroll, { passive: true });
|
||
return () => window.removeEventListener('scroll', onScroll);
|
||
}, []);
|
||
|
||
useEffect(() => {
|
||
document.body.style.overflow = open ? 'hidden' : '';
|
||
return () => { document.body.style.overflow = ''; };
|
||
}, [open]);
|
||
|
||
const close = () => setOpen(false);
|
||
|
||
const links = [
|
||
{ href: '#about', label: 'О сервере' },
|
||
{ href: '#features', label: 'Особенности' },
|
||
{ href: '#join', label: 'Регистрация' },
|
||
{ href: '#news', label: 'Новости' },
|
||
];
|
||
|
||
const bootstrap = getBootstrap();
|
||
const auth = bootstrap.auth || {};
|
||
const playUrl = bootstrap.play_url || '#join';
|
||
|
||
return (
|
||
<nav className={`nav ${scrolled ? 'scrolled' : ''}`}>
|
||
<div className="wrap nav-inner">
|
||
<a href="#" className="nav-logo" onClick={close}>
|
||
<span className="nav-logo-mark" style={{ color: 'var(--moon)' }}>
|
||
<MoonMark size={32} />
|
||
</span>
|
||
<span>MoonWell</span>
|
||
</a>
|
||
|
||
<div className="nav-links nav-links-desktop">
|
||
{links.map((l) => (
|
||
<a key={l.href} href={l.href}>{l.label}</a>
|
||
))}
|
||
{auth.is_admin && <a href={auth.admin_url}>Админка</a>}
|
||
<a
|
||
href={auth.authenticated ? auth.cabinet_url : (auth.login_url || '/login')}
|
||
className="nav-cta"
|
||
>
|
||
{auth.authenticated ? 'Личный кабинет' : 'Войти в кабинет'}
|
||
<span style={{ color: 'var(--moon)' }}>{Icons.arrow}</span>
|
||
</a>
|
||
<a href={playUrl} className="nav-cta">
|
||
Играть <span style={{ color: 'var(--moon)' }}>{Icons.arrow}</span>
|
||
</a>
|
||
</div>
|
||
|
||
<button
|
||
className={`nav-burger ${open ? 'is-open' : ''}`}
|
||
onClick={() => setOpen((v) => !v)}
|
||
aria-label={open ? 'Закрыть меню' : 'Открыть меню'}
|
||
aria-expanded={open}
|
||
>
|
||
<span></span>
|
||
<span></span>
|
||
<span></span>
|
||
</button>
|
||
</div>
|
||
|
||
<div
|
||
className={`nav-drawer-backdrop ${open ? 'is-open' : ''}`}
|
||
onClick={close}
|
||
aria-hidden="true"
|
||
/>
|
||
<aside className={`nav-drawer ${open ? 'is-open' : ''}`} aria-hidden={!open}>
|
||
<div className="nav-drawer-head">
|
||
<span className="eyebrow">Навигация</span>
|
||
</div>
|
||
<div className="nav-drawer-links">
|
||
{links.map((l, i) => (
|
||
<a
|
||
key={l.href}
|
||
href={l.href}
|
||
onClick={close}
|
||
style={{ transitionDelay: `${60 + i * 40}ms` }}
|
||
>
|
||
<span className="num">{String(i + 1).padStart(2, '0')}</span>
|
||
<span className="lbl">{l.label}</span>
|
||
<span className="arr">{Icons.arrow}</span>
|
||
</a>
|
||
))}
|
||
<a
|
||
href={auth.authenticated ? auth.cabinet_url : (auth.login_url || '/login')}
|
||
onClick={close}
|
||
>
|
||
<span className="num">{String(links.length + 1).padStart(2, '0')}</span>
|
||
<span className="lbl">{auth.authenticated ? 'Личный кабинет' : 'Войти в кабинет'}</span>
|
||
<span className="arr">{Icons.arrow}</span>
|
||
</a>
|
||
{auth.is_admin && (
|
||
<a href={auth.admin_url} onClick={close}>
|
||
<span className="num">{String(links.length + 2).padStart(2, '0')}</span>
|
||
<span className="lbl">Админка</span>
|
||
<span className="arr">{Icons.arrow}</span>
|
||
</a>
|
||
)}
|
||
</div>
|
||
<a href={playUrl} className="btn btn-primary nav-drawer-cta" onClick={close}>
|
||
Играть {Icons.arrow}
|
||
</a>
|
||
<div className="nav-drawer-foot">
|
||
<span className="eyebrow" style={{ color: 'var(--text-mute)' }}>moonwell · 3.3.5a</span>
|
||
</div>
|
||
</aside>
|
||
</nav>
|
||
);
|
||
}
|