Files
livewire-material/resources/views/components/account-menu.blade.php
T
Andreas Reinhold / reiniandClaude Sonnet 5 f5c90c3874 Draw the account menu without Tailwind
Plan step 36 (navigation group, third batch): <x-account-menu>'s class
lists move into resources/css/components/account-menu.css, keyed on
data-md-account-menu (the avatar trigger), data-md-account-menu-avatar
and data-md-account-menu-header. The trigger already carried the
target-extension and state-layer fixes from an earlier pass (N-01,
N-15); this rewrite only moves them onto the shared md-state-layer/
md-touch-target/md-focus-ring classes and their z-index-ordering
comment into the stylesheet. The header's name and email keep their
type and ink as the fixed text classes on the view
(md-type-title-sm/md-ink, md-type-body-sm/md-ink-variant), since they
are the caller's own prose.

Hooks renamed: data-account-menu to data-md-account-menu,
data-account-theme to data-md-account-menu-theme, updated in
AppBarTest.php, which carries account-menu's render tests.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 00:42:28 +02:00

82 lines
3.1 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-md-account-menu
class="md-state-layer md-touch-target md-focus-ring"
>
@if ($image)
{{-- Behind the state layer (md-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="" data-md-account-menu-avatar />
@elseif (filled($initials))
{{ $initials }}
@else
<x-livewire-material::icon name="person" />
@endif
</button>
</x-slot:trigger>
@if (filled($name) || filled($email))
<div data-md-account-menu-header>
@if (filled($name))
<p class="md-truncate md-type-title-sm md-ink">{{ $name }}</p>
@endif
@if (filled($email))
<p class="md-truncate md-type-body-sm md-ink-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-md-account-menu-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>