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
+50 -17

No files matched your search

+3
View File
@@ -1817,4 +1817,7 @@
<data name="{0} filtered, waiting for you" xml:space="preserve"> <data name="{0} filtered, waiting for you" xml:space="preserve">
<value>{0} filtrate, ti aspettano</value> <value>{0} filtrate, ti aspettano</value>
</data> </data>
<data name="Menu" xml:space="preserve">
<value>Menu</value>
</data>
</root> </root>
+3
View File
@@ -1817,4 +1817,7 @@
<data name="{0} filtered, waiting for you" xml:space="preserve"> <data name="{0} filtered, waiting for you" xml:space="preserve">
<value>{0} filtered, waiting for you</value> <value>{0} filtered, waiting for you</value>
</data> </data>
<data name="Menu" xml:space="preserve">
<value>Menu</value>
</data>
</root> </root>
+44 -17
View File
@@ -1,12 +1,22 @@
@inherits LocalizableComponentBase @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"> <div class="relative md:w-52">
<DButton Size="DSize.Sm" class="md:hidden" OnClick="ToggleNavMenu"> <DButton Size="DSize.Sm" class="md:hidden" OnClick="ToggleNavMenu" aria-label="@Localizer["Menu"]" title="@Localizer["Menu"]"
<i class="ion-md-menu text-lg"></i> aria-expanded="@(isMenuOpen ? "true" : "false")" aria-controls="nav-panel" data-nav-toggle>
<i class="ion-md-menu text-lg" aria-hidden="true"></i>
</DButton> </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"> <div class="flex flex-col gap-3 p-3 md:p-4">
@if (CascadingState.IsSignedIn) @if (CascadingState.IsSignedIn)
@@ -116,21 +126,19 @@
</div> </div>
</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"> <img alt="" class="h-[30px]" src="imgs/icon-512.png">
</div> </a>
<div class="md:hidden"> <a class="md:hidden grid place-items-center h-11 w-11" href="settings" aria-label="@Localizer["Settings"]" title="@Localizer["Settings"]">
<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 neo-raised neo-2xs"
<img alt="@CascadingState.Persona?.Name" class="object-cover w-[30px] h-[30px] rounded-full"
src="@(CascadingState.Persona?.Avatar ?? "imgs/icon-192.png")"> src="@(CascadingState.Persona?.Avatar ?? "imgs/icon-192.png")">
</a> </a>
</div>
</nav> </nav>
@code { @code {
string menuToggle = "hidden"; bool isMenuOpen;
bool isPersonaMenuOpen; bool isPersonaMenuOpen;
string PersonaBackground => CascadingState.Persona?.Header is { Length: > 0 } header && !header.Contains("/missing-") string PersonaBackground => CascadingState.Persona?.Header is { Length: > 0 } header && !header.Contains("/missing-")
@@ -144,21 +152,40 @@
protected override void OnInitialized() protected override void OnInitialized()
{ {
IsLoading = false; IsLoading = false;
if (!CascadingState.Status.IsMobileMedia()) Navigation.LocationChanged += OnLocationChanged;
menuToggle = default; }
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) async Task SwitchPersona(string personaId)
{ {
isMenuOpen = false;
var result = await CascadingState.SwitchPersona(personaId); var result = await CascadingState.SwitchPersona(personaId);
if (result.IsInvalid) if (result.IsInvalid)
Toast.AddResponseError(result); Toast.AddResponseError(result);
} }
private void ToggleNavMenu() private void ToggleNavMenu() => isMenuOpen = !isMenuOpen;
{
menuToggle = menuToggle is { Length: > 0 } ? default : "hidden";
}
protected async Task ResetToOriginalColour() protected async Task ResetToOriginalColour()
{ {