🗂️ Add Collapsible Community Navigation Section
✨ Hierarchical Navigation Structure: - Created collapsible Community section with expandable sub-items - Sub-items: Collections, Decks, Forums (ready for future implementation) - Smooth expand/collapse animation with chevron icon rotation - Auto-expands when navigating to any community page 🎨 Visual Design: - Main Community item with chevron indicator (rotates 90° when expanded) - Sub-items indented with left border for visual hierarchy - Smaller sub-item styling (text-sm, rounded-xl, lighter shadows) - Consistent hover and active states throughout 🔧 Technical Features: - State management for expand/collapse (isCommunityExpanded) - useEffect to auto-expand on community page navigation - Proper ARIA attributes (aria-expanded, aria-haspopup) - Keyboard navigation support (Enter/Space to toggle) - Mobile menu integration (closes on sub-item click) 🎯 User Experience: - Intuitive collapsible navigation matching the design mockup - Clear visual hierarchy between main and sub-navigation - Smooth transitions and proper focus management - Accessibility-compliant with screen reader support Navigation now matches the requested collapsible structure! 📁
This commit is contained in:
parent
f7cde325ca
commit
e5f629f8e2
1 changed files with 108 additions and 3 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
import { useState } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useTheme } from '../lib/theme-context';
|
import { useTheme } from '../lib/theme-context';
|
||||||
|
|
@ -127,6 +127,16 @@ export default function Layout({ children, user = { email: 'me@randallstillwell.
|
||||||
const { theme, toggleTheme } = useTheme();
|
const { theme, toggleTheme } = useTheme();
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||||
|
const [isCommunityExpanded, setIsCommunityExpanded] = useState(
|
||||||
|
router.pathname.startsWith('/community')
|
||||||
|
);
|
||||||
|
|
||||||
|
// Auto-expand Community section when navigating to community pages
|
||||||
|
useEffect(() => {
|
||||||
|
if (router.pathname.startsWith('/community')) {
|
||||||
|
setIsCommunityExpanded(true);
|
||||||
|
}
|
||||||
|
}, [router.pathname]);
|
||||||
|
|
||||||
const navigation = [
|
const navigation = [
|
||||||
{ name: 'Dashboard', href: '/dashboard', icon: 'grid', active: router.pathname === '/dashboard' },
|
{ name: 'Dashboard', href: '/dashboard', icon: 'grid', active: router.pathname === '/dashboard' },
|
||||||
|
|
@ -134,14 +144,24 @@ export default function Layout({ children, user = { email: 'me@randallstillwell.
|
||||||
{ name: 'Cards', href: '/cards', icon: 'card', active: router.pathname === '/cards' },
|
{ name: 'Cards', href: '/cards', icon: 'card', active: router.pathname === '/cards' },
|
||||||
{ name: 'Decks', href: '/decks', icon: 'deck', active: router.pathname === '/decks', badge: '12' },
|
{ name: 'Decks', href: '/decks', icon: 'deck', active: router.pathname === '/decks', badge: '12' },
|
||||||
{ name: 'Analytics', href: '/analytics', icon: 'analytics', active: router.pathname === '/analytics' },
|
{ name: 'Analytics', href: '/analytics', icon: 'analytics', active: router.pathname === '/analytics' },
|
||||||
{ name: 'Community', href: '/community', icon: 'community', active: router.pathname === '/community' },
|
|
||||||
{ name: 'Community Collections', href: '/community/collections', icon: 'community', active: router.pathname === '/community/collections' },
|
|
||||||
{ name: 'Settings', href: '/settings', icon: 'settings', active: router.pathname === '/settings' },
|
{ name: 'Settings', href: '/settings', icon: 'settings', active: router.pathname === '/settings' },
|
||||||
...(user?.role === 'admin' ? [
|
...(user?.role === 'admin' ? [
|
||||||
{ name: 'Admin Tools', href: '/admin/card-editor', icon: 'admin', active: router.pathname.startsWith('/admin'), badge: 'ADMIN' }
|
{ name: 'Admin Tools', href: '/admin/card-editor', icon: 'admin', active: router.pathname.startsWith('/admin'), badge: 'ADMIN' }
|
||||||
] : [])
|
] : [])
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const communityNavigation = {
|
||||||
|
name: 'Community',
|
||||||
|
icon: 'community',
|
||||||
|
active: router.pathname.startsWith('/community'),
|
||||||
|
expanded: isCommunityExpanded,
|
||||||
|
items: [
|
||||||
|
{ name: 'Collections', href: '/community/collections', active: router.pathname === '/community/collections' },
|
||||||
|
{ name: 'Decks', href: '/community/decks', active: router.pathname === '/community/decks' },
|
||||||
|
{ name: 'Forums', href: '/community/forums', active: router.pathname === '/community/forums' }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
const bottomNavigation = [
|
const bottomNavigation = [
|
||||||
{ name: 'Notifications', href: '/notifications', icon: 'notifications', active: router.pathname === '/notifications' },
|
{ name: 'Notifications', href: '/notifications', icon: 'notifications', active: router.pathname === '/notifications' },
|
||||||
{ name: 'Settings', href: '/settings', icon: 'settings', active: router.pathname === '/settings' },
|
{ name: 'Settings', href: '/settings', icon: 'settings', active: router.pathname === '/settings' },
|
||||||
|
|
@ -321,6 +341,91 @@ export default function Layout({ children, user = { email: 'me@randallstillwell.
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
{/* Community Section with Sub-items */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div
|
||||||
|
className={`
|
||||||
|
nav-item flex items-center justify-between px-4 py-3 rounded-2xl
|
||||||
|
transition-all duration-200 cursor-pointer
|
||||||
|
focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2
|
||||||
|
${communityNavigation.active
|
||||||
|
? 'shadow-lg nav-item-active'
|
||||||
|
: 'hover:shadow-md nav-item-hover'
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
style={{
|
||||||
|
backgroundColor: communityNavigation.active ? 'var(--bg-tertiary)' : 'transparent',
|
||||||
|
color: communityNavigation.active ? 'var(--text-primary)' : 'var(--text-secondary)',
|
||||||
|
'--tw-ring-color': 'var(--accent-ember)',
|
||||||
|
'--tw-ring-offset-color': 'var(--bg-secondary)'
|
||||||
|
}}
|
||||||
|
onClick={() => setIsCommunityExpanded(!isCommunityExpanded)}
|
||||||
|
role="menuitem"
|
||||||
|
aria-expanded={isCommunityExpanded}
|
||||||
|
aria-haspopup="menu"
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsCommunityExpanded(!isCommunityExpanded);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span className="mr-3" aria-hidden="true">{getIcon(communityNavigation.icon)}</span>
|
||||||
|
<span className="font-medium">{communityNavigation.name}</span>
|
||||||
|
</div>
|
||||||
|
<svg
|
||||||
|
className={`w-4 h-4 transition-transform duration-200 ${isCommunityExpanded ? 'rotate-90' : ''}`}
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Community Sub-items */}
|
||||||
|
{isCommunityExpanded && (
|
||||||
|
<div className="ml-4 space-y-1 border-l-2 pl-4" style={{ borderColor: 'var(--border)' }}>
|
||||||
|
{communityNavigation.items.map((subItem) => (
|
||||||
|
<Link key={subItem.name} href={subItem.href}>
|
||||||
|
<div
|
||||||
|
className={`
|
||||||
|
nav-item flex items-center px-4 py-2 rounded-xl
|
||||||
|
transition-all duration-200 cursor-pointer text-sm
|
||||||
|
focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2
|
||||||
|
${subItem.active
|
||||||
|
? 'shadow-md nav-item-active'
|
||||||
|
: 'hover:shadow-sm nav-item-hover'
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
style={{
|
||||||
|
backgroundColor: subItem.active ? 'var(--bg-tertiary)' : 'transparent',
|
||||||
|
color: subItem.active ? 'var(--text-primary)' : 'var(--text-secondary)',
|
||||||
|
'--tw-ring-color': 'var(--accent-ember)',
|
||||||
|
'--tw-ring-offset-color': 'var(--bg-secondary)'
|
||||||
|
}}
|
||||||
|
onClick={() => setIsMobileMenuOpen(false)}
|
||||||
|
role="menuitem"
|
||||||
|
aria-current={subItem.active ? 'page' : undefined}
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsMobileMenuOpen(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="font-medium">{subItem.name}</span>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* Bottom Section */}
|
{/* Bottom Section */}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue