Package views now write <x-livewire-material::button>, so a configured prefix (which Blade spells <x-m::button>) and an application component of the same name can no longer break or shadow them. The showcase rewrites its examples to the configured prefix. Adds the README, and waits for the adaptive rail to hear a resize before the navigation tests press its menu button. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
78 lines
3.5 KiB
PHP
78 lines
3.5 KiB
PHP
{{-- An M3 Expressive FAB menu: a FAB that opens into a short list of related actions.
|
|
|
|
<div class="fixed end-4 bottom-4">
|
|
<x-fab-menu label="New">
|
|
<x-fab-menu-item label="Upload files" icon="upload_file" wire:click="uploadFiles" />
|
|
<x-fab-menu-item label="Upload a folder" icon="drive_folder_upload" wire:click="uploadFolder" />
|
|
</x-fab-menu>
|
|
</div>
|
|
|
|
Two to six items. The FAB (`icon`, `add` by default, in `color`'s container) turns into a
|
|
round close button in the colour itself while the list is open above it, end-aligned; the
|
|
list is a `popover="auto"` menu with the menu keyboard of `<x-menu>`. `label` names the FAB
|
|
for screen readers. Give the items the same `color`.
|
|
|
|
FabMenuBaselineTokens (androidx Compose Material 3, Apache-2.0): 56px items, 4px apart, 8px
|
|
above the close button. --}}
|
|
|
|
@props([
|
|
'icon' => 'add',
|
|
'label' => null,
|
|
'color' => 'primary',
|
|
'position' => 'top-end',
|
|
])
|
|
|
|
@php
|
|
$color = in_array($color, ['primary', 'secondary', 'tertiary'], true) ? $color : 'primary';
|
|
$key = \Illuminate\Support\Str::lower(\Illuminate\Support\Str::random(10));
|
|
$anchor = "--material-fab-menu-{$key}";
|
|
|
|
$colours = [
|
|
'primary' => 'bg-primary-container text-on-primary-container aria-expanded:bg-primary aria-expanded:text-on-primary',
|
|
'secondary' => 'bg-secondary-container text-on-secondary-container aria-expanded:bg-secondary aria-expanded:text-on-secondary',
|
|
'tertiary' => 'bg-tertiary-container text-on-tertiary-container aria-expanded:bg-tertiary aria-expanded:text-on-tertiary',
|
|
][$color];
|
|
@endphp
|
|
|
|
<div x-data="materialMenu" {{ $attributes->class('relative inline-flex') }}>
|
|
<span x-ref="trigger" class="inline-flex" style="anchor-name: {{ $anchor }}"
|
|
x-on:click="toggle('first')"
|
|
x-on:keydown.down.prevent="open('first')"
|
|
x-on:keydown.up.prevent="open('last')"
|
|
>
|
|
<button
|
|
type="button"
|
|
@if ($label) aria-label="{{ $label }}" @endif
|
|
@class([
|
|
'group/fab state-layer focus-ring inline-flex size-14 shrink-0 cursor-pointer items-center justify-center rounded-corner-lg shadow-elevation-3 hover:shadow-elevation-4',
|
|
'transition-[border-radius,background-color,color,box-shadow] duration-(--md-sys-motion-spatial-default-duration) ease-spatial-default aria-expanded:rounded-corner-full',
|
|
$colours,
|
|
])
|
|
>
|
|
<x-livewire-material::icon :name="$icon" class="size-6 group-aria-expanded/fab:hidden" />
|
|
<x-livewire-material::icon name="close" class="hidden size-5 group-aria-expanded/fab:block" />
|
|
</button>
|
|
</span>
|
|
|
|
<div
|
|
x-ref="menu"
|
|
id="material-fab-menu-{{ $key }}"
|
|
popover="auto"
|
|
role="menu"
|
|
@if ($label) aria-label="{{ $label }}" @endif
|
|
tabindex="-1"
|
|
style="position-anchor: {{ $anchor }}"
|
|
x-on:keydown="navigate($event)"
|
|
x-on:click="activate($event)"
|
|
@class([
|
|
'm-0 flex-col gap-1 overflow-visible border-0 bg-transparent p-0 open:flex [inset:auto]',
|
|
'mb-2 items-end [position-area:top_span-left]' => $position === 'top-end',
|
|
'mb-2 items-start [position-area:top_span-right]' => $position === 'top-start',
|
|
'mt-2 items-end [position-area:bottom_span-left]' => $position === 'bottom-end',
|
|
'mt-2 items-start [position-area:bottom_span-right]' => $position === 'bottom-start',
|
|
])
|
|
>
|
|
{{ $slot }}
|
|
</div>
|
|
</div>
|