@cfasim-ui/components 0.7.7 → 0.8.0
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/dist/Box/Box.d.ts +7 -11
- package/dist/Button/Button.d.ts +7 -11
- package/dist/ButtonGroup/ButtonGroup.d.ts +8 -12
- package/dist/Container/Container.d.ts +7 -11
- package/dist/Expander/Expander.d.ts +12 -14
- package/dist/Grid/Grid.d.ts +6 -5
- package/dist/Hint/Hint.d.ts +2 -1
- package/dist/Icon/Icon.d.ts +3 -2
- package/dist/LightDarkToggle/LightDarkToggle.d.ts +2 -1
- package/dist/MultiSelect/MultiSelect.d.ts +6 -6
- package/dist/NumberInput/NumberInput.d.ts +13 -11
- package/dist/ParamEditor/ParamEditor.d.ts +3 -2
- package/dist/ParamEditor/ParamEditorImpl.d.ts +3 -5
- package/dist/{ParamEditorImpl-D0xZTyAN.js → ParamEditorImpl-CkElC6PI.js} +3075 -3041
- package/dist/SelectBox/SelectBox.d.ts +8 -6
- package/dist/{SelectBox-CByXZfaC.js → SelectBox-Df8dE2r-.js} +6 -6
- package/dist/SidebarLayout/SidebarLayout.d.ts +21 -20
- package/dist/Spinner/Spinner.d.ts +3 -2
- package/dist/TextInput/TextInput.d.ts +7 -5
- package/dist/Toggle/Toggle.d.ts +8 -6
- package/dist/ToggleGroup/ToggleGroup.d.ts +8 -6
- package/dist/_internal/FieldLabel.d.ts +2 -1
- package/dist/index.js +20 -20
- package/docs/Box.md +49 -0
- package/docs/Button.md +67 -0
- package/docs/ButtonGroup.md +64 -0
- package/docs/Container.md +103 -0
- package/docs/Expander.md +34 -0
- package/docs/Grid.md +170 -0
- package/docs/Hint.md +29 -0
- package/docs/Icon.md +188 -0
- package/docs/MultiSelect.md +143 -0
- package/docs/NumberInput.md +485 -0
- package/docs/ParamEditor.md +97 -0
- package/docs/SelectBox.md +182 -0
- package/docs/SidebarLayout.md +106 -0
- package/docs/Spinner.md +51 -0
- package/docs/TextInput.md +83 -0
- package/docs/Toggle.md +81 -0
- package/docs/ToggleGroup.md +163 -0
- package/docs/index.json +194 -0
- package/package.json +24 -20
- package/src/Box/Box.md +41 -0
- package/src/Box/Box.vue +52 -0
- package/src/Button/Button.md +59 -0
- package/src/Button/Button.vue +81 -0
- package/src/ButtonGroup/ButtonGroup.md +62 -0
- package/src/ButtonGroup/ButtonGroup.vue +91 -0
- package/src/Container/Container.md +99 -0
- package/src/Container/Container.vue +62 -0
- package/src/Expander/Expander.md +23 -0
- package/src/Expander/Expander.vue +95 -0
- package/src/Grid/Grid.md +175 -0
- package/src/Grid/Grid.vue +145 -0
- package/src/Hint/Hint.md +24 -0
- package/src/Hint/Hint.vue +83 -0
- package/src/Icon/Icon.md +176 -0
- package/src/Icon/Icon.vue +104 -0
- package/src/Icon/defaultIcons.ts +92 -0
- package/src/Icon/github.svg +1 -0
- package/src/Icon/registry.ts +68 -0
- package/src/LightDarkToggle/LightDarkToggle.vue +49 -0
- package/src/MultiSelect/MultiSelect.md +142 -0
- package/src/MultiSelect/MultiSelect.vue +279 -0
- package/src/NumberInput/NumberInput.md +455 -0
- package/src/NumberInput/NumberInput.vue +575 -0
- package/src/ParamEditor/ParamEditor.md +87 -0
- package/src/ParamEditor/ParamEditor.vue +43 -0
- package/src/ParamEditor/ParamEditorImpl.vue +358 -0
- package/src/SelectBox/SelectBox.md +169 -0
- package/src/SelectBox/SelectBox.vue +260 -0
- package/src/SidebarLayout/SidebarLayout.md +106 -0
- package/src/SidebarLayout/SidebarLayout.vue +468 -0
- package/src/Spinner/Spinner.md +45 -0
- package/src/Spinner/Spinner.vue +55 -0
- package/src/TextInput/TextInput.md +68 -0
- package/src/TextInput/TextInput.vue +54 -0
- package/src/Toggle/Toggle.md +68 -0
- package/src/Toggle/Toggle.vue +81 -0
- package/src/ToggleGroup/ToggleGroup.md +158 -0
- package/src/ToggleGroup/ToggleGroup.vue +138 -0
- package/src/_internal/FieldLabel.vue +27 -0
- package/src/_internal/field.ts +27 -0
- package/src/_internal/gap.ts +17 -0
- package/src/_internal/input.css +54 -0
- package/src/_internal/listbox.css +53 -0
- package/src/env.d.ts +4 -0
- package/src/index.ts +33 -0
- package/src/svg.d.ts +5 -0
package/docs/Expander.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Expander
|
|
2
|
+
|
|
3
|
+
A collapsible section that toggles content visibility.
|
|
4
|
+
|
|
5
|
+
## Examples
|
|
6
|
+
|
|
7
|
+
<ComponentDemo>
|
|
8
|
+
<Expander label="Show advanced options">
|
|
9
|
+
<p style="margin: 0">Here is the hidden content.</p>
|
|
10
|
+
</Expander>
|
|
11
|
+
|
|
12
|
+
<template #code>
|
|
13
|
+
|
|
14
|
+
```vue
|
|
15
|
+
<Expander label="Show advanced options">
|
|
16
|
+
<p>Here is the hidden content.</p>
|
|
17
|
+
</Expander>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
</template>
|
|
21
|
+
</ComponentDemo>
|
|
22
|
+
|
|
23
|
+
## Model
|
|
24
|
+
|
|
25
|
+
| Name | Type |
|
|
26
|
+
|------|------|
|
|
27
|
+
| `v-model:open` | `boolean` |
|
|
28
|
+
|
|
29
|
+
## Props
|
|
30
|
+
|
|
31
|
+
| Prop | Type | Required | Default |
|
|
32
|
+
|------|------|----------|---------|
|
|
33
|
+
| `label` | `string` | No | — |
|
|
34
|
+
|
package/docs/Grid.md
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Grid
|
|
2
|
+
|
|
3
|
+
A CSS-grid wrapper for arranging elements in equal, proportional, or auto-fitting columns.
|
|
4
|
+
|
|
5
|
+
For single-direction stacks (vertical or a flex row that wraps), use [Container](./container) instead.
|
|
6
|
+
|
|
7
|
+
## Examples
|
|
8
|
+
|
|
9
|
+
### Equal columns
|
|
10
|
+
|
|
11
|
+
Pass a number for N equal-width columns.
|
|
12
|
+
|
|
13
|
+
<ComponentDemo>
|
|
14
|
+
<Grid :cols="3" gap="medium">
|
|
15
|
+
<Box variant="info">Column 1</Box>
|
|
16
|
+
<Box variant="info">Column 2</Box>
|
|
17
|
+
<Box variant="info">Column 3</Box>
|
|
18
|
+
</Grid>
|
|
19
|
+
|
|
20
|
+
<template #code>
|
|
21
|
+
|
|
22
|
+
```vue
|
|
23
|
+
<Grid :cols="3" gap="medium">
|
|
24
|
+
<Box variant="info">Column 1</Box>
|
|
25
|
+
<Box variant="info">Column 2</Box>
|
|
26
|
+
<Box variant="info">Column 3</Box>
|
|
27
|
+
</Grid>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
</template>
|
|
31
|
+
</ComponentDemo>
|
|
32
|
+
|
|
33
|
+
### Proportional widths
|
|
34
|
+
|
|
35
|
+
Pass an array of `fr` weights for asymmetric layouts.
|
|
36
|
+
|
|
37
|
+
<ComponentDemo>
|
|
38
|
+
<Grid :cols="[2, 3, 1]" gap="medium">
|
|
39
|
+
<Box variant="info">2fr</Box>
|
|
40
|
+
<Box variant="success">3fr</Box>
|
|
41
|
+
<Box variant="warning">1fr</Box>
|
|
42
|
+
</Grid>
|
|
43
|
+
|
|
44
|
+
<template #code>
|
|
45
|
+
|
|
46
|
+
```vue
|
|
47
|
+
<Grid :cols="[2, 3, 1]" gap="medium">
|
|
48
|
+
<Box variant="info">2fr</Box>
|
|
49
|
+
<Box variant="success">3fr</Box>
|
|
50
|
+
<Box variant="warning">1fr</Box>
|
|
51
|
+
</Grid>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
</template>
|
|
55
|
+
</ComponentDemo>
|
|
56
|
+
|
|
57
|
+
### Mixed track sizes
|
|
58
|
+
|
|
59
|
+
Strings pass through unchanged, so you can combine fixed and flexible tracks.
|
|
60
|
+
|
|
61
|
+
<ComponentDemo>
|
|
62
|
+
<Grid :cols="['200px', '1fr']" gap="medium">
|
|
63
|
+
<Box variant="info">Fixed 200px</Box>
|
|
64
|
+
<Box variant="info">Fills remaining space</Box>
|
|
65
|
+
</Grid>
|
|
66
|
+
|
|
67
|
+
<template #code>
|
|
68
|
+
|
|
69
|
+
```vue
|
|
70
|
+
<Grid :cols="['200px', '1fr']" gap="medium">
|
|
71
|
+
<Box variant="info">Fixed 200px</Box>
|
|
72
|
+
<Box variant="info">Fills remaining space</Box>
|
|
73
|
+
</Grid>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
</template>
|
|
77
|
+
</ComponentDemo>
|
|
78
|
+
|
|
79
|
+
### Small-width breakpoint
|
|
80
|
+
|
|
81
|
+
`colsSmall` overrides `cols` when the grid's own width is at or below `breakpoint`. The default breakpoint is `640px` unless you specify it. The check is a CSS container query against the grid itself, so it triggers based on the grid's available width (e.g. when nested inside a sidebar), not the viewport.
|
|
82
|
+
|
|
83
|
+
<ComponentDemo>
|
|
84
|
+
<Grid :cols="3" :cols-small="1" breakpoint="480px" gap="small">
|
|
85
|
+
<Box variant="info">Card 1</Box>
|
|
86
|
+
<Box variant="info">Card 2</Box>
|
|
87
|
+
<Box variant="info">Card 3</Box>
|
|
88
|
+
</Grid>
|
|
89
|
+
|
|
90
|
+
<template #code>
|
|
91
|
+
|
|
92
|
+
```vue
|
|
93
|
+
<Grid :cols="3" :cols-small="1" breakpoint="480px" gap="small">
|
|
94
|
+
<Box variant="info">Card 1</Box>
|
|
95
|
+
<Box variant="info">Card 2</Box>
|
|
96
|
+
<Box variant="info">Card 3</Box>
|
|
97
|
+
</Grid>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
</template>
|
|
101
|
+
</ComponentDemo>
|
|
102
|
+
|
|
103
|
+
### Responsive auto-fit
|
|
104
|
+
|
|
105
|
+
`minColWidth` switches to `repeat(auto-fit, minmax(...))` so items reflow to fit the viewport without media queries. Great for metric tiles and card grids.
|
|
106
|
+
|
|
107
|
+
<ComponentDemo>
|
|
108
|
+
<Grid min-col-width="180px" gap="medium">
|
|
109
|
+
<Box variant="info">Card 1</Box>
|
|
110
|
+
<Box variant="info">Card 2</Box>
|
|
111
|
+
<Box variant="info">Card 3</Box>
|
|
112
|
+
<Box variant="info">Card 4</Box>
|
|
113
|
+
<Box variant="info">Card 5</Box>
|
|
114
|
+
</Grid>
|
|
115
|
+
|
|
116
|
+
<template #code>
|
|
117
|
+
|
|
118
|
+
```vue
|
|
119
|
+
<Grid min-col-width="180px" gap="medium">
|
|
120
|
+
<Box variant="info">Card 1</Box>
|
|
121
|
+
<Box variant="info">Card 2</Box>
|
|
122
|
+
<Box variant="info">Card 3</Box>
|
|
123
|
+
<Box variant="info">Card 4</Box>
|
|
124
|
+
<Box variant="info">Card 5</Box>
|
|
125
|
+
</Grid>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
</template>
|
|
129
|
+
</ComponentDemo>
|
|
130
|
+
|
|
131
|
+
### Nested grids
|
|
132
|
+
|
|
133
|
+
<ComponentDemo>
|
|
134
|
+
<Grid :cols="2" gap="medium">
|
|
135
|
+
<Box variant="info">Left</Box>
|
|
136
|
+
<Grid :cols="2" gap="small">
|
|
137
|
+
<Box variant="success">a</Box>
|
|
138
|
+
<Box variant="success">b</Box>
|
|
139
|
+
<Box variant="success">c</Box>
|
|
140
|
+
<Box variant="success">d</Box>
|
|
141
|
+
</Grid>
|
|
142
|
+
</Grid>
|
|
143
|
+
|
|
144
|
+
<template #code>
|
|
145
|
+
|
|
146
|
+
```vue
|
|
147
|
+
<Grid :cols="2" gap="medium">
|
|
148
|
+
<Box variant="info">Left</Box>
|
|
149
|
+
<Grid :cols="2" gap="small">
|
|
150
|
+
<Box variant="success">a</Box>
|
|
151
|
+
<Box variant="success">b</Box>
|
|
152
|
+
<Box variant="success">c</Box>
|
|
153
|
+
<Box variant="success">d</Box>
|
|
154
|
+
</Grid>
|
|
155
|
+
</Grid>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
</template>
|
|
159
|
+
</ComponentDemo>
|
|
160
|
+
|
|
161
|
+
## Props
|
|
162
|
+
|
|
163
|
+
| Prop | Type | Required | Default |
|
|
164
|
+
|------|------|----------|---------|
|
|
165
|
+
| `cols` | `GridCols` | No | — |
|
|
166
|
+
| `colsSmall` | `GridCols` | No | — |
|
|
167
|
+
| `breakpoint` | `string` | No | — |
|
|
168
|
+
| `gap` | `GridGap \| string` | No | — |
|
|
169
|
+
| `minColWidth` | `string` | No | — |
|
|
170
|
+
|
package/docs/Hint.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Hint
|
|
2
|
+
|
|
3
|
+
An info icon that shows a tooltip on hover. Used alongside form labels to provide additional context.
|
|
4
|
+
|
|
5
|
+
## Examples
|
|
6
|
+
|
|
7
|
+
<ComponentDemo>
|
|
8
|
+
<span style="display: flex; align-items: center; gap: 8px;">
|
|
9
|
+
Population size <Hint text="The total number of individuals in the simulation." />
|
|
10
|
+
</span>
|
|
11
|
+
|
|
12
|
+
<template #code>
|
|
13
|
+
|
|
14
|
+
```vue
|
|
15
|
+
<span style="display: flex; align-items: center; gap: 8px;">
|
|
16
|
+
Population size
|
|
17
|
+
<Hint text="The total number of individuals in the simulation." />
|
|
18
|
+
</span>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
</template>
|
|
22
|
+
</ComponentDemo>
|
|
23
|
+
|
|
24
|
+
## Props
|
|
25
|
+
|
|
26
|
+
| Prop | Type | Required | Default |
|
|
27
|
+
|------|------|----------|---------|
|
|
28
|
+
| `text` | `string` | Yes | — |
|
|
29
|
+
|
package/docs/Icon.md
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Icon
|
|
2
|
+
|
|
3
|
+
Renders a [Material Symbols](https://fonts.google.com/icons) icon as an inline
|
|
4
|
+
SVG. Icons are resolved from a registry by name, so you reference them exactly as
|
|
5
|
+
before — `<Icon icon="help" />` — but no webfont is loaded: only the SVGs you
|
|
6
|
+
actually use end up in your bundle.
|
|
7
|
+
|
|
8
|
+
A [base set](#base-set) of common icons is pre-registered and works with zero
|
|
9
|
+
setup. These SVGs are bundled inside `cfasim-ui`, so they need no extra install
|
|
10
|
+
or config — just `<Icon icon="help" />`. To use any other icon,
|
|
11
|
+
[register it](#registering-icons).
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
### Sizes
|
|
16
|
+
|
|
17
|
+
<ComponentDemo>
|
|
18
|
+
<Icon icon="help" size="sm" aria-label="help" />
|
|
19
|
+
<Icon icon="help" size="md" aria-label="help" />
|
|
20
|
+
<Icon icon="help" size="lg" aria-label="help" />
|
|
21
|
+
|
|
22
|
+
<template #code>
|
|
23
|
+
|
|
24
|
+
```vue
|
|
25
|
+
<Icon icon="help" size="sm" aria-label="help" />
|
|
26
|
+
<Icon icon="help" size="md" aria-label="help" />
|
|
27
|
+
<Icon icon="help" size="lg" aria-label="help" />
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
</template>
|
|
31
|
+
</ComponentDemo>
|
|
32
|
+
|
|
33
|
+
### Filled
|
|
34
|
+
|
|
35
|
+
<ComponentDemo>
|
|
36
|
+
<Icon icon="favorite" size="lg" aria-label="favorite" />
|
|
37
|
+
<Icon icon="favorite" size="lg" :fill="true" aria-label="favorite filled" />
|
|
38
|
+
|
|
39
|
+
<template #code>
|
|
40
|
+
|
|
41
|
+
```vue
|
|
42
|
+
<Icon icon="favorite" size="lg" aria-label="favorite" />
|
|
43
|
+
<Icon icon="favorite" size="lg" :fill="true" aria-label="favorite filled" />
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
</template>
|
|
47
|
+
</ComponentDemo>
|
|
48
|
+
|
|
49
|
+
### Inline in text
|
|
50
|
+
|
|
51
|
+
<ComponentDemo>
|
|
52
|
+
<p style="margin: 0">Click the <Icon icon="help" size="sm" :inline="true" aria-label="help" /> icon for more info.</p>
|
|
53
|
+
|
|
54
|
+
<template #code>
|
|
55
|
+
|
|
56
|
+
```vue
|
|
57
|
+
<p>Click the <Icon icon="help" size="sm" :inline="true" aria-label="help" /> icon for more info.</p>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
</template>
|
|
61
|
+
</ComponentDemo>
|
|
62
|
+
|
|
63
|
+
## Base set
|
|
64
|
+
|
|
65
|
+
These icons are bundled with `cfasim-ui` and need no install or registration —
|
|
66
|
+
just pass the name to `icon`. Anything not listed here must be
|
|
67
|
+
[registered](#registering-icons).
|
|
68
|
+
|
|
69
|
+
<script setup>
|
|
70
|
+
const baseIcons = [
|
|
71
|
+
"help", "favorite", "dark_mode", "light_mode",
|
|
72
|
+
"chevron_left", "chevron_right", "keyboard_arrow_up", "keyboard_arrow_down",
|
|
73
|
+
"keyboard_double_arrow_left", "keyboard_double_arrow_right",
|
|
74
|
+
"arrow_upward", "arrow_downward", "arrow_back", "arrow_forward",
|
|
75
|
+
"menu", "more_vert", "more_horiz",
|
|
76
|
+
"add", "edit", "delete", "close", "check", "search", "tune", "download",
|
|
77
|
+
"github",
|
|
78
|
+
];
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<Grid class="base-icons" :cols="4" :colsSmall="2" gap="sm">
|
|
82
|
+
<div v-for="name in baseIcons" :key="name" class="base-icons__cell">
|
|
83
|
+
<Icon :icon="name" size="lg" :aria-label="name" />
|
|
84
|
+
<code>{{ name }}</code>
|
|
85
|
+
</div>
|
|
86
|
+
</Grid>
|
|
87
|
+
|
|
88
|
+
<style>
|
|
89
|
+
.base-icons__cell {
|
|
90
|
+
display: flex;
|
|
91
|
+
flex-direction: column;
|
|
92
|
+
align-items: center;
|
|
93
|
+
gap: 0.5rem;
|
|
94
|
+
padding: 0.75rem 0.5rem;
|
|
95
|
+
border: 1px solid var(--vp-c-divider);
|
|
96
|
+
border-radius: 8px;
|
|
97
|
+
text-align: center;
|
|
98
|
+
}
|
|
99
|
+
.base-icons__cell code {
|
|
100
|
+
font-size: 0.65rem;
|
|
101
|
+
word-break: break-word;
|
|
102
|
+
}
|
|
103
|
+
</style>
|
|
104
|
+
|
|
105
|
+
`favorite` also has a filled variant via `:fill="true"`.
|
|
106
|
+
|
|
107
|
+
## Registering icons
|
|
108
|
+
|
|
109
|
+
The base set above is bundled with `cfasim-ui` and needs none of the steps below
|
|
110
|
+
— this section is only for additional icons. `cfasim-ui` does not depend on
|
|
111
|
+
`@material-symbols/svg-400`, so you install the icons yourself; each one you
|
|
112
|
+
import is inlined into _your_ bundle (still no webfont).
|
|
113
|
+
|
|
114
|
+
### 1. Install the icons and the Vite SVG loader
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
pnpm add -D @material-symbols/svg-400 vite-svg-loader
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 2. Add `svgLoader` to your Vite config
|
|
121
|
+
|
|
122
|
+
Add it to your existing `plugins` array — leave the other plugins in place:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
// vite.config.ts
|
|
126
|
+
import svgLoader from "vite-svg-loader";
|
|
127
|
+
|
|
128
|
+
export default defineConfig({
|
|
129
|
+
plugins: [
|
|
130
|
+
// ...your existing plugins
|
|
131
|
+
svgLoader(),
|
|
132
|
+
],
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 3. Register the icons once at startup
|
|
137
|
+
|
|
138
|
+
Import each SVG from the `outlined` style with the `?component` query, then pass
|
|
139
|
+
them to `registerIcons`. The key you register is the name you pass to `icon`.
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
// main.ts
|
|
143
|
+
import rocket from "@material-symbols/svg-400/outlined/rocket.svg?component";
|
|
144
|
+
import settings from "@material-symbols/svg-400/outlined/settings.svg?component";
|
|
145
|
+
import { registerIcons } from "@cfasim-ui/components";
|
|
146
|
+
|
|
147
|
+
registerIcons({ rocket, settings });
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### 4. Use them by name
|
|
151
|
+
|
|
152
|
+
```vue
|
|
153
|
+
<Icon icon="rocket" />
|
|
154
|
+
<Icon icon="settings" size="lg" />
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
An unregistered name renders nothing and logs a one-time console warning with the
|
|
158
|
+
exact code to register it.
|
|
159
|
+
|
|
160
|
+
### Filled variants
|
|
161
|
+
|
|
162
|
+
To support `:fill="true"` on a registered icon, register both the outline and
|
|
163
|
+
`-fill` files:
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
import favorite from "@material-symbols/svg-400/outlined/favorite.svg?component";
|
|
167
|
+
import favoriteFill from "@material-symbols/svg-400/outlined/favorite-fill.svg?component";
|
|
168
|
+
|
|
169
|
+
registerIcons({ favorite: { outline: favorite, fill: favoriteFill } });
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
> The `weight` and `grade` props are deprecated no-ops — static SVGs ship at
|
|
173
|
+
> weight 400, grade 0. `size`, `fill`, `inline`, and color (`currentColor`) work
|
|
174
|
+
> as before.
|
|
175
|
+
|
|
176
|
+
## Props
|
|
177
|
+
|
|
178
|
+
| Prop | Type | Required | Default |
|
|
179
|
+
|------|------|----------|---------|
|
|
180
|
+
| `icon` | `string` | Yes | — |
|
|
181
|
+
| `size` | `IconSize \| number` | No | `"md"` |
|
|
182
|
+
| `fill` | `boolean` | No | `false` |
|
|
183
|
+
| `weight` | `number` | No | — |
|
|
184
|
+
| `grade` | `number` | No | — |
|
|
185
|
+
| `decorative` | `boolean` | No | `true` |
|
|
186
|
+
| `ariaLabel` | `string` | No | — |
|
|
187
|
+
| `inline` | `boolean` | No | `false` |
|
|
188
|
+
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# MultiSelect
|
|
2
|
+
|
|
3
|
+
A multi-select combobox with autocomplete. Built on reka-ui's Combobox: type to
|
|
4
|
+
filter the options, click to add them, and selected values appear as removable
|
|
5
|
+
chips. Binds to a `string[]` via `v-model`.
|
|
6
|
+
|
|
7
|
+
## Examples
|
|
8
|
+
|
|
9
|
+
<script setup>
|
|
10
|
+
import { ref } from 'vue'
|
|
11
|
+
const states = ref(['ca'])
|
|
12
|
+
const stationery = ref([])
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<ComponentDemo>
|
|
16
|
+
<div style="width: 280px">
|
|
17
|
+
<MultiSelect
|
|
18
|
+
v-model="states"
|
|
19
|
+
label="States"
|
|
20
|
+
placeholder="Search states…"
|
|
21
|
+
:options="[
|
|
22
|
+
{ value: 'ca', label: 'California' },
|
|
23
|
+
{ value: 'ny', label: 'New York' },
|
|
24
|
+
{ value: 'tx', label: 'Texas' },
|
|
25
|
+
{ value: 'wa', label: 'Washington' },
|
|
26
|
+
{ value: 'fl', label: 'Florida' },
|
|
27
|
+
]"
|
|
28
|
+
/>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<template #code>
|
|
32
|
+
|
|
33
|
+
```vue
|
|
34
|
+
<script setup>
|
|
35
|
+
import { ref } from "vue";
|
|
36
|
+
const states = ref(["ca"]);
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<MultiSelect
|
|
40
|
+
v-model="states"
|
|
41
|
+
label="States"
|
|
42
|
+
placeholder="Search states…"
|
|
43
|
+
:options="[
|
|
44
|
+
{ value: 'ca', label: 'California' },
|
|
45
|
+
{ value: 'ny', label: 'New York' },
|
|
46
|
+
{ value: 'tx', label: 'Texas' },
|
|
47
|
+
{ value: 'wa', label: 'Washington' },
|
|
48
|
+
{ value: 'fl', label: 'Florida' },
|
|
49
|
+
]"
|
|
50
|
+
/>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
</template>
|
|
54
|
+
</ComponentDemo>
|
|
55
|
+
|
|
56
|
+
Type in the field to filter, click an option to add it, and use a chip's `✕`
|
|
57
|
+
button (or press Backspace in an empty input) to remove a selection.
|
|
58
|
+
|
|
59
|
+
### Hidden label
|
|
60
|
+
|
|
61
|
+
Use `hide-label` to visually hide the label while keeping it available to screen
|
|
62
|
+
readers. Prefer this over `aria-label` whenever you have label text.
|
|
63
|
+
|
|
64
|
+
<ComponentDemo>
|
|
65
|
+
<div style="width: 280px">
|
|
66
|
+
<MultiSelect
|
|
67
|
+
v-model="stationery"
|
|
68
|
+
label="Supplies"
|
|
69
|
+
hide-label
|
|
70
|
+
placeholder="Add supplies…"
|
|
71
|
+
:options="[
|
|
72
|
+
{ value: 'pen', label: 'Pen' },
|
|
73
|
+
{ value: 'pencil', label: 'Pencil' },
|
|
74
|
+
{ value: 'marker', label: 'Marker' },
|
|
75
|
+
{ value: 'eraser', label: 'Eraser' },
|
|
76
|
+
]"
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<template #code>
|
|
81
|
+
|
|
82
|
+
```vue
|
|
83
|
+
<MultiSelect
|
|
84
|
+
v-model="supplies"
|
|
85
|
+
label="Supplies"
|
|
86
|
+
hide-label
|
|
87
|
+
placeholder="Add supplies…"
|
|
88
|
+
:options="[
|
|
89
|
+
{ value: 'pen', label: 'Pen' },
|
|
90
|
+
{ value: 'pencil', label: 'Pencil' },
|
|
91
|
+
{ value: 'marker', label: 'Marker' },
|
|
92
|
+
{ value: 'eraser', label: 'Eraser' },
|
|
93
|
+
]"
|
|
94
|
+
/>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
</template>
|
|
98
|
+
</ComponentDemo>
|
|
99
|
+
|
|
100
|
+
## Accessibility
|
|
101
|
+
|
|
102
|
+
MultiSelect is built on reka-ui's Combobox, which implements the
|
|
103
|
+
[ARIA combobox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/):
|
|
104
|
+
|
|
105
|
+
- The text field has `role="combobox"` with `aria-expanded`, `aria-controls`,
|
|
106
|
+
`aria-autocomplete="list"`, and `aria-activedescendant` tracking the
|
|
107
|
+
highlighted option as you arrow through the list.
|
|
108
|
+
- The popup is a `role="listbox"` with `aria-multiselectable="true"`; each option
|
|
109
|
+
is a `role="option"` and reflects its state via `aria-selected`.
|
|
110
|
+
- Keyboard support is handled for you: type to filter, ↑/↓ to move, Enter to
|
|
111
|
+
toggle the highlighted option, Escape to close, and Backspace in an empty input
|
|
112
|
+
removes the last chip.
|
|
113
|
+
- Pass `label` so the field is named by a real `<label>` (associated via
|
|
114
|
+
`aria-labelledby`); use `hide-label` to keep it for screen readers while hiding
|
|
115
|
+
it visually, or `aria-label` when there's no visible label text.
|
|
116
|
+
- Each chip's remove button has an `aria-label` (e.g. "Remove California").
|
|
117
|
+
|
|
118
|
+
## Model
|
|
119
|
+
|
|
120
|
+
| Name | Type |
|
|
121
|
+
|------|------|
|
|
122
|
+
| `v-model` | `string[]` |
|
|
123
|
+
|
|
124
|
+
## Props
|
|
125
|
+
|
|
126
|
+
| Prop | Type | Required | Default |
|
|
127
|
+
|------|------|----------|---------|
|
|
128
|
+
| `label` | `string` | No | — |
|
|
129
|
+
| `hideLabel` | `boolean` | No | — |
|
|
130
|
+
| `ariaLabel` | `string` | No | — |
|
|
131
|
+
| `hint` | `string` | No | — |
|
|
132
|
+
| `options` | `MultiSelectOption[]` | Yes | — |
|
|
133
|
+
| `placeholder` | `string` | No | — |
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
### MultiSelectOption
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
interface MultiSelectOption {
|
|
140
|
+
value: string;
|
|
141
|
+
label: string;
|
|
142
|
+
}
|
|
143
|
+
```
|