727 lines
35 KiB
TypeScript
727 lines
35 KiB
TypeScript
|
|
'use client';
|
||
|
|
|
||
|
|
import { useState, useEffect, useRef } from 'react';
|
||
|
|
import Image from 'next/image';
|
||
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
||
|
|
import {
|
||
|
|
Navbar as HeroNavbar,
|
||
|
|
NavbarBrand,
|
||
|
|
NavbarContent,
|
||
|
|
NavbarItem,
|
||
|
|
Button,
|
||
|
|
Dropdown,
|
||
|
|
DropdownTrigger,
|
||
|
|
DropdownMenu,
|
||
|
|
DropdownItem
|
||
|
|
} from '@heroui/react';
|
||
|
|
import { ArrowRight, ChevronDown, Menu, X, Sun, Moon, Globe, Layers, Rocket, ArrowLeftRight, Coins, LayoutDashboard, Radio, BookOpen, ShieldCheck, GraduationCap, MessageCircle, Headphones, Users, Briefcase, Mail, Newspaper, Rainbow } from 'lucide-react';
|
||
|
|
import ProductMenu from './ProductMenu';
|
||
|
|
import ResourceMenu from './ResourceMenu';
|
||
|
|
import { useLanguage } from '@/contexts/LanguageContext';
|
||
|
|
import { useTheme } from '@/contexts/ThemeContext';
|
||
|
|
|
||
|
|
export default function Navbar() {
|
||
|
|
const [scrolled, setScrolled] = useState(false);
|
||
|
|
const { language, setLanguage, t } = useLanguage();
|
||
|
|
const { theme, toggleTheme } = useTheme();
|
||
|
|
const [showProductMenu, setShowProductMenu] = useState(false);
|
||
|
|
const [showResourceMenu, setShowResourceMenu] = useState(false);
|
||
|
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||
|
|
const [mobileProductOpen, setMobileProductOpen] = useState(false);
|
||
|
|
const [mobileResourceOpen, setMobileResourceOpen] = useState(false);
|
||
|
|
const [navBottom, setNavBottom] = useState(64);
|
||
|
|
const navRef = useRef<HTMLElement | null>(null);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const handleScroll = () => {
|
||
|
|
setScrolled(window.scrollY > 50);
|
||
|
|
};
|
||
|
|
|
||
|
|
window.addEventListener('scroll', handleScroll);
|
||
|
|
return () => window.removeEventListener('scroll', handleScroll);
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (mobileMenuOpen) {
|
||
|
|
document.body.style.overflow = 'hidden';
|
||
|
|
} else {
|
||
|
|
document.body.style.overflow = '';
|
||
|
|
}
|
||
|
|
return () => { document.body.style.overflow = ''; };
|
||
|
|
}, [mobileMenuOpen]);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (!showProductMenu && !showResourceMenu) return;
|
||
|
|
|
||
|
|
const handleClickOutside = (e: MouseEvent) => {
|
||
|
|
const target = e.target as Element;
|
||
|
|
if (showProductMenu && !target.closest('.product-menu-container')) {
|
||
|
|
setShowProductMenu(false);
|
||
|
|
}
|
||
|
|
if (showResourceMenu && !target.closest('.resource-menu-container')) {
|
||
|
|
setShowResourceMenu(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// 延迟注册,避免当前 click 事件冒泡立即触发
|
||
|
|
const timer = requestAnimationFrame(() => {
|
||
|
|
document.addEventListener('click', handleClickOutside);
|
||
|
|
});
|
||
|
|
return () => {
|
||
|
|
cancelAnimationFrame(timer);
|
||
|
|
document.removeEventListener('click', handleClickOutside);
|
||
|
|
};
|
||
|
|
}, [showProductMenu, showResourceMenu]);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const handleResize = () => {
|
||
|
|
if (window.innerWidth >= 1024) {
|
||
|
|
setMobileMenuOpen(false);
|
||
|
|
setMobileProductOpen(false);
|
||
|
|
setMobileResourceOpen(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
window.addEventListener('resize', handleResize);
|
||
|
|
return () => window.removeEventListener('resize', handleResize);
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const measure = () => {
|
||
|
|
const el = navRef.current ?? (document.querySelector('header, nav') as HTMLElement);
|
||
|
|
if (el) {
|
||
|
|
navRef.current = el;
|
||
|
|
const h = Math.ceil(el.getBoundingClientRect().height);
|
||
|
|
setNavBottom(h);
|
||
|
|
document.documentElement.style.setProperty('--navbar-height', `${h}px`);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
measure();
|
||
|
|
const ro = new ResizeObserver(measure);
|
||
|
|
const el = document.querySelector('header, nav') as HTMLElement;
|
||
|
|
if (el) ro.observe(el);
|
||
|
|
return () => ro.disconnect();
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const isDark = theme === 'dark';
|
||
|
|
|
||
|
|
return (
|
||
|
|
<>
|
||
|
|
<HeroNavbar
|
||
|
|
maxWidth="full"
|
||
|
|
position="static"
|
||
|
|
style={{
|
||
|
|
position: 'fixed',
|
||
|
|
top: 0,
|
||
|
|
left: 0,
|
||
|
|
right: 0,
|
||
|
|
zIndex: 50,
|
||
|
|
backgroundColor: isDark
|
||
|
|
? scrolled ? 'rgba(10, 10, 10, 0.7)' : 'rgba(10, 10, 10, 0.9)'
|
||
|
|
: scrolled ? 'rgba(255, 255, 255, 0.7)' : 'rgba(255, 255, 255, 0.9)',
|
||
|
|
backdropFilter: 'blur(50px)',
|
||
|
|
borderTop: 'none',
|
||
|
|
borderLeft: 'none',
|
||
|
|
borderRight: 'none',
|
||
|
|
borderBottom: isDark
|
||
|
|
? scrolled ? '1px solid #27272a' : '1px solid #18181b'
|
||
|
|
: '1px solid #f3f4f6',
|
||
|
|
boxShadow: 'none',
|
||
|
|
transition: 'background-color 0.3s ease-out, border-color 0.3s ease-out'
|
||
|
|
}}
|
||
|
|
classNames={{
|
||
|
|
wrapper: "px-4 lg:px-10 2xl:px-20 3xl:px-32 py-5"
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
{/* Logo */}
|
||
|
|
<NavbarBrand>
|
||
|
|
<motion.div
|
||
|
|
initial={{ y: '-3rem', opacity: 0 }}
|
||
|
|
animate={{ y: 0, opacity: 1 }}
|
||
|
|
transition={{ duration: 1, ease: 'easeOut' }}
|
||
|
|
>
|
||
|
|
<Image
|
||
|
|
src="/logo0.svg"
|
||
|
|
alt="Logo"
|
||
|
|
width={160}
|
||
|
|
height={40}
|
||
|
|
priority
|
||
|
|
style={{
|
||
|
|
filter: isDark ? 'invert(1) brightness(1.2)' : 'none',
|
||
|
|
transition: 'filter 0.3s ease-out'
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
</motion.div>
|
||
|
|
</NavbarBrand>
|
||
|
|
|
||
|
|
{/* Center Menu */}
|
||
|
|
<NavbarContent className="hidden lg:flex gap-6" justify="center">
|
||
|
|
{/* Product */}
|
||
|
|
<NavbarItem className="product-menu-container">
|
||
|
|
<Button
|
||
|
|
variant="light"
|
||
|
|
className="font-bold text-sm select-none bg-transparent"
|
||
|
|
style={{ color: showProductMenu ? '#059669' : (isDark ? '#a1a1aa' : '#4b5563') }}
|
||
|
|
endContent={
|
||
|
|
<ChevronDown
|
||
|
|
size={14}
|
||
|
|
color={showProductMenu ? '#059669' : (isDark ? '#a1a1aa' : '#4b5563')}
|
||
|
|
style={{
|
||
|
|
transform: showProductMenu ? 'rotate(180deg)' : 'rotate(0deg)',
|
||
|
|
transition: 'transform 0.2s ease'
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
}
|
||
|
|
onPress={() => {
|
||
|
|
setShowProductMenu(!showProductMenu);
|
||
|
|
setShowResourceMenu(false);
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
{t('nav.product')}
|
||
|
|
</Button>
|
||
|
|
</NavbarItem>
|
||
|
|
|
||
|
|
{/* Ecosystem */}
|
||
|
|
<NavbarItem>
|
||
|
|
<Button
|
||
|
|
variant="light"
|
||
|
|
className="font-bold text-sm select-none bg-transparent"
|
||
|
|
style={{ color: isDark ? '#a1a1aa' : '#4b5563' }}
|
||
|
|
>
|
||
|
|
{t('nav.ecosystem')}
|
||
|
|
</Button>
|
||
|
|
</NavbarItem>
|
||
|
|
|
||
|
|
{/* Resource */}
|
||
|
|
<NavbarItem className="resource-menu-container">
|
||
|
|
<Button
|
||
|
|
variant="light"
|
||
|
|
className="font-bold text-sm select-none bg-transparent"
|
||
|
|
style={{ color: showResourceMenu ? '#059669' : (isDark ? '#a1a1aa' : '#4b5563') }}
|
||
|
|
endContent={
|
||
|
|
<ChevronDown
|
||
|
|
size={14}
|
||
|
|
color={showResourceMenu ? '#059669' : (isDark ? '#a1a1aa' : '#4b5563')}
|
||
|
|
style={{
|
||
|
|
transform: showResourceMenu ? 'rotate(180deg)' : 'rotate(0deg)',
|
||
|
|
transition: 'transform 0.2s ease'
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
}
|
||
|
|
onPress={() => {
|
||
|
|
setShowResourceMenu(!showResourceMenu);
|
||
|
|
setShowProductMenu(false);
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
{t('nav.resource')}
|
||
|
|
</Button>
|
||
|
|
</NavbarItem>
|
||
|
|
</NavbarContent>
|
||
|
|
|
||
|
|
{/* Right Content */}
|
||
|
|
<NavbarContent justify="end" className="gap-2 lg:gap-4">
|
||
|
|
{/* Launch App Button */}
|
||
|
|
<NavbarItem>
|
||
|
|
<motion.div
|
||
|
|
initial={{ y: '-3rem', opacity: 0 }}
|
||
|
|
animate={{ y: 0, opacity: 1 }}
|
||
|
|
transition={{ duration: 1, ease: 'easeOut', delay: 0.2 }}
|
||
|
|
>
|
||
|
|
<Button
|
||
|
|
className="font-bold text-sm hover:scale-105 transition-transform duration-200 ease-out"
|
||
|
|
style={{
|
||
|
|
backgroundColor: isDark ? '#ffffff' : '#111827',
|
||
|
|
color: isDark ? '#0a0a0a' : '#fafafa',
|
||
|
|
borderRadius: '9999px',
|
||
|
|
padding: '10px 20px',
|
||
|
|
transition: 'background-color 0.3s ease-out, color 0.3s ease-out, transform 0.2s ease-out',
|
||
|
|
boxShadow: '0px 4px 6px -4px rgba(229,231,235,1), 0px 10px 15px -3px rgba(229,231,235,1)'
|
||
|
|
}}
|
||
|
|
endContent={
|
||
|
|
<ArrowRight size={16} color={isDark ? '#0a0a0a' : '#ffffff'} />
|
||
|
|
}
|
||
|
|
onPress={() => window.open('http://152.69.205.186:3010/market', '_blank')}
|
||
|
|
>
|
||
|
|
{t('nav.launchApp')}
|
||
|
|
</Button>
|
||
|
|
</motion.div>
|
||
|
|
</NavbarItem>
|
||
|
|
|
||
|
|
{/* Theme Toggle - desktop only */}
|
||
|
|
<NavbarItem className="hidden lg:flex">
|
||
|
|
<Button
|
||
|
|
isIconOnly
|
||
|
|
variant="light"
|
||
|
|
onPress={toggleTheme}
|
||
|
|
className="transition-colors"
|
||
|
|
>
|
||
|
|
<div className="relative w-5 h-5">
|
||
|
|
<AnimatePresence mode="wait" initial={false}>
|
||
|
|
{isDark ? (
|
||
|
|
<motion.div key="moon" className="absolute inset-0 flex items-center justify-center"
|
||
|
|
initial={{ opacity: 0, rotate: -90, scale: 0.5 }}
|
||
|
|
animate={{ opacity: 1, rotate: 0, scale: 1 }}
|
||
|
|
exit={{ opacity: 0, rotate: 90, scale: 0.5 }}
|
||
|
|
transition={{ duration: 0.3 }}
|
||
|
|
>
|
||
|
|
<Moon size={18} color="#a1a1aa" />
|
||
|
|
</motion.div>
|
||
|
|
) : (
|
||
|
|
<motion.div key="sun" className="absolute inset-0 flex items-center justify-center"
|
||
|
|
initial={{ opacity: 0, rotate: 90, scale: 0.5 }}
|
||
|
|
animate={{ opacity: 1, rotate: 0, scale: 1 }}
|
||
|
|
exit={{ opacity: 0, rotate: -90, scale: 0.5 }}
|
||
|
|
transition={{ duration: 0.3 }}
|
||
|
|
>
|
||
|
|
<Sun size={18} color="#4B5563" />
|
||
|
|
</motion.div>
|
||
|
|
)}
|
||
|
|
</AnimatePresence>
|
||
|
|
</div>
|
||
|
|
</Button>
|
||
|
|
</NavbarItem>
|
||
|
|
|
||
|
|
{/* Language Selector - desktop only */}
|
||
|
|
<NavbarItem className="hidden lg:flex">
|
||
|
|
<Dropdown
|
||
|
|
classNames={{
|
||
|
|
content: 'bg-bg-surface border-none shadow-lg min-w-[120px] w-[120px]'
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<DropdownTrigger>
|
||
|
|
<Button
|
||
|
|
variant="light"
|
||
|
|
className="font-bold text-sm text-text-secondary"
|
||
|
|
startContent={<Globe size={16} color={isDark ? '#a1a1aa' : '#4B5563'} />}
|
||
|
|
endContent={<ChevronDown size={12} color={isDark ? '#a1a1aa' : '#4B5563'} />}
|
||
|
|
>
|
||
|
|
{language === 'zh' ? '中文' : 'EN'}
|
||
|
|
</Button>
|
||
|
|
</DropdownTrigger>
|
||
|
|
<DropdownMenu
|
||
|
|
aria-label="Language selection"
|
||
|
|
onAction={(key) => setLanguage(key as 'zh' | 'en')}
|
||
|
|
selectedKeys={new Set([language])}
|
||
|
|
classNames={{
|
||
|
|
list: "py-2"
|
||
|
|
}}
|
||
|
|
itemClasses={{
|
||
|
|
base: "py-3 flex items-center justify-center"
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<DropdownItem
|
||
|
|
key="zh"
|
||
|
|
className="text-text-primary hover:bg-bg-elevated text-center"
|
||
|
|
>
|
||
|
|
中文
|
||
|
|
</DropdownItem>
|
||
|
|
<DropdownItem
|
||
|
|
key="en"
|
||
|
|
className="text-text-primary hover:bg-bg-elevated text-center"
|
||
|
|
>
|
||
|
|
English
|
||
|
|
</DropdownItem>
|
||
|
|
</DropdownMenu>
|
||
|
|
</Dropdown>
|
||
|
|
</NavbarItem>
|
||
|
|
{/* Hamburger - mobile only */}
|
||
|
|
<NavbarItem className="lg:hidden">
|
||
|
|
<Button
|
||
|
|
isIconOnly
|
||
|
|
variant="light"
|
||
|
|
onPress={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||
|
|
aria-label="Toggle menu"
|
||
|
|
>
|
||
|
|
<AnimatePresence mode="wait" initial={false}>
|
||
|
|
{mobileMenuOpen ? (
|
||
|
|
<motion.div
|
||
|
|
key="close"
|
||
|
|
initial={{ opacity: 0, rotate: -90 }}
|
||
|
|
animate={{ opacity: 1, rotate: 0 }}
|
||
|
|
exit={{ opacity: 0, rotate: 90 }}
|
||
|
|
transition={{ duration: 0.15 }}
|
||
|
|
>
|
||
|
|
<X size={20} color={isDark ? '#a1a1aa' : '#4b5563'} />
|
||
|
|
</motion.div>
|
||
|
|
) : (
|
||
|
|
<motion.div
|
||
|
|
key="menu"
|
||
|
|
initial={{ opacity: 0, rotate: 90 }}
|
||
|
|
animate={{ opacity: 1, rotate: 0 }}
|
||
|
|
exit={{ opacity: 0, rotate: -90 }}
|
||
|
|
transition={{ duration: 0.15 }}
|
||
|
|
>
|
||
|
|
<Menu size={20} color={isDark ? '#a1a1aa' : '#4b5563'} />
|
||
|
|
</motion.div>
|
||
|
|
)}
|
||
|
|
</AnimatePresence>
|
||
|
|
</Button>
|
||
|
|
</NavbarItem>
|
||
|
|
</NavbarContent>
|
||
|
|
</HeroNavbar>
|
||
|
|
|
||
|
|
{/* Mobile Menu */}
|
||
|
|
<AnimatePresence>
|
||
|
|
{mobileMenuOpen && (
|
||
|
|
<>
|
||
|
|
{/* Backdrop — click outside to close */}
|
||
|
|
<motion.div
|
||
|
|
key="mobile-backdrop"
|
||
|
|
initial={{ opacity: 0 }}
|
||
|
|
animate={{ opacity: 1 }}
|
||
|
|
exit={{ opacity: 0 }}
|
||
|
|
transition={{ duration: 0.15 }}
|
||
|
|
className="lg:hidden fixed inset-0 z-[45]"
|
||
|
|
onClick={() => {
|
||
|
|
setMobileMenuOpen(false);
|
||
|
|
setMobileProductOpen(false);
|
||
|
|
setMobileResourceOpen(false);
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
<motion.div
|
||
|
|
initial={{ opacity: 0, y: -8 }}
|
||
|
|
animate={{ opacity: 1, y: 0 }}
|
||
|
|
exit={{ opacity: 0, y: -8 }}
|
||
|
|
transition={{ duration: 0.2, ease: 'easeOut' }}
|
||
|
|
className="lg:hidden fixed left-0 right-0 z-[50]"
|
||
|
|
style={{
|
||
|
|
top: navBottom,
|
||
|
|
backgroundColor: isDark ? 'rgba(10, 10, 10, 0.97)' : 'rgba(255, 255, 255, 0.97)',
|
||
|
|
backdropFilter: 'blur(50px)',
|
||
|
|
outline: 'none',
|
||
|
|
border: 'none',
|
||
|
|
borderBottom: isDark ? '1px solid #27272a' : '1px solid #e5e7eb',
|
||
|
|
maxHeight: `calc(100vh - ${navBottom}px)`,
|
||
|
|
overflowY: 'auto'
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<div className="flex flex-col px-4 py-3">
|
||
|
|
|
||
|
|
{/* Product */}
|
||
|
|
<button
|
||
|
|
className="flex items-center justify-between w-full px-3 py-4 text-sm font-bold rounded-lg transition-colors"
|
||
|
|
style={{ color: mobileProductOpen ? '#059669' : (isDark ? '#a1a1aa' : '#4b5563') }}
|
||
|
|
onClick={() => {
|
||
|
|
setMobileProductOpen(!mobileProductOpen);
|
||
|
|
setMobileResourceOpen(false);
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
{t('nav.product')}
|
||
|
|
<ChevronDown
|
||
|
|
size={14}
|
||
|
|
color={mobileProductOpen ? '#059669' : (isDark ? '#a1a1aa' : '#4b5563')}
|
||
|
|
style={{ transform: mobileProductOpen ? 'rotate(180deg)' : 'rotate(0deg)', transition: 'transform 0.2s ease' }}
|
||
|
|
/>
|
||
|
|
</button>
|
||
|
|
|
||
|
|
{/* Product Accordion */}
|
||
|
|
<AnimatePresence>
|
||
|
|
{mobileProductOpen && (
|
||
|
|
<motion.div
|
||
|
|
key="product-accordion"
|
||
|
|
initial={{ height: 0, opacity: 0 }}
|
||
|
|
animate={{ height: 'auto', opacity: 1 }}
|
||
|
|
exit={{ height: 0, opacity: 0 }}
|
||
|
|
transition={{ duration: 0.2, ease: 'easeOut' }}
|
||
|
|
style={{ overflow: 'hidden' }}
|
||
|
|
>
|
||
|
|
<div className="px-2 pb-3 flex flex-col gap-4">
|
||
|
|
{/* Core Yield Assets */}
|
||
|
|
<div>
|
||
|
|
<span className={`text-[10px] font-bold uppercase tracking-[1.2px] font-domine px-1 ${isDark ? 'text-[#52525b]' : 'text-[#9CA3AF]'}`}>
|
||
|
|
{language === 'zh' ? '核心收益资产' : 'Core Yield Assets'}
|
||
|
|
</span>
|
||
|
|
<div className="flex flex-col gap-2 mt-2">
|
||
|
|
<div className={`flex items-center gap-3 p-3 rounded-xl cursor-pointer ${isDark ? 'bg-[#27272a]' : 'bg-[#F9FAFB]'}`}>
|
||
|
|
<div className="w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0" style={{ background: isDark ? '#ffffff' : '#000000' }}>
|
||
|
|
<Rainbow size={16} color={isDark ? '#000000' : '#ffffff'} />
|
||
|
|
</div>
|
||
|
|
<div className="flex items-center gap-2">
|
||
|
|
<span className={`text-sm font-bold font-domine ${isDark ? 'text-[#fafafa]' : 'text-[#111827]'}`}>AX-Fund</span>
|
||
|
|
<div className="px-1.5 py-0.5 rounded flex-shrink-0" style={{ background: '#D1FAE5' }}>
|
||
|
|
<span className="text-[9px] font-bold uppercase font-domine" style={{ color: '#047857' }}>
|
||
|
|
{language === 'zh' ? '最高22% APY' : 'up to 22% APY'}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div className={`flex items-center gap-3 p-3 rounded-xl cursor-pointer ${isDark ? 'bg-[#27272a]' : 'bg-[#F9FAFB]'}`}>
|
||
|
|
<div className="w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0" style={{ background: isDark ? '#27272a' : '#ffffff', outline: `1px solid ${isDark ? '#3f3f46' : '#E5E7EB'}`, outlineOffset: '-1px' }}>
|
||
|
|
<Layers size={16} color={isDark ? '#d1d5db' : '#111827'} />
|
||
|
|
</div>
|
||
|
|
<div className="flex items-center gap-2">
|
||
|
|
<span className={`text-sm font-bold font-domine ${isDark ? 'text-[#fafafa]' : 'text-[#111827]'}`}>AX-Pool</span>
|
||
|
|
<div className="px-1.5 py-0.5 rounded flex-shrink-0" style={{ background: isDark ? '#3f3f46' : '#E5E7EB' }}>
|
||
|
|
<span className={`text-[9px] font-bold uppercase font-domine ${isDark ? 'text-[#a1a1aa]' : 'text-[#4B5563]'}`}>
|
||
|
|
{language === 'zh' ? '多元化' : 'Diversified'}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Platforms & Protocols */}
|
||
|
|
<div>
|
||
|
|
<span className={`text-[10px] font-bold uppercase tracking-[1.2px] font-domine px-1 ${isDark ? 'text-[#52525b]' : 'text-[#9CA3AF]'}`}>
|
||
|
|
{language === 'zh' ? '平台与协议' : 'Platforms & Protocols'}
|
||
|
|
</span>
|
||
|
|
<div className="flex flex-col gap-0.5 mt-2">
|
||
|
|
{[
|
||
|
|
{ Icon: Rocket, titleZh: 'Launchpad', titleEn: 'Launchpad' },
|
||
|
|
{ Icon: ArrowLeftRight, titleZh: 'DeFi市场', titleEn: 'DeFi Market' },
|
||
|
|
{ Icon: Coins, titleZh: 'Token Factory', titleEn: 'Token Factory' },
|
||
|
|
].map(({ Icon, titleZh, titleEn }) => (
|
||
|
|
<div key={titleEn} className={`flex items-center gap-3 px-2 py-2.5 rounded-lg cursor-pointer transition-colors ${isDark ? 'hover:bg-[#27272a]' : 'hover:bg-[#F9FAFB]'}`}>
|
||
|
|
<div className={`w-7 h-7 rounded-lg flex items-center justify-center flex-shrink-0 ${isDark ? 'bg-[#27272a] outline outline-1 outline-[#3f3f46]' : 'bg-[#F9FAFB] outline outline-1 outline-[#F3F4F6]'}`} style={{ outlineOffset: '-1px' }}>
|
||
|
|
<Icon size={14} color={isDark ? '#d1d5db' : '#111827'} />
|
||
|
|
</div>
|
||
|
|
<span className={`text-sm font-bold font-domine ${isDark ? 'text-[#fafafa]' : 'text-black'}`}>
|
||
|
|
{language === 'zh' ? titleZh : titleEn}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Infrastructure */}
|
||
|
|
<div>
|
||
|
|
<span className={`text-[10px] font-bold uppercase tracking-[1.2px] font-domine px-1 ${isDark ? 'text-[#52525b]' : 'text-[#9CA3AF]'}`}>
|
||
|
|
{language === 'zh' ? '基础设施' : 'Infrastructure'}
|
||
|
|
</span>
|
||
|
|
<div className="flex flex-col gap-0.5 mt-2">
|
||
|
|
{[
|
||
|
|
{ Icon: LayoutDashboard, titleZh: 'Asset Cockpit', titleEn: 'Asset Cockpit' },
|
||
|
|
{ Icon: Radio, titleZh: 'Oracle Network', titleEn: 'Oracle Network' },
|
||
|
|
].map(({ Icon, titleZh, titleEn }) => (
|
||
|
|
<div key={titleEn} className={`flex items-center gap-3 px-2 py-2.5 rounded-lg cursor-pointer transition-colors ${isDark ? 'hover:bg-[#27272a]' : 'hover:bg-[#F9FAFB]'}`}>
|
||
|
|
<div className={`w-7 h-7 rounded-lg flex items-center justify-center flex-shrink-0 ${isDark ? 'bg-[#27272a] outline outline-1 outline-[#3f3f46]' : 'bg-[#F9FAFB] outline outline-1 outline-[#F3F4F6]'}`} style={{ outlineOffset: '-1px' }}>
|
||
|
|
<Icon size={14} color={isDark ? '#d1d5db' : '#111827'} />
|
||
|
|
</div>
|
||
|
|
<span className={`text-sm font-bold font-domine ${isDark ? 'text-[#fafafa]' : 'text-black'}`}>
|
||
|
|
{language === 'zh' ? titleZh : titleEn}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Bottom */}
|
||
|
|
<div className={`pt-2 border-t flex items-center justify-between ${isDark ? 'border-[#27272a]' : 'border-[#F3F4F6]'}`}>
|
||
|
|
<span className="text-xs font-domine text-[#9CA3AF]">
|
||
|
|
{language === 'zh' ? '最新审计:' : 'Latest Audit: '}
|
||
|
|
<span className={`font-medium ${isDark ? 'text-[#fafafa]' : 'text-black'}`}>Oct 2025 (Certik)</span>
|
||
|
|
</span>
|
||
|
|
<span className="text-xs font-bold uppercase font-domine cursor-pointer hover:opacity-70 tracking-[0.3px]" style={{ color: '#059669' }}>
|
||
|
|
{language === 'zh' ? '查看文档 →' : 'View Docs →'}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</motion.div>
|
||
|
|
)}
|
||
|
|
</AnimatePresence>
|
||
|
|
|
||
|
|
<div style={{ height: '1px', backgroundColor: isDark ? '#27272a' : '#f3f4f6' }} />
|
||
|
|
|
||
|
|
{/* Ecosystem */}
|
||
|
|
<button
|
||
|
|
className="flex items-center w-full px-3 py-4 text-sm font-bold rounded-lg transition-colors"
|
||
|
|
style={{ color: isDark ? '#a1a1aa' : '#4b5563' }}
|
||
|
|
onClick={() => setMobileMenuOpen(false)}
|
||
|
|
>
|
||
|
|
{t('nav.ecosystem')}
|
||
|
|
</button>
|
||
|
|
<div style={{ height: '1px', backgroundColor: isDark ? '#27272a' : '#f3f4f6' }} />
|
||
|
|
|
||
|
|
{/* Resource */}
|
||
|
|
<button
|
||
|
|
className="flex items-center justify-between w-full px-3 py-4 text-sm font-bold rounded-lg transition-colors"
|
||
|
|
style={{ color: mobileResourceOpen ? '#059669' : (isDark ? '#a1a1aa' : '#4b5563') }}
|
||
|
|
onClick={() => {
|
||
|
|
setMobileResourceOpen(!mobileResourceOpen);
|
||
|
|
setMobileProductOpen(false);
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
{t('nav.resource')}
|
||
|
|
<ChevronDown
|
||
|
|
size={14}
|
||
|
|
color={mobileResourceOpen ? '#059669' : (isDark ? '#a1a1aa' : '#4b5563')}
|
||
|
|
style={{ transform: mobileResourceOpen ? 'rotate(180deg)' : 'rotate(0deg)', transition: 'transform 0.2s ease' }}
|
||
|
|
/>
|
||
|
|
</button>
|
||
|
|
|
||
|
|
{/* Resource Accordion */}
|
||
|
|
<AnimatePresence>
|
||
|
|
{mobileResourceOpen && (
|
||
|
|
<motion.div
|
||
|
|
key="resource-accordion"
|
||
|
|
initial={{ height: 0, opacity: 0 }}
|
||
|
|
animate={{ height: 'auto', opacity: 1 }}
|
||
|
|
exit={{ height: 0, opacity: 0 }}
|
||
|
|
transition={{ duration: 0.2, ease: 'easeOut' }}
|
||
|
|
style={{ overflow: 'hidden' }}
|
||
|
|
>
|
||
|
|
<div className="px-2 pb-3 flex flex-col gap-4">
|
||
|
|
{/* Documentation & Learning */}
|
||
|
|
<div>
|
||
|
|
<span className={`text-[10px] font-bold uppercase tracking-[1.2px] font-domine px-1 ${isDark ? 'text-[#52525b]' : 'text-[#9CA3AF]'}`}>
|
||
|
|
{language === 'zh' ? '文档与学习' : 'Documentation & Learning'}
|
||
|
|
</span>
|
||
|
|
<div className="flex flex-col gap-2 mt-2">
|
||
|
|
<div className={`flex items-center gap-3 p-3 rounded-xl cursor-pointer ${isDark ? 'bg-[#27272a]' : 'bg-[#F9FAFB]'}`}>
|
||
|
|
<div className="w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0" style={{ background: isDark ? '#ffffff' : '#000000' }}>
|
||
|
|
<BookOpen size={16} color={isDark ? '#000000' : '#ffffff'} />
|
||
|
|
</div>
|
||
|
|
<div className="flex items-center gap-2">
|
||
|
|
<span className={`text-sm font-bold font-domine ${isDark ? 'text-[#fafafa]' : 'text-[#111827]'}`}>
|
||
|
|
{language === 'zh' ? '文档' : 'Docs'}
|
||
|
|
</span>
|
||
|
|
<div className="px-1.5 py-0.5 rounded flex-shrink-0" style={{ background: '#D1FAE5' }}>
|
||
|
|
<span className="text-[9px] font-bold uppercase font-domine" style={{ color: '#047857' }}>
|
||
|
|
{language === 'zh' ? '已更新' : 'Updated'}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div className={`flex items-center gap-3 p-3 rounded-xl cursor-pointer ${isDark ? 'bg-[#27272a]' : 'bg-[#F9FAFB]'}`}>
|
||
|
|
<div className="w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0" style={{ background: isDark ? '#052e16' : '#ffffff', outline: '2px solid #10B981', outlineOffset: '-2px' }}>
|
||
|
|
<ShieldCheck size={16} color="#059669" />
|
||
|
|
</div>
|
||
|
|
<span className={`text-sm font-bold font-domine ${isDark ? 'text-[#fafafa]' : 'text-[#111827]'}`}>
|
||
|
|
{language === 'zh' ? '信任与安全' : 'Trust & Security'}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Help & Support */}
|
||
|
|
<div>
|
||
|
|
<span className={`text-[10px] font-bold uppercase tracking-[1.2px] font-domine px-1 ${isDark ? 'text-[#52525b]' : 'text-[#9CA3AF]'}`}>
|
||
|
|
{language === 'zh' ? '帮助与支持' : 'Help & Support'}
|
||
|
|
</span>
|
||
|
|
<div className="flex flex-col gap-0.5 mt-2">
|
||
|
|
{[
|
||
|
|
{ Icon: GraduationCap, titleZh: '学习中心', titleEn: 'Learning Center' },
|
||
|
|
{ Icon: MessageCircle, titleZh: '社区论坛', titleEn: 'Community Forum' },
|
||
|
|
{ Icon: Headphones, titleZh: '联系支持', titleEn: 'Contact Support' },
|
||
|
|
].map(({ Icon, titleZh, titleEn }) => (
|
||
|
|
<div key={titleEn} className={`flex items-center gap-3 px-2 py-2.5 rounded-lg cursor-pointer transition-colors ${isDark ? 'hover:bg-[#27272a]' : 'hover:bg-[#F9FAFB]'}`}>
|
||
|
|
<div className={`w-7 h-7 rounded-lg flex items-center justify-center flex-shrink-0 ${isDark ? 'bg-[#27272a] outline outline-1 outline-[#3f3f46]' : 'bg-[#F9FAFB] outline outline-1 outline-[#F3F4F6]'}`} style={{ outlineOffset: '-1px' }}>
|
||
|
|
<Icon size={14} color={isDark ? '#d1d5db' : '#111827'} />
|
||
|
|
</div>
|
||
|
|
<span className={`text-sm font-bold font-domine ${isDark ? 'text-[#fafafa]' : 'text-black'}`}>
|
||
|
|
{language === 'zh' ? titleZh : titleEn}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Company */}
|
||
|
|
<div>
|
||
|
|
<span className={`text-[10px] font-bold uppercase tracking-[1.2px] font-domine px-1 ${isDark ? 'text-[#52525b]' : 'text-[#9CA3AF]'}`}>
|
||
|
|
{language === 'zh' ? '公司' : 'Company'}
|
||
|
|
</span>
|
||
|
|
<div className="flex flex-col gap-0.5 mt-2">
|
||
|
|
{[
|
||
|
|
{ Icon: Users, titleZh: '关于团队', titleEn: 'About Team' },
|
||
|
|
{ Icon: Briefcase, titleZh: '招聘', titleEn: 'Careers' },
|
||
|
|
{ Icon: Mail, titleZh: '联系我们', titleEn: 'Contact Us' },
|
||
|
|
{ Icon: Newspaper, titleZh: '新闻媒体', titleEn: 'Press & Media' },
|
||
|
|
].map(({ Icon, titleZh, titleEn }) => (
|
||
|
|
<div key={titleEn} className={`flex items-center gap-3 px-2 py-2.5 rounded-lg cursor-pointer transition-colors ${isDark ? 'hover:bg-[#27272a]' : 'hover:bg-[#F9FAFB]'}`}>
|
||
|
|
<div className={`w-7 h-7 rounded-lg flex items-center justify-center flex-shrink-0 ${isDark ? 'bg-[#27272a] outline outline-1 outline-[#3f3f46]' : 'bg-[#F9FAFB] outline outline-1 outline-[#F3F4F6]'}`} style={{ outlineOffset: '-1px' }}>
|
||
|
|
<Icon size={14} color={isDark ? '#d1d5db' : '#111827'} />
|
||
|
|
</div>
|
||
|
|
<span className={`text-sm font-bold font-domine ${isDark ? 'text-[#fafafa]' : 'text-black'}`}>
|
||
|
|
{language === 'zh' ? titleZh : titleEn}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Bottom */}
|
||
|
|
<div className={`pt-2 border-t flex items-center justify-between ${isDark ? 'border-[#27272a]' : 'border-[#F3F4F6]'}`}>
|
||
|
|
<span className="text-xs font-domine text-[#9CA3AF]">
|
||
|
|
{language === 'zh' ? '最新更新:' : 'Latest Update: '}
|
||
|
|
<span className={`font-medium ${isDark ? 'text-[#fafafa]' : 'text-black'}`}>December 2025</span>
|
||
|
|
</span>
|
||
|
|
<div className="flex gap-4">
|
||
|
|
<span className={`text-xs font-bold font-domine cursor-pointer ${isDark ? 'text-[#6b7280]' : 'text-[#6B7280]'}`}>
|
||
|
|
{language === 'zh' ? '隐私政策' : 'Privacy Policy'}
|
||
|
|
</span>
|
||
|
|
<span className={`text-xs font-bold font-domine cursor-pointer ${isDark ? 'text-[#6b7280]' : 'text-[#6B7280]'}`}>
|
||
|
|
{language === 'zh' ? '服务条款' : 'Terms of Service'}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</motion.div>
|
||
|
|
)}
|
||
|
|
</AnimatePresence>
|
||
|
|
|
||
|
|
{/* Theme + Language */}
|
||
|
|
<div style={{ height: '1px', backgroundColor: isDark ? '#27272a' : '#f3f4f6', margin: '4px 0' }} />
|
||
|
|
<div className="flex items-center gap-2 px-3 py-3">
|
||
|
|
{/* Theme Toggle */}
|
||
|
|
<Button
|
||
|
|
isIconOnly
|
||
|
|
variant="light"
|
||
|
|
onPress={toggleTheme}
|
||
|
|
>
|
||
|
|
<div className="relative w-5 h-5">
|
||
|
|
<AnimatePresence mode="wait" initial={false}>
|
||
|
|
{isDark ? (
|
||
|
|
<motion.div key="moon" className="absolute inset-0 flex items-center justify-center"
|
||
|
|
initial={{ opacity: 0, rotate: -90, scale: 0.5 }} animate={{ opacity: 1, rotate: 0, scale: 1 }} exit={{ opacity: 0, rotate: 90, scale: 0.5 }} transition={{ duration: 0.3 }}>
|
||
|
|
<Moon size={18} color="#a1a1aa" />
|
||
|
|
</motion.div>
|
||
|
|
) : (
|
||
|
|
<motion.div key="sun" className="absolute inset-0 flex items-center justify-center"
|
||
|
|
initial={{ opacity: 0, rotate: 90, scale: 0.5 }} animate={{ opacity: 1, rotate: 0, scale: 1 }} exit={{ opacity: 0, rotate: -90, scale: 0.5 }} transition={{ duration: 0.3 }}>
|
||
|
|
<Sun size={18} color="#4B5563" />
|
||
|
|
</motion.div>
|
||
|
|
)}
|
||
|
|
</AnimatePresence>
|
||
|
|
</div>
|
||
|
|
</Button>
|
||
|
|
|
||
|
|
{/* Language */}
|
||
|
|
<Dropdown classNames={{ content: 'bg-bg-surface border-none shadow-lg min-w-[120px] w-[120px]' }}>
|
||
|
|
<DropdownTrigger>
|
||
|
|
<Button variant="light" className="font-bold text-sm text-text-secondary"
|
||
|
|
startContent={<Globe size={16} color={isDark ? '#a1a1aa' : '#4B5563'} />}
|
||
|
|
endContent={<ChevronDown size={12} color={isDark ? '#a1a1aa' : '#4B5563'} />}
|
||
|
|
>
|
||
|
|
{language === 'zh' ? '中文' : 'EN'}
|
||
|
|
</Button>
|
||
|
|
</DropdownTrigger>
|
||
|
|
<DropdownMenu aria-label="Language selection" onAction={(key) => setLanguage(key as 'zh' | 'en')} selectedKeys={new Set([language])} classNames={{ list: "py-2" }} itemClasses={{ base: "py-3 flex items-center justify-center" }}>
|
||
|
|
<DropdownItem key="zh" className="text-text-primary hover:bg-bg-elevated text-center">中文</DropdownItem>
|
||
|
|
<DropdownItem key="en" className="text-text-primary hover:bg-bg-elevated text-center">English</DropdownItem>
|
||
|
|
</DropdownMenu>
|
||
|
|
</Dropdown>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</motion.div>
|
||
|
|
</>
|
||
|
|
)}
|
||
|
|
</AnimatePresence>
|
||
|
|
|
||
|
|
{/* Custom Menus */}
|
||
|
|
<ProductMenu
|
||
|
|
isOpen={showProductMenu}
|
||
|
|
onClose={() => setShowProductMenu(false)}
|
||
|
|
language={language}
|
||
|
|
top={navBottom}
|
||
|
|
/>
|
||
|
|
<ResourceMenu
|
||
|
|
isOpen={showResourceMenu}
|
||
|
|
onClose={() => setShowResourceMenu(false)}
|
||
|
|
language={language}
|
||
|
|
top={navBottom}
|
||
|
|
/>
|
||
|
|
</>
|
||
|
|
);
|
||
|
|
}
|