|
1 | 1 |
|
2 | 2 | @inject IJSRuntime JsRuntime |
| 3 | +@inject TextHub.Services.Data.ToolDataService ToolDataService |
| 4 | +@inject NavigationManager Navigation |
| 5 | +@using Microsoft.AspNetCore.Components.Web |
3 | 6 | <nav class="sticky top-0 z-50 bg-background/80 backdrop-blur-md border-b border-border"> |
4 | 7 | <div class="container mx-auto px-4 py-3 md:py-4"> |
5 | 8 | <div class="flex items-center justify-between"> |
|
9 | 12 | <div class="hidden md:flex items-center gap-6"> |
10 | 13 | <NavLink class="nav-link" href="" Match="NavLinkMatch.All">Home</NavLink> |
11 | 14 | <div class="relative"> |
12 | | - <button @onclick="() => _isToolsDropdownOpen = !_isToolsDropdownOpen" type="button" class="flex items-center gap-1 font-medium hover:text-primary transition-colors cursor-pointer"> |
| 15 | + <button @onclick="() => _isToolsDropdownOpen = !_isToolsDropdownOpen" @onclick:stopPropagation="true" type="button" class="flex items-center gap-1 font-medium hover:text-primary transition-colors cursor-pointer"> |
13 | 16 | Tools |
14 | 17 | <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down w-4 h-4 transition-transform @(_isToolsDropdownOpen ? "rotate-180" : "")"> |
15 | 18 | <path d="m6 9 6 6 6-6"></path> |
16 | 19 | </svg> |
17 | 20 | </button> |
18 | 21 | @if (_isToolsDropdownOpen) |
19 | 22 | { |
20 | | - <div class="absolute top-full mt-2 w-48 dropdown-menu rounded-md py-1"> |
21 | | - <a href="uppercase" class="block px-4 py-2 text-sm dropdown-item">Uppercase</a> |
22 | | - <a href="lowercase" class="block px-4 py-2 text-sm dropdown-item">Lowercase</a> |
23 | | - <a href="word-counter" class="block px-4 py-2 text-sm dropdown-item">Word Counter</a> |
| 23 | + <div id="tools-dropdown" @onclick:stopPropagation="true" @onclick:preventDefault="true" class="absolute top-full mt-2 w-72 p-2 bg-popover text-popover-foreground border border-border rounded-md shadow-md z-50"> |
| 24 | + @* Text Case Tools Section *@ |
| 25 | + <div class="mb-1"> |
| 26 | + <button @onclick="() => ToggleTextCaseSection()" type="button" class="flex items-center justify-between w-full px-2 py-1.5 text-sm font-semibold text-muted-foreground hover:text-foreground transition-colors rounded hover:bg-muted/50"> |
| 27 | + Text Case Tools |
| 28 | + <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down w-4 h-4 transition-transform @(_isTextCaseOpen ? "rotate-180" : "")"> |
| 29 | + <path d="m6 9 6 6 6-6"></path> |
| 30 | + </svg> |
| 31 | + </button> |
| 32 | + @if (_isTextCaseOpen) |
| 33 | + { |
| 34 | + <div class="ml-2 mt-1"> |
| 35 | + @foreach (var tool in ToolDataService.GetTextCaseTools().Where(t => !t.IsComingSoon).Take(8)) |
| 36 | + { |
| 37 | + <a @onclick="() => NavigateToTool(tool.Href)" class="relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground flex items-center gap-3 cursor-pointer"> |
| 38 | + @((MarkupString)tool.Icon.Replace("w-6 h-6", "w-4 h-4")) |
| 39 | + @tool.Title |
| 40 | + </a> |
| 41 | + } |
| 42 | + </div> |
| 43 | + } |
| 44 | + </div> |
| 45 | + |
| 46 | + @* Text Analysis Section *@ |
| 47 | + <div class="mb-1"> |
| 48 | + <button @onclick="() => ToggleTextAnalysisSection()" type="button" class="flex items-center justify-between w-full px-2 py-1.5 text-sm font-semibold text-muted-foreground hover:text-foreground transition-colors rounded hover:bg-muted/50"> |
| 49 | + Text Analysis |
| 50 | + <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down w-4 h-4 transition-transform @(_isTextAnalysisOpen ? "rotate-180" : "")"> |
| 51 | + <path d="m6 9 6 6 6-6"></path> |
| 52 | + </svg> |
| 53 | + </button> |
| 54 | + @if (_isTextAnalysisOpen) |
| 55 | + { |
| 56 | + <div class="ml-2 mt-1"> |
| 57 | + @foreach (var tool in ToolDataService.GetTextAnalysisTools().Where(t => !t.IsComingSoon).Take(3)) |
| 58 | + { |
| 59 | + <a @onclick="() => NavigateToTool(tool.Href)" class="relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground flex items-center gap-3 cursor-pointer"> |
| 60 | + @((MarkupString)tool.Icon.Replace("w-6 h-6", "w-4 h-4")) |
| 61 | + @tool.Title |
| 62 | + </a> |
| 63 | + } |
| 64 | + </div> |
| 65 | + } |
| 66 | + </div> |
| 67 | + |
| 68 | + @* Text Formatting Section *@ |
| 69 | + <div class="mb-1"> |
| 70 | + <button @onclick="() => ToggleTextFormattingSection()" type="button" class="flex items-center justify-between w-full px-2 py-1.5 text-sm font-semibold text-muted-foreground hover:text-foreground transition-colors rounded hover:bg-muted/50"> |
| 71 | + Text Formatting |
| 72 | + <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down w-4 h-4 transition-transform @(_isTextFormattingOpen ? "rotate-180" : "")"> |
| 73 | + <path d="m6 9 6 6 6-6"></path> |
| 74 | + </svg> |
| 75 | + </button> |
| 76 | + @if (_isTextFormattingOpen) |
| 77 | + { |
| 78 | + <div class="ml-2 mt-1"> |
| 79 | + @foreach (var tool in ToolDataService.GetTextFormattingTools().Where(t => !t.IsComingSoon).Take(4)) |
| 80 | + { |
| 81 | + <a @onclick="() => NavigateToTool(tool.Href)" class="relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground flex items-center gap-3 cursor-pointer"> |
| 82 | + @((MarkupString)tool.Icon.Replace("w-6 h-6", "w-4 h-4")) |
| 83 | + @tool.Title |
| 84 | + </a> |
| 85 | + } |
| 86 | + </div> |
| 87 | + } |
| 88 | + </div> |
24 | 89 | </div> |
25 | 90 | } |
26 | 91 | </div> |
|
70 | 135 | { |
71 | 136 | private bool _isMobileMenuOpen; |
72 | 137 | private bool _isToolsDropdownOpen; |
| 138 | + private bool _isTextCaseOpen = false; |
| 139 | + private bool _isTextAnalysisOpen = false; |
| 140 | + private bool _isTextFormattingOpen = false; |
73 | 141 | private bool _isDarkMode = false; // Will be initialized from localStorage |
74 | 142 |
|
75 | 143 | protected override async Task OnAfterRenderAsync(bool firstRender) |
|
78 | 146 | { |
79 | 147 | // Initialize theme from localStorage or system preference |
80 | 148 | _isDarkMode = await JsRuntime.InvokeAsync<bool>("getTheme"); |
| 149 | + |
| 150 | + // Setup click outside handler for dropdown |
| 151 | + await JsRuntime.InvokeVoidAsync("setupClickOutside", "tools-dropdown", DotNetObjectReference.Create(this)); |
| 152 | + |
81 | 153 | StateHasChanged(); |
82 | 154 | } |
83 | 155 | } |
|
93 | 165 | await JsRuntime.InvokeVoidAsync("toggleTheme", _isDarkMode); |
94 | 166 | StateHasChanged(); |
95 | 167 | } |
| 168 | + |
| 169 | + [JSInvokable] |
| 170 | + public void CloseDropdown() |
| 171 | + { |
| 172 | + _isToolsDropdownOpen = false; |
| 173 | + _isTextCaseOpen = false; |
| 174 | + _isTextAnalysisOpen = false; |
| 175 | + _isTextFormattingOpen = false; |
| 176 | + StateHasChanged(); |
| 177 | + } |
| 178 | + |
| 179 | + private void NavigateToTool(string href) |
| 180 | + { |
| 181 | + // Close the dropdown first |
| 182 | + CloseDropdown(); |
| 183 | + |
| 184 | + // Navigate to the tool |
| 185 | + Navigation.NavigateTo(href); |
| 186 | + } |
| 187 | + |
| 188 | + private void ToggleTextCaseSection() |
| 189 | + { |
| 190 | + // Close other sections and toggle this one |
| 191 | + _isTextAnalysisOpen = false; |
| 192 | + _isTextFormattingOpen = false; |
| 193 | + _isTextCaseOpen = !_isTextCaseOpen; |
| 194 | + StateHasChanged(); |
| 195 | + } |
| 196 | + |
| 197 | + private void ToggleTextAnalysisSection() |
| 198 | + { |
| 199 | + // Close other sections and toggle this one |
| 200 | + _isTextCaseOpen = false; |
| 201 | + _isTextFormattingOpen = false; |
| 202 | + _isTextAnalysisOpen = !_isTextAnalysisOpen; |
| 203 | + StateHasChanged(); |
| 204 | + } |
| 205 | + |
| 206 | + private void ToggleTextFormattingSection() |
| 207 | + { |
| 208 | + // Close other sections and toggle this one |
| 209 | + _isTextCaseOpen = false; |
| 210 | + _isTextAnalysisOpen = false; |
| 211 | + _isTextFormattingOpen = !_isTextFormattingOpen; |
| 212 | + StateHasChanged(); |
| 213 | + } |
96 | 214 | } |
0 commit comments