144 lines
5.3 KiB
TypeScript
144 lines
5.3 KiB
TypeScript
import { useState, useEffect } from 'react'
|
|
import { Link, useLocation } from '@tanstack/react-router'
|
|
import { menuItems } from '../../data/siteData'
|
|
|
|
export default function Header() {
|
|
const [isMenuOpen, setIsMenuOpen] = useState(false)
|
|
const [isScrolled, setIsScrolled] = useState(false)
|
|
const [isSearchOpen, setIsSearchOpen] = useState(false)
|
|
const location = useLocation()
|
|
|
|
useEffect(() => {
|
|
const handleScroll = () => {
|
|
setIsScrolled(window.scrollY > 80)
|
|
}
|
|
window.addEventListener('scroll', handleScroll)
|
|
return () => window.removeEventListener('scroll', handleScroll)
|
|
}, [])
|
|
|
|
// 关闭菜单当路由变化时
|
|
useEffect(() => {
|
|
setIsMenuOpen(false)
|
|
}, [location.pathname])
|
|
|
|
return (
|
|
<header className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${
|
|
isScrolled ? 'bg-white shadow-md' : 'bg-transparent'
|
|
}`}>
|
|
<nav className="container mx-auto px-4">
|
|
<div className="flex items-center justify-between py-4">
|
|
{/* Logo */}
|
|
<Link to="/" className="flex items-center">
|
|
<span className={`text-2xl font-bold font-sans transition-colors ${
|
|
isScrolled ? 'text-primary' : 'text-white'
|
|
}`}>
|
|
Finance <span className="text-secondary">Ideas</span>
|
|
</span>
|
|
</Link>
|
|
|
|
{/* Desktop Navigation */}
|
|
<div className="hidden lg:flex items-center gap-8">
|
|
<ul className="flex items-center gap-6">
|
|
{menuItems.map((item) => (
|
|
<li key={item.name}>
|
|
<Link
|
|
to={item.href}
|
|
className={`font-medium transition-colors hover:text-secondary ${
|
|
isScrolled ? 'text-title' : 'text-white'
|
|
} ${location.pathname === item.href ? 'text-secondary' : ''}`}
|
|
>
|
|
{item.name}
|
|
</Link>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
{/* Search Button */}
|
|
<button
|
|
onClick={() => setIsSearchOpen(true)}
|
|
className={`p-2 transition-colors hover:text-secondary ${
|
|
isScrolled ? 'text-title' : 'text-white'
|
|
}`}
|
|
aria-label="Search"
|
|
>
|
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
{/* Mobile Menu Button */}
|
|
<button
|
|
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
|
className={`lg:hidden p-2 transition-colors ${
|
|
isScrolled ? 'text-title' : 'text-white'
|
|
}`}
|
|
aria-label="Toggle menu"
|
|
>
|
|
{isMenuOpen ? (
|
|
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
|
</svg>
|
|
) : (
|
|
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
|
</svg>
|
|
)}
|
|
</button>
|
|
</div>
|
|
|
|
{/* Mobile Navigation */}
|
|
<div className={`lg:hidden overflow-hidden transition-all duration-300 ${
|
|
isMenuOpen ? 'max-h-64 pb-4' : 'max-h-0'
|
|
}`}>
|
|
<ul className="flex flex-col gap-4">
|
|
{menuItems.map((item) => (
|
|
<li key={item.name}>
|
|
<Link
|
|
to={item.href}
|
|
className={`block font-medium transition-colors hover:text-secondary ${
|
|
isScrolled ? 'text-title' : 'text-white'
|
|
} ${location.pathname === item.href ? 'text-secondary' : ''}`}
|
|
>
|
|
{item.name}
|
|
</Link>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
|
|
{/* Search Popup */}
|
|
{isSearchOpen && (
|
|
<div className="fixed inset-0 bg-black/80 flex items-center justify-center z-50">
|
|
<div className="w-full max-w-2xl px-4">
|
|
<form className="relative" onSubmit={(e) => e.preventDefault()}>
|
|
<input
|
|
type="search"
|
|
placeholder="Search your Keyword"
|
|
className="w-full h-16 px-6 pr-14 text-lg rounded-lg border-0 focus:ring-2 focus:ring-primary"
|
|
autoFocus
|
|
/>
|
|
<button
|
|
type="submit"
|
|
className="absolute right-4 top-1/2 -translate-y-1/2 text-text hover:text-primary"
|
|
>
|
|
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
</svg>
|
|
</button>
|
|
</form>
|
|
</div>
|
|
<button
|
|
onClick={() => setIsSearchOpen(false)}
|
|
className="absolute top-8 right-8 text-white text-4xl hover:text-secondary"
|
|
aria-label="Close search"
|
|
>
|
|
×
|
|
</button>
|
|
</div>
|
|
)}
|
|
</header>
|
|
)
|
|
}
|