- Navbar: remove theme switcher, clean nav icons (no text labels), active state is accent dot underline + icon color — no pill bg - useTheme: drop 'system' from stored type; first visit = auto by system preference, stays unset until user explicitly clicks - ThemeBubble: fixed bottom-right pill with Sun/Moon icon + label, hover lift, click toggles light ↔ dark and persists to localStorage - App.tsx: render ThemeBubble inside ThemeProvider Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
87 lines
3.1 KiB
TypeScript
87 lines
3.1 KiB
TypeScript
import { Link, useLocation } from 'react-router-dom';
|
||
import { useAuth } from '@/hooks/useAuth';
|
||
import UserAvatar from '@/components/UserAvatar';
|
||
import { Home, PlusSquare, Shield } from 'lucide-react';
|
||
|
||
export default function Navbar() {
|
||
const { user } = useAuth();
|
||
const location = useLocation();
|
||
|
||
if (!user) return null;
|
||
|
||
const isFeed = location.pathname === '/feed' || location.pathname === '/';
|
||
const isUpload = location.pathname === '/upload';
|
||
const isProfile = location.pathname.startsWith('/profile');
|
||
|
||
return (
|
||
<nav className="sticky top-0 z-50 nav-glass border-b border-[var(--border-light)]">
|
||
<div className="nav-accent-bar" />
|
||
<div className="mx-auto flex h-[58px] max-w-[900px] items-center justify-between px-5">
|
||
|
||
{/* Логотип */}
|
||
<Link to="/feed" className="wordmark flex items-center gap-2 group select-none">
|
||
<span className="text-xl">🐾</span>
|
||
<span className="group-hover:text-[var(--accent)] transition-colors duration-200">
|
||
Котограм<span className="wordmark-dot" />
|
||
</span>
|
||
</Link>
|
||
|
||
{/* Навигация */}
|
||
<div className="flex items-center gap-1">
|
||
<NavIcon to="/feed" active={isFeed} icon={Home} label="Лента" />
|
||
<NavIcon to="/upload" active={isUpload} icon={PlusSquare} label="Добавить" />
|
||
|
||
{/* Аватар */}
|
||
<Link
|
||
to="/profile"
|
||
title="Профиль"
|
||
className={[
|
||
'relative ml-1 flex items-center rounded-full transition-all duration-200',
|
||
isProfile
|
||
? 'ring-[2.5px] ring-[var(--accent)] ring-offset-2 ring-offset-[var(--bg)]'
|
||
: 'opacity-75 hover:opacity-100',
|
||
].join(' ')}
|
||
>
|
||
<UserAvatar
|
||
avatarUrl={user.avatar_url}
|
||
username={user.username}
|
||
className="h-8 w-8"
|
||
fallbackClassName="text-[11px] avatar-colored bg-[var(--accent)]"
|
||
/>
|
||
{user.is_admin && (
|
||
<span className="absolute -top-0.5 -right-0.5 h-3.5 w-3.5 rounded-full bg-[var(--accent)] flex items-center justify-center ring-2 ring-[var(--bg)]">
|
||
<Shield className="h-2 w-2 text-white" strokeWidth={3} />
|
||
</span>
|
||
)}
|
||
</Link>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
);
|
||
}
|
||
|
||
function NavIcon({
|
||
to, active, icon: Icon, label,
|
||
}: {
|
||
to: string; active: boolean; icon: React.ComponentType<any>; label: string;
|
||
}) {
|
||
return (
|
||
<Link
|
||
to={to}
|
||
title={label}
|
||
className={[
|
||
'relative flex items-center justify-center h-9 w-9 rounded-xl',
|
||
'transition-all duration-200',
|
||
active
|
||
? 'text-[var(--accent)] bg-[var(--accent-soft)]'
|
||
: 'text-[var(--fg-tertiary)] hover:text-[var(--fg)] hover:bg-[var(--border-light)]',
|
||
].join(' ')}
|
||
>
|
||
<Icon className="h-5 w-5" strokeWidth={active ? 2.2 : 1.7} />
|
||
{active && (
|
||
<span className="absolute -bottom-[11px] left-1/2 -translate-x-1/2 h-[2.5px] w-4 rounded-full bg-[var(--accent)]" />
|
||
)}
|
||
</Link>
|
||
);
|
||
}
|