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
82 lines
3.3 KiB
PHP
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>
|