Add buttons, menus and the rest of M3 Expressive's actions
tests / browser (firefox, firefox) (push) Successful in 1m54s
tests / browser (safari, webkit) (push) Successful in 2m17s
tests / lint (push) Successful in 59s
tests / feature (8.4) (push) Successful in 1m7s
tests / feature (8.5) (push) Successful in 1m0s
tests / browser (chrome, chromium) (push) Successful in 1m49s
tests / browser (firefox, firefox) (push) Successful in 1m54s
tests / browser (safari, webkit) (push) Successful in 2m17s
tests / lint (push) Successful in 59s
tests / feature (8.4) (push) Successful in 1m7s
tests / feature (8.5) (push) Successful in 1m0s
tests / browser (chrome, chromium) (push) Successful in 1m49s
<x-button> (label buttons, icon buttons and toggles in five sizes, with filled, tonal, outlined, elevated and text variants in any colour role), <x-tooltip>, <x-menu> with items, groups and separators, <x-button-group>, <x-group> as a connected button group, <x-split-button>, <x-fab>, <x-fab-menu> and <x-loading>. Sizes, colours and shapes come from androidx Compose Material 3's tokens; the loading indicator ports its Morph into SVG + SMIL. Menus follow WAI-ARIA's menu button pattern on popovers placed by CSS anchor positioning. Browser tests run in Chromium, Firefox and WebKit. The showcase fetches the icon names on demand: inlined, they tripped Pest's test server into HTTP 431s under Firefox. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
This commit is contained in:
co-authored by
Claude Opus 5
parent
b48e879254
commit
cd64f4f371
@@ -0,0 +1,97 @@
|
||||
{{-- A choice of a few options as an M3 Expressive connected button group — the successor of the
|
||||
segmented button.
|
||||
|
||||
<x-group label="Theme" wire:model.live="theme" :options="[
|
||||
['id' => 'light', 'name' => 'Light', 'icon' => 'light_mode'],
|
||||
['id' => 'dark', 'name' => 'Dark', 'icon' => 'dark_mode'],
|
||||
]" />
|
||||
|
||||
Native radios under the segments (checkboxes with `multiple`), so `wire:model` and `x-model`
|
||||
bind as on any input, the arrow keys move the choice, and a screen reader announces a group.
|
||||
The chosen segment rounds fully and takes the selected colour; `variant` is `tonal` (the
|
||||
default), `filled` or `outlined`, as for toggle buttons. The segments share the row unless
|
||||
`inline`. An option with `'disabled' => true` greys its own segment.
|
||||
|
||||
ReStride's props, kept: `label`, `hint`, `name` (needed with `x-model`, which names no
|
||||
property), `options`, `option-value`, `option-label`; plus `option-icon`, `size`, `variant`,
|
||||
`multiple`, `inline`. A validation message for the bound property replaces the hint. --}}
|
||||
|
||||
@props([
|
||||
'label' => null,
|
||||
'hint' => null,
|
||||
'name' => null,
|
||||
'options' => [],
|
||||
'optionValue' => 'id',
|
||||
'optionLabel' => 'name',
|
||||
'optionIcon' => 'icon',
|
||||
'size' => 'sm',
|
||||
'variant' => 'tonal',
|
||||
'multiple' => false,
|
||||
'inline' => false,
|
||||
])
|
||||
|
||||
@php
|
||||
$model = $attributes->whereStartsWith('wire:model')->first();
|
||||
$name ??= $model;
|
||||
$messages = $model !== null && isset($errors) ? \Illuminate\Support\Arr::flatten($errors->get($model)) : [];
|
||||
$size = in_array($size, ['xs', 'sm', 'md', 'lg', 'xl'], true) ? $size : 'sm';
|
||||
|
||||
$segment = [
|
||||
'xs' => 'h-8 gap-2 px-3 type-label-lg',
|
||||
'sm' => 'h-10 gap-2 px-4 type-label-lg',
|
||||
'md' => 'h-14 gap-2 px-6 type-title-md',
|
||||
'lg' => 'h-24 gap-3 px-12 type-headline-sm',
|
||||
'xl' => 'h-34 gap-4 px-16 type-headline-lg',
|
||||
][$size];
|
||||
|
||||
$iconSize = ['xs' => 'size-5', 'sm' => 'size-5', 'md' => 'size-6', 'lg' => 'size-8', 'xl' => 'size-10'][$size];
|
||||
|
||||
$colours = match ($variant) {
|
||||
'filled' => 'bg-surface-container text-on-surface-variant has-checked:bg-primary has-checked:text-on-primary',
|
||||
'outlined' => 'border border-outline-variant text-on-surface-variant has-checked:border-transparent has-checked:bg-inverse-surface has-checked:text-inverse-on-surface',
|
||||
default => 'bg-secondary-container text-on-secondary-container has-checked:bg-secondary has-checked:text-on-secondary',
|
||||
};
|
||||
@endphp
|
||||
|
||||
<fieldset {{ $attributes->only(['class', 'wire:key'])->class('min-w-0') }}>
|
||||
@if (filled($label))
|
||||
<legend class="mb-2 type-label-lg text-on-surface-variant">{{ $label }}</legend>
|
||||
@endif
|
||||
|
||||
<div data-button-group="connected" data-size="{{ $size }}" @class(['flex gap-0.5', 'w-full' => ! $inline, 'w-fit' => $inline])>
|
||||
@foreach ($options as $option)
|
||||
<label @class([
|
||||
'state-layer relative flex cursor-pointer select-none items-center justify-center whitespace-nowrap',
|
||||
'transition-[border-radius,background-color,color] duration-(--md-sys-motion-spatial-fast-duration) ease-spatial-fast',
|
||||
'min-w-0 flex-1' => ! $inline,
|
||||
$segment,
|
||||
$colours,
|
||||
'has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-secondary',
|
||||
'has-disabled:cursor-not-allowed has-disabled:bg-on-surface/10 has-disabled:text-on-surface/38',
|
||||
])>
|
||||
<input
|
||||
{{ $attributes->whereStartsWith(['wire:model', 'x-model']) }}
|
||||
type="{{ $multiple ? 'checkbox' : 'radio' }}"
|
||||
name="{{ $multiple ? $name.'[]' : $name }}"
|
||||
value="{{ data_get($option, $optionValue) }}"
|
||||
@disabled(data_get($option, 'disabled'))
|
||||
class="peer sr-only"
|
||||
/>
|
||||
|
||||
@if (filled(data_get($option, $optionIcon)))
|
||||
<x-icon :name="data_get($option, $optionIcon)" :class="$iconSize" />
|
||||
@endif
|
||||
|
||||
<span class="truncate">{{ data_get($option, $optionLabel) }}</span>
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@if ($messages !== [])
|
||||
@foreach ($messages as $message)
|
||||
<p class="mt-1 type-body-sm text-error">{{ $message }}</p>
|
||||
@endforeach
|
||||
@elseif (filled($hint))
|
||||
<p class="mt-1 type-body-sm text-on-surface-variant">{{ $hint }}</p>
|
||||
@endif
|
||||
</fieldset>
|
||||
Reference in New Issue
Block a user