@antadesign/anta 0.3.28 → 0.3.30
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/NOTICES.md +32 -1
- package/dist/anta_helpers.d.ts +21 -0
- package/dist/anta_helpers.js +24 -0
- package/dist/box-observation.d.ts +8 -0
- package/dist/box-observation.js +41 -0
- package/dist/box-types.d.ts +2 -0
- package/dist/bundle.css +1 -1
- package/dist/bundle.js +66 -29
- package/dist/components/Box.d.ts +28 -12
- package/dist/components/Box.js +18 -11
- package/dist/components/Breadcrumbs.css +1 -1
- package/dist/components/Breadcrumbs.d.ts +1 -1
- package/dist/components/Button.d.ts +3 -3
- package/dist/components/ButtonCopy.d.ts +1 -1
- package/dist/components/Calendar.js +3 -5
- package/dist/components/Checkbox.d.ts +2 -2
- package/dist/components/Input.d.ts +8 -2
- package/dist/components/Input.js +4 -0
- package/dist/components/InputAutocomplete.js +3 -12
- package/dist/components/InputDate.css +1 -1
- package/dist/components/InputDate.js +2 -0
- package/dist/components/Menu.d.ts +4 -4
- package/dist/components/Menu.js +1 -1
- package/dist/components/Panel.d.ts +17 -0
- package/dist/components/Panel.js +22 -0
- package/dist/components/RadioGroup.d.ts +2 -1
- package/dist/components/Select.d.ts +14 -14
- package/dist/components/Select.js +47 -35
- package/dist/components/SelectFaceted.d.ts +17 -6
- package/dist/components/SelectFaceted.js +61 -41
- package/dist/components/Steps.css +1 -1
- package/dist/components/Switch.d.ts +3 -3
- package/dist/components/TabPanel.d.ts +3 -8
- package/dist/components/TabPanel.js +2 -1
- package/dist/components/Tabs.d.ts +2 -2
- package/dist/components/Tabs.js +2 -1
- package/dist/components/Tooltip.d.ts +1 -1
- package/dist/components/Tooltip.js +1 -1
- package/dist/components/select-parts.css +1 -1
- package/dist/elements/a-avatar.css +1 -1
- package/dist/elements/a-banner.css +1 -1
- package/dist/elements/a-box.css +1 -1
- package/dist/elements/a-box.d.ts +6 -6
- package/dist/elements/a-box.js +76 -23
- package/dist/elements/a-button.css +1 -1
- package/dist/elements/a-button.d.ts +9 -1
- package/dist/elements/a-button.js +64 -1
- package/dist/elements/a-calendar.css +1 -1
- package/dist/elements/a-capture.css +1 -1
- package/dist/elements/a-card.css +1 -1
- package/dist/elements/a-checkbox.css +1 -1
- package/dist/elements/a-checkbox.d.ts +3 -0
- package/dist/elements/a-checkbox.js +17 -1
- package/dist/elements/a-copy.css +1 -1
- package/dist/elements/a-dialog.css +1 -1
- package/dist/elements/a-expander.css +1 -1
- package/dist/elements/a-icon.css +1 -1
- package/dist/elements/a-icon.shapes.css +1 -1
- package/dist/elements/a-icon.shapes.d.ts +1 -1
- package/dist/elements/a-input-time.css +1 -1
- package/dist/elements/a-input-time.d.ts +8 -0
- package/dist/elements/a-input-time.js +115 -8
- package/dist/elements/a-input.css +1 -1
- package/dist/elements/a-input.d.ts +27 -1
- package/dist/elements/a-input.js +176 -35
- package/dist/elements/a-loader.css +1 -1
- package/dist/elements/a-menu-group.css +1 -1
- package/dist/elements/a-menu-item.css +1 -1
- package/dist/elements/a-menu-item.js +1 -0
- package/dist/elements/a-menu-separator.css +1 -1
- package/dist/elements/a-menu.css +1 -1
- package/dist/elements/a-menu.d.ts +8 -13
- package/dist/elements/a-menu.js +146 -45
- package/dist/elements/a-panel.css +1 -0
- package/dist/elements/a-panel.d.ts +16 -0
- package/dist/elements/a-panel.js +153 -0
- package/dist/elements/a-progress.css +1 -1
- package/dist/elements/a-radio-group.css +1 -1
- package/dist/elements/a-radio.css +1 -1
- package/dist/elements/a-radio.d.ts +4 -0
- package/dist/elements/a-radio.js +18 -0
- package/dist/elements/a-slider.css +1 -1
- package/dist/elements/a-slider.d.ts +1 -0
- package/dist/elements/a-slider.js +3 -3
- package/dist/elements/a-switch.css +1 -1
- package/dist/elements/a-switch.d.ts +3 -0
- package/dist/elements/a-switch.js +17 -1
- package/dist/elements/a-tab.css +1 -1
- package/dist/elements/a-tabpanel.css +1 -1
- package/dist/elements/a-tabs.css +1 -1
- package/dist/elements/a-tag.css +1 -1
- package/dist/elements/a-text.css +1 -1
- package/dist/elements/a-title.css +1 -1
- package/dist/elements/a-toast.css +1 -1
- package/dist/elements/a-toaster.css +1 -1
- package/dist/elements/a-tooltip.css +1 -1
- package/dist/elements/a-tooltip.js +30 -7
- package/dist/elements/focus.d.ts +9 -0
- package/dist/elements/focus.js +41 -0
- package/dist/elements/index.d.ts +1 -0
- package/dist/elements/index.js +3 -0
- package/dist/elements/panel-focus.d.ts +10 -0
- package/dist/elements/panel-focus.js +86 -0
- package/dist/elements/shadow-aria.d.ts +10 -0
- package/dist/elements/shadow-aria.js +155 -0
- package/dist/general_types.d.ts +48 -19
- package/dist/generate-icons.mjs +3 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +2 -0
- package/dist/jsx-runtime.d.ts +20 -1
- package/dist/jsx-runtime.js +12 -0
- package/dist/reset.css +1 -1
- package/dist/theme-antithesis.css +1 -1
- package/dist/theme-antune.css +1 -1
- package/dist/toaster.d.ts +1 -1
- package/dist/tokens.css +1 -1
- package/docs/accessibility.md +4 -2
- package/docs/changelog.md +89 -4
- package/docs/colors.md +6 -6
- package/docs/components/box.md +98 -34
- package/docs/components/breadcrumbs.md +33 -0
- package/docs/components/button.md +2 -2
- package/docs/components/capture.md +8 -8
- package/docs/components/checkbox.md +9 -4
- package/docs/components/input-autocomplete.md +6 -0
- package/docs/components/input-date.md +8 -2
- package/docs/components/input.md +99 -40
- package/docs/components/menu.md +3 -3
- package/docs/components/panel.md +189 -0
- package/docs/components/radio.md +4 -2
- package/docs/components/select-faceted.md +57 -2
- package/docs/components/select.md +63 -51
- package/docs/components/steps.md +4 -4
- package/docs/components/switch.md +12 -6
- package/docs/components/tabs.md +21 -8
- package/docs/components/tooltip.md +6 -4
- package/docs/index.md +2 -0
- package/docs/install-config.md +5 -4
- package/docs/normalization.md +3 -1
- package/docs/packages/plot.md +3771 -0
- package/docs/theming.md +1 -1
- package/package.json +16 -9
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Panel
|
|
2
|
+
|
|
3
|
+
`Panel` is a container that can maximize to the browser viewport without moving
|
|
4
|
+
or remounting its children. It fills its parent's width and height and uses
|
|
5
|
+
`overflow: scroll` by default.
|
|
6
|
+
|
|
7
|
+
Give the parent a defined height for Panel to fill vertically. With an
|
|
8
|
+
auto-height parent, percentage height follows normal CSS sizing and the Panel
|
|
9
|
+
can grow with its content. A parent's `min-height` alone does not generally
|
|
10
|
+
establish a height for `height: 100%` to fill.
|
|
11
|
+
|
|
12
|
+
> **Warning:** For now, Panel is a nonvisual utility container only. Supply your
|
|
13
|
+
> own appearance and controls. Built-in chrome, resizing, and folding are not
|
|
14
|
+
> available yet.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
### Declarative
|
|
19
|
+
|
|
20
|
+
Panel manages its own state. Use `data-custom-event="paneltoggle"` on
|
|
21
|
+
an Anta button to toggle maximization without a click handler.
|
|
22
|
+
|
|
23
|
+
```tsx title="Declarative Panel"
|
|
24
|
+
import { Box, Button, Input, Panel } from '@antadesign/anta'
|
|
25
|
+
|
|
26
|
+
function Workspace() {
|
|
27
|
+
return (
|
|
28
|
+
<Panel style={{ background: 'var(--bg-3)' }}>
|
|
29
|
+
<Box
|
|
30
|
+
display="flex"
|
|
31
|
+
gap={16}
|
|
32
|
+
padding={16}
|
|
33
|
+
>
|
|
34
|
+
<Input aria-label="Workspace note" placeholder="Write a note" />
|
|
35
|
+
<Button data-custom-event="paneltoggle">
|
|
36
|
+
Maximize / restore
|
|
37
|
+
</Button>
|
|
38
|
+
</Box>
|
|
39
|
+
</Panel>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Controlled
|
|
45
|
+
|
|
46
|
+
Pass `maximized` to manage state in your application. The button updates it
|
|
47
|
+
through `onClick`; `onStateChange` accepts requests from declarative controls
|
|
48
|
+
inside the panel.
|
|
49
|
+
|
|
50
|
+
```tsx title="Controlled Panel"
|
|
51
|
+
import { useState } from 'react'
|
|
52
|
+
import { Box, Button, Input, Panel } from '@antadesign/anta'
|
|
53
|
+
|
|
54
|
+
function Workspace() {
|
|
55
|
+
const [maximized, setMaximized] = useState(false)
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<Panel
|
|
59
|
+
style={{ background: 'var(--bg-3)' }}
|
|
60
|
+
maximized={maximized}
|
|
61
|
+
onStateChange={(_event, { next }) => setMaximized(next)}
|
|
62
|
+
>
|
|
63
|
+
<Box
|
|
64
|
+
display="flex"
|
|
65
|
+
gap={16}
|
|
66
|
+
padding={16}
|
|
67
|
+
>
|
|
68
|
+
<Input aria-label="Workspace note" placeholder="Write a note" />
|
|
69
|
+
<Button onClick={() => setMaximized(value => !value)}>
|
|
70
|
+
{maximized ? 'Restore panel' : 'Maximize panel'}
|
|
71
|
+
</Button>
|
|
72
|
+
</Box>
|
|
73
|
+
</Panel>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## How does this work?
|
|
79
|
+
|
|
80
|
+
Maximizing applies `popover="manual"` to the internal content slot and calls
|
|
81
|
+
`showPopover()` to fill the viewport. Children keep their DOM ancestry, theme,
|
|
82
|
+
and event bubbling. A placeholder preserves the panel's normal layout size.
|
|
83
|
+
Restoring calls `hidePopover()` and returns the same content to normal layout.
|
|
84
|
+
|
|
85
|
+
```html title="Internal content while maximized"
|
|
86
|
+
<!-- Managed by Panel inside its shadow root. -->
|
|
87
|
+
<slot part="content" popover="manual" tabindex="-1"></slot>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
While maximized, Panel traps focus inside its content. Tab and Shift+Tab cycle
|
|
91
|
+
through its controls. Restoring releases the trap and returns focus to the
|
|
92
|
+
previous external control, when available.
|
|
93
|
+
|
|
94
|
+
Provide a keyboard-accessible **Restore** control: Escape and outside clicks
|
|
95
|
+
do not restore the panel. Panel adds no dialog role, background inertness, or
|
|
96
|
+
scroll lock. Use [Dialog](./dialog.md) for modal interactions.
|
|
97
|
+
|
|
98
|
+
Panel fills the browser viewport, without invoking the Fullscreen API.
|
|
99
|
+
Browsers without Popover API support keep it in normal layout.
|
|
100
|
+
|
|
101
|
+
## Component props
|
|
102
|
+
|
|
103
|
+
For uncontrolled state, omit `maximized` and optionally set `defaultMaximized`.
|
|
104
|
+
Call `event.preventDefault()` synchronously in `onStateChange` to cancel an
|
|
105
|
+
uncontrolled transition. In controlled mode, reject a request by leaving
|
|
106
|
+
`maximized` unchanged.
|
|
107
|
+
|
|
108
|
+
| Prop | Type | Default | Description |
|
|
109
|
+
|------|------|---------|-------------|
|
|
110
|
+
| `defaultMaximized?` | boolean | — | Initial viewport maximization for an uncontrolled panel, read once on connect. |
|
|
111
|
+
| `maximized?` | boolean | — | Fill the browser viewport and contain keyboard focus within the panel. When supplied, the application owns the state. |
|
|
112
|
+
| `onStateChange?` | (event, detail) => void | — | Requested maximization change. Update `maximized` to accept a controlled request, or prevent the event to veto an uncontrolled request. |
|
|
113
|
+
|
|
114
|
+
## Web component
|
|
115
|
+
|
|
116
|
+
Import `@antadesign/anta/elements` to register the elements in this example.
|
|
117
|
+
The button toggles the nearest panel without JavaScript handlers.
|
|
118
|
+
|
|
119
|
+
```html title="Declarative web component"
|
|
120
|
+
<a-panel style="background: var(--bg-3)">
|
|
121
|
+
<a-box display="flex" padding="16px" gap="16px" style="--box-padding: 16px; --box-gap: 16px">
|
|
122
|
+
<a-input aria-label="Workspace note" placeholder="Write a note"></a-input>
|
|
123
|
+
<a-button role="button" tabindex="0" data-custom-event="paneltoggle">
|
|
124
|
+
Maximize / restore
|
|
125
|
+
</a-button>
|
|
126
|
+
</a-box>
|
|
127
|
+
</a-panel>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Use `state="normal"` or `state="maximized"` for controlled state, or `default-state="maximized"`
|
|
131
|
+
for an initially maximized, uncontrolled panel.
|
|
132
|
+
|
|
133
|
+
`requestMaximize()` and `requestRestore()` emit a cancelable, non-bubbling
|
|
134
|
+
`statechange` with `{ next, prev }` using those state strings.
|
|
135
|
+
Anta buttons can send `panelmaximizerequest`, `panelrestorerequest`, or
|
|
136
|
+
`paneltoggle` through `data-custom-event` to the nearest Panel.
|
|
137
|
+
|
|
138
|
+
## Styling
|
|
139
|
+
|
|
140
|
+
Panel defaults to `display: block`, `width: 100%`, `height: 100%`,
|
|
141
|
+
`box-sizing: border-box`, and `overflow: scroll`. Padding and borders stay inside
|
|
142
|
+
its assigned size. It does not set `flex-grow`.
|
|
143
|
+
|
|
144
|
+
Override these through ordinary CSS. Use `height: auto` for content-driven
|
|
145
|
+
height or `overflow: auto` for scrollbars only when content overflows.
|
|
146
|
+
With `overflow: scroll`, scrollbar visibility and reserved space follow the
|
|
147
|
+
browser and operating system's scrollbar settings.
|
|
148
|
+
|
|
149
|
+
Set the background on Panel. Its maximized surface inherits that background,
|
|
150
|
+
so inner content can keep its natural height. Style light-DOM children, such as
|
|
151
|
+
[Box](./box.md) or [Capture](./capture.md), for content layout shared between both states.
|
|
152
|
+
Use `:state(maximized)` for viewport styling and `::part(content)` for the
|
|
153
|
+
maximized scrolling surface. In normal mode, the content slot uses `display: contents`.
|
|
154
|
+
The maximized surface also uses `overflow: scroll`; override
|
|
155
|
+
`::part(content)` to change its overflow separately from the normal Panel.
|
|
156
|
+
|
|
157
|
+
Enter a note, then select **Maximize panel** and **Restore panel**. The input
|
|
158
|
+
keeps its value. The `panel-demo` classes supply this example's appearance;
|
|
159
|
+
replace them with your own selectors.
|
|
160
|
+
|
|
161
|
+
```html title="A persistent workspace panel"
|
|
162
|
+
<a-panel class="panel-demo">
|
|
163
|
+
<a-box class="panel-demo-content">
|
|
164
|
+
<a-box class="panel-demo-toolbar" display="flex">
|
|
165
|
+
<a-text weight="medium">Workspace preview</a-text>
|
|
166
|
+
<a-button role="button" tabindex="0" data-custom-event="paneltoggle">
|
|
167
|
+
<span class="panel-demo-maximize">Maximize panel</span>
|
|
168
|
+
<span class="panel-demo-restore">Restore panel</span>
|
|
169
|
+
</a-button>
|
|
170
|
+
</a-box>
|
|
171
|
+
<a-capture>
|
|
172
|
+
<a-input placeholder="Write a note before maximizing" aria-label="Workspace note"></a-input>
|
|
173
|
+
<a-text>Panel keeps this Box and Capture in their original DOM location.</a-text>
|
|
174
|
+
</a-capture>
|
|
175
|
+
</a-box>
|
|
176
|
+
</a-panel>
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```css title="Preview layout and appearance"
|
|
180
|
+
.panel-demo { border: 1px solid var(--border-4); border-radius: 8px; background: var(--bg-3); }
|
|
181
|
+
.panel-demo-content { display: flex; flex-direction: column; gap: 20px; padding: 20px; }
|
|
182
|
+
.panel-demo-toolbar { justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; }
|
|
183
|
+
.panel-demo-content a-capture { display: grid; gap: 16px; }
|
|
184
|
+
.panel-demo-content a-input { width: 100%; }
|
|
185
|
+
.panel-demo-restore { display: none; }
|
|
186
|
+
.panel-demo:state(maximized) .panel-demo-content { padding: max(20px, env(safe-area-inset-top)) max(20px, env(safe-area-inset-right)) max(20px, env(safe-area-inset-bottom)) max(20px, env(safe-area-inset-left)); }
|
|
187
|
+
.panel-demo:state(maximized) .panel-demo-maximize { display: none; }
|
|
188
|
+
.panel-demo:state(maximized) .panel-demo-restore { display: inline; }
|
|
189
|
+
```
|
package/docs/components/radio.md
CHANGED
|
@@ -165,7 +165,9 @@ Under the hood the split is deliberate. The `<a-radio-group>` web component
|
|
|
165
165
|
**never mutates the DOM** — it coordinates entirely off-DOM: selection is set as a
|
|
166
166
|
property on each `<a-radio>` (which reflects it into `:state(selected)` +
|
|
167
167
|
`aria-checked` via its own `ElementInternals`), the form value goes through
|
|
168
|
-
`ElementInternals`, and focus is tracked with `aria-activedescendant`.
|
|
168
|
+
`ElementInternals`, and focus is tracked with `aria-activedescendant`. Each
|
|
169
|
+
option's light-DOM label and hint also become its accessible name and description
|
|
170
|
+
through direct element references, without generated IDs. The
|
|
169
171
|
**roving `tabindex`** — which option Tab lands on — is the one piece rendered
|
|
170
172
|
declaratively, by the `RadioGroup` wrapper, from the current value. That's why
|
|
171
173
|
selection is owned by the element but the wrapper holds the value: it needs it to
|
|
@@ -270,7 +272,7 @@ element — `Input`, and the composed `Select` — skip it and expose only
|
|
|
270
272
|
| `size?` | 'small' \| 'medium' \| 'large' | 'medium' | Size applied to every option (an option's own `size` wins). |
|
|
271
273
|
| `status?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' | 'neutral' | Validation/feedback tone for the group `hint` — recolors it (same tone set as `Input`'s `status`). Use `critical` for an error message, etc.; omit for the neutral default. |
|
|
272
274
|
| `tone?` | 'brand' \| 'neutral' \| 'info' \| 'success' \| 'warning' \| 'critical' \| (string & {}) | 'neutral' | Mark tone applied to every option (an option's own `tone` wins), or any literal CSS color for a one-off custom tone. In the default `all` scope it colors the selected-ring fill + dot and the unselected ring border. Named tones track light/dark mode. Set `toneScope="selected"` to tone only the selected option and leave the rest neutral. The option text stays neutral — recolor it in plain CSS via the `--text-N-{tone}` tokens. |
|
|
273
|
-
| `toneScope?` |
|
|
275
|
+
| `toneScope?` | 'all' \| 'selected' | 'all' | Apply `tone` to every state, or only to the selected option so unselected rings stay neutral. An option's own `toneScope` wins. |
|
|
274
276
|
| `value?` | string | — | Controlled selected value. The group follows this prop and reports a requested change through `onStateChange`. Leave it undefined for uncontrolled use. |
|
|
275
277
|
|
|
276
278
|
## Web Component
|
|
@@ -14,6 +14,12 @@ application-defined facet keys, not Anta terms. Each facet has one of four
|
|
|
14
14
|
Use `value` with `onValueChange` to control it, or use `defaultValue` for an
|
|
15
15
|
uncontrolled filter.
|
|
16
16
|
|
|
17
|
+
Without `searchable`, the trigger opens a menu of facets. With `searchable`, it
|
|
18
|
+
opens a dialog containing the search field and that menu. A filtered option
|
|
19
|
+
facet also opens a dialog containing its search field and options menu. Text and
|
|
20
|
+
custom facets use dialogs for their editable content. An unfiltered option facet
|
|
21
|
+
keeps the menu popup pattern.
|
|
22
|
+
|
|
17
23
|
Pass `placement` to control where the root facet menu opens relative to its
|
|
18
24
|
trigger. It uses the same values as `Menu` and still flips or clamps when space
|
|
19
25
|
is limited. `offset` sets the gap in pixels between the trigger and the menu:
|
|
@@ -22,11 +28,32 @@ is limited. `offset` sets the gap in pixels between the trigger and the menu:
|
|
|
22
28
|
<SelectFaceted placement="top-end" offset={8} facets={FACETS} />
|
|
23
29
|
```
|
|
24
30
|
|
|
31
|
+
Open **Metadata** in the first demo to choose key–value tags such as
|
|
32
|
+
**env production** or **region us-east**. Its `renderOption` callback reads
|
|
33
|
+
custom option fields and renders a `Tag` with `label` and `value`. Search
|
|
34
|
+
for **production** to see the same tag in global results.
|
|
35
|
+
|
|
25
36
|
```tsx
|
|
26
37
|
const FACETS = [
|
|
27
38
|
{ key: 'assignee', label: 'Assignee', kind: 'multiple', filter: true, options: people },
|
|
28
39
|
{ key: 'owner', label: 'Owner', kind: 'single', filter: true, options: people },
|
|
29
40
|
{ key: 'status', label: 'Status', kind: 'single', options: [{ value: 'open', label: 'Open' }, /* … */] },
|
|
41
|
+
{
|
|
42
|
+
key: 'metadata',
|
|
43
|
+
label: 'Metadata',
|
|
44
|
+
kind: 'multiple',
|
|
45
|
+
icon: 'tag',
|
|
46
|
+
filter: true,
|
|
47
|
+
options: [
|
|
48
|
+
{ value: 'env:production', label: 'env: production', dataKey: 'env', dataValue: 'production' },
|
|
49
|
+
{ value: 'env:staging', label: 'env: staging', dataKey: 'env', dataValue: 'staging' },
|
|
50
|
+
{ value: 'region:us-east', label: 'region: us-east', dataKey: 'region', dataValue: 'us-east' },
|
|
51
|
+
{ value: 'team:platform', label: 'team: platform', dataKey: 'team', dataValue: 'platform' },
|
|
52
|
+
],
|
|
53
|
+
renderOption: (option) => (
|
|
54
|
+
<Tag label={String(option.dataKey)} value={String(option.dataValue)} />
|
|
55
|
+
),
|
|
56
|
+
},
|
|
30
57
|
{ key: 'title', label: 'Title contains', kind: 'text' },
|
|
31
58
|
{ key: 'duration', label: 'Min duration', kind: 'custom', summary: (v) => `≥ ${v.min}s`, render: /* … */ },
|
|
32
59
|
]
|
|
@@ -68,6 +95,7 @@ function Demo() {
|
|
|
68
95
|
selection={facet.kind}
|
|
69
96
|
indicator={facet.kind === 'single' ? 'check' : undefined}
|
|
70
97
|
options={facet.options}
|
|
98
|
+
renderOption={facet.renderOption}
|
|
71
99
|
value={value[facet.key]}
|
|
72
100
|
onValueChange={(v) => setFacet(facet.key, v)}
|
|
73
101
|
leading={`${facet.label}:`}
|
|
@@ -120,6 +148,27 @@ The trigger opens the facet menu. The example renders one editable
|
|
|
120
148
|
[Select](./select.md) below it for each active facet. Each Select receives the
|
|
121
149
|
facet label through `leading`, which displays it as a prefix.
|
|
122
150
|
|
|
151
|
+
### Custom option content
|
|
152
|
+
|
|
153
|
+
Pass `renderOption(option, state)` on a `single` or `multiple` facet to replace
|
|
154
|
+
each option's label, hint, and icon layout. It runs in both the facet flyout and
|
|
155
|
+
global search results. `state` contains `value`, `selected`, and `disabled`.
|
|
156
|
+
Selection handling, indicators, and option styling remain on the row.
|
|
157
|
+
Return `null` to use the default layout for an option.
|
|
158
|
+
|
|
159
|
+
The first demo's **Metadata** facet stores the key and value in custom option
|
|
160
|
+
fields. Pass them to Tag's `label` and `value` props:
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
renderOption: (option) => (
|
|
164
|
+
<Tag label={String(option.dataKey)} value={String(option.dataValue)} />
|
|
165
|
+
)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Filtering still uses `value`, `label`, and `hint`, or the facet's custom `filter`
|
|
169
|
+
function. Keep searchable text in those fields. Single-facet summaries still
|
|
170
|
+
use the option label, and multiple-facet summaries show the selected count.
|
|
171
|
+
|
|
123
172
|
### Value and changes
|
|
124
173
|
|
|
125
174
|
The value is a record keyed by facet key: `{ [facetKey]: value }`. Its shape
|
|
@@ -373,12 +422,12 @@ const recency: SelectFacetCustom<Recency> = {
|
|
|
373
422
|
| `onValueChange?` | (value, attrs) => void | — | Fires after any facet changes. `value` is the whole new record (a facet key → that facet's value; a cleared facet is absent). `attrs` says what changed: `{ facet, kind, value }` for a single facet edit, or `{ all: true }` for the "Clear all" row — narrow on `'all' in attrs` before reading `facet`. |
|
|
374
423
|
| `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 root filter menu relative to its trigger. The menu auto-flips vertically and clamps horizontally when needed. |
|
|
375
424
|
| `priority?` | 'primary' \| 'secondary' | secondary | Default trigger's button priority. |
|
|
376
|
-
| `renderTrigger?` | (state) => ReactNode | — | Replaces the default `Button` with a trigger returned from this function. Receives a `SelectFacetedTriggerState`. Return exactly one focusable element: the menu is positioned relative to that element and opens when it is clicked. Add `aria-haspopup=
|
|
425
|
+
| `renderTrigger?` | (state) => ReactNode | — | Replaces the default `Button` with a trigger returned from this function. Receives a `SelectFacetedTriggerState`. Return exactly one focusable element: the menu is positioned relative to that element and opens when it is clicked. Add `aria-haspopup={searchable ? 'dialog' : 'menu'}` and `aria-expanded={state.open}` to the returned element, on a role that supports them (an Anta `Button` already carries `role="button"`; otherwise add `role="combobox"`). `className`, `style`, and other trigger props apply only to the default Button, so add styling and attributes to the returned element. |
|
|
377
426
|
| `searchable?` | boolean | — | Adds a search field at the top of the root menu. It searches the options of every `single` and `multiple` facet in one list. For example, "alice" can appear under application-defined Assignee and Owner facets. `text` and `custom` facets remain available when the search is empty. Each facet uses its `filter` function when supplied, or the built-in substring match. |
|
|
378
427
|
| `searchPlaceholder?` | string | Filter… | Placeholder for the global search field. |
|
|
379
428
|
| `size?` | 'small' \| 'medium' \| 'large' | medium | Default trigger's button size. |
|
|
380
429
|
| `tone?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' \| (string & {}) | — | Default option-row tone in facet flyouts. An option's own `tone` wins. |
|
|
381
|
-
| `toneScope?` |
|
|
430
|
+
| `toneScope?` | 'all' \| 'selected' | 'all' | Apply the default row tone in every state, or only to selected rows. An option's own `toneScope` wins. |
|
|
382
431
|
| `value?` | SelectFacetedValue | — | Controlled value record, keyed by facet. When provided, update it through `onValueChange`. Leave it undefined for uncontrolled use. |
|
|
383
432
|
|
|
384
433
|
### The `SelectFacet` type
|
|
@@ -391,6 +440,12 @@ properties as [Select](./select.md). They use `kind` in place of Select's
|
|
|
391
440
|
`selection`. `text` and `custom` do not use Select options. A `single` facet
|
|
392
441
|
returns one option value; a `multiple` facet returns an array of option values.
|
|
393
442
|
|
|
443
|
+
When `selectAll` is enabled, Alt-clicking an option in a `multiple` facet clears
|
|
444
|
+
the other values and selects that option. Use Option-click on macOS. After a
|
|
445
|
+
700ms hover, a tooltip beside the row describes the shortcut. Moving to another
|
|
446
|
+
row restarts the delay. An option's `tooltip` replaces that hint; set it
|
|
447
|
+
to `''` to hide the hint.
|
|
448
|
+
|
|
394
449
|
| Field | Type | Description |
|
|
395
450
|
|---|---|---|
|
|
396
451
|
| `key` | `string` | The key for this facet's value in the record. `attrs.facet` returns it. It must be unique in `facets`. |
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
field shows the selected value. Click the field to open the menu. The menu
|
|
5
5
|
closes when a user clicks outside it or presses Esc.
|
|
6
6
|
|
|
7
|
-
Select uses an [
|
|
7
|
+
Select uses an [Anta Button](./button.md) for its field and a [Menu](./menu.md) for its
|
|
8
8
|
dropdown. Pass `options` as an array of strings or objects. Use `value` with
|
|
9
9
|
**`onValueChange`** to control the selection, or use `defaultValue` for an
|
|
10
10
|
uncontrolled Select.
|
|
@@ -70,7 +70,8 @@ its box goes mixed when only some are on. Pass **`selectAll={false}`** to drop i
|
|
|
70
70
|
|
|
71
71
|
In a multi-select, **`Alt`+Click** a row to clear every other selection and
|
|
72
72
|
select that row. On macOS, use Option instead of Alt.
|
|
73
|
-
|
|
73
|
+
After hovering for 700ms, the standard tooltip describes this shortcut beside
|
|
74
|
+
the row. Moving to another row restarts the delay.
|
|
74
75
|
Set `SelectOption.tooltip` to replace it, or to `''` to hide it. The shortcut
|
|
75
76
|
is available only when `selectAll` is enabled, so `selectAll={false}` removes it.
|
|
76
77
|
|
|
@@ -105,12 +106,14 @@ option's **value, label, or hint**. It's **whitespace-flexible** (a typed space
|
|
|
105
106
|
matches any run of whitespace) and **bolds the matched substring** in the results.
|
|
106
107
|
Pass a **function** `(option, query) => boolean` for custom matching (no highlight).
|
|
107
108
|
|
|
108
|
-
With `filter`,
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
109
|
+
With `filter`, the trigger opens a dialog containing the search field and an
|
|
110
|
+
options menu. This keeps the textbox outside the menu in the accessibility tree.
|
|
111
|
+
Opening the dialog focuses the search field. ↑ and ↓ move
|
|
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.
|
|
114
117
|
|
|
115
118
|
## Value and changes
|
|
116
119
|
|
|
@@ -229,7 +232,7 @@ section marks the leaf everywhere it occurs (selection is value-keyed).
|
|
|
229
232
|
<Select status="info" statusIcon="sparkles" hint="Custom glyph" options={OPTIONS} defaultValue="stream" />
|
|
230
233
|
```
|
|
231
234
|
|
|
232
|
-
The trigger is an [
|
|
235
|
+
The trigger is an [Anta Button](./button.md) styled as a field.
|
|
233
236
|
**`size`** (`small` · `medium` · `large`) sets the height and type scale.
|
|
234
237
|
**`status`** paints a validation tone on the border, `hint`, and chevron, and
|
|
235
238
|
prefixes a glyph (`info` / `success` / `warning` / `critical`); **`statusIcon`**
|
|
@@ -245,7 +248,7 @@ the glyph and, for `critical`, validity. See the
|
|
|
245
248
|
## Customization
|
|
246
249
|
|
|
247
250
|
Select has no `<a-select>` element. The React and Preact wrapper coordinates an
|
|
248
|
-
|
|
251
|
+
Anta Button and a Menu. You can use the wrapper, render custom option content with
|
|
249
252
|
`renderOption`, replace its selection mark with `renderIndicator`, or build the
|
|
250
253
|
same interaction from the raw elements.
|
|
251
254
|
|
|
@@ -259,22 +262,21 @@ uses the same values as `Menu` and still flips or clamps when space is limited.
|
|
|
259
262
|
|
|
260
263
|
### Build it from elements
|
|
261
264
|
|
|
262
|
-
The wrapper renders a
|
|
265
|
+
The wrapper renders a `<Button>` immediately followed by a `<Menu>`, with a label
|
|
266
|
+
and hint inside an `<a-select-field>` layout wrapper. It updates the field
|
|
263
267
|
and each option's `selected` state from the value, and reports a pick through
|
|
264
268
|
`onValueChange`. Menu handles positioning, dismissal, and keyboard navigation.
|
|
265
269
|
|
|
266
270
|
The following example shows the equivalent markup without React or Preact. Place
|
|
267
|
-
the Menu directly after the
|
|
271
|
+
the Menu directly after the Button so it can use that Button as its trigger.
|
|
268
272
|
|
|
269
273
|
```html
|
|
270
274
|
<!-- `.select` scopes this example's CSS. Use an application selector instead. -->
|
|
271
275
|
<div class="select" style="width: 240px">
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
<a-icon slot="trailing" shape="chevron-down" style="transition: transform 150ms ease"></a-icon>
|
|
277
|
-
</a-input>
|
|
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>
|
|
278
280
|
<a-menu role="menu">
|
|
279
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>
|
|
280
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>
|
|
@@ -284,12 +286,14 @@ the Menu directly after the Input so it can use that Input as its trigger.
|
|
|
284
286
|
<script type="module">
|
|
285
287
|
import '@antadesign/anta/elements'
|
|
286
288
|
const root = document.querySelector('.select')
|
|
287
|
-
const field = root.querySelector('a-
|
|
289
|
+
const field = root.querySelector('a-button')
|
|
288
290
|
const menu = root.querySelector('a-menu')
|
|
289
291
|
const chevron = field.querySelector('a-icon')
|
|
290
292
|
// Flip the chevron with the menu's open state (as <Select> does).
|
|
291
293
|
menu.addEventListener('statechange', (e) => {
|
|
292
|
-
|
|
294
|
+
const open = e.detail.next === 'open'
|
|
295
|
+
field.setAttribute('aria-expanded', String(open))
|
|
296
|
+
chevron.style.transform = open ? 'rotate(180deg)' : ''
|
|
293
297
|
})
|
|
294
298
|
// On pick: mark the row selected + reflect its label into the field.
|
|
295
299
|
menu.addEventListener('click', (e) => {
|
|
@@ -300,12 +304,16 @@ the Menu directly after the Input so it can use that Input as its trigger.
|
|
|
300
304
|
el.toggleAttribute('selected', selected)
|
|
301
305
|
el.setAttribute('aria-checked', String(selected))
|
|
302
306
|
})
|
|
303
|
-
field.
|
|
307
|
+
field.querySelector('a-button-label').textContent = item.querySelector('a-menu-item-label').textContent
|
|
304
308
|
})
|
|
305
309
|
</script>
|
|
306
310
|
```
|
|
307
311
|
|
|
308
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.
|
|
309
317
|
|
|
310
318
|
### Custom option rendering
|
|
311
319
|
|
|
@@ -461,7 +469,7 @@ empty selection still shows the `placeholder` — and takes the resolved `select
|
|
|
461
469
|
options (`selected.length` is the count). Return `undefined` for any case you'd rather
|
|
462
470
|
leave to the default, as above for the single-label case.
|
|
463
471
|
|
|
464
|
-
Return a **string**: it flows into the same
|
|
472
|
+
Return a **string**: it flows into the same Button label the default uses, so a
|
|
465
473
|
long summary **ellipsizes at the field's width** the way a long value does
|
|
466
474
|
(`Engineering, Design, … `). For rich content — chips, several nodes — reach for
|
|
467
475
|
`renderTrigger` below, which replaces the whole field.
|
|
@@ -523,7 +531,8 @@ focusable element, such as the Anta `Button` in this example. Select positions
|
|
|
523
531
|
the menu relative to that element and opens it when it is clicked. A fragment,
|
|
524
532
|
multiple sibling elements, or a non-focusable wrapper prevents Select from finding
|
|
525
533
|
the trigger. It logs a console warning when that happens. Add
|
|
526
|
-
`aria-haspopup=
|
|
534
|
+
`aria-haspopup={filter ? 'dialog' : 'menu'}` and
|
|
535
|
+
`aria-expanded={state.open}` to the element.
|
|
527
536
|
|
|
528
537
|
`state` includes `open`, `value`, `selected`, `disabled`, and `icon`. `selected`
|
|
529
538
|
is the resolved option list, so `selected.length` is the multi-select count. The
|
|
@@ -633,11 +642,11 @@ scannable list instead of hiding matches behind flyouts.
|
|
|
633
642
|
| `defaultValue?` | V \| V[] | — | Initial selected option value for uncontrolled use. |
|
|
634
643
|
| `disabled?` | boolean | — | Disable the whole select. |
|
|
635
644
|
| `filter?` | boolean \| (option, query) => boolean | — | Add a search field at the top of the menu that filters the options as you type. `true` uses the built-in matcher — a case-insensitive substring of the option's **value / label / hint**. Pass a **function** `(option, query) => boolean` for custom matching (called per option; return `true` to keep it). |
|
|
636
|
-
| `hint?` | string | — | Helper text under the field
|
|
637
|
-
| `icon?` | IconShape | — | Leading icon shown at the left of the field
|
|
645
|
+
| `hint?` | string | — | Helper text under the field. |
|
|
646
|
+
| `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. |
|
|
638
647
|
| `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. |
|
|
639
|
-
| `label?` | string | — | Field label, above the trigger
|
|
640
|
-
| `leading?` | ReactNode | — | Content
|
|
648
|
+
| `label?` | string | — | Field label, above the trigger. |
|
|
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`. |
|
|
641
650
|
| `offset?` | number | 4 | Gap in pixels between the trigger and the options menu. |
|
|
642
651
|
| `onValueChange?` | (value, attrs) => void | — | Fires after the selection changes, with the new value and a `{ value, option }` snapshot. Select has no discrete element state, so there is no cancelable `onStateChange` (see the Input event-model note). |
|
|
643
652
|
| `placeholder?` | string | — | Text shown when nothing is selected. |
|
|
@@ -645,19 +654,19 @@ scannable list instead of hiding matches behind flyouts.
|
|
|
645
654
|
| `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. |
|
|
646
655
|
| `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`. |
|
|
647
656
|
| `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. |
|
|
648
|
-
| `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 flows into the default trigger's
|
|
649
|
-
| `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=
|
|
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 flows into the default trigger's Button label, 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
|
+
| `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. |
|
|
650
659
|
| `round?` | boolean \| number \| string | — | Round the field corners — `true` for fully round, or a number / CSS length. |
|
|
651
660
|
| `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. |
|
|
652
661
|
| `selectAllLabel?` | string | Select all | Label for the `selectAll` row. |
|
|
653
662
|
| `selection?` | 'single' \| 'multiple' | single | Selection mode. `'single'` (the default) keeps `value` a single value and closes the menu on pick. Switch to `'multiple'` for checkboxes + an array value. |
|
|
654
663
|
| `size?` | 'small' \| 'medium' \| 'large' | medium | Field size. |
|
|
655
|
-
| `status?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' | neutral | Validation/feedback tone for the field
|
|
656
|
-
| `statusIcon?` | (string & {}) \| false \| IconShape | — | Glyph shown before the `hint` when `status` is set
|
|
664
|
+
| `status?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' | neutral | Validation/feedback tone for the field. |
|
|
665
|
+
| `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. |
|
|
657
666
|
| `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. |
|
|
658
|
-
| `toneScope?` |
|
|
667
|
+
| `toneScope?` | 'all' \| 'selected' | 'all' | Apply the default row tone in every state, or only to selected rows. An option's own `toneScope` wins. |
|
|
659
668
|
| `value?` | V \| V[] | — | Controlled value: the selected option's `value`. Update it through `onValueChange`. Leave it undefined for uncontrolled use. |
|
|
660
|
-
| `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
|
|
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 Button label, so it ellipsizes at the field's width when long (`3 selected: Engineering, Des… `). `renderSummary` overrides this. |
|
|
661
670
|
|
|
662
671
|
### The `SelectItem` type
|
|
663
672
|
|
|
@@ -757,16 +766,16 @@ inline `SelectGroup`, and a flyout `SelectSubmenu` (whose items are themselves
|
|
|
757
766
|
|
|
758
767
|
Use the web component directly when you are not using React or Preact and a native control does not fit.
|
|
759
768
|
|
|
760
|
-
`Select`
|
|
769
|
+
`Select` uses a layout wrapper. Compose an `<a-button>` trigger with an
|
|
761
770
|
`<a-menu>` of options. The menu owns opening and dismissal; the short controller
|
|
762
771
|
reflects a picked option into the field and selected row.
|
|
763
772
|
|
|
764
773
|
```html
|
|
765
774
|
<div data-anta-composition="select" style="display: grid; gap: 4px; width: 280px">
|
|
766
|
-
<a-
|
|
767
|
-
<
|
|
768
|
-
<
|
|
769
|
-
</a-
|
|
775
|
+
<a-button role="button" tabindex="0" aria-label="Output" aria-haspopup="menu" aria-expanded="false">
|
|
776
|
+
<a-button-label>stream</a-button-label>
|
|
777
|
+
<a-icon shape="chevron-down" aria-hidden="true"></a-icon>
|
|
778
|
+
</a-button>
|
|
770
779
|
<a-menu role="menu">
|
|
771
780
|
<a-menu-item role="menuitem" tabindex="0" value="output_text"><a-menu-item-label>output_text</a-menu-item-label></a-menu-item>
|
|
772
781
|
<a-menu-item role="menuitem" tabindex="0" value="stream" selected><a-menu-item-label>stream</a-menu-item-label></a-menu-item>
|
|
@@ -776,8 +785,11 @@ reflects a picked option into the field and selected row.
|
|
|
776
785
|
import '@antadesign/anta/elements'
|
|
777
786
|
|
|
778
787
|
const root = document.querySelector('[data-anta-composition="select"]')
|
|
779
|
-
const field = root.querySelector('a-
|
|
788
|
+
const field = root.querySelector('a-button')
|
|
780
789
|
const menu = root.querySelector('a-menu')
|
|
790
|
+
menu.addEventListener('statechange', (event) => {
|
|
791
|
+
field.setAttribute('aria-expanded', String(event.detail.next === 'open'))
|
|
792
|
+
})
|
|
781
793
|
|
|
782
794
|
menu.addEventListener('click', (event) => {
|
|
783
795
|
const item = event.target instanceof Element ? event.target.closest('a-menu-item') : null
|
|
@@ -786,7 +798,7 @@ reflects a picked option into the field and selected row.
|
|
|
786
798
|
const selected = option === item
|
|
787
799
|
option.toggleAttribute('selected', selected)
|
|
788
800
|
})
|
|
789
|
-
field.
|
|
801
|
+
field.querySelector('a-button-label').textContent = item.querySelector('a-menu-item-label').textContent.trim()
|
|
790
802
|
})
|
|
791
803
|
</script>
|
|
792
804
|
```
|
|
@@ -824,17 +836,18 @@ corners.
|
|
|
824
836
|
|
|
825
837
|
## Styling
|
|
826
838
|
|
|
827
|
-
Select
|
|
828
|
-
inherits both surfaces' hooks. Reach for props first, then plain CSS or `::part` for
|
|
839
|
+
Select composes an [Anta Button](./button.md) and a [Menu](./menu.md). Reach for props first, then plain CSS or `::part` for
|
|
829
840
|
the rest. Don't override an element's internal `--*` output tokens.
|
|
830
841
|
|
|
831
842
|
**Selection color** routes through `tone`: set it per option or on the Select as a
|
|
832
843
|
default, then use `toneScope="selected"` for chosen rows only. It takes a named tone or any CSS color (a custom color keeps
|
|
833
844
|
its hue, with lightness pinned to the brand text).
|
|
834
845
|
|
|
835
|
-
**The field**
|
|
836
|
-
|
|
837
|
-
|
|
846
|
+
**The field** takes `size`, `status`, and `round`. Set its width with `style` /
|
|
847
|
+
`className`, forwarded to the `<a-select-field>` wrapper. Style the trigger through
|
|
848
|
+
`a-select-field > a-button` and its `a-button-label`. The trigger uses
|
|
849
|
+
`:focus-visible` for its focus ring and ellipsizes long values. **The popover**
|
|
850
|
+
takes Menu's `::part(menu)`.
|
|
838
851
|
|
|
839
852
|
```tsx
|
|
840
853
|
// Tone the selection; give the trigger a width.
|
|
@@ -844,16 +857,15 @@ popover** takes Menu's `::part(menu)`.
|
|
|
844
857
|
**Borderless trigger.** The field's border is an inset `box-shadow`, so you can drop
|
|
845
858
|
it *at rest* and let the standard hover / focus shadow return on its own; no need to
|
|
846
859
|
re-declare it. Pair that with hiding the chevron for a field that reads as plain text
|
|
847
|
-
until you reach for it. Hide the chevron by targeting
|
|
848
|
-
wrapper is inline-`display:contents`, so hit the icon itself).
|
|
860
|
+
until you reach for it. Hide the chevron by targeting `a-select-chevron`.
|
|
849
861
|
|
|
850
862
|
```css
|
|
851
863
|
/* Drop the resting border-shadow; the standard hover / focus shadow still shows. */
|
|
852
|
-
.ghost-select a-
|
|
864
|
+
.ghost-select a-select-field > a-button:not(:hover):not(:focus-visible) {
|
|
853
865
|
box-shadow: none;
|
|
854
866
|
}
|
|
855
867
|
/* Center the chosen label in the field. */
|
|
856
|
-
.ghost-select a-
|
|
857
|
-
/*
|
|
858
|
-
.ghost-select a-
|
|
868
|
+
.ghost-select a-select-field > a-button > a-button-label { text-align: center; }
|
|
869
|
+
/* Hide the chevron. */
|
|
870
|
+
.ghost-select a-select-chevron { display: none; }
|
|
859
871
|
```
|