tests / browser (chrome, chromium) (push) Successful in 3m34s
tests / browser (safari, webkit) (push) Successful in 6m2s
tests / lint (push) Successful in 59s
tests / feature (8.4) (push) Successful in 1m6s
tests / feature (8.5) (push) Successful in 1m9s
tests / browser (firefox, firefox) (push) Successful in 4m20s
M3 Expressive top app bars (small, centered, medium and large flexible, search) that collapse with CSS sticky, docked and floating toolbars, primary and secondary tabs with a view-transition indicator, section navigation, the account menu and theme toggles. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
80 lines
2.9 KiB
PHP
80 lines
2.9 KiB
PHP
{{-- Who is signed in, and what belongs to them rather than to a page: an avatar that opens a menu.
|
|
|
|
`name` and `email` head the menu; `avatar` is an image URL or, by default, the initials of the
|
|
name. The slot holds the items (`<x-menu-item>`: settings, billing…), a theme item follows unless
|
|
`:theme="false"`, and the `footer` slot comes last, after a separator — the place for signing
|
|
out, furthest from the thumb:
|
|
|
|
<x-account-menu :name="$user->name" :email="$user->email">
|
|
<x-menu-item label="Settings" icon="settings" :link="route('settings')" />
|
|
<x-slot:footer>
|
|
<form method="POST" action="{{ route('logout') }}">
|
|
@csrf
|
|
<x-menu-item label="Sign out" icon="logout" type="submit" />
|
|
</form>
|
|
</x-slot:footer>
|
|
</x-account-menu>
|
|
|
|
`label` names the button and the menu ("Account"). `position` as on `<x-menu>` (`bottom-end`). --}}
|
|
|
|
@props([
|
|
'name' => null,
|
|
'email' => null,
|
|
'avatar' => null,
|
|
'label' => null,
|
|
'theme' => true,
|
|
'position' => 'bottom-end',
|
|
])
|
|
|
|
@php
|
|
$label ??= __('Account');
|
|
$image = filled($avatar) && (str_contains((string) $avatar, '/') || str_contains((string) $avatar, '.'));
|
|
$initials = $image ? null : ($avatar ?? \Illuminate\Support\Str::of((string) $name)->explode(' ')->filter()->take(2)->map(fn (string $word): string => mb_strtoupper(mb_substr($word, 0, 1)))->join(''));
|
|
@endphp
|
|
|
|
<x-menu :$label :$position {{ $attributes }}>
|
|
<x-slot:trigger>
|
|
<button
|
|
type="button"
|
|
aria-label="{{ $label }}"
|
|
data-account-menu
|
|
class="focus-ring inline-flex size-10 shrink-0 cursor-pointer items-center justify-center overflow-hidden rounded-corner-full bg-primary-container type-label-lg text-on-primary-container"
|
|
>
|
|
@if ($image)
|
|
<img src="{{ $avatar }}" alt="" class="size-full object-cover" />
|
|
@elseif (filled($initials))
|
|
{{ $initials }}
|
|
@else
|
|
<x-icon name="person" />
|
|
@endif
|
|
</button>
|
|
</x-slot:trigger>
|
|
|
|
@if (filled($name) || filled($email))
|
|
<div class="px-3 pt-2 pb-3">
|
|
@if (filled($name))
|
|
<p class="truncate type-title-sm text-on-surface">{{ $name }}</p>
|
|
@endif
|
|
@if (filled($email))
|
|
<p class="truncate type-body-sm text-on-surface-variant">{{ $email }}</p>
|
|
@endif
|
|
</div>
|
|
|
|
<x-menu-separator />
|
|
@endif
|
|
|
|
{{ $slot }}
|
|
|
|
@if ($theme)
|
|
<x-menu-item icon="contrast" x-on:click="$store.theme.toggle()" data-account-theme>
|
|
<span x-text="$store.theme.resolved === 'dark' ? @js(__('Light theme')) : @js(__('Dark theme'))">{{ __('Theme') }}</span>
|
|
</x-menu-item>
|
|
@endif
|
|
|
|
@isset($footer)
|
|
<x-menu-separator />
|
|
|
|
{{ $footer }}
|
|
@endisset
|
|
</x-menu>
|