Skip to content

Commit 29ebd28

Browse files
committed
Refactor MainLayout with dropdown menus and mobile nav
Redesigned the MainLayout navigation to use 'Features' and 'Examples' dropdown menus for desktop, and a collapsible mobile menu for smaller screens. Added MainLayout.razor.cs for code-behind logic to manage menu state and active classes. Updated CSS with new utility classes to support layout and transitions.
1 parent fb04ee1 commit 29ebd28

File tree

3 files changed

+412
-69
lines changed

3 files changed

+412
-69
lines changed

samples/Blazouter.WebAssembly.Sample/Layouts/MainLayout.razor

Lines changed: 196 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,218 @@
11
@inherits LayoutComponentBase
22

33
@inject RouterNavigationService NavService
4+
@inject RouterStateService RouterState
45

56
<div class="min-h-screen bg-gray-50 dark:bg-gray-900 transition-colors">
67
<!-- Navigation -->
78
<nav class="bg-white dark:bg-gray-800 shadow-lg border-b border-gray-200 dark:border-gray-700">
89
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
910
<div class="flex justify-between h-16">
10-
<div class="flex items-center space-x-8">
11-
<!-- Logo -->
12-
<div class="flex-shrink-0 flex items-center">
13-
<span class="text-2xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent">
14-
Blazouter
11+
<!-- Logo (Left) -->
12+
<div class="flex-shrink-0 flex items-center">
13+
<span class="text-2xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent">
14+
Blazouter
15+
</span>
16+
</div>
17+
18+
<!-- Desktop Navigation Links (Center) -->
19+
<div class="hidden lg:flex flex-1 items-center justify-center space-x-1">
20+
<!-- Primary Links -->
21+
<RouterLink Href="/" Exact="true" ActiveClass="text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400">
22+
<span class="inline-flex items-center px-3 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
23+
Home
24+
</span>
25+
</RouterLink>
26+
<RouterLink Href="/about" ActiveClass="text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400">
27+
<span class="inline-flex items-center px-3 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
28+
About
1529
</span>
30+
</RouterLink>
31+
32+
<!-- Features Dropdown -->
33+
<div class="relative group">
34+
<button class="@GetFeaturesDropdownClass()">
35+
Features
36+
<svg class="ml-1 w-4 h-4 transition-transform group-hover:rotate-180" fill="none" stroke="currentColor" viewBox="0 0 24 24">
37+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
38+
</svg>
39+
</button>
40+
<div class="absolute left-1/2 -translate-x-1/2 mt-0 w-56 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50">
41+
<div class="pt-2">
42+
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-xl border border-gray-200 dark:border-gray-700 py-2">
43+
<RouterLink Href="/navigation" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
44+
<span class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
45+
🧭 Navigation
46+
</span>
47+
</RouterLink>
48+
<RouterLink Href="/transitions" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
49+
<span class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
50+
✨ Transitions
51+
</span>
52+
</RouterLink>
53+
<RouterLink Href="/cache" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
54+
<span class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
55+
⚡ Cache
56+
</span>
57+
</RouterLink>
58+
<RouterLink Href="/middleware" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
59+
<span class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
60+
🔗 Middleware
61+
</span>
62+
</RouterLink>
63+
<RouterLink Href="/typescript" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
64+
<span class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
65+
📘 TypeScript
66+
</span>
67+
</RouterLink>
68+
<RouterLink Href="/attribute-examples" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
69+
<span class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
70+
🏷️ Attribute Routing
71+
</span>
72+
</RouterLink>
73+
</div>
74+
</div>
75+
</div>
1676
</div>
1777

