<x-field> renders data-md-field with its props and parts as data-md-* attributes (box, control, label, outline, support, counter, trailing buttons), and field.css moves into material.components on tokens: px geometry, spacing and state tokens, the 600px breakpoint (plan step 36). Its icons take a size prop, 24/20/16 by the field's size. Every control the field wraps now marks itself data-md-field-control, so the inputs, the pickers, choices, field.js, menu.css's select and listbox rules and timepicker.css follow the renamed hooks. Browser tests cover the error icon, the disabled field's hover, the counter and the width bound from 600px. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
41 lines
2.2 KiB
PHP
41 lines
2.2 KiB
PHP
{{-- A file picker, in the text field's chrome (resources/views/components/field.blade.php).
|
|
|
|
M3 has no file field, and building one would redo what the browser's input already does — the
|
|
picker, a file dropped on it, the keyboard, what a screen reader announces, the `change` a
|
|
Livewire upload listens for. So the input stays native, its label always floats (the button
|
|
stands where a resting label would), and only its `::file-selector-button` is restyled: a tonal
|
|
pill, so it reads as the thing to press (resources/css/components/field.css).
|
|
|
|
The browser's own line beside the button ("No file chosen", "3 files") is drawn transparent:
|
|
it cannot be truncated or wrapped, and the caller shows what was chosen anyway, as previews. A
|
|
screen reader still hears it. The errors are the model's and, for a `multiple` input, each
|
|
file's (`photos.*`): a file refused for its size belongs under this field as much as the
|
|
batch's own count does. `label`, `hint`, `variant`; every other attribute reaches the `<input>`. --}}
|
|
|
|
@props([
|
|
'label' => null,
|
|
'hint' => null,
|
|
'variant' => null,
|
|
])
|
|
|
|
@php
|
|
$model = $attributes->whereStartsWith('wire:model')->first();
|
|
$id = $attributes->get('id') ?? 'field-'.substr(md5('file|'.$model.'|'.$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)
|
|
? array_values(array_unique(\Illuminate\Support\Arr::flatten([$errors->get($errorKey), $errors->get($errorKey.'.*')])))
|
|
: [];
|
|
@endphp
|
|
|
|
<x-livewire-material::field :$id :$label :$hint :$messages :$variant floated :class="$attributes->get('class')">
|
|
<input
|
|
{{ $attributes->except(['class', 'id', 'type']) }}
|
|
type="file"
|
|
id="{{ $id }}"
|
|
@if ($messages !== []) aria-invalid="true" @endif
|
|
@if ($messages !== [] || filled($hint)) aria-describedby="{{ $id }}-support" @endif
|
|
data-md-field-control
|
|
/>
|
|
</x-livewire-material::field>
|