@antadesign/anta 0.3.33 → 0.3.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +14 -1
- package/README.md +9 -0
- package/dist/anta_helpers.d.ts +46 -30
- package/dist/anta_helpers.js +66 -25
- package/dist/bundle.css +1 -1
- package/dist/bundle.js +2 -1353
- package/dist/components/Banner.d.ts +25 -40
- package/dist/components/Banner.js +15 -17
- package/dist/components/Breadcrumbs.d.ts +0 -1
- package/dist/components/Breadcrumbs.js +0 -1
- package/dist/components/Button.d.ts +2 -3
- package/dist/components/Button.js +1 -0
- package/dist/components/ButtonCopy.js +3 -2
- package/dist/components/Calendar.js +13 -13
- package/dist/components/Card.d.ts +9 -17
- package/dist/components/Card.js +50 -46
- package/dist/components/Dialog.d.ts +1 -0
- package/dist/components/Dialog.js +16 -10
- package/dist/components/Expander.d.ts +6 -21
- package/dist/components/Expander.js +24 -23
- package/dist/components/Icon.d.ts +8 -14
- package/dist/components/Input.d.ts +77 -96
- package/dist/components/Input.js +23 -27
- package/dist/components/InputAutocomplete.d.ts +29 -10
- package/dist/components/InputAutocomplete.js +25 -8
- package/dist/components/InputDate.d.ts +0 -1
- package/dist/components/InputDate.js +2 -3
- package/dist/components/InputTime.d.ts +10 -4
- package/dist/components/InputTime.js +20 -16
- package/dist/components/Loader.d.ts +2 -2
- package/dist/components/Menu.d.ts +5 -4
- package/dist/components/Menu.js +13 -8
- package/dist/components/MenuItem.js +1 -14
- package/dist/components/MenuItemCopy.js +2 -2
- package/dist/components/Panel.d.ts +3 -1
- package/dist/components/Panel.js +17 -11
- package/dist/components/Progress.d.ts +21 -23
- package/dist/components/Progress.js +13 -9
- package/dist/components/RadioGroup.js +6 -8
- package/dist/components/{select-parts.css → Select.css} +1 -1
- package/dist/components/Select.d.ts +53 -36
- package/dist/components/Select.js +53 -98
- package/dist/components/SelectFaceted.d.ts +7 -4
- package/dist/components/SelectFaceted.js +21 -10
- package/dist/components/Slider.d.ts +28 -11
- package/dist/components/Slider.js +20 -20
- package/dist/components/Steps.css +1 -1
- package/dist/components/Steps.d.ts +0 -1
- package/dist/components/Steps.js +1 -12
- package/dist/components/Tabs.d.ts +17 -20
- package/dist/components/Tabs.js +7 -9
- package/dist/components/Text.d.ts +55 -36
- package/dist/components/Text.js +35 -22
- package/dist/components/Title.d.ts +19 -27
- package/dist/components/Title.js +2 -1
- package/dist/components/Toaster.js +9 -2
- package/dist/components/Tooltip.d.ts +10 -15
- package/dist/components/copy-props.d.ts +14 -3
- package/dist/components/copy-props.js +14 -3
- package/dist/components/select-tree.d.ts +24 -0
- package/dist/components/select-tree.js +35 -0
- package/dist/element-base.d.ts +10 -0
- package/dist/element-base.js +15 -0
- package/dist/elements/a-avatar.css +1 -1
- package/dist/elements/a-avatar.js +2 -28
- package/dist/elements/a-banner.css +1 -1
- package/dist/elements/a-banner.d.ts +4 -1
- package/dist/elements/a-banner.js +22 -112
- package/dist/elements/a-breadcrumbs.d.ts +1 -0
- package/dist/elements/a-breadcrumbs.js +1 -0
- package/dist/elements/a-button.css +1 -1
- package/dist/elements/a-button.d.ts +10 -7
- package/dist/elements/a-button.js +18 -1
- package/dist/elements/a-calendar.d.ts +14 -13
- package/dist/elements/a-calendar.js +29 -24
- package/dist/elements/a-card.css +1 -1
- package/dist/elements/a-card.d.ts +0 -9
- package/dist/elements/a-card.js +0 -184
- package/dist/elements/a-checkbox.css +1 -1
- package/dist/elements/a-copy.css +1 -1
- package/dist/elements/a-copy.d.ts +1 -5
- package/dist/elements/a-copy.js +129 -108
- package/dist/elements/a-dialog.css +1 -1
- package/dist/elements/a-dialog.d.ts +1 -1
- package/dist/elements/a-dialog.js +68 -194
- package/dist/elements/a-expander.css +1 -1
- package/dist/elements/a-expander.d.ts +4 -30
- package/dist/elements/a-expander.js +85 -344
- package/dist/elements/a-icon.d.ts +5 -23
- package/dist/elements/a-icon.js +1 -2
- package/dist/elements/a-input-date.d.ts +1 -0
- package/dist/elements/a-input-date.js +1 -0
- package/dist/elements/a-input-time.css +1 -1
- package/dist/elements/a-input-time.d.ts +2 -1
- package/dist/elements/a-input-time.js +101 -168
- package/dist/elements/a-input.css +1 -1
- package/dist/elements/a-input.d.ts +10 -8
- package/dist/elements/a-input.js +155 -230
- package/dist/elements/a-loader.d.ts +5 -8
- package/dist/elements/a-loader.js +1 -2
- package/dist/elements/a-menu-item.css +1 -1
- package/dist/elements/a-menu-item.d.ts +5 -3
- package/dist/elements/a-menu-item.js +5 -1
- package/dist/elements/a-menu.css +1 -1
- package/dist/elements/a-menu.d.ts +5 -7
- package/dist/elements/a-menu.js +383 -299
- package/dist/elements/a-panel.css +1 -1
- package/dist/elements/a-panel.js +19 -37
- package/dist/elements/a-progress.css +1 -1
- package/dist/elements/a-progress.d.ts +0 -22
- package/dist/elements/a-progress.js +0 -100
- package/dist/elements/a-radio-group.d.ts +1 -1
- package/dist/elements/a-radio-group.js +6 -4
- package/dist/elements/a-select-faceted.d.ts +1 -0
- package/dist/elements/a-select-faceted.js +1 -0
- package/dist/elements/a-select.d.ts +1 -0
- package/dist/elements/a-select.js +1 -0
- package/dist/elements/a-slider.css +1 -1
- package/dist/elements/a-slider.d.ts +11 -3
- package/dist/elements/a-slider.js +244 -242
- package/dist/elements/a-steps.d.ts +1 -0
- package/dist/elements/a-steps.js +1 -0
- package/dist/elements/a-switch.css +1 -1
- package/dist/elements/a-tab.css +1 -1
- package/dist/elements/a-tabpanel.d.ts +2 -2
- package/dist/elements/a-tabpanel.js +13 -3
- package/dist/elements/a-tabs.css +1 -1
- package/dist/elements/a-tabs.d.ts +1 -1
- package/dist/elements/a-tabs.js +6 -4
- package/dist/elements/a-tag.css +1 -1
- package/dist/elements/a-tag.d.ts +1 -0
- package/dist/elements/a-tag.js +1 -0
- package/dist/elements/a-text.css +1 -1
- package/dist/elements/a-text.d.ts +6 -43
- package/dist/elements/a-text.js +110 -193
- package/dist/elements/a-title.css +1 -1
- package/dist/elements/a-title.d.ts +1 -0
- package/dist/elements/a-title.js +1 -0
- package/dist/elements/a-toast.css +1 -1
- package/dist/elements/a-toast.d.ts +10 -45
- package/dist/elements/a-toast.js +80 -66
- package/dist/elements/a-toaster.css +1 -1
- package/dist/elements/a-toaster.js +9 -44
- package/dist/elements/a-tooltip.css +1 -1
- package/dist/elements/a-tooltip.d.ts +1 -1
- package/dist/elements/a-tooltip.js +142 -136
- package/dist/elements/copy-behavior.d.ts +1 -1
- package/dist/elements/copy-behavior.js +21 -21
- package/dist/elements/index.d.ts +9 -3
- package/dist/elements/index.js +6 -6
- package/dist/elements/input-focus-modality.d.ts +15 -0
- package/dist/elements/input-focus-modality.js +44 -0
- package/dist/elements/input-trailing-actions.d.ts +5 -0
- package/dist/elements/input-trailing-actions.js +38 -0
- package/dist/elements/shadow-aria.d.ts +2 -0
- package/dist/elements/shadow-aria.js +2 -1
- package/dist/elements/value-input.d.ts +3 -0
- package/dist/elements/value-input.js +12 -0
- package/dist/general_types.d.ts +103 -96
- package/dist/index.d.ts +1 -1
- package/dist/input-events.d.ts +4 -0
- package/dist/input-events.js +15 -0
- package/dist/jsx-runtime.d.ts +70 -23
- package/dist/jsx-runtime.js +26 -19
- package/dist/native-event-props.d.ts +2 -0
- package/dist/native-event-props.js +10 -0
- package/dist/reset.css +1 -1
- package/dist/theme-antithesis.css +10 -1
- package/dist/theme-antune.css +1 -1
- package/docs/accessibility.md +17 -3
- package/docs/changelog.md +384 -2
- package/docs/comparison.md +2 -2
- package/docs/components/avatar.md +5 -0
- package/docs/components/banner.md +117 -187
- package/docs/components/breadcrumbs.md +9 -6
- package/docs/components/button.md +22 -0
- package/docs/components/card.md +74 -28
- package/docs/components/checkbox.md +9 -10
- package/docs/components/dialog.md +83 -32
- package/docs/components/expander.md +198 -435
- package/docs/components/icon.md +11 -17
- package/docs/components/input-autocomplete.md +152 -52
- package/docs/components/input-date.md +175 -148
- package/docs/components/input-time.md +81 -38
- package/docs/components/input.md +343 -349
- package/docs/components/loader.md +2 -1
- package/docs/components/menu.md +35 -6
- package/docs/components/panel.md +18 -0
- package/docs/components/progress.md +83 -49
- package/docs/components/radio.md +39 -43
- package/docs/components/select-faceted.md +54 -7
- package/docs/components/select.md +390 -386
- package/docs/components/slider.md +136 -19
- package/docs/components/steps.md +6 -3
- package/docs/components/switch.md +8 -9
- package/docs/components/tabs.md +110 -104
- package/docs/components/text.md +110 -86
- package/docs/components/title.md +47 -33
- package/docs/components/toaster.md +242 -84
- package/docs/components/tooltip.md +204 -113
- package/docs/install-config.md +461 -96
- package/docs/packages/plot.md +228 -143
- package/docs/theming.md +29 -13
- package/package.json +43 -4
- package/theme-antithesis-license.md +66 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Select
|
|
2
2
|
|
|
3
|
-
`Select` lets people choose one or more options from a dropdown.
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
`Select` lets people choose one or more options from a dropdown. Use it to
|
|
4
|
+
choose a setting, filter a view, or configure a list. Add search for longer
|
|
5
|
+
lists, and use groups or submenus to organize related options.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
Give the field a visible `label`. If you omit it, provide `aria-label` or
|
|
8
|
+
`aria-labelledby` so people using assistive technology can identify the control.
|
|
9
|
+
Include any meaningful prefix in that name; leading content and the chevron
|
|
10
|
+
are decorative.
|
|
11
11
|
|
|
12
12
|
## Options
|
|
13
13
|
|
|
@@ -25,202 +25,227 @@ uncontrolled Select.
|
|
|
25
25
|
/>
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
Use strings for a simple list, or option objects when people need more context.
|
|
29
|
+
Add a `label` for a readable name, a `hint` for supporting information, or an
|
|
30
|
+
`icon` to help distinguish choices. Use `tone` to communicate a meaningful
|
|
31
|
+
status alongside the text.
|
|
32
|
+
|
|
33
|
+
Set `disabled` on an unavailable option to keep it visible without allowing
|
|
34
|
+
selection. Disabled options are also skipped during keyboard navigation.
|
|
35
|
+
|
|
36
|
+
### Groups and submenus
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
// The same tree drives both modes — selection is global, so `selection` is the
|
|
40
|
+
// only difference between the two selects below.
|
|
41
|
+
const [one, setOne] = useState<string | null>('stream')
|
|
42
|
+
const [many, setMany] = useState(['stream', 'tag.env'])
|
|
43
|
+
const fields = [
|
|
44
|
+
'output_text', // a plain option
|
|
45
|
+
{ label: 'Log', options: ['stream', 'message', 'severity'] }, // an inline group
|
|
46
|
+
{
|
|
47
|
+
label: 'Metadata', // a submenu (flyout)
|
|
48
|
+
icon: 'braces',
|
|
49
|
+
submenu: [
|
|
50
|
+
'metadata.host',
|
|
51
|
+
'metadata.region',
|
|
52
|
+
{ label: 'Tags', options: ['tag.env', 'tag.team'] }, // group inside submenu
|
|
53
|
+
{ label: 'Custom', submenu: ['custom.a', 'custom.b'] }, // submenu inside submenu
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
{ label: 'Time', submenu: ['vtime', 'wall_clock'], disabled: true }, // disabled → cascades
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
<Select label="Field · single" filter options={fields} value={one} onValueChange={setOne} />
|
|
60
|
+
|
|
61
|
+
<Select label="Fields · multiple" selection="multiple" filter options={fields} value={many} onValueChange={setMany} />
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Use groups to divide a list into named sections. Use submenus when a category
|
|
65
|
+
needs a separate list of choices. Both can contain further groups or submenus.
|
|
66
|
+
|
|
67
|
+
The examples use the same options with single and multiple selection. Group
|
|
68
|
+
headings and submenu parents organize the choices; only their options are
|
|
69
|
+
selectable. Keep option values unique across the whole tree. Disabling a group
|
|
70
|
+
or submenu disables its descendants.
|
|
71
|
+
|
|
72
|
+
When someone searches, matching options appear together in a flat list with
|
|
73
|
+
group headings. They can find a match without opening each submenu.
|
|
33
74
|
|
|
34
75
|
## Selection modes
|
|
35
76
|
|
|
36
77
|
```tsx
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
78
|
+
const options = ['output_text', 'stream', 'container', 'vtime', 'custom']
|
|
79
|
+
const [check, setCheck] = useState<string | null>('stream')
|
|
80
|
+
const [radio, setRadio] = useState<string | null>('stream')
|
|
81
|
+
const [many, setMany] = useState(['stream', 'container'])
|
|
41
82
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
<Select indicator="radio" tone="#c026d3" toneScope="selected" options={OPTIONS} value={one} onValueChange={setOne} />
|
|
83
|
+
<Select label="Checkmark · brand tone" indicator="check" tone="brand"
|
|
84
|
+
toneScope="selected" options={options} value={check} onValueChange={setCheck} />
|
|
45
85
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
onValueChange={setMany}
|
|
52
|
-
/>
|
|
86
|
+
<Select label="Radio · custom tone" indicator="radio" tone="#c026d3"
|
|
87
|
+
toneScope="selected" options={options} value={radio} onValueChange={setRadio} />
|
|
88
|
+
|
|
89
|
+
<Select label="Multiple · brand tone" selection="multiple" tone="brand"
|
|
90
|
+
toneScope="selected" placeholder="Select fields…"
|
|
91
|
+
options={options} value={many} onValueChange={setMany} />
|
|
53
92
|
```
|
|
54
93
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
## Filter
|
|
94
|
+
Use single selection when someone must choose one option, such as a default
|
|
95
|
+
field. Choosing an option closes the menu. Use `selection="multiple"` for a
|
|
96
|
+
set of choices, such as the fields shown in a table. The menu stays open so
|
|
97
|
+
people can continue selecting.
|
|
98
|
+
|
|
99
|
+
Multiple selection shows checkboxes and summarizes the chosen options in the
|
|
100
|
+
field. Use `verbose` to list their labels or customize the
|
|
101
|
+
[selection summary](#selection-summary).
|
|
102
|
+
|
|
103
|
+
### Indicators and selection colors
|
|
104
|
+
|
|
105
|
+
For single selection, choose the mark that suits the list: `indicator="none"`
|
|
106
|
+
highlights the chosen row, `"check"` adds a checkmark, and `"radio"` shows a
|
|
107
|
+
radio indicator beside every option. Multiple selection always uses checkboxes.
|
|
108
|
+
|
|
109
|
+
Use `tone` with `toneScope="selected"` to give chosen options a shared color.
|
|
110
|
+
Named tones and custom CSS colors are supported. Tint-based indicators color
|
|
111
|
+
the row background; radio and checkbox indicators color the text and mark.
|
|
112
|
+
|
|
113
|
+
### Select all and selection shortcuts
|
|
114
|
+
|
|
115
|
+
Multiple selection includes a **Select all** row. It selects or clears enabled
|
|
116
|
+
options and shows a mixed checkbox when only some are selected. With an active
|
|
117
|
+
search, it affects only matching options. Set `selectAll={false}` to remove it.
|
|
118
|
+
|
|
119
|
+
Alt-click an option to select it and clear every other choice. On macOS, use
|
|
120
|
+
Option instead of Alt. The default row tooltip explains
|
|
121
|
+
this shortcut. Set an option's `tooltip` to replace the hint, or to `''` to hide
|
|
122
|
+
it. The shortcut is available only while `selectAll` is enabled.
|
|
123
|
+
|
|
124
|
+
<a id="filter"></a>
|
|
125
|
+
|
|
126
|
+
## Filtering options
|
|
89
127
|
|
|
90
128
|
```tsx
|
|
91
|
-
|
|
92
|
-
|
|
129
|
+
const options = [
|
|
130
|
+
{ value: 'output_text', hint: 'Raw log message or event payload' },
|
|
131
|
+
{ value: 'stream', hint: 'Log level: error · info · internal' },
|
|
132
|
+
{ value: 'container', hint: 'Host where the event occurred' },
|
|
133
|
+
{ value: 'vtime', hint: 'Simulation time in seconds' },
|
|
134
|
+
{ value: 'source', hint: 'Emitting system or process' },
|
|
135
|
+
{ value: 'message', hint: 'Assertion description' },
|
|
136
|
+
{ value: 'condition', hint: 'Passing or failing' },
|
|
137
|
+
{ value: 'fault', hint: 'Injected fault type and name' },
|
|
138
|
+
]
|
|
139
|
+
const [one, setOne] = useState<string | null>('stream')
|
|
140
|
+
const [many, setMany] = useState(['stream'])
|
|
93
141
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
value={
|
|
99
|
-
|
|
100
|
-
|
|
142
|
+
<Select label="Single · filter" filter indicator="check"
|
|
143
|
+
options={options} value={one} onValueChange={setOne} />
|
|
144
|
+
|
|
145
|
+
<Select label="Multiple · filter" filter selection="multiple"
|
|
146
|
+
placeholder="Fields…" options={options} value={many} onValueChange={setMany} />
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
For prefix matching instead of the built-in search:
|
|
150
|
+
|
|
151
|
+
```tsx
|
|
152
|
+
<Select label="Field" options={options}
|
|
153
|
+
filter={(option, query) => String(option.value).startsWith(query)} />
|
|
101
154
|
```
|
|
102
155
|
|
|
103
|
-
Add
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
Pass a
|
|
156
|
+
Add `filter` when people need to find an option in a longer list. Search matches
|
|
157
|
+
option values, labels, and hints without requiring the same capitalization.
|
|
158
|
+
Matching text is highlighted in the built-in option layout.
|
|
159
|
+
|
|
160
|
+
Pass a function to `filter` when your application needs different matching rules.
|
|
161
|
+
Custom matching does not highlight text. Use `renderEmpty` to explain when no
|
|
162
|
+
options match; see [Empty and loading states](#empty-and-loading-states).
|
|
163
|
+
|
|
164
|
+
### Keyboard interaction
|
|
108
165
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
through matching options while focus stays in that field. Enter
|
|
113
|
-
selects the active option, and Esc closes the dialog. The search field
|
|
114
|
-
stays visible while the results scroll. A "No matches" row appears when nothing
|
|
115
|
-
matches, and the query resets when the dialog closes. In `multiple` mode,
|
|
116
|
-
`selectAll` affects only the visible matches.
|
|
166
|
+
Opening a searchable Select focuses the search field. Press ↑ or
|
|
167
|
+
↓ to move through matching options, then Enter to select
|
|
168
|
+
the active option. Focus stays in the search field while you navigate.
|
|
117
169
|
|
|
118
|
-
|
|
170
|
+
The search field stays visible while results scroll. Press Esc or
|
|
171
|
+
select outside the popup to dismiss it. Closing the popup resets the query.
|
|
172
|
+
|
|
173
|
+
<a id="value-and-changes"></a>
|
|
174
|
+
|
|
175
|
+
## Managing the value
|
|
119
176
|
|
|
120
177
|
```tsx
|
|
121
|
-
const [field, setField] = useState('stream')
|
|
178
|
+
const [field, setField] = useState<string | null>('stream')
|
|
122
179
|
|
|
123
180
|
<Select
|
|
124
181
|
label="Field"
|
|
125
182
|
options={['output_text', 'stream', 'container', 'vtime', 'custom']}
|
|
183
|
+
placeholder="Select a field…"
|
|
126
184
|
value={field}
|
|
127
|
-
onValueChange={
|
|
185
|
+
onValueChange={setField}
|
|
186
|
+
clearable
|
|
128
187
|
/>
|
|
188
|
+
<Button label="Reset selection" size="small" priority="tertiary"
|
|
189
|
+
onClick={() => setField(null)} />
|
|
129
190
|
```
|
|
130
191
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
option's `value` **unique across the whole tree**. Selection is value-keyed and
|
|
135
|
-
global across groups / submenus, so a value repeated in two sections is one logical
|
|
136
|
-
pick (both rows toggle together; the trigger resolves to the last). Dev builds
|
|
137
|
-
`console.warn` on a duplicate.
|
|
138
|
-
|
|
139
|
-
Values aren't limited to strings. `Select` **infers its value type from `options`**:
|
|
140
|
-
pass `options={[{ value: 365 }, { value: 90 }]}` and `value`, `onValueChange`'s value,
|
|
141
|
-
and `attrs.value` are all `number` (the same for `boolean`). Values compare with `===`,
|
|
142
|
-
so `365` and `"365"` stay distinct and no string conversion is needed. The exported
|
|
143
|
-
`OptionValue` type is `string | number | boolean`; for anything richer (a date, an
|
|
144
|
-
object), give `value` a **stable primitive key** (a date's ISO string, a record's id)
|
|
145
|
-
and read the full value back off `attrs.option`.
|
|
192
|
+
Pass `value` and update it in `onValueChange` when your application manages the
|
|
193
|
+
selection. Set it to `null` to clear a single selection while keeping control.
|
|
194
|
+
The example's **Reset selection** button does this from application state.
|
|
146
195
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
196
|
+
For a Select that manages its own selection, omit `value` or pass `undefined`.
|
|
197
|
+
Use `defaultValue` to choose an initial option. Clear reports `null` for single
|
|
198
|
+
selection and `[]` for multiple selection.
|
|
199
|
+
|
|
200
|
+
Single selection uses one option value. Multiple selection uses an array of
|
|
201
|
+
values. Keep each option's value unique across groups and submenus.
|
|
202
|
+
|
|
203
|
+
Values can be strings, numbers, or booleans. Select preserves their types, so a
|
|
204
|
+
numeric ID can remain a number in your application state. Use a stable primitive
|
|
205
|
+
key for richer data, and attach the record to its option when you need it in a
|
|
206
|
+
callback:
|
|
153
207
|
|
|
154
|
-
|
|
208
|
+
```tsx
|
|
155
209
|
<Select
|
|
156
|
-
|
|
157
|
-
|
|
210
|
+
label="Run"
|
|
211
|
+
options={runs.map((run) => ({ value: run.id, label: run.name, run }))}
|
|
212
|
+
onValueChange={(id, attrs) => {
|
|
213
|
+
if (!('all' in attrs)) openRun(attrs.option.run)
|
|
214
|
+
}}
|
|
158
215
|
/>
|
|
159
216
|
```
|
|
160
217
|
|
|
161
|
-
For an
|
|
162
|
-
|
|
163
|
-
|
|
218
|
+
For dates, use an ISO string as the option value and convert it back in the
|
|
219
|
+
callback. The API reference describes the additional change information passed
|
|
220
|
+
to `onValueChange`.
|
|
164
221
|
|
|
165
|
-
|
|
166
|
-
comes first (a single value, or an array in `multiple` mode), with a snapshot
|
|
167
|
-
second: `{ value, option }` for the changed row, plus `selected` in `multiple` mode
|
|
168
|
-
(or `{ all: true }` for the Select-all row). There's **no `onStateChange`**: Select
|
|
169
|
-
has no discrete element state to veto (Checkbox and RadioGroup do), so it follows
|
|
170
|
-
Input's model of one value callback, and a controlled consumer rejects a pick by not
|
|
171
|
-
updating `value`. See the [event-model note on Input](./input.md#events) for
|
|
172
|
-
the rationale.
|
|
222
|
+
<a id="deriving-the-selection-tree"></a>
|
|
173
223
|
|
|
174
|
-
###
|
|
224
|
+
### Building a grouped selection summary
|
|
175
225
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
every group / submenu carrying a rolled-up `selectionState` — `'all'`, `'none'`, or
|
|
180
|
-
`'some'` of its descendants. It's a pure function (no `Select` instance, reads
|
|
181
|
-
nothing off the event), so it behaves the same controlled or uncontrolled: hand it
|
|
182
|
-
your current `value` and render a grouped summary, a section indicator, or a diff.
|
|
226
|
+
Use `optionsWithSelection(options, values)` to build a grouped summary or show
|
|
227
|
+
how much of each section is selected. It returns your options tree annotated
|
|
228
|
+
with selected leaves and each group's `selectionState`: `all`, `some`, or `none`.
|
|
183
229
|
|
|
184
|
-
The
|
|
185
|
-
watch the
|
|
230
|
+
The example displays a summary alongside the Select. Choose permissions and
|
|
231
|
+
watch the section indicators update.
|
|
186
232
|
|
|
187
233
|
```tsx
|
|
188
234
|
import { Select, optionsWithSelection } from '@antadesign/anta'
|
|
189
235
|
|
|
190
|
-
const [values, setValues] = useState<string[]>(['repo.read', 'billing.view'])
|
|
236
|
+
const [values, setValues] = useState<string[]>(['repo.read', 'billing.view', 'billing.manage'])
|
|
191
237
|
|
|
192
238
|
// `values` drives the Select; the tree is derived from it and rendered read-only.
|
|
193
239
|
const tree = optionsWithSelection(scopes, values)
|
|
194
|
-
// tree: [ { label: 'Repositories', selectionState: 'some', options: [
|
|
195
|
-
// { value: 'repo.read', label: 'Read', selected: true },
|
|
196
|
-
// { value: 'repo.write', label: 'Write', selected: false }, … ] },
|
|
197
|
-
// { label: 'Members', selectionState: 'some', options: [ …,
|
|
198
|
-
// { label: 'Billing', selectionState: 'all', submenu: [
|
|
199
|
-
// { value: 'billing.view', label: 'View invoices', selected: true },
|
|
200
|
-
// { value: 'billing.manage', label: 'Manage plan', selected: true } ] } ] },
|
|
201
|
-
// { value: 'audit.read', label: 'Audit log', selected: false } ]
|
|
202
|
-
|
|
203
|
-
<Select selection="multiple" options={scopes} value={values} onValueChange={setValues} />
|
|
204
|
-
{/* render `tree` however you like — a summary, section indicators, a diff */}
|
|
205
|
-
```
|
|
206
240
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
`selected` flag off `options`, so a `selected: true` in the tree changes nothing — it
|
|
211
|
-
rides along as inert data. Control the `Select` with the value array; keep the
|
|
212
|
-
annotated tree for rendering.
|
|
213
|
-
|
|
214
|
-
```tsx
|
|
215
|
-
// ✗ Wrong — `selected` is ignored, so the Select isn't controlled by this
|
|
216
|
-
<Select options={optionsWithSelection(scopes, values)} />
|
|
217
|
-
|
|
218
|
-
// ✓ Right — selection is value-keyed; drive it with `value`
|
|
219
|
-
<Select options={scopes} value={values} onValueChange={setValues} />
|
|
241
|
+
<Select label="Scopes" selection="multiple" filter placeholder="Select scopes…"
|
|
242
|
+
options={scopes} value={values} onValueChange={setValues} />
|
|
243
|
+
{/* render `tree` however you like — a summary, section indicators, a diff */}
|
|
220
244
|
```
|
|
221
245
|
|
|
222
|
-
|
|
223
|
-
|
|
246
|
+
Keep `value` as the source of selection. The annotated tree is a derived view
|
|
247
|
+
for rendering; passing it back as `options` does not control which rows are
|
|
248
|
+
selected.
|
|
224
249
|
|
|
225
250
|
## Size and status
|
|
226
251
|
|
|
@@ -229,93 +254,44 @@ section marks the leaf everywhere it occurs (selection is value-keyed).
|
|
|
229
254
|
<Select label="Medium · success" size="medium" status="success" hint="Field is valid" options={OPTIONS} defaultValue="stream" />
|
|
230
255
|
<Select label="Large · critical" size="large" status="critical" hint="Pick a field" options={OPTIONS} placeholder="Required…" />
|
|
231
256
|
{/* statusIcon overrides the per-status glyph, or `false` drops it, as on Input */}
|
|
232
|
-
<Select status="info" statusIcon="sparkles" hint="
|
|
257
|
+
<Select label="Small · custom icon" size="small" status="info" statusIcon="sparkles" hint="statusIcon override" options={OPTIONS} defaultValue="stream" />
|
|
233
258
|
```
|
|
234
259
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
260
|
+
Choose `size` to fit the surrounding controls. Use `status` and a helpful `hint`
|
|
261
|
+
to explain validation or feedback. `statusIcon` lets you replace the status icon
|
|
262
|
+
or hide it with `false`.
|
|
263
|
+
|
|
264
|
+
A `critical` status also marks the control as invalid for assistive technology.
|
|
265
|
+
Use `disabled` when the whole Select is unavailable.
|
|
266
|
+
|
|
267
|
+
### Using Select in forms
|
|
241
268
|
|
|
242
|
-
|
|
243
|
-
the
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
269
|
+
Select reports choices through `onValueChange`. It does not submit a form value
|
|
270
|
+
or block submission. Include the selected value in your application's form
|
|
271
|
+
handling, and validate it before accepting the submission.
|
|
272
|
+
|
|
273
|
+
<a id="build-it-from-elements"></a>
|
|
247
274
|
|
|
248
275
|
## Customization
|
|
249
276
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
277
|
+
Customize the option content, selection indicators, summary, or trigger to suit
|
|
278
|
+
the task. Select coordinates an [Input](./input.md) field and a [Menu](./menu.md):
|
|
279
|
+
Input provides the field, label, and hint; Menu provides positioning, dismissal,
|
|
280
|
+
and keyboard navigation. Select keeps their displayed selection in sync with
|
|
281
|
+
the value.
|
|
254
282
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
`offset` sets the gap in pixels between the field and the menu:
|
|
283
|
+
Use `placement` when the menu should open on a particular side of the trigger.
|
|
284
|
+
It adjusts to the available space. Set `offset` to change the gap:
|
|
258
285
|
|
|
259
286
|
```tsx
|
|
260
287
|
<Select placement="top-end" offset={8} options={OPTIONS} label="Field" />
|
|
261
288
|
```
|
|
262
289
|
|
|
263
|
-
|
|
290
|
+
For field and menu appearance, use the supported hooks in [Styling](#styling).
|
|
264
291
|
|
|
265
|
-
|
|
266
|
-
and hint inside an `<a-select-field>` layout wrapper. It updates the field
|
|
267
|
-
and each option's `selected` state from the value, and reports a pick through
|
|
268
|
-
`onValueChange`. Menu handles positioning, dismissal, and keyboard navigation.
|
|
292
|
+
<a id="custom-option-rendering"></a>
|
|
269
293
|
|
|
270
|
-
|
|
271
|
-
the Menu directly after the Button so it can use that Button as its trigger.
|
|
272
|
-
|
|
273
|
-
```html
|
|
274
|
-
<!-- `.select` scopes this example's CSS. Use an application selector instead. -->
|
|
275
|
-
<div class="select" style="width: 240px">
|
|
276
|
-
<a-button role="button" tabindex="0" aria-label="Field" aria-haspopup="menu" aria-expanded="false">
|
|
277
|
-
<a-button-label>stream</a-button-label>
|
|
278
|
-
<a-icon shape="chevron-down" aria-hidden="true" style="transition: transform 150ms ease"></a-icon>
|
|
279
|
-
</a-button>
|
|
280
|
-
<a-menu role="menu">
|
|
281
|
-
<a-menu-item role="menuitemradio" tabindex="0" aria-checked="false" value="output_text"><a-menu-item-label>output_text</a-menu-item-label></a-menu-item>
|
|
282
|
-
<a-menu-item role="menuitemradio" tabindex="0" aria-checked="true" value="stream" selected><a-menu-item-label>stream</a-menu-item-label></a-menu-item>
|
|
283
|
-
<a-menu-item role="menuitemradio" tabindex="0" aria-checked="false" value="container"><a-menu-item-label>container</a-menu-item-label></a-menu-item>
|
|
284
|
-
</a-menu>
|
|
285
|
-
</div>
|
|
286
|
-
<script type="module">
|
|
287
|
-
import '@antadesign/anta/elements'
|
|
288
|
-
const root = document.querySelector('.select')
|
|
289
|
-
const field = root.querySelector('a-button')
|
|
290
|
-
const menu = root.querySelector('a-menu')
|
|
291
|
-
const chevron = field.querySelector('a-icon')
|
|
292
|
-
// Flip the chevron with the menu's open state (as <Select> does).
|
|
293
|
-
menu.addEventListener('statechange', (e) => {
|
|
294
|
-
const open = e.detail.next === 'open'
|
|
295
|
-
field.setAttribute('aria-expanded', String(open))
|
|
296
|
-
chevron.style.transform = open ? 'rotate(180deg)' : ''
|
|
297
|
-
})
|
|
298
|
-
// On pick: mark the row selected + reflect its label into the field.
|
|
299
|
-
menu.addEventListener('click', (e) => {
|
|
300
|
-
const item = e.target.closest('a-menu-item')
|
|
301
|
-
if (!item) return
|
|
302
|
-
menu.querySelectorAll('a-menu-item').forEach((el) => {
|
|
303
|
-
const selected = el === item
|
|
304
|
-
el.toggleAttribute('selected', selected)
|
|
305
|
-
el.setAttribute('aria-checked', String(selected))
|
|
306
|
-
})
|
|
307
|
-
field.querySelector('a-button-label').textContent = item.querySelector('a-menu-item-label').textContent
|
|
308
|
-
})
|
|
309
|
-
</script>
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
The React and Preact `Select` wrapper performs those updates for you.
|
|
313
|
-
Because the Menu directly follows the Anta Button, it associates the focused
|
|
314
|
-
field with the popup through a direct ARIA element reference. No popup ID is
|
|
315
|
-
required. If a custom composition supplies its own `aria-controls`, Anta keeps
|
|
316
|
-
that authored relationship.
|
|
317
|
-
|
|
318
|
-
### Custom option rendering
|
|
294
|
+
### Custom option content
|
|
319
295
|
|
|
320
296
|
```tsx
|
|
321
297
|
const TONE = { in_progress: 'info', completed: 'neutral', incomplete: 'critical' }
|
|
@@ -351,24 +327,22 @@ const LABEL = { in_progress: 'In progress', completed: 'Completed', incomplete:
|
|
|
351
327
|
</div>
|
|
352
328
|
```
|
|
353
329
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
`filter` still matches the option's `value`, `label`, and `hint`. Select does
|
|
366
|
-
not highlight matches in custom content because it cannot identify text within
|
|
367
|
-
the returned node. `indicator` and `selection` still provide the selection mark.
|
|
368
|
-
Use `renderIndicator` to replace that mark too; see
|
|
330
|
+
Use `renderOption` when people need richer context to choose an option, such
|
|
331
|
+
as a run's status and time. Attach that information to each option and render
|
|
332
|
+
it beside the label. Keep `label` meaningful because it becomes the closed
|
|
333
|
+
field's text and remains searchable.
|
|
334
|
+
|
|
335
|
+
Select provides the selectable row, keyboard behavior, accessible attributes,
|
|
336
|
+
and selection indicator. Your renderer supplies the content. Give its root
|
|
337
|
+
`flex: 1` and `min-width: 0` so it can fill and shrink within the row.
|
|
338
|
+
|
|
339
|
+
Search still uses the option's value, label, and hint. Custom content is not
|
|
340
|
+
highlighted automatically. To replace the selection mark as well, use
|
|
369
341
|
[Custom indicators](#custom-indicators).
|
|
370
342
|
|
|
371
|
-
|
|
343
|
+
<a id="limiting-the-width"></a>
|
|
344
|
+
|
|
345
|
+
### Menu width and long labels
|
|
372
346
|
|
|
373
347
|
```tsx
|
|
374
348
|
// Same ellipsis-ready row for both; it truncates once the menu is capped.
|
|
@@ -391,19 +365,16 @@ const renderPath = (o) => (
|
|
|
391
365
|
</div>
|
|
392
366
|
```
|
|
393
367
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
368
|
+
Cap the menu width when long option content should stay within a compact
|
|
369
|
+
layout. The examples show the same paths: one menu grows to fit them, while the
|
|
370
|
+
other stays within the field's width and truncates the labels.
|
|
371
|
+
|
|
372
|
+
Set `max-width` on the menu's `::part(menu)`. Give custom row content
|
|
373
|
+
`min-width: 0` and the single-line ellipsis styles shown in the recipe.
|
|
374
|
+
Both are needed: shrinkable text alone does not limit the menu's width.
|
|
399
375
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
(`min-width: 0`) and single-line ellipsis (`overflow: hidden; text-overflow:
|
|
403
|
-
ellipsis; white-space: nowrap`). Nest a **`Tooltip truncatedOnly`** in that element
|
|
404
|
-
to reveal the full text on hover, only when it's clipped. `min-width: 0` alone won't
|
|
405
|
-
bound it: a shrink-to-fit popover grows to its content's max-content width, so the
|
|
406
|
-
cap is what does the work.
|
|
376
|
+
Use `Tooltip truncatedOnly` to reveal a full label when it has been clipped.
|
|
377
|
+
Built-in labels wrap; this recipe is for custom single-line content.
|
|
407
378
|
|
|
408
379
|
### Custom indicators
|
|
409
380
|
|
|
@@ -427,26 +398,33 @@ cap is what does the work.
|
|
|
427
398
|
/>
|
|
428
399
|
```
|
|
429
400
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
both
|
|
401
|
+
Use `renderIndicator` to draw a selection mark that fits your content. This
|
|
402
|
+
example fills a colored ring when a log level is selected.
|
|
403
|
+
|
|
404
|
+
Keep `indicator="check"`, `indicator="radio"`, or `selection="multiple"` so
|
|
405
|
+
the row exposes its checked state to assistive technology. Render a decorative
|
|
406
|
+
mark; Select keeps the row itself selectable. You can combine this with
|
|
407
|
+
`renderOption` to customize both the content and its indicator.
|
|
437
408
|
|
|
438
409
|
### Selection summary
|
|
439
410
|
|
|
440
411
|
```tsx
|
|
441
|
-
const teams = [
|
|
412
|
+
const teams = [
|
|
413
|
+
{ value: 'eng', label: 'Engineering' },
|
|
414
|
+
{ value: 'design', label: 'Design' },
|
|
415
|
+
{ value: 'ops', label: 'Operations' },
|
|
416
|
+
{ value: 'sales', label: 'Sales' },
|
|
417
|
+
{ value: 'support', label: 'Support' },
|
|
418
|
+
]
|
|
442
419
|
const [a, setA] = useState(['eng', 'design', 'ops'])
|
|
443
420
|
const [b, setB] = useState(['eng', 'design'])
|
|
444
421
|
|
|
445
422
|
// verbose: the count summary lists the picks
|
|
446
|
-
<Select selection="multiple" placeholder="Pick teams" options={teams} value={a} onValueChange={setA} verbose />
|
|
423
|
+
<Select label="verbose" selection="multiple" placeholder="Pick teams" options={teams} value={a} onValueChange={setA} verbose />
|
|
447
424
|
|
|
448
425
|
// renderSummary: build the text yourself
|
|
449
426
|
<Select
|
|
427
|
+
label="renderSummary"
|
|
450
428
|
selection="multiple"
|
|
451
429
|
placeholder="Pick teams"
|
|
452
430
|
options={teams}
|
|
@@ -463,20 +441,16 @@ const [b, setB] = useState(['eng', 'design'])
|
|
|
463
441
|
/>
|
|
464
442
|
```
|
|
465
443
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
options (`selected.length` is the count). Return `undefined` for any case you'd rather
|
|
470
|
-
leave to the default, as above for the single-label case.
|
|
444
|
+
Use `verbose` when people should see the chosen labels in a multiple-selection
|
|
445
|
+
field. For several choices, it shows a count followed by their names. Long text
|
|
446
|
+
truncates to the field's width.
|
|
471
447
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
`renderTrigger` below, which replaces the whole field.
|
|
448
|
+
Use `renderSummary` for task-specific text, such as “All teams” or “2 teams”.
|
|
449
|
+
Return `undefined` to keep the standard summary for a case, as this example
|
|
450
|
+
does for a single selection. An empty selection shows the placeholder.
|
|
476
451
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
ellipsizing at the field width like any long value. `renderSummary` overrides it.
|
|
452
|
+
The summary is text. For chips or other rich content, use a
|
|
453
|
+
[Custom trigger](#custom-trigger).
|
|
480
454
|
|
|
481
455
|
### Custom trigger
|
|
482
456
|
|
|
@@ -526,31 +500,30 @@ const [hidden, setHidden] = useState([])
|
|
|
526
500
|
/>
|
|
527
501
|
```
|
|
528
502
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
the trigger. It logs a console warning when that happens. Add
|
|
534
|
-
`aria-haspopup={filter ? 'dialog' : 'menu'}` and
|
|
535
|
-
`aria-expanded={state.open}` to the element.
|
|
503
|
+
Use `renderTrigger` when a button or another control suits the task better
|
|
504
|
+
than a field. The first example opens a field filter with a selection
|
|
505
|
+
count. The second configures column visibility: a selected option means that
|
|
506
|
+
its column is hidden, and the eye indicator reflects that choice.
|
|
536
507
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
custom trigger. `state.icon` contains the Select `icon` prop so the replacement
|
|
541
|
-
trigger can display the same icon.
|
|
508
|
+
Return one focusable element with a visible label or accessible name. Add
|
|
509
|
+
`aria-haspopup="menu"` and `aria-expanded={open}` as shown. For a searchable
|
|
510
|
+
Select, use `aria-haspopup="dialog"` instead.
|
|
542
511
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
`eye-closed`. The Tag uses `selected.length`, so it appears only after a column
|
|
547
|
-
is hidden.
|
|
512
|
+
Style and label the returned element directly. Default field props such as
|
|
513
|
+
`label`, `size`, and `status` apply to the standard field. Use the renderer's
|
|
514
|
+
`disabled` state to disable your custom trigger when appropriate.
|
|
548
515
|
|
|
549
|
-
|
|
516
|
+
You can replace the trigger as the selection or layout changes, including
|
|
517
|
+
switching between a custom trigger and the default field. The menu reconnects
|
|
518
|
+
to the replacement element.
|
|
519
|
+
|
|
520
|
+
<a id="empty-state"></a>
|
|
521
|
+
|
|
522
|
+
### Empty and loading states
|
|
550
523
|
|
|
551
524
|
```tsx
|
|
552
525
|
const [options, setOptions] = useState(['bug', 'feature', 'chore', 'docs'])
|
|
553
|
-
const [value, setValue] = useState()
|
|
526
|
+
const [value, setValue] = useState<string | null>(null)
|
|
554
527
|
const [loading, setLoading] = useState(false) // application fetch state
|
|
555
528
|
|
|
556
529
|
<Select
|
|
@@ -577,84 +550,42 @@ const [loading, setLoading] = useState(false) // application fetch state
|
|
|
577
550
|
/>
|
|
578
551
|
```
|
|
579
552
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
removed every option; an empty `query` means that no options were supplied.
|
|
584
|
-
|
|
585
|
-
Select has no built-in empty message. If `renderEmpty` is omitted, an empty list
|
|
586
|
-
has no content. Loading also belongs to application state, so return a
|
|
587
|
-
`'Loading…'` branch when that state is true.
|
|
588
|
-
|
|
589
|
-
The returned content replaces the option rows. A `MenuSeparator` with text is a
|
|
590
|
-
muted caption and an `aria-live="polite"` region. Return a `MenuItem`, such as
|
|
591
|
-
the `Create "…"` row, when the content must be selectable by keyboard.
|
|
592
|
-
|
|
593
|
-
### Groups and submenus
|
|
594
|
-
|
|
595
|
-
```tsx
|
|
596
|
-
// The same tree drives both modes — selection is global, so `selection` is the
|
|
597
|
-
// only difference between the two selects below.
|
|
598
|
-
const fields = [
|
|
599
|
-
'output_text', // a plain option
|
|
600
|
-
{ label: 'Log', options: ['stream', 'message'] }, // an inline group
|
|
601
|
-
{
|
|
602
|
-
label: 'Metadata', // a submenu (flyout)
|
|
603
|
-
icon: 'braces',
|
|
604
|
-
submenu: [
|
|
605
|
-
'metadata.host',
|
|
606
|
-
{ label: 'Tags', options: ['tag.env', 'tag.team'] }, // group inside submenu
|
|
607
|
-
{ label: 'Custom', submenu: ['custom.a', 'custom.b'] }, // submenu inside submenu
|
|
608
|
-
],
|
|
609
|
-
},
|
|
610
|
-
{ label: 'Time', submenu: ['vtime'], disabled: true }, // disabled → cascades
|
|
611
|
-
]
|
|
612
|
-
|
|
613
|
-
<Select label="Field · single" filter options={fields} value={one} onValueChange={setOne} />
|
|
614
|
-
|
|
615
|
-
<Select label="Fields · multiple" selection="multiple" filter options={fields} value={many} onValueChange={setMany} />
|
|
616
|
-
```
|
|
617
|
-
|
|
618
|
-
An `options` entry is a plain option, a **group** (`{ label, options }`, an inline
|
|
619
|
-
titled section), or a **submenu** (`{ label, submenu }`, a flyout). They nest and mix
|
|
620
|
-
freely. Discriminated by shape — an `options` array is a group, a `submenu` array is a
|
|
621
|
-
submenu, everything else is an option (so those two keys are reserved on an option).
|
|
553
|
+
Use `renderEmpty` to explain an empty list or offer a next step. This example
|
|
554
|
+
shows loading feedback while the application fetches options, then lets people
|
|
555
|
+
create a tag when their search has no matches.
|
|
622
556
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
above share the exact same `fields` tree and differ only by `selection`. `disabled` on
|
|
627
|
-
a group or submenu cascades to all its descendants. Because selection is global, a
|
|
628
|
-
leaf's `value` must be **unique across the whole tree** — the same value under two
|
|
629
|
-
sections is one pick (dev builds warn).
|
|
557
|
+
Select provides no default empty message. Return the message your task needs.
|
|
558
|
+
Loading state belongs to the application; pass an empty options list while
|
|
559
|
+
loading when you want this content to replace the rows.
|
|
630
560
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
561
|
+
Use `MenuSeparator` for a status message announced politely to assistive
|
|
562
|
+
technology. Use `MenuItem` for an action that people can reach and select with
|
|
563
|
+
the keyboard. Validate the query before offering to create an option.
|
|
634
564
|
|
|
635
565
|
## Component props
|
|
636
566
|
|
|
637
567
|
| Prop | Type | Default | Description |
|
|
638
568
|
|------|------|---------|-------------|
|
|
639
569
|
| `options` | SelectItem[] | — | The options to choose from — bare strings, `SelectOption` objects, `SelectGroup`s (inline titled sections), or `SelectSubmenu`s (flyout branches). Groups and submenus nest and mix with plain options. Selection stays global (one `value`, leaf options only); a filter query flattens the tree into grouped results. `Select` infers its value type `V` from these options: `{ value: 365 }` makes `onValueChange` report `number`. A mix of value types widens `V` to the union. Each leaf `value` is the option's identity and must be **unique across the whole tree** (selection is value-keyed, so a value repeated in two sections is one logical pick: both rows toggle together, the trigger resolves to the last). Values that stringify alike (`365` and `"365"`) also collide as row keys; dev builds `console.warn` on either. |
|
|
640
|
-
| `clearable?` | boolean | — | Add a "Clear" row pinned in the menu **footer** that empties the selection (single →
|
|
570
|
+
| `clearable?` | boolean | — | Add a "Clear" row pinned in the menu **footer** that empties the selection (single → `null`, multiple → `[]`). Shown only while something is selected, so it never scrolls away in a long or filtered list. |
|
|
641
571
|
| `clearLabel?` | string | Clear | Label for the `clearable` footer row. |
|
|
642
|
-
| `defaultValue?` | V \| V[] | — | Initial
|
|
572
|
+
| `defaultValue?` | V \| null \| V[] | — | Initial option value for uncontrolled use. `null` starts empty. |
|
|
573
|
+
| `dimActions?` | boolean | true | Dim the default trigger's leading content and chevron at rest. They brighten on hover or focus. Ignored by `renderTrigger`. |
|
|
643
574
|
| `disabled?` | boolean | — | Disable the whole select. |
|
|
644
|
-
| `filter?` | boolean \| (option, query) => boolean | — | Add a search field
|
|
575
|
+
| `filter?` | boolean \| (option, query) => boolean | — | Add a search field that filters option values, labels, and hints. `true` uses case-insensitive substring matching. A space in the trimmed query matches a run of whitespace. Built-in row text highlights matches. A custom `(option, query) => boolean` receives the trimmed query and returns whether to keep the option, without automatic highlighting. It runs once per option on each render, including for an empty query. Rows, empty content, and filtered Select all use the same results. A query flattens groups and submenus into grouped results. Closing the popup resets the query. |
|
|
645
576
|
| `hint?` | string | — | Helper text under the field. |
|
|
646
577
|
| `icon?` | IconShape | — | Leading icon shown at the left of the field. With a custom `renderTrigger`, it's passed through as `state.icon` instead — the consumer places it. |
|
|
647
578
|
| `indicator?` | 'none' \| 'check' \| 'radio' | none | The per-row mark for **single**-select: `'none'` (a tint-only highlight), `'check'` (a trailing checkmark on the selected row, keeping the tint — the canonical Select look), or `'radio'` (a leading radio on every row). Multi-select always uses checkboxes. |
|
|
648
|
-
| `label?` | string | — |
|
|
649
|
-
| `leading?` | ReactNode | — | Content before the default trigger's value, such as a key prefix before the value. It replaces the icon derived from `icon`. Include an `<Icon>` in this content when both are needed. Ignored by `renderTrigger`. |
|
|
579
|
+
| `label?` | string | — | Visible field label and accessible name for the default trigger. If omitted, provide `aria-label` or `aria-labelledby` that identifies the field. Leading content and the chevron are hidden from assistive technologies and do not label the trigger. |
|
|
580
|
+
| `leading?` | ReactNode | — | Content before the default trigger's value, such as a key prefix before the value. It replaces the icon derived from `icon`. Include an `<Icon>` in this content when both are needed. Hidden from assistive technologies. Include any meaningful prefix in `label` or `aria-label`. Ignored by `renderTrigger`. |
|
|
650
581
|
| `offset?` | number | 4 | Gap in pixels between the trigger and the options menu. |
|
|
651
|
-
| `onValueChange?` | (value, attrs) => void | — |
|
|
582
|
+
| `onValueChange?` | (value, attrs) => void | — | Reports the new value and a `{ value, option }` snapshot after a pick. Clear reports `null` with `{ all: true, selected: false }`. Narrow on `'all' in attrs` before reading `option`. Update `value` to accept a controlled change. There is no cancelable `onStateChange`; see the Input event-model note. |
|
|
652
583
|
| `placeholder?` | string | — | Text shown when nothing is selected. |
|
|
653
584
|
| `placement?` | 'left' \| 'right' \| 'bottom' \| 'top' \| 'bottom-start' \| 'bottom-end' \| 'top-start' \| 'top-end' \| 'right-start' \| 'right-end' \| 'left-start' \| 'left-end' | bottom-start | Preferred placement of the options menu relative to the trigger. The menu auto-flips vertically and clamps horizontally when needed. |
|
|
654
585
|
| `renderEmpty?` | (state) => ReactNode | — | Render content in the menu body when the (filtered) option list is empty — a "no results" message, a loading indicator (gated on your own external loading state), or a "create from the query" row. Receives an `EmptyState` (`query`, trimmed). There is no built-in empty message: when omitted, an empty list renders nothing. Whatever you return goes where the option rows would — a plain node is inert; return a `MenuItem` (e.g. a "Create" row) to make it focusable and selectable. |
|
|
655
586
|
| `renderIndicator?` | (state) => ReactNode | — | Replace each row's selection **mark** with your own node, drawn at the leading edge. The row stays the control (`role` + `aria-checked` from `indicator` / `selection`); only the drawn mark changes, so pair it with an `indicator` (`'check'` / `'radio'`) or `selection="multiple"` for the semantics. Composes with `renderOption`. |
|
|
656
587
|
| `renderOption?` | (option, state) => ReactNode | — | Replaces the built-in `label`, `hint`, and `icon` layout for each option row. Select still supplies the row container, click handling, ARIA attributes, and selection indicator. Read extra option fields through `SelectOption`'s index signature. `state` contains `value`, `selected`, and `disabled`. Filtering still matches the option's `value`, `label`, and `hint`, but Select cannot highlight matches within the returned content. |
|
|
657
|
-
| `renderSummary?` | (selected) => string \| undefined | — | `multiple` only: build the trigger's selection summary text yourself, replacing the built-in "`All` / one label / `N selected`" logic. Receives the resolved selected options (`selected.length` is the count) and runs only while something is selected — an empty selection still shows the `placeholder`. Return a **string**: it
|
|
588
|
+
| `renderSummary?` | (selected) => string \| undefined | — | `multiple` only: build the trigger's selection summary text yourself, replacing the built-in "`All` / one label / `N selected`" logic. Receives the resolved selected options (`selected.length` is the count) and runs only while something is selected — an empty selection still shows the `placeholder`. Return a **string**: it becomes the default trigger's value, so a long summary ellipsizes at the field's width just like a long value (`Engineering, Design, … `). Return `undefined` to fall back to the default for that case (e.g. customize only the count, keeping the single-label case built-in). For rich content (chips, multiple nodes) use `renderTrigger`, which replaces the whole field. |
|
|
658
589
|
| `renderTrigger?` | (state) => ReactNode | — | Replaces the default field with a trigger returned from this function. Receives `open`, `value`, `selected`, `disabled`, and `icon`. Return exactly one focusable element, such as an Anta `Button`. The menu is positioned relative to that element and opens when it is clicked. Do not return a fragment, multiple siblings, or a non-focusable wrapper. Add `aria-haspopup={filter ? 'dialog' : 'menu'}` and `aria-expanded={state.open}` to the returned button. An Anta `Button` already carries the correct role. Field props (`label`, `hint`, `size`, `status`, `placeholder`, and `round`) and `className` / `style` apply only to the default field. Add styling and attributes to the returned element instead. |
|
|
659
590
|
| `round?` | boolean \| number \| string | — | Round the field corners — `true` for fully round, or a number / CSS length. |
|
|
660
591
|
| `selectAll?` | boolean | true | `multiple` only: shows a "Select all" row that toggles every enabled option, or only the visible options when a filter query is active. Its checkbox is mixed when some options are selected. It is on by default. Set it to `false` to remove the row and the Alt/Option-click shortcut that selects only one row. |
|
|
@@ -665,10 +596,34 @@ scannable list instead of hiding matches behind flyouts.
|
|
|
665
596
|
| `statusIcon?` | (string & {}) \| false \| IconShape | — | Glyph shown before the `hint` when `status` is set. Each status has a default; pass a shape to override, or `false` to drop it. |
|
|
666
597
|
| `tone?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' \| (string & {}) | — | Default option-row tone. An option's own `tone` wins. A named tone or a custom CSS color. Most visible with tint-based marks (`indicator` `'none'` / `'check'`); with `'radio'` / `'checkbox'` it also tones the indicator. |
|
|
667
598
|
| `toneScope?` | 'all' \| 'selected' | 'all' | Apply the default row tone in every state, or only to selected rows. An option's own `toneScope` wins. |
|
|
668
|
-
| `value?` | V \| V[] | — | Controlled value
|
|
669
|
-
| `verbose?` | boolean | — | `multiple` only: spell the picks out in the count summary — `3 selected: A, B, C` (labels comma-joined) in place of the bare `3 selected`. Applies to the multi-count case only: `All` stays `All`, a single pick stays its own label, and an empty selection stays the `placeholder`. The list flows into the
|
|
599
|
+
| `value?` | V \| null \| V[] | — | Controlled option value. Use `null` for an empty selection; clearing to `null` keeps the Select controlled. Omit or pass `undefined` for uncontrolled use. Empty strings, `0`, and `false` remain option values. |
|
|
600
|
+
| `verbose?` | boolean | — | `multiple` only: spell the picks out in the count summary — `3 selected: A, B, C` (labels comma-joined) in place of the bare `3 selected`. Applies to the multi-count case only: `All` stays `All`, a single pick stays its own label, and an empty selection stays the `placeholder`. The list flows into the default trigger's value, so it ellipsizes at the field's width when long (`3 selected: Engineering, Des… `). `renderSummary` overrides this. |
|
|
601
|
+
|
|
602
|
+
### Change details
|
|
603
|
+
|
|
604
|
+
`onValueChange(value, attrs)` reports the new value and a snapshot of the change:
|
|
605
|
+
|
|
606
|
+
| Change | Snapshot |
|
|
607
|
+
| --- | --- |
|
|
608
|
+
| One option | `value` and the resolved `option`; multiple selection also includes `selected`. |
|
|
609
|
+
| Select all or Clear | `all: true` and `selected`, with no single `option`. |
|
|
610
|
+
|
|
611
|
+
Single-selection callbacks receive `V | null`; multiple-selection callbacks
|
|
612
|
+
receive `V[]`. Clear reports `null` or `[]` respectively. These types are the
|
|
613
|
+
same whether or not `clearable` shows the Clear action.
|
|
614
|
+
|
|
615
|
+
`value={null}` keeps a single Select controlled and empty. Omit `value` or use
|
|
616
|
+
`undefined` for uncontrolled mode. Updating a controlled value, including a
|
|
617
|
+
reset to `null`, does not itself call `onValueChange`.
|
|
618
|
+
|
|
619
|
+
Check `'all' in attrs` before reading `attrs.option`, including in single mode
|
|
620
|
+
when `clearable` is enabled. A controlled Select accepts a change when you update
|
|
621
|
+
`value`; it has no cancelable `onStateChange`. See the
|
|
622
|
+
[Input event model](./input.md#events) for the shared approach.
|
|
623
|
+
|
|
624
|
+
<a id="the-selectitem-type"></a>
|
|
670
625
|
|
|
671
|
-
###
|
|
626
|
+
### Option types
|
|
672
627
|
|
|
673
628
|
`options` takes a `SelectItem[]`. Each entry is one of four shapes, discriminated by
|
|
674
629
|
its keys — an `options` array is a group, a `submenu` array is a submenu, and a bare
|
|
@@ -704,7 +659,7 @@ the same thing):
|
|
|
704
659
|
|
|
705
660
|
| Field | Type | Description |
|
|
706
661
|
|---|---|---|
|
|
707
|
-
| `value` | `OptionValue` | The option's identity: `string`, `number`, or `boolean` (`Select` infers the type from your `options`, compared with `===`). What `value` / `defaultValue` name and `onValueChange` reports. Required, unique across the tree. See [
|
|
662
|
+
| `value` | `OptionValue` | The option's identity: `string`, `number`, or `boolean` (`Select` infers the type from your `options`, compared with `===`). What `value` / `defaultValue` name and `onValueChange` reports. Required, unique across the tree. See [Managing the value](#managing-the-value) for non-string values and rich data. |
|
|
708
663
|
| `label` | `string` | Visible label. Defaults to `value`. |
|
|
709
664
|
| `hint` | `string` | Secondary line under the label. |
|
|
710
665
|
| `icon` | `IconShape` | Leading icon, after the selection indicator. |
|
|
@@ -764,18 +719,20 @@ inline `SelectGroup`, and a flyout `SelectSubmenu` (whose items are themselves
|
|
|
764
719
|
|
|
765
720
|
## Web Component
|
|
766
721
|
|
|
767
|
-
|
|
722
|
+
Without JSX, compose the Input and Menu elements to build a Select. There is
|
|
723
|
+
no separate `a-select` element to register.
|
|
768
724
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
725
|
+
Compose an `<a-input button>` trigger with an `<a-menu>` of options. The menu owns opening and dismissal; the short controller
|
|
726
|
+
reflects a picked option into the field and selected row. Keep the Menu directly
|
|
727
|
+
after its trigger. Menu associates them through an ARIA element reference,
|
|
728
|
+
without requiring a popup ID, and preserves an authored `aria-controls`.
|
|
772
729
|
|
|
773
730
|
```html
|
|
774
731
|
<div data-anta-composition="select" style="display: grid; gap: 4px; width: 280px">
|
|
775
|
-
<a-
|
|
776
|
-
<
|
|
777
|
-
<a-icon shape="chevron-down" aria-hidden="true"></a-icon>
|
|
778
|
-
</a-
|
|
732
|
+
<a-input button value="stream" aria-haspopup="menu" aria-expanded="false">
|
|
733
|
+
<span slot="label">Output</span>
|
|
734
|
+
<a-icon slot="trailing" shape="chevron-down" aria-hidden="true"></a-icon>
|
|
735
|
+
</a-input>
|
|
779
736
|
<a-menu role="menu">
|
|
780
737
|
<a-menu-item role="menuitem" tabindex="0" value="output_text"><a-menu-item-label>output_text</a-menu-item-label></a-menu-item>
|
|
781
738
|
<a-menu-item role="menuitem" tabindex="0" value="stream" selected><a-menu-item-label>stream</a-menu-item-label></a-menu-item>
|
|
@@ -785,21 +742,21 @@ reflects a picked option into the field and selected row.
|
|
|
785
742
|
import '@antadesign/anta/elements'
|
|
786
743
|
|
|
787
744
|
const root = document.querySelector('[data-anta-composition="select"]')
|
|
788
|
-
const field = root.querySelector('a-
|
|
745
|
+
const field = root.querySelector('a-input')
|
|
789
746
|
const menu = root.querySelector('a-menu')
|
|
790
747
|
menu.addEventListener('statechange', (event) => {
|
|
791
748
|
field.setAttribute('aria-expanded', String(event.detail.next === 'open'))
|
|
792
749
|
})
|
|
793
750
|
|
|
794
|
-
menu.addEventListener('
|
|
751
|
+
menu.addEventListener('menuselect', (event) => {
|
|
795
752
|
const item = event.target instanceof Element ? event.target.closest('a-menu-item') : null
|
|
796
753
|
if (!item || item.closest('a-menu') !== menu) return
|
|
797
754
|
menu.querySelectorAll(':scope > a-menu-item').forEach((option) => {
|
|
798
755
|
const selected = option === item
|
|
799
756
|
option.toggleAttribute('selected', selected)
|
|
800
757
|
})
|
|
801
|
-
field.
|
|
802
|
-
})
|
|
758
|
+
field.value = item.querySelector('a-menu-item-label').textContent.trim()
|
|
759
|
+
}, true)
|
|
803
760
|
</script>
|
|
804
761
|
```
|
|
805
762
|
|
|
@@ -808,8 +765,8 @@ reflects a picked option into the field and selected row.
|
|
|
808
765
|
When a native single-value select is enough, add `data-anta` to `<select>`. The
|
|
809
766
|
modern customizable-select model keeps native selection, keyboard behavior, and
|
|
810
767
|
form submission while exposing its trigger `button`, `selectedcontent`, options,
|
|
811
|
-
and optgroups for styling. Anta styles
|
|
812
|
-
|
|
768
|
+
and optgroups for styling. Anta styles the customizable parts. Browser support and fallback behavior are
|
|
769
|
+
covered in [Install and configure](../install-config.md#browser-support). Use `Select` for filtering, multiple
|
|
813
770
|
selection, nested menus, or custom option coordination.
|
|
814
771
|
|
|
815
772
|
Use `data-anta-size="small"` or `"large"` for the field size. Native selects
|
|
@@ -836,7 +793,7 @@ corners.
|
|
|
836
793
|
|
|
837
794
|
## Styling
|
|
838
795
|
|
|
839
|
-
Select composes an [
|
|
796
|
+
Select composes an [Input](./input.md) and a [Menu](./menu.md). Reach for props first, then plain CSS or `::part` for
|
|
840
797
|
the rest. Don't override an element's internal `--*` output tokens.
|
|
841
798
|
|
|
842
799
|
**Selection color** routes through `tone`: set it per option or on the Select as a
|
|
@@ -844,28 +801,75 @@ default, then use `toneScope="selected"` for chosen rows only. It takes a named
|
|
|
844
801
|
its hue, with lightness pinned to the brand text).
|
|
845
802
|
|
|
846
803
|
**The field** takes `size`, `status`, and `round`. Set its width with `style` /
|
|
847
|
-
`className`, forwarded to
|
|
848
|
-
|
|
804
|
+
`className`, forwarded to `<a-input>`. Style the field through `::part(field)`
|
|
805
|
+
and its selected text through `::part(input-label)`. `::part(input)` targets
|
|
806
|
+
the native button for alignment and other control styles. The trigger uses
|
|
849
807
|
`:focus-visible` for its focus ring and ellipsizes long values. **The popover**
|
|
850
808
|
takes Menu's `::part(menu)`.
|
|
851
809
|
|
|
810
|
+
The leading content and chevron dim at rest and brighten on hover or focus.
|
|
811
|
+
Set `dimActions={false}` to keep them fully visible. Custom triggers control
|
|
812
|
+
their own adornments. Use Input's parts to style slots and `a-select-chevron`
|
|
813
|
+
to style the chevron.
|
|
814
|
+
|
|
815
|
+
Select's leading layout, chevron color, and custom indicator wrapper use external
|
|
816
|
+
CSS. Consumer `style` props, custom color or radius values, and render-hook content
|
|
817
|
+
can still produce inline styles. Follow the [CSP guidance](../install-config.md#content-security-policy)
|
|
818
|
+
when choosing styles for a restricted policy.
|
|
819
|
+
|
|
820
|
+
### Migrate field styles
|
|
821
|
+
|
|
822
|
+
The default Select field now renders `<a-input button>` and uses Input's label,
|
|
823
|
+
hint, and leading slots. This changes the wrapper's DOM structure. Pass a class
|
|
824
|
+
name through `className` to target your Select in CSS. The class is applied to
|
|
825
|
+
`<a-input>`, and `::part(field)` selects the visible field inside its shadow DOM.
|
|
826
|
+
For example, this Select rounds its field and gives the selected text more weight:
|
|
827
|
+
|
|
828
|
+
```tsx
|
|
829
|
+
<Select className="my-select" label="Team" options={['Design', 'Engineering']} />
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
```css
|
|
833
|
+
.my-select::part(field) {
|
|
834
|
+
border-radius: 6px;
|
|
835
|
+
}
|
|
836
|
+
.my-select::part(input-label) {
|
|
837
|
+
font-weight: 600;
|
|
838
|
+
}
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
With `className="my-select"`, update existing selectors as follows:
|
|
842
|
+
|
|
843
|
+
| Previous selector | Input field selector |
|
|
844
|
+
| --- | --- |
|
|
845
|
+
| `.my-select > a-button` | `.my-select::part(field)` |
|
|
846
|
+
| `.my-select > a-button > a-button-label` | `.my-select::part(input-label)` |
|
|
847
|
+
| `.my-select > a-select-label` | `.my-select::part(label)` |
|
|
848
|
+
| `.my-select > a-select-hint` | `.my-select::part(hint)` |
|
|
849
|
+
| `.my-select a-select-leading` | `.my-select::part(leading)` |
|
|
850
|
+
|
|
851
|
+
Existing raw compositions using `a-select-field`, `a-select-label`,
|
|
852
|
+
`a-select-hint`, and `a-select-leading` retain their CSS, JSX types, and Button
|
|
853
|
+
label and hint behavior. New raw compositions can use `<a-input button>` as
|
|
854
|
+
shown above.
|
|
855
|
+
|
|
852
856
|
```tsx
|
|
853
857
|
// Tone the selection; give the trigger a width.
|
|
854
858
|
<Select tone="brand" toneScope="selected" style={{ width: '220px' }} options={OPTIONS} />
|
|
855
859
|
```
|
|
856
860
|
|
|
857
|
-
**Borderless trigger.**
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
+
**Borderless trigger.** Hide the field's inset `box-shadow` while it is not hovered.
|
|
862
|
+
The standard hover border and `:focus-visible` keyboard focus outline remain
|
|
863
|
+
available. Hide the chevron by targeting `a-select-chevron` to make the field
|
|
864
|
+
read as plain text at rest.
|
|
861
865
|
|
|
862
866
|
```css
|
|
863
|
-
/*
|
|
864
|
-
.ghost-select a-
|
|
867
|
+
/* Hide the border off hover; keep the standard keyboard focus outline. */
|
|
868
|
+
.ghost-select a-input:not(:hover)::part(field) {
|
|
865
869
|
box-shadow: none;
|
|
866
870
|
}
|
|
867
871
|
/* Center the chosen label in the field. */
|
|
868
|
-
.ghost-select a-
|
|
872
|
+
.ghost-select a-input::part(input) { text-align: center; }
|
|
869
873
|
/* Hide the chevron. */
|
|
870
874
|
.ghost-select a-select-chevron { display: none; }
|
|
871
875
|
```
|