CloudPro/components/Navigation/MobileMenu.tsx
2025-09-16 16:15:57 +08:00

127 lines
6.5 KiB
TypeScript

'use client';
import { useState } from 'react';
import { ChevronDown, ChevronRight } from 'lucide-react';
interface NavigationItem {
key: string;
label: string;
href: string;
current: boolean;
dropdown?: Array<{ label: string; href: string }>;
}
interface MobileMenuProps {
isOpen: boolean;
items: NavigationItem[];
onClose: () => void;
currentLang: string;
}
export function MobileMenu({ isOpen, items, onClose, currentLang }: MobileMenuProps) {
const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set());
const toggleExpanded = (key: string) => {
const newExpanded = new Set(expandedItems);
if (newExpanded.has(key)) {
newExpanded.delete(key);
} else {
newExpanded.add(key);
}
setExpandedItems(newExpanded);
};
if (!isOpen) return null;
return (
<div className="lg:hidden fixed inset-0 z-50 bg-black bg-opacity-50" data-mobile-menu>
<div className="fixed inset-y-0 right-0 max-w-xs w-full bg-gradient-to-b from-amber-900 to-yellow-800 shadow-xl transform transition-transform duration-300 ease-in-out">
<div className="flex flex-col h-full">
{/* Header */}
<div className="flex items-center justify-between p-4 border-b border-amber-700">
<h2 className="text-lg font-semibold text-yellow-300">
{currentLang === 'zh' ? '菜单' : 'Menu'}
</h2>
<button
onClick={onClose}
className="p-2 rounded-md text-yellow-100 hover:text-yellow-300 hover:bg-amber-800 focus:outline-none focus:ring-2 focus:ring-yellow-400 transition-colors duration-200"
aria-label="Close menu"
>
<ChevronRight className="w-5 h-5" />
</button>
</div>
{/* Navigation Items */}
<div className="flex-1 overflow-y-auto py-4">
<nav className="space-y-1 px-4" role="navigation">
{items.map((item) => (
<div key={item.key}>
{item.dropdown ? (
<div>
<button
onClick={() => toggleExpanded(item.key)}
className={`w-full flex items-center justify-between px-4 py-3 text-left text-sm font-medium rounded-md transition-colors duration-200 min-h-[44px] ${
item.current
? 'bg-amber-800 text-yellow-300'
: 'text-yellow-100 hover:text-yellow-300 hover:bg-amber-800/50'
}`}
aria-expanded={expandedItems.has(item.key)}
aria-controls={`mobile-submenu-${item.key}`}
>
<span>{item.label}</span>
<ChevronDown
className={`w-4 h-4 transition-transform duration-200 ${
expandedItems.has(item.key)
? 'rotate-180'
: ''
}`}
/>
</button>
{expandedItems.has(item.key) && (
<div
id={`mobile-submenu-${item.key}`}
className="mt-1 space-y-1 pl-4"
>
{item.dropdown.map((subItem, index) => (
<a
key={index}
href={subItem.href}
className="block px-4 py-3 text-sm text-yellow-200 hover:text-yellow-300 hover:bg-amber-800/30 rounded-md transition-colors duration-200 min-h-[44px] flex items-center"
onClick={onClose}
>
{subItem.label}
</a>
))}
</div>
)}
</div>
) : (
<a
href={item.href}
className={`block px-4 py-3 text-sm font-medium rounded-md transition-colors duration-200 min-h-[44px] flex items-center ${
item.current
? 'bg-amber-800 text-yellow-300'
: 'text-yellow-100 hover:text-yellow-300 hover:bg-amber-800/50'
}`}
aria-current={item.current ? 'page' : undefined}
onClick={onClose}
>
{item.label}
</a>
)}
</div>
))}
</nav>
</div>
{/* Footer */}
<div className="p-4 border-t border-amber-700">
<p className="text-xs text-yellow-200 text-center">© 2024 CloudPro</p>
</div>
</div>
</div>
</div>
);
}