140 lines
5.0 KiB
TypeScript
140 lines
5.0 KiB
TypeScript
import { useState, useEffect, useRef } from 'react';
|
|
import { Link, useLocation } from 'react-router-dom';
|
|
import { useLanguage } from '../../contexts/LanguageContext';
|
|
import { useAuth } from '../../contexts/AuthContext';
|
|
import AuthModal from '../AuthModal';
|
|
|
|
export default function MarketingNavbar() {
|
|
const { language, setLanguage } = useLanguage();
|
|
const { user, signOut } = useAuth();
|
|
const location = useLocation();
|
|
const isHome = location.pathname === '/';
|
|
const [scrolled, setScrolled] = useState(false);
|
|
const [activeSection, setActiveSection] = useState('');
|
|
const [userMenuOpen, setUserMenuOpen] = useState(false);
|
|
const [showAuthModal, setShowAuthModal] = useState(false);
|
|
const userMenuRef = useRef<HTMLDivElement>(null);
|
|
|
|
// Scroll: sticky style + active nav section
|
|
useEffect(() => {
|
|
const handleScroll = () => {
|
|
setScrolled(window.scrollY > 10);
|
|
if (!isHome) return;
|
|
let current = '';
|
|
document.querySelectorAll('section[id]').forEach((s) => {
|
|
if (window.scrollY >= (s as HTMLElement).offsetTop - 100) {
|
|
current = s.id;
|
|
}
|
|
});
|
|
setActiveSection(current);
|
|
};
|
|
window.addEventListener('scroll', handleScroll, { passive: true });
|
|
return () => window.removeEventListener('scroll', handleScroll);
|
|
}, [isHome]);
|
|
|
|
// Close user menu on outside click
|
|
useEffect(() => {
|
|
const handle = (e: MouseEvent) => {
|
|
if (userMenuRef.current && !userMenuRef.current.contains(e.target as Node)) {
|
|
setUserMenuOpen(false);
|
|
}
|
|
};
|
|
document.addEventListener('mousedown', handle);
|
|
return () => document.removeEventListener('mousedown', handle);
|
|
}, []);
|
|
|
|
const navLinks = [
|
|
{ href: '/', label: language === 'zh' ? '首页' : 'Home' },
|
|
{ href: '/docs', label: language === 'zh' ? '文档' : 'Docs' },
|
|
{ href: '/blog', label: language === 'zh' ? '博客' : 'Blog' },
|
|
];
|
|
|
|
const anchorLinks = isHome
|
|
? [{ href: '#pricing', label: language === 'zh' ? '价格' : 'Pricing' }]
|
|
: [];
|
|
|
|
return (
|
|
<>
|
|
<nav style={{ opacity: scrolled ? 1 : undefined }}>
|
|
<div className="nav-inner">
|
|
<Link to="/" className="nav-logo">
|
|
<div className="logo-icon">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2">
|
|
<path d="M4 6h16M4 10h10M4 14h12M4 18h8" />
|
|
</svg>
|
|
</div>
|
|
<span style={{ fontFamily: "'Lora', serif" }}>TexPixel</span>
|
|
</Link>
|
|
|
|
<ul className="nav-links">
|
|
{navLinks.map((link) => (
|
|
<li key={link.href}>
|
|
<Link
|
|
to={link.href}
|
|
className={location.pathname === link.href ? 'active' : ''}
|
|
>
|
|
{link.label}
|
|
</Link>
|
|
</li>
|
|
))}
|
|
{anchorLinks.map((link) => (
|
|
<li key={link.href}>
|
|
<a
|
|
href={link.href}
|
|
className={activeSection === link.href.slice(1) ? 'active' : ''}
|
|
>
|
|
{link.label}
|
|
</a>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
<div className="nav-right">
|
|
<button
|
|
className="lang-switch"
|
|
onClick={() => setLanguage(language === 'zh' ? 'en' : 'zh')}
|
|
>
|
|
{language === 'zh' ? 'EN' : '中文'}
|
|
</button>
|
|
|
|
{user ? (
|
|
<div className="nav-user" ref={userMenuRef} style={{ position: 'relative' }}>
|
|
<div
|
|
className="nav-avatar"
|
|
onClick={() => setUserMenuOpen((o) => !o)}
|
|
style={{ cursor: 'pointer' }}
|
|
>
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8">
|
|
<circle cx="12" cy="8" r="4" />
|
|
<path d="M4 20c0-4 3.6-7 8-7s8 3 8 7" />
|
|
</svg>
|
|
</div>
|
|
{userMenuOpen && (
|
|
<div className="nav-user-menu" style={{ display: 'block' }}>
|
|
<div className="nav-menu-divider" />
|
|
<button
|
|
className="nav-menu-item nav-menu-logout"
|
|
onClick={() => { signOut(); setUserMenuOpen(false); }}
|
|
style={{ background: 'none', border: 'none', width: '100%', textAlign: 'left', cursor: 'pointer' }}
|
|
>
|
|
{language === 'zh' ? '退出登录' : 'Sign Out'}
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
) : (
|
|
<div className="nav-login-btn">
|
|
<button className="btn-cta" onClick={() => setShowAuthModal(true)}>
|
|
{language === 'zh' ? '登录' : 'Login'}
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
{showAuthModal && <AuthModal onClose={() => setShowAuthModal(false)} />}
|
|
</>
|
|
);
|
|
}
|