End a select's long value before its arrow, with an ellipsis

Where the browser has the customizable select, select.css made the
select `display: flex` with `text-overflow: ellipsis`, but the closed
select draws its value in a block of its user-agent shadow tree: a flex
item at its automatic minimum width that takes no `text-overflow` from
the select, and no author style reaches it (`display: block` on the
select does not help either). So a long option in a narrow field, or
at `size="sm"` and `xs`, ran on past the end padding under the arrow,
which `overflow: hidden` only clips at the select's edge. The native
select in Firefox cut the value at the padding with no ellipsis.

The view now gives the select a first child `<button type="button"
wire:ignore>` holding `<selectedcontent>`, the customizable select's own
copy of the chosen option, and select.css makes that the flex item that
shrinks and ends in an ellipsis; the native select takes `nowrap`,
`overflow: hidden` and the ellipsis on itself. `wire:ignore`, because a
Livewire render parses the server's empty element into the first
option's label and morphed it back over the chosen one. A browser
without the customizable select never renders the button; `type` keeps
it from being a form's default button.

A browser test measures the value against the arrow in md, sm, xs,
filled and icon selects 240px wide, and again after choosing another
option re-renders the component; it fails without the change in Chrome
and Firefox.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Andreas Reinhold / reini
2026-09-17 06:50:53 +02:00
co-authored by Claude Opus 5
parent c5a3870b7f
commit 1885c3e54e
6 changed files with 117 additions and 4 deletions
@@ -671,7 +671,7 @@ M3 text fields. `variant`: `outlined` or `filled`; without it, `config('livewire
- `<x-textarea>`: grows from `rows` (3) to `max-rows`, then scrolls; `:autogrow="false"` for a fixed, hand-resizable one; `counter`.
- Width: M3 asks that a text field never span the full width of a large screen, so from `medium` (600px) every field stops at **40rem**; below that it fills its pane. A width rule of your own on its `class` (`max-inline-size: 24rem`) narrows or widens it, because your unlayered CSS outranks the package's, and `full` (on `<x-field>`, `<x-input>`, `<x-textarea>`) takes the bound off for a field that really is the width of its pane — a search row, an editor. `<x-search>`'s bar carries M3's own bound, 720px.
- `counter` (on `<x-input>` and `<x-textarea>`) puts M3's character counter at the end of the supporting-text row, beside the hint or the error: `n/max`, counted on every keystroke against the field's own `maxlength`, and in the error colour once the value is past it. It needs `maxlength` — without one there is nothing to count against and nothing is drawn. It is said as "Character count, 5/20" from a polite region a second after typing stops.
- `<x-select>`: native `<select>` (M3 menu where the browser supports customizable selects). `options` as `['id' => …, 'name' => …, 'disabled' => bool]`, `option-value`, `option-label`, `placeholder` + `placeholder-value`, or `<option>`s in the slot; `icon`, `size`.
- `<x-select>`: native `<select>` (M3 menu where the browser supports customizable selects). `options` as `['id' => …, 'name' => …, 'disabled' => bool]`, `option-value`, `option-label`, `placeholder` + `placeholder-value`, or `<option>`s in the slot; `icon`, `size`. A value too long for the field ends before the arrow with an ellipsis; the customizable select draws it in a `<button>` holding `<selectedcontent>`, which the view renders as the select's first child.
- `<x-file>`: native file input; errors from `photos` and `photos.*`. Show previews of what was chosen yourself.
- `<x-field id="…" label="…" :messages="$messages">` wraps a custom control given `data-md-field-control`; only for controls the package does not have.