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:
1 parent
6731077191
commit
f1cb90ac03
3 files changed
+52
-19
No files matched your search
+46
-19
@@ -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()
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user