18-
<!-- Navigation Links -->
19-
<div class="hidden md:flex space-x-4">
20-
<RouterLink Href="/" Exact="true" ActiveClass="text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400">
21-
<span class="inline-flex items-center px-1 pt-1 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
22-
Home
23-
</span>
24-
</RouterLink>
25-
<RouterLink Href="/about" ActiveClass="text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400">
26-
<span class="inline-flex items-center px-1 pt-1 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
27-
About
28-
</span>
29-
</RouterLink>
30-
<RouterLink Href="/navigation" ActiveClass="text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400">
31-
<span class="inline-flex items-center px-1 pt-1 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
32-
Navigation
33-
</span>
34-
</RouterLink>
35-
<RouterLink Href="/transitions" ActiveClass="text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400">
36-
<span class="inline-flex items-center px-1 pt-1 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
37-
Transitions
38-
</span>
39-
</RouterLink>
40-
<RouterLink Href="/users" ActiveClass="text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400">
41-
<span class="inline-flex items-center px-1 pt-1 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
42-
Users
43-
</span>
44-
</RouterLink>
45-
<RouterLink Href="/cache" ActiveClass="text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400">
46-
<span class="inline-flex items-center px-1 pt-1 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
47-
Cache
48-
</span>
49-
</RouterLink>
50-
<RouterLink Href="/protected" ActiveClass="text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400">
51-
<span class="inline-flex items-center px-1 pt-1 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
52-
Protected
53-
</span>
54-
</RouterLink>
55-
<RouterLink Href="/lazy" ActiveClass="text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400">
56-
<span class="inline-flex items-center px-1 pt-1 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
57-
Lazy Load
58-
</span>
59-
</RouterLink>
60-
<RouterLink Href="/typescript" ActiveClass="text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400">
61-
<span class="inline-flex items-center px-1 pt-1 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
62-
TypeScript
63-
</span>
64-
</RouterLink>
65-
<RouterLink Href="/middleware" ActiveClass="text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400">
66-
<span class="inline-flex items-center px-1 pt-1 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
67-
Middleware
68-
</span>
69-
</RouterLink>
70-
<RouterLink Href="/error-example" ActiveClass="text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400">
71-
<span class="inline-flex items-center px-1 pt-1 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
72-
Error Example
73-
</span>
74-
</RouterLink>
75-
<RouterLink Href="/attribute-examples" ActiveClass="text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400">
76-
<span class="inline-flex items-center px-1 pt-1 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
77-
Attribute Routing
78-
</span>
79-
</RouterLink>
78+
<!-- Examples Dropdown -->
79+
<div class="relative group">
80+
<button class="@GetExamplesDropdownClass()">
81+
Examples
82+
<svg class="ml-1 w-4 h-4 transition-transform group-hover:rotate-180" fill="none" stroke="currentColor" viewBox="0 0 24 24">
83+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
84+
</svg>
85+
</button>
86+
<div class="absolute left-1/2 -translate-x-1/2 mt-0 w-48 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50">
87+
<div class="pt-2">
88+
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-xl border border-gray-200 dark:border-gray-700 py-2">
89+
<RouterLink Href="/users" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
90+
<span class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
91+
👥 Users
92+
</span>
93+
</RouterLink>
94+
<RouterLink Href="/protected" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
95+
<span class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
96+
🔒 Protected
97+
</span>
98+
</RouterLink>
99+
<RouterLink Href="/lazy" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
100+
<span class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
101+
⏳ Lazy Load
102+
</span>
103+
</RouterLink>
104+
<RouterLink Href="/error-example" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
105+
<span class="block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
106+
⚠️ Error Example
107+
</span>
108+
</RouterLink>
109+
</div>
110+
</div>
111+
</div>
80112
</div>
81113
</div>
82114

