Files
livewire-material/resources/views/components/account-menu.blade.php
T
Andreas Reinhold / reiniandClaude Opus 5 1218371568 Give the account-menu avatar a state layer
The trigger had the focus ring but no hover and no pressed feedback at all,
where every comparable trigger in the library carries `state-layer` and M3 asks
for two visual indicators per state. An avatar image sits behind the layer, so
the wash shows over a photograph too.
Plan: docs/plans/material-3-alignment.md, step 21 (navigation N-15).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-14 06:12:44 +02:00

82 lines
3.3 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-livewire-material::menu :$label :$position {{ $attributes }}>
<x-slot:trigger>
<button
type="button"
aria-label="{{ $label }}"
data-account-menu
class="state-layer touch-target focus-ring inline-flex size-10 shrink-0 cursor-pointer items-center justify-center rounded-corner-full bg-primary-container type-label-lg text-on-primary-container"
>
@if ($image)
{{-- Behind the state layer (`state-layer` paints its ::before at z-index -1), so an
avatar image still shows the hover and pressed states over it. --}}
<img src="{{ $avatar }}" alt="" class="relative -z-20 size-full rounded-corner-full object-cover" />
@elseif (filled($initials))
{{ $initials }}
@else
<x-livewire-material::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-livewire-material::menu-separator />
@endif
{{ $slot }}
@if ($theme)
<x-livewire-material::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-livewire-material::menu-item>
@endif
@isset($footer)
<x-livewire-material::menu-separator />
{{ $footer }}
@endisset
</x-livewire-material::menu>