275 lines
12 KiB
TypeScript
275 lines
12 KiB
TypeScript
|
|
'use client';
|
|||
|
|
|
|||
|
|
import Image from 'next/image';
|
|||
|
|
import { AnimatePresence, motion } from 'framer-motion';
|
|||
|
|
import { useTheme } from '@/contexts/ThemeContext';
|
|||
|
|
import { Layers, Rocket, ArrowLeftRight, Coins, LayoutDashboard, Radio, Rainbow } from 'lucide-react';
|
|||
|
|
|
|||
|
|
interface ProductMenuProps {
|
|||
|
|
isOpen: boolean;
|
|||
|
|
onClose: () => void;
|
|||
|
|
language: 'zh' | 'en';
|
|||
|
|
top?: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default function ProductMenu({ isOpen, onClose, language, top = 64 }: ProductMenuProps) {
|
|||
|
|
const { theme } = useTheme();
|
|||
|
|
const isDark = theme === 'dark';
|
|||
|
|
|
|||
|
|
const content = {
|
|||
|
|
zh: {
|
|||
|
|
coreYieldAssets: '核心收益资产',
|
|||
|
|
axFund: 'AX-Fund',
|
|||
|
|
axFundDesc: '购买AX-Fund收益代币,获得市场中性收益策略的敞口。收益随时间在代币价格中累积。',
|
|||
|
|
axFundBadge: '最高22% APY',
|
|||
|
|
axPool: 'AX-Pool',
|
|||
|
|
axPoolDesc: '以ALP提供流动性支持收益代币市场并赚取交易费',
|
|||
|
|
axPoolBadge: '多元化',
|
|||
|
|
platformsProtocols: '平台与协议',
|
|||
|
|
launchpad: 'Launchpad',
|
|||
|
|
launchpadDesc: '发行与代币化新RWA',
|
|||
|
|
defiMarket: 'DeFi市场',
|
|||
|
|
defiMarketDesc: '资产代币的交换与借贷',
|
|||
|
|
tokenFactory: 'Token Factory',
|
|||
|
|
tokenFactoryDesc: '标准化铸造协议',
|
|||
|
|
infrastructure: '基础设施',
|
|||
|
|
assetCockpit: 'Asset Cockpit',
|
|||
|
|
assetCockpitDesc: '投资组合分析与管理',
|
|||
|
|
oracleNetwork: 'Oracle Network',
|
|||
|
|
oracleNetworkDesc: '实时链下数据源',
|
|||
|
|
latestAudit: '最新审计:',
|
|||
|
|
auditInfo: 'Oct 2025 (Certik)',
|
|||
|
|
viewDocs: '查看文档 →',
|
|||
|
|
},
|
|||
|
|
en: {
|
|||
|
|
coreYieldAssets: 'Core Yield Assets',
|
|||
|
|
axFund: 'AX-Fund',
|
|||
|
|
axFundDesc: 'Buy the AX-Fund Yield Token to gain exposure to market-neutral yield strategies. Yield accrues in token price over time.',
|
|||
|
|
axFundBadge: 'up to 22% APY',
|
|||
|
|
axPool: 'AX-Pool',
|
|||
|
|
axPoolDesc: 'Provide liquidity as ALP to support Yield Token markets and earn trading fees',
|
|||
|
|
axPoolBadge: 'Diversified',
|
|||
|
|
platformsProtocols: 'Platforms & Protocols',
|
|||
|
|
launchpad: 'Launchpad',
|
|||
|
|
launchpadDesc: 'Issue & tokenize new RWAs.',
|
|||
|
|
defiMarket: 'Defi Market',
|
|||
|
|
defiMarketDesc: 'Swap & Lending for assets tokens.',
|
|||
|
|
tokenFactory: 'Token Factory',
|
|||
|
|
tokenFactoryDesc: 'Standardized minting protocol.',
|
|||
|
|
infrastructure: 'Infrastructure',
|
|||
|
|
assetCockpit: 'Asset Cockpit',
|
|||
|
|
assetCockpitDesc: 'Portfolio analytics & mgmt.',
|
|||
|
|
oracleNetwork: 'Oracle Network',
|
|||
|
|
oracleNetworkDesc: 'Real-time off-chain data feeds.',
|
|||
|
|
latestAudit: 'Latest Audit:',
|
|||
|
|
auditInfo: 'Oct 2025 (Certik)',
|
|||
|
|
viewDocs: 'View Documentation →',
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const t = content[language];
|
|||
|
|
|
|||
|
|
const platformItems = [
|
|||
|
|
{ Icon: Rocket, title: t.launchpad, desc: t.launchpadDesc },
|
|||
|
|
{ Icon: ArrowLeftRight, title: t.defiMarket, desc: t.defiMarketDesc },
|
|||
|
|
{ Icon: Coins, title: t.tokenFactory, desc: t.tokenFactoryDesc },
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
const infraItems = [
|
|||
|
|
{ Icon: LayoutDashboard, title: t.assetCockpit, desc: t.assetCockpitDesc },
|
|||
|
|
{ Icon: Radio, title: t.oracleNetwork, desc: t.oracleNetworkDesc },
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<AnimatePresence>
|
|||
|
|
{isOpen && (
|
|||
|
|
<>
|
|||
|
|
{/* Transparent backdrop for click-outside */}
|
|||
|
|
<motion.div
|
|||
|
|
initial={{ opacity: 0 }}
|
|||
|
|
animate={{ opacity: 1 }}
|
|||
|
|
exit={{ opacity: 0 }}
|
|||
|
|
transition={{ duration: 0.2 }}
|
|||
|
|
className="fixed inset-0 z-40"
|
|||
|
|
onClick={onClose}
|
|||
|
|
/>
|
|||
|
|
|
|||
|
|
{/* Menu — centered card */}
|
|||
|
|
<motion.div
|
|||
|
|
initial={{ opacity: 0, y: -8, x: '-50%' }}
|
|||
|
|
animate={{ opacity: 1, y: 0, x: '-50%' }}
|
|||
|
|
exit={{ opacity: 0, y: -8, x: '-50%' }}
|
|||
|
|
transition={{ duration: 0.25, ease: 'easeOut' }}
|
|||
|
|
className={`fixed left-1/2 z-50 rounded-2xl border transition-colors ${
|
|||
|
|
isDark ? 'bg-[#18181b] border-[#27272a]' : 'bg-white border-[#E5E7EB]'
|
|||
|
|
}`}
|
|||
|
|
style={{ top: top, width: '960px', boxShadow: '0px 20px 50px rgba(0,0,0,0.10)' }}
|
|||
|
|
>
|
|||
|
|
<div style={{ padding: '32px' }}>
|
|||
|
|
|
|||
|
|
{/* Three-column layout */}
|
|||
|
|
<div className="flex gap-12">
|
|||
|
|
|
|||
|
|
{/* Left — Core Yield Assets (large cards) */}
|
|||
|
|
<div className="flex flex-col gap-6 flex-shrink-0" style={{ width: '345px' }}>
|
|||
|
|
<span className={`text-xs font-bold uppercase tracking-[1.2px] font-domine ${isDark ? 'text-[#52525b]' : 'text-[#9CA3AF]'}`}>
|
|||
|
|
{t.coreYieldAssets}
|
|||
|
|
</span>
|
|||
|
|
|
|||
|
|
{/* AX-Fund card */}
|
|||
|
|
<div
|
|||
|
|
className={`p-5 rounded-2xl cursor-pointer transition-opacity hover:opacity-75 ${
|
|||
|
|
isDark
|
|||
|
|
? 'bg-[#27272a] outline outline-1 outline-[#3f3f46]'
|
|||
|
|
: 'bg-[#F9FAFB] outline outline-1 outline-[#E5E7EB]'
|
|||
|
|
}`}
|
|||
|
|
style={{ outlineOffset: '-1px' }}
|
|||
|
|
>
|
|||
|
|
<div className="flex items-start gap-4">
|
|||
|
|
<div
|
|||
|
|
className="w-12 h-12 rounded-full flex items-center justify-center flex-shrink-0"
|
|||
|
|
style={{ background: isDark ? '#ffffff' : '#000000' }}
|
|||
|
|
>
|
|||
|
|
<Rainbow size={24} color={isDark ? '#000000' : '#ffffff'} />
|
|||
|
|
</div>
|
|||
|
|
<div className="flex flex-col gap-1 flex-1">
|
|||
|
|
<div className="flex items-center gap-2">
|
|||
|
|
<span className={`font-bold text-lg font-domine leading-7 ${isDark ? 'text-[#fafafa]' : 'text-[#111827]'}`}>
|
|||
|
|
{t.axFund}
|
|||
|
|
</span>
|
|||
|
|
<div className="px-2 py-0.5 rounded flex items-center" style={{ background: '#D1FAE5' }}>
|
|||
|
|
<span className="text-[10px] font-bold uppercase font-domine leading-none" style={{ color: '#047857' }}>
|
|||
|
|
{t.axFundBadge}
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<p className="text-sm font-domine text-[#6B7280] leading-relaxed">{t.axFundDesc}</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* AX-Pool card */}
|
|||
|
|
<div
|
|||
|
|
className={`p-5 rounded-2xl cursor-pointer transition-opacity hover:opacity-75 ${
|
|||
|
|
isDark
|
|||
|
|
? 'bg-[#27272a] outline outline-1 outline-[#3f3f46]'
|
|||
|
|
: 'bg-[#F9FAFB] outline outline-1 outline-[#E5E7EB]'
|
|||
|
|
}`}
|
|||
|
|
style={{ outlineOffset: '-1px' }}
|
|||
|
|
>
|
|||
|
|
<div className="flex items-start gap-4">
|
|||
|
|
<div
|
|||
|
|
className="w-12 h-12 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={24} color={isDark ? '#d1d5db' : '#111827'} />
|
|||
|
|
</div>
|
|||
|
|
<div className="flex flex-col gap-1 flex-1">
|
|||
|
|
<div className="flex items-center gap-2">
|
|||
|
|
<span className={`font-bold text-lg font-domine leading-7 ${isDark ? 'text-[#fafafa]' : 'text-[#111827]'}`}>
|
|||
|
|
{t.axPool}
|
|||
|
|
</span>
|
|||
|
|
<div className="px-2 py-0.5 rounded flex items-center" style={{ background: isDark ? '#3f3f46' : '#E5E7EB' }}>
|
|||
|
|
<span className={`text-[10px] font-bold uppercase font-domine leading-none ${isDark ? 'text-[#a1a1aa]' : 'text-[#4B5563]'}`}>
|
|||
|
|
{t.axPoolBadge}
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<p className="text-sm font-domine text-[#6B7280] leading-relaxed">{t.axPoolDesc}</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Middle — Platforms & Protocols */}
|
|||
|
|
<div className="flex flex-col gap-6 flex-shrink-0" style={{ width: '227px' }}>
|
|||
|
|
<span className={`text-xs font-bold uppercase tracking-[1.2px] font-domine ${isDark ? 'text-[#52525b]' : 'text-[#9CA3AF]'}`}>
|
|||
|
|
{t.platformsProtocols}
|
|||
|
|
</span>
|
|||
|
|
<div className="flex flex-col gap-6">
|
|||
|
|
{platformItems.map(({ Icon, title, desc }) => (
|
|||
|
|
<div
|
|||
|
|
key={title}
|
|||
|
|
className={`flex items-start gap-4 cursor-pointer rounded-lg p-2 -mx-2 transition-colors ${
|
|||
|
|
isDark ? 'hover:bg-[#27272a]' : 'hover:bg-[#F9FAFB]'
|
|||
|
|
}`}
|
|||
|
|
>
|
|||
|
|
<div
|
|||
|
|
className={`w-10 h-10 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={20} color={isDark ? '#d1d5db' : '#111827'} />
|
|||
|
|
</div>
|
|||
|
|
<div className="flex flex-col gap-0.5">
|
|||
|
|
<span className={`text-sm font-bold font-domine leading-5 ${isDark ? 'text-[#fafafa]' : 'text-black'}`}>
|
|||
|
|
{title}
|
|||
|
|
</span>
|
|||
|
|
<span className="text-xs font-domine text-[#6B7280] leading-4">{desc}</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
))}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Right — Infrastructure */}
|
|||
|
|
<div className="flex flex-col gap-6 flex-1">
|
|||
|
|
<span className={`text-xs font-bold uppercase tracking-[1.2px] font-domine ${isDark ? 'text-[#52525b]' : 'text-[#9CA3AF]'}`}>
|
|||
|
|
{t.infrastructure}
|
|||
|
|
</span>
|
|||
|
|
<div className="flex flex-col gap-6">
|
|||
|
|
{infraItems.map(({ Icon, title, desc }) => (
|
|||
|
|
<div
|
|||
|
|
key={title}
|
|||
|
|
className={`flex items-start gap-4 cursor-pointer rounded-lg p-2 -mx-2 transition-colors ${
|
|||
|
|
isDark ? 'hover:bg-[#27272a]' : 'hover:bg-[#F9FAFB]'
|
|||
|
|
}`}
|
|||
|
|
>
|
|||
|
|
<div
|
|||
|
|
className={`w-10 h-10 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={20} color={isDark ? '#d1d5db' : '#111827'} />
|
|||
|
|
</div>
|
|||
|
|
<div className="flex flex-col gap-0.5">
|
|||
|
|
<span className={`text-sm font-bold font-domine leading-5 ${isDark ? 'text-[#fafafa]' : 'text-black'}`}>
|
|||
|
|
{title}
|
|||
|
|
</span>
|
|||
|
|
<span className="text-xs font-domine text-[#6B7280] leading-4">{desc}</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
))}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Bottom bar */}
|
|||
|
|
<div className={`mt-8 pt-6 border-t flex items-center justify-between ${isDark ? 'border-[#27272a]' : 'border-[#F3F4F6]'}`}>
|
|||
|
|
<div className="text-xs font-domine">
|
|||
|
|
<span className="text-[#9CA3AF]">{t.latestAudit} </span>
|
|||
|
|
<span className={`font-medium ${isDark ? 'text-[#fafafa]' : 'text-black'}`}>{t.auditInfo}</span>
|
|||
|
|
</div>
|
|||
|
|
<span
|
|||
|
|
className="text-xs font-bold uppercase font-domine cursor-pointer hover:opacity-70 transition-opacity tracking-[0.3px]"
|
|||
|
|
style={{ color: '#059669' }}
|
|||
|
|
>
|
|||
|
|
{t.viewDocs}
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
</div>
|
|||
|
|
</motion.div>
|
|||
|
|
</>
|
|||
|
|
)}
|
|||
|
|
</AnimatePresence>
|
|||
|
|
);
|
|||
|
|
}
|