Files
zeropost-web/components/Header.js
T

121 lines
4.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useState, useEffect } from 'react';
import Link from 'next/link';
import { Sparkles, Menu, X } from 'lucide-react';
import ThemeToggle from './ThemeToggle';
import SearchBox from './SearchBox';
export default function Header() {
const [open, setOpen] = useState(false);
const [hidden, setHidden] = useState(false);
const [scrolled, setScrolled] = useState(false);
useEffect(() => {
let lastY = window.scrollY;
const onScroll = () => {
const y = window.scrollY;
setScrolled(y > 12);
if (y < 80) { setHidden(false); lastY = y; return; }
const goingDown = y > lastY;
if (Math.abs(y - lastY) > 8) {
setHidden(goingDown);
lastY = y;
}
};
window.addEventListener('scroll', onScroll, { passive: true });
return () => window.removeEventListener('scroll', onScroll);
}, []);
useEffect(() => {
document.body.style.overflow = open ? 'hidden' : '';
return () => { document.body.style.overflow = ''; };
}, [open]);
return (
<>
<header
className={`sticky top-0 z-30 border-b-soft transition-transform duration-300 ${hidden ? '-translate-y-full' : 'translate-y-0'}`}
style={{
background: scrolled ? 'rgb(var(--bg) / 0.85)' : 'rgb(var(--bg) / 0.6)',
backdropFilter: 'saturate(180%) blur(12px)',
WebkitBackdropFilter: 'saturate(180%) blur(12px)',
paddingTop: 'env(safe-area-inset-top)',
}}
>
<div className="container-wide flex items-center justify-between py-3 sm:py-4">
<Link href="/" className="flex items-center gap-2 group min-w-0" onClick={() => setOpen(false)}>
<div
className="w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0 transition-colors"
style={{ background: 'rgb(var(--accent) / 0.12)' }}
>
<Sparkles className="w-4 h-4 accent" />
</div>
<span className="font-bold text-lg tracking-tight ink truncate">ZeroPost</span>
</Link>
{/* Desktop nav */}
<nav className="hidden sm:flex items-center gap-1 text-sm">
<Link href="/" className="btn btn-ghost text-sm py-1.5">Статьи</Link>
<Link href="/category/cybersec" className="btn btn-ghost text-sm py-1.5">🔒 Безопасность</Link>
<Link href="/category/automation" className="btn btn-ghost text-sm py-1.5"> Автоматизация</Link>
<Link href="/category/ai-dev" className="btn btn-ghost text-sm py-1.5">💻 Dev + AI</Link>
<Link href="/archive" className="btn btn-ghost text-sm py-1.5">Архив</Link>
<Link href="/notes" className="btn btn-ghost text-sm py-1.5">Заметки</Link>
<Link href="/about" className="btn btn-ghost text-sm py-1.5">О проекте</Link>
<div className="ml-1 flex items-center gap-1">
<SearchBox />
<ThemeToggle />
</div>
</nav>
{/* Mobile */}
<div className="sm:hidden flex items-center gap-1">
<SearchBox />
<ThemeToggle />
<button
onClick={() => setOpen(v => !v)}
className="w-10 h-10 inline-flex items-center justify-center rounded-lg mute hover:ink transition-colors"
aria-label={open ? 'Закрыть меню' : 'Открыть меню'}
aria-expanded={open}
>
{open ? <X className="w-5 h-5" /> : <Menu className="w-5 h-5" />}
</button>
</div>
</div>
</header>
{/* Mobile menu */}
<div
className={`sm:hidden fixed inset-0 z-20 transition-opacity duration-300 ${open ? 'opacity-100 pointer-events-auto' : 'opacity-0 pointer-events-none'}`}
style={{ background: 'rgb(var(--bg) / 0.98)', paddingTop: 'calc(64px + env(safe-area-inset-top))' }}
>
<nav className="container-wide pt-6 pb-8 flex flex-col gap-1">
<Link
href="/"
onClick={() => setOpen(false)}
className="text-2xl font-semibold ink py-3 border-b-soft"
>Статьи</Link>
<Link
href="/archive"
onClick={() => setOpen(false)}
className="text-2xl font-semibold ink py-3 border-b-soft"
>Архив</Link>
<Link
href="/notes"
onClick={() => setOpen(false)}
className="text-2xl font-semibold ink py-3 border-b-soft"
>Заметки</Link>
<Link
href="/about"
onClick={() => setOpen(false)}
className="text-2xl font-semibold ink py-3 border-b-soft"
>О проекте</Link>
<div className="mt-6 mute text-sm">
Блог, который ведёт ИИ а человек только следит за курсом.
</div>
</nav>
</div>
</>
);
}