Hide top nav on mobile, add "Plus" sheet to bottom tab bar
The top nav (logo + hamburger) was still sm:hidden-only on its inner sections but the <nav> element itself had no breakpoint class, so it kept rendering as an empty-looking bar above the new bottom tab bar. Hide the whole element below sm and drop the now-dead hamburger/Alpine toggle and its responsive menu, since nothing renders it anymore. Account/settings links (Profile, Foyer, Catégories, Récurrences, Log Out) that used to live in that hamburger now live in a "Plus" slide-up sheet on the bottom tab bar. Logging out needed a real route since the sheet isn't a Livewire component and can't call the Volt navigation component's method directly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
d97cc41ee2
commit
6a776b0042
@@ -7,35 +7,115 @@
|
|||||||
];
|
];
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<nav aria-label="Navigation principale" class="sm:hidden fixed inset-x-0 bottom-0 z-40 pb-[max(env(safe-area-inset-bottom),8px)] pt-2 px-4 bg-surface/90 backdrop-blur border-t border-border">
|
<div x-data="{ more: false }" class="sm:hidden">
|
||||||
<div class="flex justify-around">
|
<nav aria-label="Navigation principale" class="fixed inset-x-0 bottom-0 z-40 pb-[max(env(safe-area-inset-bottom),8px)] pt-2 px-4 bg-surface/90 backdrop-blur border-t border-border">
|
||||||
@foreach ($tabs as $tab)
|
<div class="flex justify-around">
|
||||||
@continue (! Route::has($tab['route']))
|
@foreach ($tabs as $tab)
|
||||||
@php $active = request()->routeIs($tab['route']); @endphp
|
@continue (! Route::has($tab['route']))
|
||||||
<a
|
@php $active = request()->routeIs($tab['route']); @endphp
|
||||||
href="{{ route($tab['route']) }}"
|
<a
|
||||||
wire:navigate
|
href="{{ route($tab['route']) }}"
|
||||||
aria-current="{{ $active ? 'page' : 'false' }}"
|
wire:navigate
|
||||||
class="flex flex-col items-center gap-0.5 px-3 py-1 rounded-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring {{ $active ? 'text-brand' : 'text-text-muted' }}"
|
aria-current="{{ $active ? 'page' : 'false' }}"
|
||||||
|
class="flex flex-col items-center gap-0.5 px-3 py-1 rounded-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring {{ $active ? 'text-brand' : 'text-text-muted' }}"
|
||||||
|
>
|
||||||
|
<span class="w-6 h-6 flex items-center justify-center rounded-lg {{ $active ? 'bg-brand text-surface' : '' }}">
|
||||||
|
@switch($tab['icon'])
|
||||||
|
@case('home')
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4" aria-hidden="true"><path d="M3 11l9-8 9 8" /><path d="M5 10v10h14V10" /></svg>
|
||||||
|
@break
|
||||||
|
@case('grid')
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4" aria-hidden="true"><rect x="3" y="3" width="7" height="7" rx="1.5" /><rect x="14" y="3" width="7" height="7" rx="1.5" /><rect x="3" y="14" width="7" height="7" rx="1.5" /><rect x="14" y="14" width="7" height="7" rx="1.5" /></svg>
|
||||||
|
@break
|
||||||
|
@case('list')
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4" aria-hidden="true"><path d="M8 6h13M8 12h13M8 18h13" /><path d="M3 6h.01M3 12h.01M3 18h.01" /></svg>
|
||||||
|
@break
|
||||||
|
@case('chart')
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4" aria-hidden="true"><path d="M3 3v18h18" /><path d="M7 15l4-6 4 3 5-8" /></svg>
|
||||||
|
@break
|
||||||
|
@endswitch
|
||||||
|
</span>
|
||||||
|
<span class="text-[10px] font-semibold leading-none">{{ $tab['label'] }}</span>
|
||||||
|
</a>
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
@click="more = true"
|
||||||
|
aria-haspopup="true"
|
||||||
|
:aria-expanded="more.toString()"
|
||||||
|
class="flex flex-col items-center gap-0.5 px-3 py-1 rounded-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring text-text-muted"
|
||||||
>
|
>
|
||||||
<span class="w-6 h-6 flex items-center justify-center rounded-lg {{ $active ? 'bg-brand text-surface' : '' }}">
|
<span class="w-6 h-6 flex items-center justify-center rounded-lg">
|
||||||
@switch($tab['icon'])
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4" aria-hidden="true"><circle cx="12" cy="5" r="1.5" /><circle cx="12" cy="12" r="1.5" /><circle cx="12" cy="19" r="1.5" /></svg>
|
||||||
@case('home')
|
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4" aria-hidden="true"><path d="M3 11l9-8 9 8" /><path d="M5 10v10h14V10" /></svg>
|
|
||||||
@break
|
|
||||||
@case('grid')
|
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4" aria-hidden="true"><rect x="3" y="3" width="7" height="7" rx="1.5" /><rect x="14" y="3" width="7" height="7" rx="1.5" /><rect x="3" y="14" width="7" height="7" rx="1.5" /><rect x="14" y="14" width="7" height="7" rx="1.5" /></svg>
|
|
||||||
@break
|
|
||||||
@case('list')
|
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4" aria-hidden="true"><path d="M8 6h13M8 12h13M8 18h13" /><path d="M3 6h.01M3 12h.01M3 18h.01" /></svg>
|
|
||||||
@break
|
|
||||||
@case('chart')
|
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4" aria-hidden="true"><path d="M3 3v18h18" /><path d="M7 15l4-6 4 3 5-8" /></svg>
|
|
||||||
@break
|
|
||||||
@endswitch
|
|
||||||
</span>
|
</span>
|
||||||
<span class="text-[10px] font-semibold leading-none">{{ $tab['label'] }}</span>
|
<span class="text-[10px] font-semibold leading-none">Plus</span>
|
||||||
</a>
|
</button>
|
||||||
@endforeach
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div
|
||||||
|
x-show="more"
|
||||||
|
x-cloak
|
||||||
|
x-transition.opacity
|
||||||
|
class="fixed inset-0 z-50 bg-text/40"
|
||||||
|
@click="more = false"
|
||||||
|
role="presentation"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
x-show="more"
|
||||||
|
x-cloak
|
||||||
|
x-transition:enter="transition ease-out duration-200"
|
||||||
|
x-transition:enter-start="translate-y-full"
|
||||||
|
x-transition:enter-end="translate-y-0"
|
||||||
|
x-transition:leave="transition ease-in duration-150"
|
||||||
|
x-transition:leave-start="translate-y-0"
|
||||||
|
x-transition:leave-end="translate-y-full"
|
||||||
|
class="fixed inset-x-0 bottom-0 z-50 pb-[max(env(safe-area-inset-bottom),16px)] pt-2 px-4 bg-surface rounded-t-3xl shadow-lg"
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-label="Menu"
|
||||||
|
@keydown.escape.window="more = false"
|
||||||
|
>
|
||||||
|
<div class="w-10 h-1 rounded-full bg-border mx-auto mb-4"></div>
|
||||||
|
|
||||||
|
<div class="px-2 pb-2">
|
||||||
|
<div class="font-medium text-base text-text" x-data="{{ json_encode(['name' => auth()->user()->name]) }}" x-text="name" x-on:profile-updated.window="name = $event.detail.name"></div>
|
||||||
|
<div class="font-medium text-sm text-text-muted">{{ auth()->user()->email }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="px-2 py-2">
|
||||||
|
<x-theme-toggle />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pb-2 space-y-1">
|
||||||
|
<x-responsive-nav-link :href="route('profile')" wire:navigate>
|
||||||
|
{{ __('Profile') }}
|
||||||
|
</x-responsive-nav-link>
|
||||||
|
|
||||||
|
<x-responsive-nav-link :href="route('household.settings')" wire:navigate>
|
||||||
|
{{ __('Foyer') }}
|
||||||
|
</x-responsive-nav-link>
|
||||||
|
|
||||||
|
@if (Route::has('categories'))
|
||||||
|
<x-responsive-nav-link :href="route('categories')" wire:navigate>
|
||||||
|
{{ __('Catégories') }}
|
||||||
|
</x-responsive-nav-link>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if (Route::has('recurring'))
|
||||||
|
<x-responsive-nav-link :href="route('recurring')" wire:navigate>
|
||||||
|
{{ __('Récurrences') }}
|
||||||
|
</x-responsive-nav-link>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<form method="POST" action="{{ route('logout') }}">
|
||||||
|
@csrf
|
||||||
|
<button type="submit" class="block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-text-muted hover:text-text hover:bg-surface-alt hover:border-border-strong focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-inset transition duration-150 ease-in-out">
|
||||||
|
{{ __('Log Out') }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</div>
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ new class extends Component
|
|||||||
}
|
}
|
||||||
}; ?>
|
}; ?>
|
||||||
|
|
||||||
<nav x-data="{ open: false }" class="bg-surface rounded-full shadow-sm">
|
{{-- Hidden on mobile: primary routes live in the bottom tab bar, and
|
||||||
|
account/settings links live in its "Plus" panel — see bottom-tab-bar. --}}
|
||||||
|
<nav class="hidden sm:block bg-surface rounded-full shadow-sm">
|
||||||
<!-- Primary Navigation Menu -->
|
<!-- Primary Navigation Menu -->
|
||||||
<div class="px-2.5 py-2">
|
<div class="px-2.5 py-2">
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
@@ -27,12 +29,12 @@ new class extends Component
|
|||||||
<span class="w-9 h-9 rounded-full bg-brand flex items-center justify-center shrink-0">
|
<span class="w-9 h-9 rounded-full bg-brand flex items-center justify-center shrink-0">
|
||||||
<x-application-logo class="block h-5 w-auto text-surface" />
|
<x-application-logo class="block h-5 w-auto text-surface" />
|
||||||
</span>
|
</span>
|
||||||
<span class="hidden sm:inline font-display text-lg text-text">{{ config('app.name', 'SuiviCPT') }}</span>
|
<span class="font-display text-lg text-text">{{ config('app.name', 'SuiviCPT') }}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Navigation Links -->
|
<!-- Navigation Links -->
|
||||||
<div class="hidden sm:flex sm:items-center sm:gap-1">
|
<div class="flex items-center gap-1">
|
||||||
<x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')" wire:navigate>
|
<x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')" wire:navigate>
|
||||||
{{ __('Tableau de bord') }}
|
{{ __('Tableau de bord') }}
|
||||||
</x-nav-link>
|
</x-nav-link>
|
||||||
@@ -55,7 +57,7 @@ new class extends Component
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Settings Dropdown -->
|
<!-- Settings Dropdown -->
|
||||||
<div class="hidden sm:flex sm:items-center sm:ms-6 gap-2">
|
<div class="flex items-center ms-6 gap-2">
|
||||||
<x-theme-toggle />
|
<x-theme-toggle />
|
||||||
|
|
||||||
<x-dropdown align="right" width="48">
|
<x-dropdown align="right" width="48">
|
||||||
@@ -98,67 +100,6 @@ new class extends Component
|
|||||||
</x-slot>
|
</x-slot>
|
||||||
</x-dropdown>
|
</x-dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Hamburger -->
|
|
||||||
<div class="-me-2 flex items-center sm:hidden">
|
|
||||||
<button
|
|
||||||
@click="open = ! open"
|
|
||||||
:aria-expanded="open.toString()"
|
|
||||||
aria-label="Menu"
|
|
||||||
class="inline-flex items-center justify-center p-2 rounded-md text-text-muted hover:text-text hover:bg-surface-alt focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring transition duration-150 ease-in-out"
|
|
||||||
>
|
|
||||||
<svg class="h-6 w-6" aria-hidden="true" stroke="currentColor" fill="none" viewBox="0 0 24 24">
|
|
||||||
<path :class="{'hidden': open, 'inline-flex': ! open }" class="inline-flex" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
|
||||||
<path :class="{'hidden': ! open, 'inline-flex': open }" class="hidden" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{-- Primary routes (Tableau de bord/Plan/Suivi/Flux) live in the bottom tab
|
|
||||||
bar on mobile now — this menu only needs account/settings links. --}}
|
|
||||||
<!-- Responsive Navigation Menu -->
|
|
||||||
<div :class="{'block': open, 'hidden': ! open}" class="hidden sm:hidden mt-2 bg-surface rounded-3xl shadow-sm px-2">
|
|
||||||
<!-- Responsive Settings Options -->
|
|
||||||
<div class="pt-2 pb-1">
|
|
||||||
<div class="px-4">
|
|
||||||
<div class="font-medium text-base text-text" x-data="{{ json_encode(['name' => auth()->user()->name]) }}" x-text="name" x-on:profile-updated.window="name = $event.detail.name"></div>
|
|
||||||
<div class="font-medium text-sm text-text-muted">{{ auth()->user()->email }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="px-4 py-2">
|
|
||||||
<x-theme-toggle />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-3 space-y-1">
|
|
||||||
<x-responsive-nav-link :href="route('profile')" wire:navigate>
|
|
||||||
{{ __('Profile') }}
|
|
||||||
</x-responsive-nav-link>
|
|
||||||
|
|
||||||
<x-responsive-nav-link :href="route('household.settings')" wire:navigate>
|
|
||||||
{{ __('Foyer') }}
|
|
||||||
</x-responsive-nav-link>
|
|
||||||
|
|
||||||
@if (Route::has('categories'))
|
|
||||||
<x-responsive-nav-link :href="route('categories')" wire:navigate>
|
|
||||||
{{ __('Catégories') }}
|
|
||||||
</x-responsive-nav-link>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
@if (Route::has('recurring'))
|
|
||||||
<x-responsive-nav-link :href="route('recurring')" wire:navigate>
|
|
||||||
{{ __('Récurrences') }}
|
|
||||||
</x-responsive-nav-link>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<!-- Authentication -->
|
|
||||||
<button wire:click="logout" class="w-full text-start">
|
|
||||||
<x-responsive-nav-link>
|
|
||||||
{{ __('Log Out') }}
|
|
||||||
</x-responsive-nav-link>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Livewire\Actions\Logout;
|
||||||
use App\Livewire\Categories\Index as CategoriesIndex;
|
use App\Livewire\Categories\Index as CategoriesIndex;
|
||||||
use App\Livewire\Dashboard\Index as DashboardIndex;
|
use App\Livewire\Dashboard\Index as DashboardIndex;
|
||||||
use App\Livewire\Household\AcceptInvite;
|
use App\Livewire\Household\AcceptInvite;
|
||||||
@@ -52,4 +53,10 @@ Route::get('recurrences', RecurringIndex::class)
|
|||||||
Route::get('invites/{token}', AcceptInvite::class)
|
Route::get('invites/{token}', AcceptInvite::class)
|
||||||
->name('invites.accept');
|
->name('invites.accept');
|
||||||
|
|
||||||
|
Route::post('logout', function (Logout $logout) {
|
||||||
|
$logout();
|
||||||
|
|
||||||
|
return redirect('/');
|
||||||
|
})->middleware(['auth'])->name('logout');
|
||||||
|
|
||||||
require __DIR__.'/auth.php';
|
require __DIR__.'/auth.php';
|
||||||
|
|||||||
Reference in New Issue
Block a user