The phone menu closes after a choice

On a phone the menu stayed open over the page after a link was tapped, and only the hamburger closed it. It now closes
on any navigation, on a persona switch, on a tap outside it (a dimmed backdrop) and on Escape; its height is capped at
the screen's (dvh) with its own scroll, and its width follows a narrow screen. The hamburger has a name ("Menu") and says
whether the menu is open. The header's logo is a link home and the persona's picture a 44px link to Settings (it was
30px). From md, the sidebar scrolls on its own when the window is short, as on a phone held sideways.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-06 22:44:57 +02:00
1 parent 6731077191
commit f1cb90ac03
3 files changed
+52 -19

No files matched your search

+46 -19
View File
@@ -1,12 +1,22 @@
@inherits LocalizableComponentBase
<nav class="flex justify-between items-center md:items-start pt-3 px-3 md:p-0">
@* On a phone the menu opens from the hamburger over the page, and closes after a choice (any navigation, a persona
switch), on a tap outside it or on Escape; from md it is the sidebar, which scrolls on its own when the window is
short (a phone held sideways). *@
<nav class="flex justify-between items-center md:items-start pt-3 px-3 md:p-0 md:h-full md:overflow-y-auto md:overscroll-contain"
@onkeydown="OnKeyDown">
@if (isMenuOpen)
{
<div class="md:hidden fixed inset-0 z-40 bg-black/30" aria-hidden="true" @onclick="CloseMenu"></div>
}
<div class="relative md:w-52">
<DButton Size="DSize.Sm" class="md:hidden" OnClick="ToggleNavMenu">
<i class="ion-md-menu text-lg"></i>
<DButton Size="DSize.Sm" class="md:hidden" OnClick="ToggleNavMenu" aria-label="@Localizer["Menu"]" title="@Localizer["Menu"]"
aria-expanded="@(isMenuOpen ? "true" : "false")" aria-controls="nav-panel" data-nav-toggle>
<i class="ion-md-menu text-lg" aria-hidden="true"></i>
</DButton>
<div class="absolute bg-base-100 md:relative top-full md:top-auto mt-2 w-52 md:w-full overflow-y-auto rounded-lg neo-raised neo-sm md:shadow-none! z-50 @menuToggle md:block">
<div id="nav-panel" data-nav-panel
class="@(isMenuOpen ? "block" : "hidden") md:block absolute bg-base-100 md:relative top-full md:top-auto mt-2 w-[min(18rem,calc(100vw-1.5rem))] md:w-full max-h-[calc(100dvh-5rem)] md:max-h-none overflow-y-auto overscroll-contain rounded-lg neo-raised neo-sm md:shadow-none! z-50">
<div class="flex flex-col gap-3 p-3 md:p-4">
@if (CascadingState.IsSignedIn)
@@ -116,21 +126,19 @@
</div>
</div>
<div class="md:hidden">
<a class="md:hidden grid place-items-center h-11 w-11" href="" aria-label="@Localizer["Home"]" title="@Localizer["Home"]">
<img alt="" class="h-[30px]" src="imgs/icon-512.png">
</div>
</a>
<div class="md:hidden">
<a class="block rounded-full neo-raised neo-2xs" href="settings">
<img alt="@CascadingState.Persona?.Name" class="object-cover w-[30px] h-[30px] rounded-full"
src="@(CascadingState.Persona?.Avatar ?? "imgs/icon-192.png")">
</a>
</div>
<a class="md:hidden grid place-items-center h-11 w-11" href="settings" aria-label="@Localizer["Settings"]" title="@Localizer["Settings"]">
<img alt="@CascadingState.Persona?.Name" class="object-cover w-[30px] h-[30px] rounded-full neo-raised neo-2xs"
src="@(CascadingState.Persona?.Avatar ?? "imgs/icon-192.png")">
</a>
</nav>
@code {
string menuToggle = "hidden";
bool isMenuOpen;
bool isPersonaMenuOpen;
string PersonaBackground => CascadingState.Persona?.Header is { Length: > 0 } header && !header.Contains("/missing-")
@@ -144,21 +152,40 @@
protected override void OnInitialized()
{
IsLoading = false;
if (!CascadingState.Status.IsMobileMedia())
menuToggle = default;
Navigation.LocationChanged += OnLocationChanged;
}
void OnLocationChanged(object sender, LocationChangedEventArgs e)
{
if (!isMenuOpen)
return;
isMenuOpen = false;
InvokeAsync(StateHasChanged);
}
void CloseMenu() => isMenuOpen = false;
void OnKeyDown(KeyboardEventArgs e)
{
if (e.Key == "Escape")
isMenuOpen = false;
}
public override async ValueTask DisposeAsync()
{
Navigation.LocationChanged -= OnLocationChanged;
await base.DisposeAsync();
}
async Task SwitchPersona(string personaId)
{
isMenuOpen = false;
var result = await CascadingState.SwitchPersona(personaId);
if (result.IsInvalid)
Toast.AddResponseError(result);
}
private void ToggleNavMenu()
{
menuToggle = menuToggle is { Length: > 0 } ? default : "hidden";
}
private void ToggleNavMenu() => isMenuOpen = !isMenuOpen;
protected async Task ResetToOriginalColour()
{