Files
livewire-material/resources/views/components/radio.blade.php
T
Andreas Reinhold / reiniandClaude Fable 5.1 b1642aed1c Key the views on M3's window size classes, not Tailwind's
Plan step 15. Every `sm:`/`md:`/`lg:`/`xl:` in the components, the showcase
(sections, layout, shell) and the pagination and error views becomes the M3
window size class the audits' breakpoint maps prescribe: the full-screen
dialog, the FAB, the snackbar's width, `radio inline`, the section picker and
the page numbers move from 640px to `medium` (600px, M3's compact boundary,
N-07 / C-07 / IN-28); `button responsive` moves from 1024px to `expanded`
(840px); `section-nav`'s grid-to-row switch from 1280px to `large` (1200px,
N-07); and `drawer pane` from 1280px to `expanded`, where M3 shows two panes
for list-detail (C-07). The pane's default width is now 360dp, M3's fixed
supporting pane at expanded.

Showcase example grids take two columns from `medium` and three or more from
`expanded`; the showcase's own theme picker swaps at `expanded`, the nearest
class boundary to the 768px it used (no M3 rule applies to it).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-14 04:03:54 +02:00

82 lines
3.8 KiB
PHP

{{-- One choice from a few, each with room for a line of explanation: M3's radio buttons.
A `<fieldset>` whose legend is the question, so a screen reader reads the group's name with each
option, and native radios sharing a name, so the arrow keys move between them. `options` as
`['id' => …, 'name' => …, 'hint' => …]` (`'disabled' => true` greys one out), `option-value`,
`option-label` and `option-hint` to read other keys, `inline` to lay them in a row from `medium`
(600px, M3's compact/medium boundary — compact always stacks), and
`value` for the option chosen when the page loads (with `wire:model`, Livewire sets it).
Errors are read from the bag under the `wire:model` name
(resources/css/components/selection.css). --}}
@props([
'label' => null,
'hint' => null,
'options' => [],
'optionValue' => 'id',
'optionLabel' => 'name',
'optionHint' => 'hint',
'inline' => false,
'value' => null,
])
@php
$model = $attributes->whereStartsWith('wire:model')->first();
$name = $attributes->get('name') ?? $model ?? 'radio-'.substr(md5($label.'|'.json_encode($options)), 0, 12);
$id = 'radio-'.substr(md5($name.'|'.$label), 0, 12);
// A plain form's field is named, not bound: its errors are under its name (`files[]` `files`, `a[b]` `a.b`).
$errorKey = $model ?? (filled($attributes->get('name')) ? str_replace(['[]', '[', ']'], ['', '.', ''], (string) $attributes->get('name')) : null);
$messages = $errorKey !== null && isset($errors) ? \Illuminate\Support\Arr::flatten($errors->get($errorKey)) : [];
@endphp
<fieldset
{{ $attributes->only(['class', 'wire:key'])->class(['min-w-0']) }}
@if ($messages !== [] || filled($hint)) aria-describedby="{{ $id }}-support" @endif
>
@if (filled($label))
<legend class="mb-3 type-label-lg text-on-surface-variant">{{ $label }}</legend>
@endif
<div @class(['grid gap-4', 'medium:flex medium:flex-wrap medium:gap-x-6' => $inline])>
@foreach ($options as $option)
@php($optionHintText = data_get($option, $optionHint))
<label data-selection @if ($messages !== []) data-invalid @endif @class([
'flex gap-4',
'items-start' => filled($optionHintText),
'items-center' => blank($optionHintText),
])>
<span data-radio @class(['mt-0.5' => filled($optionHintText)])>
<input
{{ $attributes->except(['class', 'wire:key', 'name', 'id']) }}
type="radio"
name="{{ $name }}"
value="{{ data_get($option, $optionValue) }}"
@disabled(data_get($option, 'disabled'))
@checked($value !== null && (string) $value === (string) data_get($option, $optionValue))
@if ($messages !== []) aria-invalid="true" @endif
/>
<span data-dot></span>
</span>
<span class="min-w-0">
<span class="block type-body-lg text-on-surface" data-selection-label>{{ data_get($option, $optionLabel) }}</span>
@if (filled($optionHintText))
<span class="mt-0.5 block type-body-sm text-on-surface-variant">{{ $optionHintText }}</span>
@endif
</span>
</label>
@endforeach
</div>
@if ($messages !== [])
<div id="{{ $id }}-support" class="mt-2 type-body-sm text-error" role="alert">
@foreach ($messages as $message)
<p>{{ $message }}</p>
@endforeach
</div>
@elseif (filled($hint))
<p id="{{ $id }}-support" class="mt-2 type-body-sm text-on-surface-variant">{{ $hint }}</p>
@endif
</fieldset>