FutureCloud/components/Navigation.tsx

157 lines
6.5 KiB
TypeScript
Raw Normal View History

2025-09-15 16:58:31 +08:00
'use client';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { useState } from 'react';
import { LanguageType } from '../lib/content';
interface NavigationProps {
/** 🔧 修改 #1currentLang 从 string 改为 LanguageType */
currentLang: LanguageType;
/** 🔧 修改 #2setCurrentLang 返回值改为 void原来写成了 string签名也保持只接受 LanguageType */
setCurrentLang: (lang: LanguageType) => void;
content: {
nav: {
home: string;
services: string;
solutions: string;
pricing: string;
news: string;
contact: string;
};
};
/** 🔧 修改 #3createLocalizedPath 的签名改为 (path: string) => string */
createLocalizedPath?: (path: string) => string;
}
export default function Navigation({
currentLang,
setCurrentLang,
content,
createLocalizedPath,
}: NavigationProps) {
const pathname = usePathname();
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
// Helper to build localized URLs
const getLocalizedPath = (path: string) => {
return createLocalizedPath ? createLocalizedPath(path) : path;
};
const navItems = [
{ label: content.nav.home, href: getLocalizedPath('/') },
{ label: content.nav.services, href: getLocalizedPath('/services') },
{ label: content.nav.solutions, href: getLocalizedPath('/solutions') },
{ label: content.nav.pricing, href: getLocalizedPath('/pricing') },
{ label: content.nav.news, href: getLocalizedPath('/news') },
{ label: content.nav.contact, href: getLocalizedPath('/contact') },
];
return (
<nav className="relative z-10 px-6 py-4 border-b border-cyan-500/20 backdrop-blur-sm">
<div className="max-w-7xl mx-auto flex justify-between items-center">
{/* Logo */}
<Link
href="/"
className="text-2xl font-bold bg-gradient-to-r from-cyan-400 to-blue-500 bg-clip-text text-transparent hover:scale-105 transition-transform duration-300"
>
CloudAgency
</Link>
{/* Desktop Nav */}
<div className="hidden md:flex space-x-8">
{navItems.map((item, idx) => {
const isActive = pathname === item.href;
return (
<Link
key={idx}
href={item.href}
className={`relative group transition-colors duration-300 ${
isActive ? 'text-cyan-400' : 'text-gray-300 hover:text-cyan-400'
}`}
>
{item.label}
<span
className={`absolute -bottom-1 left-0 h-0.5 bg-cyan-400 transition-all duration-300 ${
isActive ? 'w-full' : 'w-0 group-hover:w-full'
}`}
/>
</Link>
);
})}
</div>
{/* Right side */}
<div className="flex items-center space-x-4">
{/* 🔧 修改 #4onChange 时将 value 强制断言为 LanguageType */}
<select
value={currentLang}
onChange={(e) => setCurrentLang(e.target.value as LanguageType)}
className="bg-gray-800 border border-cyan-500/30 rounded-lg px-3 py-1 text-sm focus:outline-none focus:border-cyan-400 transition-colors"
>
<option value="zh-CN"></option>
<option value="zh-TW"></option>
<option value="en">English</option>
</select>
{/* Mobile menu button */}
<button
className="md:hidden text-gray-300 hover:text-cyan-400 transition-colors"
onClick={() => setIsMobileMenuOpen((o) => !o)}
>
<svg
className="w-6 h-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
{isMobileMenuOpen ? (
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
) : (
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 6h16M4 12h16M4 18h16"
/>
)}
</svg>
</button>
</div>
</div>
{/* Mobile Menu */}
{isMobileMenuOpen && (
<div className="md:hidden absolute top-full left-0 right-0 bg-gray-900/95 backdrop-blur-sm border-b border-cyan-500/20">
<div className="px-6 py-4 space-y-4">
{navItems.map((item, idx) => {
const isActive = pathname === item.href;
return (
<Link
key={idx}
href={item.href}
className={`block py-2 transition-colors duration-300 ${
isActive
? 'text-cyan-400'
: 'text-gray-300 hover:text-cyan-400'
}`}
onClick={() => setIsMobileMenuOpen(false)}
>
{item.label}
</Link>
);
})}
</div>
</div>
)}
</nav>
);
}