feat(homepage): add responsive multi-level nav (#208)
This commit is contained in:
parent
a4392e0901
commit
76826cb805
15
ui/homepage/app/demo/page.tsx
Normal file
15
ui/homepage/app/demo/page.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
"use client"
|
||||
import { useLanguage } from '@i18n/LanguageProvider'
|
||||
import { translations } from '@i18n/translations'
|
||||
|
||||
export default function DemoPage() {
|
||||
const { language } = useLanguage()
|
||||
const { account } = translations[language].nav
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center text-white">
|
||||
<h1 className="text-2xl font-bold">{account.demo}</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -6,9 +6,14 @@ import { translations } from '../i18n/translations'
|
||||
|
||||
export default function NavBar() {
|
||||
const [menuOpen, setMenuOpen] = useState(false)
|
||||
const [openSection, setOpenSection] = useState<string | null>(null)
|
||||
const { language } = useLanguage()
|
||||
const nav = translations[language].nav
|
||||
|
||||
const toggleSection = (section: string) => {
|
||||
setOpenSection((prev) => (prev === section ? null : section))
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className="fixed top-0 w-full z-50 bg-black/30 backdrop-blur border-b border-white/10">
|
||||
<div className="max-w-7xl mx-auto px-4 py-4 flex justify-between items-center">
|
||||
@ -17,18 +22,60 @@ export default function NavBar() {
|
||||
CloudNative Suite
|
||||
</a>
|
||||
|
||||
{/* 桌面导航 */}
|
||||
{/* Desktop Navigation */}
|
||||
<div className="hidden md:flex items-center gap-6 text-sm text-white">
|
||||
<a href="#features" className="hover:text-purple-300">{nav.features}</a>
|
||||
<a href="#open-sources" className="hover:text-purple-300">{nav.openSource}</a>
|
||||
<a href="#download" className="hover:text-purple-300">{nav.download}</a>
|
||||
<a href="#contact" className="hover:text-purple-300">{nav.contact}</a>
|
||||
<a href="/login" className="hover:text-purple-300">{nav.login}</a>
|
||||
<a href="/register" className="hover:text-purple-300">{nav.register}</a>
|
||||
{/* Open Source */}
|
||||
<div className="relative group">
|
||||
<button className="hover:text-purple-300">{nav.openSource.title}</button>
|
||||
<div className="absolute left-0 mt-2 hidden group-hover:block bg-black/80 backdrop-blur rounded-md shadow-lg border border-white/10 py-2">
|
||||
<a href="#features" className="block px-4 py-2 hover:bg-white/10 whitespace-nowrap">
|
||||
{nav.openSource.features}
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/XProxyTeam/XControl"
|
||||
className="block px-4 py-2 hover:bg-white/10 whitespace-nowrap"
|
||||
>
|
||||
{nav.openSource.code}
|
||||
</a>
|
||||
<a href="#download" className="block px-4 py-2 hover:bg-white/10 whitespace-nowrap">
|
||||
{nav.openSource.download}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Services */}
|
||||
<div className="relative group">
|
||||
<button className="hover:text-purple-300">{nav.services.title}</button>
|
||||
<div className="absolute left-0 mt-2 hidden group-hover:block bg-black/80 backdrop-blur rounded-md shadow-lg border border-white/10 py-2">
|
||||
<a href="#" className="block px-4 py-2 hover:bg-white/10 whitespace-nowrap">
|
||||
{nav.services.artifact}
|
||||
</a>
|
||||
<a href="/docs" className="block px-4 py-2 hover:bg-white/10 whitespace-nowrap">
|
||||
{nav.services.docs}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Account */}
|
||||
<div className="relative group">
|
||||
<button className="hover:text-purple-300">{nav.account.title}</button>
|
||||
<div className="absolute right-0 mt-2 hidden group-hover:block bg-black/80 backdrop-blur rounded-md shadow-lg border border-white/10 py-2">
|
||||
<a href="/register" className="block px-4 py-2 hover:bg-white/10 whitespace-nowrap">
|
||||
{nav.account.register}
|
||||
</a>
|
||||
<a href="/login" className="block px-4 py-2 hover:bg-white/10 whitespace-nowrap">
|
||||
{nav.account.login}
|
||||
</a>
|
||||
<a href="/demo" className="block px-4 py-2 hover:bg-white/10 whitespace-nowrap">
|
||||
{nav.account.demo}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<LanguageToggle />
|
||||
</div>
|
||||
|
||||
{/* 移动端汉堡菜单按钮 */}
|
||||
{/* Mobile Hamburger Button */}
|
||||
<button
|
||||
className="md:hidden flex items-center text-white focus:outline-none"
|
||||
onClick={() => setMenuOpen(!menuOpen)}
|
||||
@ -42,61 +89,148 @@ export default function NavBar() {
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
{menuOpen ? (
|
||||
// 关闭图标 (X)
|
||||
<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>
|
||||
|
||||
{/* 移动端菜单内容,展开时显示 */}
|
||||
{/* Mobile Menu */}
|
||||
{menuOpen && (
|
||||
<div className="md:hidden bg-black/80 backdrop-blur border-t border-white/10 px-4 pb-4">
|
||||
<a
|
||||
href="#features"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className="block py-2 text-white hover:text-purple-300"
|
||||
>
|
||||
{nav.features}
|
||||
</a>
|
||||
<a
|
||||
href="#open-sources"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className="block py-2 text-white hover:text-purple-300"
|
||||
>
|
||||
{nav.openSource}
|
||||
</a>
|
||||
<a
|
||||
href="#download"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className="block py-2 text-white hover:text-purple-300"
|
||||
>
|
||||
{nav.download}
|
||||
</a>
|
||||
<a
|
||||
href="#contact"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className="block py-2 text-white hover:text-purple-300"
|
||||
>
|
||||
{nav.contact}
|
||||
</a>
|
||||
<a
|
||||
href="/login"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className="block py-2 text-white hover:text-purple-300"
|
||||
>
|
||||
{nav.login}
|
||||
</a>
|
||||
<a
|
||||
href="/register"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className="block py-2 text-white hover:text-purple-300"
|
||||
>
|
||||
{nav.register}
|
||||
</a>
|
||||
{/* Open Source */}
|
||||
<div>
|
||||
<button
|
||||
onClick={() => toggleSection('openSource')}
|
||||
className="w-full flex justify-between items-center py-2 text-white"
|
||||
>
|
||||
<span>{nav.openSource.title}</span>
|
||||
<svg
|
||||
className={`w-4 h-4 transform transition-transform ${
|
||||
openSection === 'openSource' ? 'rotate-180' : ''
|
||||
}`}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
{openSection === 'openSource' && (
|
||||
<div className="pl-4">
|
||||
<a
|
||||
href="#features"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className="block py-1 text-white hover:text-purple-300"
|
||||
>
|
||||
{nav.openSource.features}
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/XProxyTeam/XControl"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className="block py-1 text-white hover:text-purple-300"
|
||||
>
|
||||
{nav.openSource.code}
|
||||
</a>
|
||||
<a
|
||||
href="#download"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className="block py-1 text-white hover:text-purple-300"
|
||||
>
|
||||
{nav.openSource.download}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Services */}
|
||||
<div>
|
||||
<button
|
||||
onClick={() => toggleSection('services')}
|
||||
className="w-full flex justify-between items-center py-2 text-white"
|
||||
>
|
||||
<span>{nav.services.title}</span>
|
||||
<svg
|
||||
className={`w-4 h-4 transform transition-transform ${
|
||||
openSection === 'services' ? 'rotate-180' : ''
|
||||
}`}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
{openSection === 'services' && (
|
||||
<div className="pl-4">
|
||||
<a
|
||||
href="#"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className="block py-1 text-white hover:text-purple-300"
|
||||
>
|
||||
{nav.services.artifact}
|
||||
</a>
|
||||
<a
|
||||
href="/docs"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className="block py-1 text-white hover:text-purple-300"
|
||||
>
|
||||
{nav.services.docs}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Account */}
|
||||
<div>
|
||||
<button
|
||||
onClick={() => toggleSection('account')}
|
||||
className="w-full flex justify-between items-center py-2 text-white"
|
||||
>
|
||||
<span>{nav.account.title}</span>
|
||||
<svg
|
||||
className={`w-4 h-4 transform transition-transform ${
|
||||
openSection === 'account' ? 'rotate-180' : ''
|
||||
}`}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
{openSection === 'account' && (
|
||||
<div className="pl-4">
|
||||
<a
|
||||
href="/register"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className="block py-1 text-white hover:text-purple-300"
|
||||
>
|
||||
{nav.account.register}
|
||||
</a>
|
||||
<a
|
||||
href="/login"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className="block py-1 text-white hover:text-purple-300"
|
||||
>
|
||||
{nav.account.login}
|
||||
</a>
|
||||
<a
|
||||
href="/demo"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className="block py-1 text-white hover:text-purple-300"
|
||||
>
|
||||
{nav.account.demo}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="pt-2">
|
||||
<LanguageToggle />
|
||||
</div>
|
||||
@ -104,4 +238,5 @@ export default function NavBar() {
|
||||
)}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -12,12 +12,23 @@ export type Translation = {
|
||||
downloadSubtitle: string
|
||||
footerLinks: [string, string, string]
|
||||
nav: {
|
||||
features: string
|
||||
openSource: string
|
||||
download: string
|
||||
contact: string
|
||||
login: string
|
||||
register: string
|
||||
openSource: {
|
||||
title: string
|
||||
features: string
|
||||
code: string
|
||||
download: string
|
||||
}
|
||||
services: {
|
||||
title: string
|
||||
artifact: string
|
||||
docs: string
|
||||
}
|
||||
account: {
|
||||
title: string
|
||||
register: string
|
||||
login: string
|
||||
demo: string
|
||||
}
|
||||
}
|
||||
termsTitle: string
|
||||
termsPoints: string[]
|
||||
@ -39,12 +50,23 @@ export const translations: Record<'en' | 'zh', Translation> = {
|
||||
downloadSubtitle: 'Select your platform',
|
||||
footerLinks: ['Privacy Policy', 'Terms of Service', 'Contact Us'],
|
||||
nav: {
|
||||
features: 'Features',
|
||||
openSource: 'Open Source',
|
||||
download: 'Download',
|
||||
contact: 'Contact',
|
||||
login: 'Login',
|
||||
register: 'Register',
|
||||
openSource: {
|
||||
title: 'Open Source',
|
||||
features: 'Features',
|
||||
code: 'Code',
|
||||
download: 'Download',
|
||||
},
|
||||
services: {
|
||||
title: 'Services',
|
||||
artifact: 'Artifact / Mirror',
|
||||
docs: 'Docs / Solutions',
|
||||
},
|
||||
account: {
|
||||
title: 'Account',
|
||||
register: 'Register',
|
||||
login: 'Login',
|
||||
demo: 'Demo',
|
||||
},
|
||||
},
|
||||
termsTitle: 'Terms of Service',
|
||||
termsPoints: [
|
||||
@ -69,12 +91,23 @@ export const translations: Record<'en' | 'zh', Translation> = {
|
||||
downloadSubtitle: '选择适合的平台',
|
||||
footerLinks: ['隐私政策', '服务条款', '联系我们'],
|
||||
nav: {
|
||||
features: '功能特性',
|
||||
openSource: '开源项目',
|
||||
download: '下载',
|
||||
contact: '联系我们',
|
||||
login: '登录',
|
||||
register: '注册',
|
||||
openSource: {
|
||||
title: '开源项目',
|
||||
features: '功能特性',
|
||||
code: '代码',
|
||||
download: '下载',
|
||||
},
|
||||
services: {
|
||||
title: '服务',
|
||||
artifact: 'Artifact / 镜像',
|
||||
docs: '文档 / 解决方案',
|
||||
},
|
||||
account: {
|
||||
title: '账户',
|
||||
register: '注册',
|
||||
login: '登录',
|
||||
demo: '演示',
|
||||
},
|
||||
},
|
||||
termsTitle: '服务条款',
|
||||
termsPoints: [
|
||||
|
||||
Loading…
Reference in New Issue
Block a user