@cfasim-ui/docs 0.5.1 → 0.6.1
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/charts/BarChart/BarChart.md +3 -0
- package/charts/BarChart/BarChart.vue +342 -317
- package/charts/ChartMenu/ChartMenu.vue +75 -5
- package/charts/ChoroplethMap/ChoroplethMap.md +5 -0
- package/charts/ChoroplethMap/ChoroplethMap.vue +193 -88
- package/charts/LineChart/LineChart.md +87 -5
- package/charts/LineChart/LineChart.vue +405 -367
- package/charts/_shared/chartProps.ts +10 -0
- package/charts/_shared/useChartFoundation.ts +15 -0
- package/charts/_shared/useChartFullscreen.ts +66 -9
- package/charts/_shared/useChartMenu.ts +13 -6
- package/charts/_shared/useChartTooltip.ts +57 -4
- package/charts/index.ts +0 -2
- package/components/ButtonGroup/ButtonGroup.md +64 -0
- package/components/ButtonGroup/ButtonGroup.vue +91 -0
- package/components/MultiSelect/MultiSelect.md +143 -0
- package/components/MultiSelect/MultiSelect.vue +338 -0
- package/components/ParamEditor/ParamEditor.md +19 -0
- package/components/ParamEditor/ParamEditor.vue +4 -0
- package/components/ParamEditor/ParamEditorImpl.vue +3 -3
- package/components/SelectBox/SelectBox.md +72 -1
- package/components/SelectBox/SelectBox.vue +157 -1
- package/components/ToggleGroup/ToggleGroup.md +163 -0
- package/components/ToggleGroup/ToggleGroup.vue +143 -0
- package/components/index.ts +5 -0
- package/index.json +56 -2
- package/package.json +1 -1
- package/charts/_shared/fullscreen.css +0 -51
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
ComboboxAnchor,
|
|
4
|
+
ComboboxContent,
|
|
5
|
+
ComboboxEmpty,
|
|
6
|
+
ComboboxInput,
|
|
7
|
+
ComboboxItem,
|
|
8
|
+
ComboboxItemIndicator,
|
|
9
|
+
ComboboxPortal,
|
|
10
|
+
ComboboxRoot,
|
|
11
|
+
ComboboxTrigger,
|
|
12
|
+
ComboboxViewport,
|
|
13
|
+
useId,
|
|
14
|
+
} from "reka-ui";
|
|
15
|
+
import { computed, nextTick, ref, watch } from "vue";
|
|
16
|
+
import Icon from "../Icon/Icon.vue";
|
|
17
|
+
import Hint from "../Hint/Hint.vue";
|
|
18
|
+
|
|
19
|
+
export interface MultiSelectOption {
|
|
20
|
+
value: string;
|
|
21
|
+
label: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const model = defineModel<string[]>({ default: () => [] });
|
|
25
|
+
|
|
26
|
+
const props = defineProps<{
|
|
27
|
+
label?: string;
|
|
28
|
+
hideLabel?: boolean;
|
|
29
|
+
ariaLabel?: string;
|
|
30
|
+
options: MultiSelectOption[];
|
|
31
|
+
placeholder?: string;
|
|
32
|
+
hint?: string;
|
|
33
|
+
}>();
|
|
34
|
+
|
|
35
|
+
const id = useId();
|
|
36
|
+
const fieldRef = ref<HTMLElement | null>(null);
|
|
37
|
+
|
|
38
|
+
// In multiple mode the list stays open after a selection, so reka keeps
|
|
39
|
+
// `isUserInputted` true and suppresses its own `resetSearchTermOnSelect`. Clear
|
|
40
|
+
// the typed text (and the underlying filter) ourselves on every selection
|
|
41
|
+
// change by dispatching a native input event the combobox listens for.
|
|
42
|
+
watch(model, () => {
|
|
43
|
+
nextTick(() => {
|
|
44
|
+
const input = fieldRef.value?.querySelector("input");
|
|
45
|
+
if (input?.value) {
|
|
46
|
+
input.value = "";
|
|
47
|
+
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const selectedOptions = computed(() =>
|
|
53
|
+
model.value
|
|
54
|
+
.map((v) => props.options.find((o) => o.value === v))
|
|
55
|
+
.filter((o): o is MultiSelectOption => Boolean(o)),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
function remove(value: string) {
|
|
59
|
+
model.value = model.value.filter((v) => v !== value);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Backspace in an empty input removes the last selected chip.
|
|
63
|
+
function onInputKeydown(event: KeyboardEvent) {
|
|
64
|
+
const target = event.target as HTMLInputElement;
|
|
65
|
+
if (event.key === "Backspace" && !target.value && model.value.length) {
|
|
66
|
+
remove(model.value[model.value.length - 1]);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<template>
|
|
72
|
+
<div class="multi-select">
|
|
73
|
+
<label
|
|
74
|
+
v-if="label"
|
|
75
|
+
:id="`${id}-label`"
|
|
76
|
+
class="multi-select-label"
|
|
77
|
+
:class="{ 'visually-hidden': hideLabel }"
|
|
78
|
+
>
|
|
79
|
+
{{ label }}
|
|
80
|
+
<Hint v-if="hint && !hideLabel" :text="hint" />
|
|
81
|
+
</label>
|
|
82
|
+
<ComboboxRoot
|
|
83
|
+
v-model="model"
|
|
84
|
+
multiple
|
|
85
|
+
open-on-click
|
|
86
|
+
class="multi-select-root"
|
|
87
|
+
>
|
|
88
|
+
<ComboboxAnchor class="multi-select-anchor">
|
|
89
|
+
<div ref="fieldRef" class="multi-select-field">
|
|
90
|
+
<ul v-if="selectedOptions.length" class="multi-select-chips">
|
|
91
|
+
<li
|
|
92
|
+
v-for="opt in selectedOptions"
|
|
93
|
+
:key="opt.value"
|
|
94
|
+
class="multi-select-chip"
|
|
95
|
+
>
|
|
96
|
+
<span class="multi-select-chip-label">{{ opt.label }}</span>
|
|
97
|
+
<button
|
|
98
|
+
type="button"
|
|
99
|
+
class="multi-select-chip-remove"
|
|
100
|
+
:aria-label="`Remove ${opt.label}`"
|
|
101
|
+
@click="remove(opt.value)"
|
|
102
|
+
>
|
|
103
|
+
<Icon icon="close" :size="14" />
|
|
104
|
+
</button>
|
|
105
|
+
</li>
|
|
106
|
+
</ul>
|
|
107
|
+
<ComboboxInput
|
|
108
|
+
class="multi-select-input"
|
|
109
|
+
:placeholder="selectedOptions.length ? undefined : placeholder"
|
|
110
|
+
:aria-labelledby="label ? `${id}-label` : undefined"
|
|
111
|
+
:aria-label="!label ? ariaLabel : undefined"
|
|
112
|
+
@keydown="onInputKeydown"
|
|
113
|
+
/>
|
|
114
|
+
</div>
|
|
115
|
+
<ComboboxTrigger
|
|
116
|
+
class="multi-select-trigger"
|
|
117
|
+
aria-label="Toggle options"
|
|
118
|
+
>
|
|
119
|
+
<span class="multi-select-icon" aria-hidden="true">
|
|
120
|
+
<svg
|
|
121
|
+
width="12"
|
|
122
|
+
height="12"
|
|
123
|
+
viewBox="0 0 12 12"
|
|
124
|
+
fill="none"
|
|
125
|
+
stroke="currentColor"
|
|
126
|
+
stroke-width="2"
|
|
127
|
+
stroke-linecap="round"
|
|
128
|
+
stroke-linejoin="round"
|
|
129
|
+
>
|
|
130
|
+
<path d="M3 4.5L6 7.5L9 4.5" />
|
|
131
|
+
</svg>
|
|
132
|
+
</span>
|
|
133
|
+
</ComboboxTrigger>
|
|
134
|
+
</ComboboxAnchor>
|
|
135
|
+
<ComboboxPortal>
|
|
136
|
+
<ComboboxContent
|
|
137
|
+
class="multi-select-content"
|
|
138
|
+
position="popper"
|
|
139
|
+
:side-offset="4"
|
|
140
|
+
:body-lock="false"
|
|
141
|
+
>
|
|
142
|
+
<ComboboxViewport class="multi-select-viewport">
|
|
143
|
+
<ComboboxEmpty class="multi-select-empty">No matches</ComboboxEmpty>
|
|
144
|
+
<ComboboxItem
|
|
145
|
+
v-for="opt in options"
|
|
146
|
+
:key="opt.value"
|
|
147
|
+
:value="opt.value"
|
|
148
|
+
class="multi-select-item"
|
|
149
|
+
>
|
|
150
|
+
<span>{{ opt.label }}</span>
|
|
151
|
+
<ComboboxItemIndicator class="multi-select-indicator">
|
|
152
|
+
<Icon icon="check" :size="14" />
|
|
153
|
+
</ComboboxItemIndicator>
|
|
154
|
+
</ComboboxItem>
|
|
155
|
+
</ComboboxViewport>
|
|
156
|
+
</ComboboxContent>
|
|
157
|
+
</ComboboxPortal>
|
|
158
|
+
</ComboboxRoot>
|
|
159
|
+
</div>
|
|
160
|
+
</template>
|
|
161
|
+
|
|
162
|
+
<style scoped>
|
|
163
|
+
.multi-select {
|
|
164
|
+
display: flex;
|
|
165
|
+
flex-direction: column;
|
|
166
|
+
gap: 0.25em;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.multi-select-label {
|
|
170
|
+
display: flex;
|
|
171
|
+
align-items: center;
|
|
172
|
+
justify-content: space-between;
|
|
173
|
+
font-size: var(--font-size-sm);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.multi-select-anchor {
|
|
177
|
+
display: flex;
|
|
178
|
+
align-items: center;
|
|
179
|
+
gap: 0.25em;
|
|
180
|
+
min-height: 2.5em;
|
|
181
|
+
padding: 0.25em 0.5em;
|
|
182
|
+
font-size: var(--font-size-sm);
|
|
183
|
+
background: var(--color-bg-0);
|
|
184
|
+
border: 1px solid var(--color-border);
|
|
185
|
+
border-radius: 0.375em;
|
|
186
|
+
transition:
|
|
187
|
+
border-color var(--transition-fast),
|
|
188
|
+
box-shadow var(--transition-fast);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.multi-select-anchor:hover {
|
|
192
|
+
border-color: var(--color-border-hover);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.multi-select-anchor:focus-within {
|
|
196
|
+
outline: none;
|
|
197
|
+
border-color: var(--color-border-focus);
|
|
198
|
+
box-shadow: var(--shadow-focus);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.multi-select-field {
|
|
202
|
+
display: flex;
|
|
203
|
+
flex-wrap: wrap;
|
|
204
|
+
align-items: center;
|
|
205
|
+
gap: 0.25em;
|
|
206
|
+
flex: 1;
|
|
207
|
+
min-width: 0;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.multi-select-chips {
|
|
211
|
+
display: contents;
|
|
212
|
+
list-style: none;
|
|
213
|
+
margin: 0;
|
|
214
|
+
padding: 0;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.multi-select-chip {
|
|
218
|
+
display: inline-flex;
|
|
219
|
+
align-items: center;
|
|
220
|
+
gap: 0.25em;
|
|
221
|
+
/* Reset list margins (e.g. a consumer's `li + li` spacing) so chips line up. */
|
|
222
|
+
margin: 0;
|
|
223
|
+
padding: 0.125em 0.25em 0.125em 0.5em;
|
|
224
|
+
background: var(--color-bg-2);
|
|
225
|
+
border: 1px solid var(--color-border);
|
|
226
|
+
border-radius: 0.25em;
|
|
227
|
+
line-height: 1.4;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.multi-select-chip-remove {
|
|
231
|
+
display: inline-flex;
|
|
232
|
+
align-items: center;
|
|
233
|
+
padding: 0;
|
|
234
|
+
border: none;
|
|
235
|
+
background: none;
|
|
236
|
+
color: var(--color-text-secondary);
|
|
237
|
+
cursor: pointer;
|
|
238
|
+
border-radius: 0.25em;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.multi-select-chip-remove:hover {
|
|
242
|
+
color: var(--color-text);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.multi-select-chip-remove:focus-visible {
|
|
246
|
+
outline: 2px solid var(--color-primary);
|
|
247
|
+
outline-offset: 1px;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.multi-select-input {
|
|
251
|
+
flex: 1;
|
|
252
|
+
min-width: 3em;
|
|
253
|
+
height: 1.75em;
|
|
254
|
+
padding: 0;
|
|
255
|
+
border: none;
|
|
256
|
+
background: none;
|
|
257
|
+
color: var(--color-text);
|
|
258
|
+
font: inherit;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.multi-select-input:focus {
|
|
262
|
+
outline: none;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.multi-select-input::placeholder {
|
|
266
|
+
color: var(--color-text-tertiary);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.multi-select-trigger {
|
|
270
|
+
display: inline-flex;
|
|
271
|
+
align-items: center;
|
|
272
|
+
flex-shrink: 0;
|
|
273
|
+
padding: 0;
|
|
274
|
+
border: none;
|
|
275
|
+
background: none;
|
|
276
|
+
color: inherit;
|
|
277
|
+
cursor: pointer;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.multi-select-icon {
|
|
281
|
+
display: flex;
|
|
282
|
+
align-items: center;
|
|
283
|
+
}
|
|
284
|
+
</style>
|
|
285
|
+
|
|
286
|
+
<style>
|
|
287
|
+
.multi-select-content {
|
|
288
|
+
z-index: 100;
|
|
289
|
+
background: var(--color-bg-0);
|
|
290
|
+
border: 1px solid var(--color-border);
|
|
291
|
+
border-radius: 0.25em;
|
|
292
|
+
box-shadow:
|
|
293
|
+
0 4px 6px -1px rgba(0, 0, 0, 0.1),
|
|
294
|
+
0 2px 4px -2px rgba(0, 0, 0, 0.1);
|
|
295
|
+
width: var(--reka-combobox-trigger-width);
|
|
296
|
+
max-height: var(--reka-combobox-content-available-height);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.multi-select-viewport {
|
|
300
|
+
padding: 0.25em;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.multi-select-empty {
|
|
304
|
+
padding: 0.5em;
|
|
305
|
+
font-size: var(--font-size-sm);
|
|
306
|
+
color: var(--color-text-secondary);
|
|
307
|
+
text-align: center;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.multi-select-item {
|
|
311
|
+
display: flex;
|
|
312
|
+
align-items: center;
|
|
313
|
+
justify-content: space-between;
|
|
314
|
+
gap: 0.5em;
|
|
315
|
+
padding: 0.25em 0.5em;
|
|
316
|
+
border-radius: 0.25em;
|
|
317
|
+
font-size: var(--font-size-sm);
|
|
318
|
+
white-space: nowrap;
|
|
319
|
+
cursor: pointer;
|
|
320
|
+
user-select: none;
|
|
321
|
+
outline: none;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.multi-select-item[data-highlighted] {
|
|
325
|
+
background: var(--color-primary);
|
|
326
|
+
color: white;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.multi-select-item[data-state="checked"] {
|
|
330
|
+
font-weight: 600;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.multi-select-indicator {
|
|
334
|
+
display: flex;
|
|
335
|
+
align-items: center;
|
|
336
|
+
flex-shrink: 0;
|
|
337
|
+
}
|
|
338
|
+
</style>
|
|
@@ -8,6 +8,8 @@ The editor is **lazy-loaded**: the underlying CodeMirror bundle and YAML/TOML pa
|
|
|
8
8
|
|
|
9
9
|
Users can switch formats from the dropdown; the editor round-trips the current value through the new format. Apply emits an `apply` event with the parsed value — the parent decides what to do with it (typically merge back into its own parameter state).
|
|
10
10
|
|
|
11
|
+
Set the editor font size with the `fontSize` prop (any CSS length, e.g. `"16px"` or `"1rem"`); it defaults to `var(--font-size-sm)`.
|
|
12
|
+
|
|
11
13
|
## Examples
|
|
12
14
|
|
|
13
15
|
<script setup>
|
|
@@ -61,6 +63,22 @@ function onApply(v) {
|
|
|
61
63
|
</template>
|
|
62
64
|
</ComponentDemo>
|
|
63
65
|
|
|
66
|
+
### Custom font size
|
|
67
|
+
|
|
68
|
+
<ComponentDemo>
|
|
69
|
+
<div style="width: 100%">
|
|
70
|
+
<ParamEditor :value="params" font-size="16px" @apply="onApply" />
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<template #code>
|
|
74
|
+
|
|
75
|
+
```vue
|
|
76
|
+
<ParamEditor :value="params" font-size="16px" @apply="onApply" />
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
</template>
|
|
80
|
+
</ComponentDemo>
|
|
81
|
+
|
|
64
82
|
## Props
|
|
65
83
|
|
|
66
84
|
| Prop | Type | Required | Default |
|
|
@@ -68,6 +86,7 @@ function onApply(v) {
|
|
|
68
86
|
| `value` | `ParamEditorValue` | Yes | — |
|
|
69
87
|
| `format` | `ParamEditorFormat` | No | — |
|
|
70
88
|
| `height` | `string` | No | — |
|
|
89
|
+
| `fontSize` | `string` | No | — |
|
|
71
90
|
| `filename` | `string` | No | — |
|
|
72
91
|
|
|
73
92
|
## Events
|
|
@@ -9,6 +9,9 @@ defineProps<{
|
|
|
9
9
|
value: ParamEditorValue;
|
|
10
10
|
format?: ParamEditorFormat;
|
|
11
11
|
height?: string;
|
|
12
|
+
/** Editor font size, as any CSS length (e.g. `"16px"`, `"1rem"`).
|
|
13
|
+
* Defaults to `var(--font-size-sm)`. */
|
|
14
|
+
fontSize?: string;
|
|
12
15
|
/** Basename for the Export download (no extension). Defaults to
|
|
13
16
|
* `params-YYYYMMDD-HHMMSS` computed at click time. */
|
|
14
17
|
filename?: string;
|
|
@@ -33,6 +36,7 @@ const Impl = defineAsyncComponent({
|
|
|
33
36
|
:value="value"
|
|
34
37
|
:format="format"
|
|
35
38
|
:height="height"
|
|
39
|
+
:font-size="fontSize"
|
|
36
40
|
:filename="filename"
|
|
37
41
|
@apply="$emit('apply', $event)"
|
|
38
42
|
/>
|
|
@@ -27,9 +27,10 @@ const props = withDefaults(
|
|
|
27
27
|
value: Value;
|
|
28
28
|
format?: Format;
|
|
29
29
|
height?: string;
|
|
30
|
+
fontSize?: string;
|
|
30
31
|
filename?: string;
|
|
31
32
|
}>(),
|
|
32
|
-
{ format: "json", height: "320px" },
|
|
33
|
+
{ format: "json", height: "320px", fontSize: "var(--font-size-sm)" },
|
|
33
34
|
);
|
|
34
35
|
|
|
35
36
|
const emit = defineEmits<{ apply: [value: Value] }>();
|
|
@@ -264,7 +265,7 @@ async function onFileChange(e: Event) {
|
|
|
264
265
|
<Codemirror
|
|
265
266
|
v-model="text"
|
|
266
267
|
:extensions="extensions"
|
|
267
|
-
:style="{ height }"
|
|
268
|
+
:style="{ height, fontSize }"
|
|
268
269
|
:indent-with-tab="false"
|
|
269
270
|
:tab-size="2"
|
|
270
271
|
class="param-editor-cm"
|
|
@@ -317,7 +318,6 @@ async function onFileChange(e: Event) {
|
|
|
317
318
|
.param-editor-cm {
|
|
318
319
|
border: 1px solid var(--color-border);
|
|
319
320
|
border-radius: 0.375em;
|
|
320
|
-
font-size: var(--font-size-sm);
|
|
321
321
|
overflow: hidden;
|
|
322
322
|
}
|
|
323
323
|
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
# SelectBox
|
|
2
2
|
|
|
3
|
-
A dropdown
|
|
3
|
+
A single-select dropdown built on reka-ui. Set `autocomplete` to turn it into a
|
|
4
|
+
filterable single-select combobox: type to narrow the options, just like
|
|
5
|
+
[MultiSelect](./multi-select) but bound to a single `string`.
|
|
4
6
|
|
|
5
7
|
## Examples
|
|
6
8
|
|
|
7
9
|
<script setup>
|
|
8
10
|
import { ref } from 'vue'
|
|
9
11
|
const interval = ref('weekly')
|
|
12
|
+
const state = ref('')
|
|
10
13
|
</script>
|
|
11
14
|
|
|
12
15
|
<ComponentDemo>
|
|
@@ -83,6 +86,73 @@ DOM.
|
|
|
83
86
|
</template>
|
|
84
87
|
</ComponentDemo>
|
|
85
88
|
|
|
89
|
+
### Autocomplete
|
|
90
|
+
|
|
91
|
+
Add `autocomplete` to make the field filterable. Type to narrow the list; the
|
|
92
|
+
selected option's label fills the input. `v-model` is still a single `string`.
|
|
93
|
+
|
|
94
|
+
<ComponentDemo>
|
|
95
|
+
<div style="width: 240px">
|
|
96
|
+
<SelectBox
|
|
97
|
+
v-model="state"
|
|
98
|
+
autocomplete
|
|
99
|
+
label="State"
|
|
100
|
+
placeholder="Search states…"
|
|
101
|
+
:options="[
|
|
102
|
+
{ value: 'ca', label: 'California' },
|
|
103
|
+
{ value: 'ny', label: 'New York' },
|
|
104
|
+
{ value: 'tx', label: 'Texas' },
|
|
105
|
+
{ value: 'wa', label: 'Washington' },
|
|
106
|
+
{ value: 'fl', label: 'Florida' },
|
|
107
|
+
]"
|
|
108
|
+
/>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<template #code>
|
|
112
|
+
|
|
113
|
+
```vue
|
|
114
|
+
<script setup>
|
|
115
|
+
import { ref } from "vue";
|
|
116
|
+
const state = ref("");
|
|
117
|
+
</script>
|
|
118
|
+
|
|
119
|
+
<SelectBox
|
|
120
|
+
v-model="state"
|
|
121
|
+
autocomplete
|
|
122
|
+
label="State"
|
|
123
|
+
placeholder="Search states…"
|
|
124
|
+
:options="[
|
|
125
|
+
{ value: 'ca', label: 'California' },
|
|
126
|
+
{ value: 'ny', label: 'New York' },
|
|
127
|
+
{ value: 'tx', label: 'Texas' },
|
|
128
|
+
{ value: 'wa', label: 'Washington' },
|
|
129
|
+
{ value: 'fl', label: 'Florida' },
|
|
130
|
+
]"
|
|
131
|
+
/>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
</template>
|
|
135
|
+
</ComponentDemo>
|
|
136
|
+
|
|
137
|
+
## Accessibility
|
|
138
|
+
|
|
139
|
+
In the default mode the field is a reka-ui Select; with `autocomplete` it's a
|
|
140
|
+
reka-ui Combobox following the
|
|
141
|
+
[ARIA combobox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/):
|
|
142
|
+
|
|
143
|
+
- The input has `role="combobox"` with `aria-expanded`, `aria-controls`, and
|
|
144
|
+
`aria-autocomplete="list"`; the popup is a `role="listbox"` and each option a
|
|
145
|
+
`role="option"` that reflects its state via `aria-selected`.
|
|
146
|
+
- Keyboard support is handled for you: type to filter, ↑/↓ to move through the
|
|
147
|
+
list, Enter to select the highlighted option, and Escape to close.
|
|
148
|
+
- Pass `label` so the field is named by a real `<label>`. In autocomplete mode
|
|
149
|
+
the label is also wired to the input via `for`/`id`, so clicking it focuses
|
|
150
|
+
the field; use `hide-label` to keep the label for screen readers while hiding
|
|
151
|
+
it visually, or `aria-label` when there's no visible label text.
|
|
152
|
+
- The chevron is a labelled toggle (`aria-label="Toggle options"`) kept out of
|
|
153
|
+
the tab order, and native browser autofill is disabled so it can't cover the
|
|
154
|
+
option list.
|
|
155
|
+
|
|
86
156
|
## Model
|
|
87
157
|
|
|
88
158
|
| Name | Type |
|
|
@@ -98,6 +168,7 @@ DOM.
|
|
|
98
168
|
| `ariaLabel` | `string` | No | — |
|
|
99
169
|
| `options` | `SelectOption[]` | Yes | — |
|
|
100
170
|
| `placeholder` | `string` | No | — |
|
|
171
|
+
| `autocomplete` | `boolean` | No | — |
|
|
101
172
|
|
|
102
173
|
|
|
103
174
|
### SelectOption
|