fix(ui): restore the main menu row that reopens the resource menu

- On a resource page, the main menu starts with a row that shows the
  resource name and opens the deepest resource menu again.
- Pass the resource name to registerMainNavContext() again.
- Document the row in DESIGN.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andras BacsaiandClaude Opus 5.5 committed 2026-10-30 17:21:08 +01:00
1 parent b3ec26c3b1
commit 49346aeb29
12 files changed
+36 -18

No files matched your search

+7 -5
View File
@@ -311,8 +311,10 @@ Class names below are examples. Keep the values and structure.
and no frame around it. Only the expanded sidebar shows Search as an input.
- A menu that replaces another menu in place starts with a back row: a
centered label and a 12px left chevron (`‹ Dashboard`).
- The main menu has no row above its items. Dashboard is the first item, right
under Search, on every page.
- On a resource page, the main menu starts with a row that opens the resource
menu again. It points right and shows the resource name (`adonisjs ›`). On
other pages the main menu has no row above its items, and Dashboard is the
first item, right under Search.
### Card (outer box, title row, well)
@@ -800,9 +802,9 @@ state because it can disappear after polling or a Livewire morph.
From `xl`, the resource menu replaces the main menu in the main sidebar
(`x-main-nav-menu`, `registerMainNavContext()`, `components/navbar.blade.php`).
Its back row (`‹ Dashboard`, `‹ Servers`, `‹ Service`) shows the menu one depth
lower, without a page load. The main menu has no row that opens the resource
menu again; to get back to it, open the resource again. Dashboard is always the
first item under Search.
lower, without a page load. On a resource page the main menu starts with a row
(`adonisjs ›`) that opens the deepest resource menu again, also without a page
load.
The global topbar owns the current resource identity, its compact status
badges, configuration warnings (`#configuration-warning-hud-slot`), and the
+3 -2
View File
@@ -4760,11 +4760,12 @@ function wireNavigate(): string
* Registers a resource menu that the main sidebar and the mobile drawer show in place of their main menu
* (see components/navbar.blade.php). The menu teleports itself into #main-nav-context-{depth}.
* Back on a menu shows the menu one depth lower, and Back at depth 0 shows the main menu.
* The main menu then shows a row (labelled with $name) that opens the deepest resource menu again.
*/
function registerMainNavContext(int $depth, string $label, string $backTitle): void
function registerMainNavContext(int $depth, string $label, string $backTitle, ?string $name = null): void
{
$levels = request()->attributes->get('mainNavContext', []);
$levels[$depth] = ['label' => $label, 'backTitle' => $backTitle];
$levels[$depth] = ['label' => $label, 'backTitle' => $backTitle, 'name' => $name];
ksort($levels);
request()->attributes->set('mainNavContext', $levels);
@@ -228,7 +228,7 @@
@php
// The main sidebar and the mobile drawer show this menu (see components/navbar.blade.php).
registerMainNavContext(0, 'Dashboard', 'Back to the main menu');
registerMainNavContext(0, 'Dashboard', 'Back to the main menu', $application->name);
@endphp
{{-- Rendered twice: in the page (CSS hides it; it keeps the command palette items), and teleported into the main sidebar.
@@ -60,7 +60,7 @@
@php
// The main sidebar and the mobile drawer show this menu (see components/navbar.blade.php).
registerMainNavContext(0, 'Dashboard', 'Back to the main menu');
registerMainNavContext(0, 'Dashboard', 'Back to the main menu', $database->name);
@endphp
{{-- Rendered twice: in the page (CSS hides it; it keeps the command palette items), and teleported into the main sidebar.
@@ -1,7 +1,7 @@
@props(['backLabel', 'backTitle' => 'Back to the main menu', 'depth' => 0, 'navOnly' => false])
@props(['backLabel', 'backTitle' => 'Back to the main menu', 'depth' => 0, 'name' => null, 'navOnly' => false])
@php
// The main sidebar and the mobile drawer replace their menu with this one (see components/navbar.blade.php).
registerMainNavContext($depth, $backLabel, $backTitle);
registerMainNavContext($depth, $backLabel, $backTitle, $name);
@endphp
{{-- A page menu, rendered twice: in the page (CSS hides it), and teleported into the
main sidebar. The slot must not contain Livewire components (they would render twice).
@@ -184,6 +184,7 @@
// components/main-nav-menu.blade.php). Each one teleports itself into #main-nav-context-{depth}
// (the desktop sidebar); the mobile drawer moves it into its own slot while it shows (see init).
$navContext = request()->attributes->get('mainNavContext', []);
$navContextName = collect($navContext)->pluck('name')->filter()->last() ?? 'Resource menu';
@endphp
{{-- On a resource page the deepest resource menu replaces the main menu. Back shows the
menu one depth lower, and then the main menu (or its submenu), without leaving the page. --}}
@@ -262,6 +263,20 @@
@endforeach
<li x-show="navLevel === -1 && !submenu" @if ($activeSubmenu || $navContext) x-cloak @endif class="contents">
<ul role="list" class="flex flex-col gap-y-0.5" :class="navSwap && 'nav-swap-' + navSwap">
@if ($navContext)
{{-- On a resource page the main menu opens the deepest resource menu again. --}}
<li class="mb-1">
<button type="button" @click="navLevel = {{ array_key_last($navContext) }}; navSwap = 'forward'"
title="Back to the {{ $navContextName }} menu" aria-label="Back to the {{ $navContextName }} menu"
class="menu-item menu-back bg-transparent! hover:bg-neutral-100! dark:hover:bg-white/[0.06]!" :class="collapsed && 'lg:justify-center lg:px-0'">
<span class="flex-1 truncate pl-6 text-center font-medium text-black dark:text-fg"
:class="collapsed && 'lg:hidden'">{{ $navContextName }}</span>
<span class="menu-back-icon flex shrink-0 items-center justify-center">
<x-reicon name="chevron-down" class="size-3 -rotate-90 text-coollabs dark:text-fg-dim" />
</span>
</button>
</li>
@endif
@foreach ($navSections as $sectionLabel => $sectionItems)
{{-- The first section needs no title. --}}
@unless ($loop->first)
@@ -228,7 +228,7 @@
@php
// The main sidebar and the mobile drawer show this menu (see components/navbar.blade.php).
registerMainNavContext(0, 'Servers', 'Back to the main menu');
registerMainNavContext(0, 'Servers', 'Back to the main menu', $server->name);
@endphp
{{-- Rendered twice: in the page (CSS hides it; it keeps the command palette items), and teleported into the main sidebar.
@@ -51,7 +51,7 @@
->filter(fn ($items) => $items->isNotEmpty());
@endphp
<x-main-nav-menu back-label="Dashboard" :nav-only="$navOnly"
<x-main-nav-menu back-label="Dashboard" :name="$service->name" :nav-only="$navOnly"
data-settings-search-items="{{ json_encode(settingsSearchItems($groupedItems, $serviceRouteParameters)) }}">
<nav aria-label="Service settings"
class="grid grid-cols-1 gap-0.5">
@@ -22,7 +22,7 @@
])->filter();
@endphp
<x-main-nav-menu back-label="Destinations" back-title="Back to Integrations"
<x-main-nav-menu back-label="Destinations" back-title="Back to Integrations" :name="$destination->name"
data-settings-search-items="{{ json_encode(settingsSearchItems(['Destination' => $destinationMenuItems], $destinationRouteParameters)) }}">
<nav aria-label="Destination settings"
class="grid grid-cols-1 gap-0.5">
@@ -47,7 +47,7 @@
@if ($showSettingsSidebar)
<section class="application-settings-workspace mt-4 w-full max-w-none lg:mt-0">
<div class="grid min-w-0 gap-8 xl:grid-cols-[210px_minmax(0,1fr)] xl:gap-8">
<x-main-nav-menu back-label="Sources" back-title="Back to Integrations"
<x-main-nav-menu back-label="Sources" back-title="Back to Integrations" :name="$github_app->name ?: 'GitHub App'"
data-settings-search-items="{{ json_encode(settingsSearchItems(['GitHub App' => $settingsMenuItems], $githubAppRouteParameters)) }}">
<nav aria-label="GitHub App settings"
class="grid grid-cols-1 gap-0.5">
@@ -36,7 +36,7 @@
@if ($showSettingsSidebar)
<section class="application-settings-workspace mt-4 w-full max-w-none lg:mt-0">
<div class="grid min-w-0 gap-8 xl:grid-cols-[210px_minmax(0,1fr)] xl:gap-8">
<x-main-nav-menu back-label="S3 Storage" back-title="Back to Integrations"
<x-main-nav-menu back-label="S3 Storage" back-title="Back to Integrations" :name="$storage->name"
data-settings-search-items="{{ json_encode(settingsSearchItems(['S3 storage' => $settingsMenuItems], $storageRouteParameters)) }}">
<nav aria-label="S3 storage settings"
class="grid grid-cols-1 gap-0.5">
+2 -2
View File
@@ -58,7 +58,7 @@ test('an application page teleports its menu into the main sidebar ', function (
->toContain('x-teleport="#main-nav-context-0"')
->toContain('application-settings-navigation in-main-nav')
->toMatch('#<button type="button" @click="navLevel = -1; navSwap = \'back\'"\s+title="Back to the main menu"#')
->not->toContain('Back to the Navbar Menu App menu')
->toContain('Back to the Navbar Menu App menu')
->not->toContain('id="main-nav-context-1"')
->and(substr_count($html, 'id="main-nav-context-0"'))->toBe(1);
});
@@ -144,7 +144,7 @@ test('a database backup page keeps the database menu one back away in the main s
->toContain('x-teleport="#main-nav-context-1"')
->toMatch('#@click="navLevel = -1; navSwap = \'back\'"\s+title="Back to the main menu"#')
->toMatch('#@click="navLevel = 0; navSwap = \'back\'"\s+title="Back to the database menu"#')
->not->toContain('Back to the orders-db menu')
->toContain('Back to the orders-db menu')
->and(substr_count($html, 'aria-label="Database settings"'))->toBe(1)
->and(substr_count($html, 'aria-label="Backup settings"'))->toBe(2);
});