Move the select's rules out of the field into select.css
<x-select> marks its field root data-md-select, and what a <select> changes in the field — covering the whole box, the customizable select's closed button, the focused look and turned arrow while open — moves from field.css to components/select.css (plan step 36). The open list stays in menu.css until the menu leaves Tailwind. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
co-authored by
Claude Opus 5
parent
c2fe64e00d
commit
23f2bdabf9
@@ -19,6 +19,7 @@
|
||||
@import './components/input.css';
|
||||
@import './components/password.css';
|
||||
@import './components/textarea.css';
|
||||
@import './components/select.css';
|
||||
|
||||
/* Containment */
|
||||
|
||||
|
||||
@@ -138,59 +138,6 @@
|
||||
transition: background-color 604800s, color 604800s;
|
||||
}
|
||||
|
||||
/* A select covers its whole field — out over the padding and the leading icon at
|
||||
the start, under the arrow to the end — and its own padding puts the value back
|
||||
where it was. So a press anywhere on the field opens it, and the list, which
|
||||
the browser anchors to the select, is the field's width (components/menu.css). */
|
||||
select[data-md-field-control] {
|
||||
--field-end: calc(var(--field-icon) + var(--field-gap) + var(--field-pad));
|
||||
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
margin-inline: calc(-1 * var(--field-start)) calc(-1 * var(--field-end));
|
||||
padding-inline: var(--field-start) var(--field-end);
|
||||
}
|
||||
|
||||
select[data-md-field-control]:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* Where the browser has a customizable select, the closed select is its own
|
||||
button: without the border, padding and arrow that button brings, it is the
|
||||
same field as before. While its list is open the field reads as focused —
|
||||
said from `:open`, because focus inside the list is in the top layer — and
|
||||
the arrow turns over, as the searchable choices' does. */
|
||||
@supports (appearance: base-select) {
|
||||
select[data-md-field-control] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 0;
|
||||
padding-block: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
select[data-md-field-control]::picker-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-md-field-box]:has(select[data-md-field-control]:open) {
|
||||
--field-edge: var(--md-sys-color-primary);
|
||||
--field-ink: var(--md-sys-color-primary);
|
||||
|
||||
[data-md-field-outline] {
|
||||
border-width: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
[data-md-field]:has(select[data-md-field-control]:open) [data-md-field-arrow] {
|
||||
rotate: 180deg;
|
||||
}
|
||||
}
|
||||
|
||||
/* `<x-file>`: the browser's own "No file chosen" line is transparent (the caller shows what was
|
||||
chosen), and its button is a tonal pill — the thing to press. */
|
||||
input[type='file'][data-md-field-control] {
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* <x-select>: the native <select> in the text field's chrome, drawn as M3's exposed dropdown menu
|
||||
* (resources/views/components/select.blade.php).
|
||||
*
|
||||
* The chrome is the field's (components/field.css); this file is what a <select> changes in it. The
|
||||
* select covers its whole field, so a press anywhere opens it and the list the browser anchors to
|
||||
* it is the field's width. Where the browser has the customizable select (`appearance:
|
||||
* base-select`), the closed select loses the button chrome that brings, the field reads as focused
|
||||
* while the list is open, and the arrow turns over. The open list itself is M3's menu, drawn by
|
||||
* components/menu.css.
|
||||
*
|
||||
* The field's root carries `data-md-select`.
|
||||
*/
|
||||
|
||||
@layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility;
|
||||
|
||||
@import './field.css';
|
||||
@import './icon.css';
|
||||
/* TODO(step 36): @import './menu.css' once the menu leaves Tailwind (actions stream); its select rules draw the open list. */
|
||||
|
||||
@layer material.components {
|
||||
/* A select covers its whole field — out over the padding and the leading icon at
|
||||
the start, under the arrow to the end — and its own padding puts the value back
|
||||
where it was. So a press anywhere on the field opens it, and the list, which
|
||||
the browser anchors to the select, is the field's width (components/menu.css). */
|
||||
select[data-md-field-control] {
|
||||
--field-end: calc(var(--field-icon) + var(--field-gap) + var(--field-pad));
|
||||
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
margin-inline: calc(-1 * var(--field-start)) calc(-1 * var(--field-end));
|
||||
padding-inline: var(--field-start) var(--field-end);
|
||||
}
|
||||
|
||||
select[data-md-field-control]:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* Where the browser has a customizable select, the closed select is its own
|
||||
button: without the border, padding and arrow that button brings, it is the
|
||||
same field as before. While its list is open the field reads as focused —
|
||||
said from `:open`, because focus inside the list is in the top layer — and
|
||||
the arrow turns over, as the searchable choices' does. */
|
||||
@supports (appearance: base-select) {
|
||||
select[data-md-field-control] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 0;
|
||||
padding-block: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
select[data-md-field-control]::picker-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-md-field-box]:has(select[data-md-field-control]:open) {
|
||||
--field-edge: var(--md-sys-color-primary);
|
||||
--field-ink: var(--md-sys-color-primary);
|
||||
|
||||
[data-md-field-outline] {
|
||||
border-width: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
[data-md-field]:has(select[data-md-field-control]:open) [data-md-field-arrow] {
|
||||
rotate: 180deg;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,8 @@
|
||||
`options` as a list of `['id' => …, 'name' => …]` (`'disabled' => true` greys one out),
|
||||
`option-value` and `option-label` to read other keys, and a `placeholder` that becomes the
|
||||
first option, valued `placeholder-value` (empty by default). Or pass `<option>`s in the slot.
|
||||
`label`, `hint`, `icon`, `variant`, `size`. --}}
|
||||
`label`, `hint`, `icon`, `variant`, `size`. The field's root carries `data-md-select`; what a
|
||||
select changes in the field's chrome is resources/css/components/select.css. --}}
|
||||
|
||||
@props([
|
||||
'label' => null,
|
||||
@@ -35,7 +36,7 @@
|
||||
$fieldIconSize = ['sm' => 20, 'xs' => 16][$size] ?? 24;
|
||||
@endphp
|
||||
|
||||
<x-livewire-material::field :$id :$label :$hint :hint-class="$hintClass" :$messages :$icon :$size :$variant floated :class="$attributes->get('class')">
|
||||
<x-livewire-material::field :$id :$label :$hint :hint-class="$hintClass" :$messages :$icon :$size :$variant floated :class="$attributes->get('class')" data-md-select>
|
||||
<select
|
||||
{{ $attributes->except(['class', 'id']) }}
|
||||
id="{{ $id }}"
|
||||
|
||||
@@ -127,6 +127,8 @@ it('draws a native select whose label always floats', function () {
|
||||
|
||||
expect($html)
|
||||
->toContain('data-md-floated')
|
||||
->toContain('data-md-select')
|
||||
->toMatch('/<select(?=[^>]*data-md-field-control)[^>]*>/')
|
||||
->toContain('<select')
|
||||
->toContain('wire:model="hours"')
|
||||
->toContain('<option value="">Choose…</option>')
|
||||
@@ -266,3 +268,13 @@ it('draws what a textarea changes in the field from its own stylesheet', functio
|
||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components/field.css'))->not->toContain('textarea[')
|
||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/textarea.css';");
|
||||
});
|
||||
|
||||
it('draws what a select changes in the field from its own stylesheet', function () {
|
||||
$css = (string) file_get_contents(__DIR__.'/../../../resources/css/components/select.css');
|
||||
|
||||
expect($css)->toContain("@import './field.css';")
|
||||
->toContain('@supports (appearance: base-select)')
|
||||
->toContain('select[data-md-field-control]:open')
|
||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components/field.css'))->not->toContain('select[')
|
||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/select.css';");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user