83-
<!-- Theme Switcher -->
84-
<div class="flex items-center">
115+
<!-- Right side: Theme Switcher + Mobile Menu Button -->
116+
<div class="flex items-center space-x-2">
85117
<ThemeSwitcher />
118+
119+
<!-- Mobile menu button -->
120+
<button @onclick="ToggleMobileMenu"
121+
class="lg:hidden inline-flex items-center justify-center p-2 rounded-md text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none transition-colors">
122+
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
123+
@if (_isMobileMenuOpen)
124+
{
125+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
126+
}
127+
else
128+
{
129+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
130+
}
131+
</svg>
132+
</button>
86133
</div>
87134
</div>
88135
</div>
136+
137+
<!-- Mobile Menu -->
138+
@if (_isMobileMenuOpen)
139+
{
140+
<div class="lg:hidden border-t border-gray-200 dark:border-gray-700">
141+
<div class="px-4 py-3 space-y-1">
142+
<!-- Primary Links -->
143+
<RouterLink Href="/" Exact="true" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
144+
<span @onclick="CloseMobileMenu" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
145+
🏠 Home
146+
</span>
147+
</RouterLink>
148+
<RouterLink Href="/about" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
149+
<span @onclick="CloseMobileMenu" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
150+
ℹ️ About
151+
</span>
152+
</RouterLink>
153+
154+
<!-- Features Section -->
155+
<div class="pt-2 pb-1">
156+
<span class="px-3 text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Features</span>
157+
</div>
158+
<RouterLink Href="/navigation" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
159+
<span @onclick="CloseMobileMenu" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
160+
🧭 Navigation
161+
</span>
162+
</RouterLink>
163+
<RouterLink Href="/transitions" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
164+
<span @onclick="CloseMobileMenu" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
165+
Transitions
166+
</span>
167+
</RouterLink>
168+
<RouterLink Href="/cache" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
169+
<span @onclick="CloseMobileMenu" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
170+
Cache
171+
</span>
172+
</RouterLink>
173+
<RouterLink Href="/middleware" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
174+
<span @onclick="CloseMobileMenu" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
175+
🔗 Middleware
176+
</span>
177+
</RouterLink>
178+
<RouterLink Href="/typescript" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
179+
<span @onclick="CloseMobileMenu" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
180+
📘 TypeScript
181+
</span>
182+
</RouterLink>
183+
<RouterLink Href="/attribute-examples" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
184+
<span @onclick="CloseMobileMenu" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
185+
🏷️ Attribute Routing
186+
</span>
187+
</RouterLink>
188+
189+
<!-- Examples Section -->
190+
<div class="pt-2 pb-1">
191+
<span class="px-3 text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Examples</span>
192+
</div>
193+
<RouterLink Href="/users" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
194+
<span @onclick="CloseMobileMenu" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
195+
👥 Users
196+
</span>
197+
</RouterLink>
198+
<RouterLink Href="/protected" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
199+
<span @onclick="CloseMobileMenu" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
200+
🔒 Protected
201+
</span>
202+
</RouterLink>
203+
<RouterLink Href="/lazy" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
204+
<span @onclick="CloseMobileMenu" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
205+
Lazy Load
206+
</span>
207+
</RouterLink>
208+
<RouterLink Href="/error-example" ActiveClass="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400">
209+
<span @onclick="CloseMobileMenu" class="block px-3 py-2 rounded-md text-base font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
210+
⚠️ Error Example
211+
</span>
212+
</RouterLink>
213+
</div>
214+
</div>
215+
}
89216
</nav>
90217

91218
<!-- Main Content -->
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
namespace Blazouter.WebAssembly.Sample.Layouts
2+
{
3+
public partial class MainLayout
4+
{
5+
private bool _isMobileMenuOpen = false;
6+
7+
// Features dropdown pages
8+
private static readonly string[] FeaturesPages = new[]
9+
{
10+
"/navigation", "/transitions", "/cache", "/middleware", "/typescript", "/attribute-examples"
11+
};
12+
13+
// Examples dropdown pages
14+
private static readonly string[] ExamplesPages = new[]
15+
{
16+
"/users", "/protected", "/lazy", "/error-example"
17+
};
18+
19+
private void ToggleMobileMenu()
20+
{
21+
_isMobileMenuOpen = !_isMobileMenuOpen;
22+
}
23+
24+
private void CloseMobileMenu()
25+
{
26+
_isMobileMenuOpen = false;
27+
}
28+
29+
private string GetFeaturesDropdownClass()
30+
{
31+
string currentPath = RouterState.CurrentPath ?? "/";
32+
bool isActive = FeaturesPages.Any(p => currentPath.StartsWith(p, StringComparison.OrdinalIgnoreCase));
33+
34+
string baseClass = "inline-flex items-center px-3 py-2 text-sm font-medium transition-colors";
35+
string activeClass = isActive
36+
? "text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400"
37+
: "text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400";
38+
39+
return $"{baseClass} {activeClass}";
40+
}
41+
42+
private string GetExamplesDropdownClass()
43+
{
44+
string currentPath = RouterState.CurrentPath ?? "/";
45+
bool isActive = ExamplesPages.Any(p => currentPath.StartsWith(p, StringComparison.OrdinalIgnoreCase));
46+
47+
string baseClass = "inline-flex items-center px-3 py-2 text-sm font-medium transition-colors";
48+
string activeClass = isActive
49+
? "text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400"
50+
: "text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400";
51+
52+
return $"{baseClass} {activeClass}";
53+
}
54+
}
55+
}

0 commit comments

Comments
 (0)