svelte-multiselect 4.0.2 → 4.0.5
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/MultiSelect.svelte +29 -23
- package/MultiSelect.svelte.d.ts +3 -1
- package/package.json +12 -12
- package/readme.md +35 -23
package/MultiSelect.svelte
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
<script >import { createEventDispatcher,
|
|
1
|
+
<script >import { createEventDispatcher, tick } from 'svelte';
|
|
2
2
|
import { fly } from 'svelte/transition';
|
|
3
3
|
import CircleSpinner from './CircleSpinner.svelte';
|
|
4
4
|
import { CrossIcon, ExpandIcon, DisabledIcon } from './icons';
|
|
5
5
|
import Wiggle from './Wiggle.svelte';
|
|
6
|
-
export let selected = [];
|
|
7
6
|
export let selectedLabels = [];
|
|
8
7
|
export let selectedValues = [];
|
|
9
8
|
export let searchText = ``;
|
|
@@ -13,7 +12,9 @@ export let maxSelectMsg = null;
|
|
|
13
12
|
export let disabled = false;
|
|
14
13
|
export let disabledTitle = `This field is disabled`;
|
|
15
14
|
export let options;
|
|
15
|
+
export let selected = options.filter((op) => op?.preselected) ?? [];
|
|
16
16
|
export let input = null;
|
|
17
|
+
export let outerDiv = null;
|
|
17
18
|
export let placeholder = undefined;
|
|
18
19
|
export let id = undefined;
|
|
19
20
|
export let name = id;
|
|
@@ -30,6 +31,7 @@ export let liSelectedClass = ``;
|
|
|
30
31
|
export let ulOptionsClass = ``;
|
|
31
32
|
export let liOptionClass = ``;
|
|
32
33
|
export let liActiveOptionClass = ``;
|
|
34
|
+
export let inputClass = ``;
|
|
33
35
|
export let removeBtnTitle = `Remove`;
|
|
34
36
|
export let removeAllTitle = `Remove all`;
|
|
35
37
|
export let defaultDisabledTitle = `This option is disabled`;
|
|
@@ -46,25 +48,21 @@ if (!(options?.length > 0))
|
|
|
46
48
|
console.error(`MultiSelect missing options`);
|
|
47
49
|
if (!Array.isArray(selected))
|
|
48
50
|
console.error(`selected prop must be an array`);
|
|
49
|
-
onMount(() => {
|
|
50
|
-
selected = _options.filter((op) => op?.preselected) ?? [];
|
|
51
|
-
});
|
|
52
51
|
const dispatch = createEventDispatcher();
|
|
53
52
|
function isObject(item) {
|
|
54
53
|
return typeof item === `object` && !Array.isArray(item) && item !== null;
|
|
55
54
|
}
|
|
56
55
|
// process proto options to full ones with mandatory labels
|
|
57
56
|
$: _options = options.map((rawOp) => {
|
|
58
|
-
// convert to objects internally if user passed list of strings or numbers as options
|
|
59
57
|
if (isObject(rawOp)) {
|
|
60
|
-
const
|
|
61
|
-
if (
|
|
62
|
-
|
|
63
|
-
return
|
|
58
|
+
const option = { ...rawOp };
|
|
59
|
+
if (option.value === undefined)
|
|
60
|
+
option.value = option.label;
|
|
61
|
+
return option;
|
|
64
62
|
}
|
|
65
63
|
else {
|
|
66
64
|
if (![`string`, `number`].includes(typeof rawOp)) {
|
|
67
|
-
console.
|
|
65
|
+
console.warn(`MultiSelect options must be objects, strings or numbers, got ${typeof rawOp}`);
|
|
68
66
|
}
|
|
69
67
|
// even if we logged error above, try to proceed hoping user knows what they're doing
|
|
70
68
|
return { label: rawOp, value: rawOp };
|
|
@@ -72,7 +70,7 @@ $: _options = options.map((rawOp) => {
|
|
|
72
70
|
});
|
|
73
71
|
$: labels = _options.map((op) => op.label);
|
|
74
72
|
$: if (new Set(labels).size !== options.length) {
|
|
75
|
-
console.
|
|
73
|
+
console.warn(`Option labels should be unique. Duplicates found: ${labels.filter((label, idx) => labels.indexOf(label) !== idx)}`);
|
|
76
74
|
}
|
|
77
75
|
let wiggle = false;
|
|
78
76
|
$: selectedLabels = selected.map((op) => op.label);
|
|
@@ -136,8 +134,8 @@ function setOptionsVisible(show) {
|
|
|
136
134
|
}
|
|
137
135
|
// handle all keyboard events this component receives
|
|
138
136
|
async function handleKeydown(event) {
|
|
139
|
-
// on escape: dismiss options dropdown and reset search text
|
|
140
|
-
if (event.key === `Escape`) {
|
|
137
|
+
// on escape or tab out of input: dismiss options dropdown and reset search text
|
|
138
|
+
if (event.key === `Escape` || event.key === `Tab`) {
|
|
141
139
|
setOptionsVisible(false);
|
|
142
140
|
searchText = ``;
|
|
143
141
|
}
|
|
@@ -208,9 +206,18 @@ const handleEnterAndSpaceKeys = (handler) => (event) => {
|
|
|
208
206
|
};
|
|
209
207
|
</script>
|
|
210
208
|
|
|
209
|
+
<svelte:window
|
|
210
|
+
on:click={(event) => {
|
|
211
|
+
if (outerDiv && !outerDiv.contains(event.target)) {
|
|
212
|
+
setOptionsVisible(false)
|
|
213
|
+
}
|
|
214
|
+
}}
|
|
215
|
+
/>
|
|
216
|
+
|
|
211
217
|
<!-- z-index: 2 when showOptions is true ensures the ul.selected of one <MultiSelect />
|
|
212
218
|
display above those of another following shortly after it -->
|
|
213
219
|
<div
|
|
220
|
+
bind:this={outerDiv}
|
|
214
221
|
class:disabled
|
|
215
222
|
class:single={maxSelect === 1}
|
|
216
223
|
class:open={showOptions}
|
|
@@ -219,16 +226,15 @@ display above those of another following shortly after it -->
|
|
|
219
226
|
class:invalid
|
|
220
227
|
class="multiselect {outerDivClass}"
|
|
221
228
|
on:mouseup|stopPropagation={() => setOptionsVisible(true)}
|
|
222
|
-
on:focusout={() => setOptionsVisible(false)}
|
|
223
229
|
title={disabled ? disabledTitle : null}
|
|
224
230
|
aria-disabled={disabled ? `true` : null}
|
|
225
231
|
>
|
|
226
|
-
<!-- invisible input, used only to prevent form submission if required=true and no options selected -->
|
|
227
232
|
<input
|
|
228
233
|
{required}
|
|
229
234
|
bind:value={formValue}
|
|
230
235
|
tabindex="-1"
|
|
231
236
|
aria-hidden="true"
|
|
237
|
+
aria-label="ignore this, used only to prevent form submission if select is required but empty"
|
|
232
238
|
class="form-control"
|
|
233
239
|
on:invalid={() => (invalid = true)}
|
|
234
240
|
/>
|
|
@@ -253,13 +259,13 @@ display above those of another following shortly after it -->
|
|
|
253
259
|
{/each}
|
|
254
260
|
<li style="display: contents;">
|
|
255
261
|
<input
|
|
262
|
+
class={inputClass}
|
|
256
263
|
bind:this={input}
|
|
257
264
|
{autocomplete}
|
|
258
265
|
bind:value={searchText}
|
|
259
266
|
on:mouseup|self|stopPropagation={() => setOptionsVisible(true)}
|
|
260
267
|
on:keydown={handleKeydown}
|
|
261
268
|
on:focus={() => setOptionsVisible(true)}
|
|
262
|
-
on:blur={() => setOptionsVisible(false)}
|
|
263
269
|
{id}
|
|
264
270
|
{name}
|
|
265
271
|
{disabled}
|
|
@@ -350,13 +356,14 @@ display above those of another following shortly after it -->
|
|
|
350
356
|
align-items: center;
|
|
351
357
|
display: flex;
|
|
352
358
|
cursor: text;
|
|
353
|
-
padding: 0 3pt;
|
|
354
359
|
border: var(--sms-border, 1pt solid lightgray);
|
|
355
360
|
border-radius: var(--sms-border-radius, 3pt);
|
|
356
|
-
background: var(--sms-
|
|
357
|
-
|
|
361
|
+
background: var(--sms-bg);
|
|
362
|
+
max-width: var(--sms-max-width);
|
|
363
|
+
padding: var(--sms-padding, 0 3pt);
|
|
358
364
|
color: var(--sms-text-color);
|
|
359
365
|
font-size: var(--sms-font-size, inherit);
|
|
366
|
+
min-height: var(--sms-min-height, 19pt);
|
|
360
367
|
}
|
|
361
368
|
:where(div.multiselect.open) {
|
|
362
369
|
z-index: var(--sms-open-z-index, 4);
|
|
@@ -378,15 +385,14 @@ display above those of another following shortly after it -->
|
|
|
378
385
|
}
|
|
379
386
|
:where(div.multiselect > ul.selected > li) {
|
|
380
387
|
align-items: center;
|
|
381
|
-
border-radius:
|
|
388
|
+
border-radius: 3pt;
|
|
382
389
|
display: flex;
|
|
383
390
|
margin: 2pt;
|
|
384
391
|
line-height: normal;
|
|
385
|
-
padding: 1pt 5pt;
|
|
386
392
|
transition: 0.3s;
|
|
387
393
|
white-space: nowrap;
|
|
388
394
|
background: var(--sms-selected-bg, rgba(0, 0, 0, 0.15));
|
|
389
|
-
|
|
395
|
+
padding: var(--sms-selected-li-padding, 1pt 5pt);
|
|
390
396
|
color: var(--sms-selected-text-color, var(--sms-text-color));
|
|
391
397
|
}
|
|
392
398
|
:where(div.multiselect button) {
|
package/MultiSelect.svelte.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { SvelteComponentTyped } from "svelte";
|
|
|
2
2
|
import type { Option, Primitive, ProtoOption } from './';
|
|
3
3
|
declare const __propDef: {
|
|
4
4
|
props: {
|
|
5
|
-
selected?: Option[] | undefined;
|
|
6
5
|
selectedLabels?: Primitive[] | undefined;
|
|
7
6
|
selectedValues?: Primitive[] | undefined;
|
|
8
7
|
searchText?: string | undefined;
|
|
@@ -12,7 +11,9 @@ declare const __propDef: {
|
|
|
12
11
|
disabled?: boolean | undefined;
|
|
13
12
|
disabledTitle?: string | undefined;
|
|
14
13
|
options: ProtoOption[];
|
|
14
|
+
selected?: Option[] | undefined;
|
|
15
15
|
input?: HTMLInputElement | null | undefined;
|
|
16
|
+
outerDiv?: HTMLDivElement | null | undefined;
|
|
16
17
|
placeholder?: string | undefined;
|
|
17
18
|
id?: string | undefined;
|
|
18
19
|
name?: string | undefined;
|
|
@@ -25,6 +26,7 @@ declare const __propDef: {
|
|
|
25
26
|
ulOptionsClass?: string | undefined;
|
|
26
27
|
liOptionClass?: string | undefined;
|
|
27
28
|
liActiveOptionClass?: string | undefined;
|
|
29
|
+
inputClass?: string | undefined;
|
|
28
30
|
removeBtnTitle?: string | undefined;
|
|
29
31
|
removeAllTitle?: string | undefined;
|
|
30
32
|
defaultDisabledTitle?: string | undefined;
|
package/package.json
CHANGED
|
@@ -5,37 +5,37 @@
|
|
|
5
5
|
"homepage": "https://svelte-multiselect.netlify.app",
|
|
6
6
|
"repository": "https://github.com/janosh/svelte-multiselect",
|
|
7
7
|
"license": "MIT",
|
|
8
|
-
"version": "4.0.
|
|
8
|
+
"version": "4.0.5",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"svelte": "index.js",
|
|
11
11
|
"bugs": "https://github.com/janosh/svelte-multiselect/issues",
|
|
12
12
|
"devDependencies": {
|
|
13
13
|
"@sveltejs/adapter-static": "^1.0.0-next.29",
|
|
14
|
-
"@sveltejs/kit": "^1.0.0-next.
|
|
15
|
-
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.
|
|
16
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
17
|
-
"@typescript-eslint/parser": "^5.
|
|
18
|
-
"@vitest/ui": "^0.
|
|
14
|
+
"@sveltejs/kit": "^1.0.0-next.302",
|
|
15
|
+
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.40",
|
|
16
|
+
"@typescript-eslint/eslint-plugin": "^5.16.0",
|
|
17
|
+
"@typescript-eslint/parser": "^5.16.0",
|
|
18
|
+
"@vitest/ui": "^0.7.9",
|
|
19
19
|
"eslint": "^8.11.0",
|
|
20
20
|
"eslint-plugin-svelte3": "^3.4.1",
|
|
21
21
|
"hastscript": "^7.0.2",
|
|
22
22
|
"jsdom": "^19.0.0",
|
|
23
23
|
"mdsvex": "^0.10.5",
|
|
24
|
-
"playwright": "^1.
|
|
25
|
-
"prettier": "^2.
|
|
24
|
+
"playwright": "^1.20.0",
|
|
25
|
+
"prettier": "^2.6.0",
|
|
26
26
|
"prettier-plugin-svelte": "^2.6.0",
|
|
27
27
|
"rehype-autolink-headings": "^6.1.1",
|
|
28
28
|
"rehype-slug": "^5.0.1",
|
|
29
29
|
"svelte": "^3.46.4",
|
|
30
|
-
"svelte-check": "^2.4.
|
|
30
|
+
"svelte-check": "^2.4.6",
|
|
31
31
|
"svelte-github-corner": "^0.1.0",
|
|
32
32
|
"svelte-preprocess": "^4.10.4",
|
|
33
|
-
"svelte-toc": "^0.2.
|
|
34
|
-
"svelte2tsx": "^0.5.
|
|
33
|
+
"svelte-toc": "^0.2.9",
|
|
34
|
+
"svelte2tsx": "^0.5.6",
|
|
35
35
|
"tslib": "^2.3.1",
|
|
36
36
|
"typescript": "^4.6.2",
|
|
37
37
|
"vite": "^2.8.6",
|
|
38
|
-
"vitest": "^0.
|
|
38
|
+
"vitest": "^0.7.9"
|
|
39
39
|
},
|
|
40
40
|
"keywords": [
|
|
41
41
|
"svelte",
|
package/readme.md
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
<h1 align="center">
|
|
2
|
-
<img src="https://raw.githubusercontent.com/janosh/svelte-
|
|
3
|
-
<br> Svelte MultiSelect
|
|
2
|
+
<img src="https://raw.githubusercontent.com/janosh/svelte-multiselect/main/static/favicon.svg" alt="Svelte MultiSelect" height="60" width="60">
|
|
3
|
+
<br class="hide-in-docs"> Svelte MultiSelect
|
|
4
4
|
</h1>
|
|
5
5
|
|
|
6
6
|
<h4 align="center">
|
|
7
7
|
|
|
8
|
+
[](https://svelte.dev/repl/a5a14b8f15d64cb083b567292480db05)
|
|
8
9
|
[](https://github.com/janosh/svelte-multiselect/actions/workflows/test.yml)
|
|
9
10
|
[](https://app.netlify.com/sites/svelte-multiselect/deploys)
|
|
10
|
-
[](https://github.com/sveltejs/svelte/blob/master/CHANGELOG.md)
|
|
11
|
+
[](https://npmjs.com/package/svelte-multiselect)
|
|
12
|
+
[](https://github.com/sveltejs/svelte/blob/master/CHANGELOG.md)
|
|
13
13
|
|
|
14
14
|
</h4>
|
|
15
15
|
|
|
16
|
-
**Keyboard-friendly,
|
|
16
|
+
**Keyboard-friendly, accessible multi-select Svelte component.**
|
|
17
|
+
<strong class="hide-in-docs">
|
|
18
|
+
<a href="https://svelte-multiselect.netlify.app">Docs</a>
|
|
19
|
+
</strong>
|
|
17
20
|
|
|
18
21
|
<slot name="examples" />
|
|
19
22
|
|
|
@@ -48,6 +51,8 @@
|
|
|
48
51
|
|
|
49
52
|
- v4.0.1 renamed the `readonly` prop to `disabled` which now prevents all form or user interaction with this component including opening the dropdown list which was still possible before. See [#45](https://github.com/janosh/svelte-multiselect/issues/45) for details. The associated CSS class applied to the outer `div` was likewise renamed to `div.multiselect.{readonly=>disabled}`.
|
|
50
53
|
|
|
54
|
+
- v4.0.3 CSS variables starting with `--sms-input-<attr>` were renamed to just `--sms-<attr>`. E.g. `--sms-input-min-height` is now `--sms-min-height`.
|
|
55
|
+
|
|
51
56
|
## Installation
|
|
52
57
|
|
|
53
58
|
```sh
|
|
@@ -93,7 +98,8 @@ Full list of props/bindable variables for this component:
|
|
|
93
98
|
| `disabled` | `false` | Disable the component. It will still be rendered but users won't be able to interact with it. |
|
|
94
99
|
| `disabledTitle` | `This field is disabled` | Tooltip text to display on hover when the component is in `disabled` state. |
|
|
95
100
|
| `placeholder` | `undefined` | String shown in the text input when no option is selected. |
|
|
96
|
-
| `input` | `
|
|
101
|
+
| `input` | `null` | Handle to the `<input>` DOM node. Only available after component mounts (`null` before then). |
|
|
102
|
+
| `outerDiv` | `null` | Handle to outer `<div class="multiselect">` that wraps the whole component. Only available after component mounts (`null` before then). |
|
|
97
103
|
| `id` | `undefined` | Applied to the `<input>` element for associating HTML form `<label>`s with this component for accessibility. Also, clicking a `<label>` with same `for` attribute as `id` will focus this component. |
|
|
98
104
|
| `name` | `id` | Applied to the `<input>` element. If not provided, will be set to the value of `id`. Sets the key of this field in a submitted form data object. Not useful at the moment since the value is stored in Svelte state, not on the `<input>`. |
|
|
99
105
|
| `required` | `false` | Whether forms can be submitted without selecting any options. Aborts submission, is scrolled into view and shows help "Please fill out" message when true and user tries to submit with no options selected. |
|
|
@@ -137,15 +143,17 @@ Full list of props/bindable variables for this component:
|
|
|
137
143
|
Example:
|
|
138
144
|
|
|
139
145
|
```svelte
|
|
140
|
-
<MultiSelect options={[`
|
|
146
|
+
<MultiSelect options={[`Red`, `Green`, `Blue`, `Yellow`, `Purple`]}>
|
|
141
147
|
<span let:idx let:option slot="option">
|
|
142
|
-
{idx + 1}
|
|
143
|
-
{option.label
|
|
148
|
+
{idx + 1}
|
|
149
|
+
{option.label}
|
|
150
|
+
<span style:background={option.label} style=" width: 1em; height: 1em;" />
|
|
144
151
|
</span>
|
|
145
152
|
|
|
146
153
|
<span let:idx let:option slot="selected">
|
|
147
|
-
|
|
154
|
+
{idx + 1}
|
|
148
155
|
{option.label}
|
|
156
|
+
<span style:background={option.label} style=" width: 1em; height: 1em;" />
|
|
149
157
|
</span>
|
|
150
158
|
|
|
151
159
|
<CustomSpinner slot="spinner">
|
|
@@ -156,13 +164,13 @@ Example:
|
|
|
156
164
|
|
|
157
165
|
`MultiSelect.svelte` dispatches the following events:
|
|
158
166
|
|
|
159
|
-
| name | detail
|
|
160
|
-
| ----------- |
|
|
161
|
-
| `add` | `{ option: Option }`
|
|
162
|
-
| `remove` | `{ option: Option }`
|
|
163
|
-
| `removeAll` | `options: Option[]`
|
|
164
|
-
| `change` | `
|
|
165
|
-
| `blur` | none
|
|
167
|
+
| name | detail | description |
|
|
168
|
+
| ----------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
169
|
+
| `add` | `{ option: Option }` | Triggers when a new option is selected. |
|
|
170
|
+
| `remove` | `{ option: Option }` | Triggers when one selected option provided as `event.detail.option` is removed. |
|
|
171
|
+
| `removeAll` | `options: Option[]` | Triggers when all selected options are removed. The payload `event.detail.options` gives the options that were previously selected. |
|
|
172
|
+
| `change` | `type: 'add' \| 'remove' \| 'removeAll'` | Triggers when a option is either added or removed, or all options are removed at once. Payload will be a single or an aarray of `Option` objects, respectively. |
|
|
173
|
+
| `blur` | none | Triggers when the input field looses focus. |
|
|
166
174
|
|
|
167
175
|
### Examples
|
|
168
176
|
|
|
@@ -221,19 +229,22 @@ If you only want to make small adjustments, you can pass the following CSS varia
|
|
|
221
229
|
- `div.multiselect`
|
|
222
230
|
- `border: var(--sms-border, 1pt solid lightgray)`: Change this to e.g. to `1px solid red` to indicate this form field is in an invalid state.
|
|
223
231
|
- `border-radius: var(--sms-border-radius, 3pt)`
|
|
224
|
-
- `
|
|
225
|
-
- `
|
|
232
|
+
- `padding: var(--sms-padding, 0 3pt)`
|
|
233
|
+
- `background: var(--sms-bg)`
|
|
226
234
|
- `color: var(--sms-text-color)`
|
|
227
|
-
- `
|
|
235
|
+
- `min-height: var(--sms-min-height)`
|
|
236
|
+
- `max-width: var(--sms-max-width)`
|
|
228
237
|
- `div.multiselect.open`
|
|
229
238
|
- `z-index: var(--sms-open-z-index, 4)`: Increase this if needed to ensure the dropdown list is displayed atop all other page elements.
|
|
230
239
|
- `div.multiselect:focus-within`
|
|
231
240
|
- `border: var(--sms-focus-border, 1pt solid var(--sms-active-color, cornflowerblue))`: Border when component has focus. Defaults to `--sms-active-color` if not set which defaults to `cornflowerblue`.
|
|
232
241
|
- `div.multiselect.disabled`
|
|
233
242
|
- `background: var(--sms-disabled-bg, lightgray)`: Background when in disabled state.
|
|
243
|
+
- `div.multiselect input::placeholder`
|
|
244
|
+
- `color: var(--sms-placeholder-color)`
|
|
234
245
|
- `div.multiselect > ul.selected > li`
|
|
235
246
|
- `background: var(--sms-selected-bg, rgba(0, 0, 0, 0.15))`: Background of selected options.
|
|
236
|
-
- `
|
|
247
|
+
- `padding: var(--sms-selected-li-padding, 5pt 1pt)`: Height of selected options.
|
|
237
248
|
- `color: var(--sms-selected-text-color, var(--sms-text-color))`: Text color for selected options.
|
|
238
249
|
- `ul.selected > li button:hover, button.remove-all:hover, button:focus`
|
|
239
250
|
- `color: var(--sms-button-hover-color, lightskyblue)`: Color of the cross-icon buttons for removing all or individual selected options when in `:focus` or `:hover` state.
|
|
@@ -274,6 +285,7 @@ This simplified version of the DOM structure of this component shows where these
|
|
|
274
285
|
|
|
275
286
|
```svelte
|
|
276
287
|
<div class="multiselect {outerDivClass}">
|
|
288
|
+
<input class={inputClass} />
|
|
277
289
|
<ul class="selected {ulSelectedClass}">
|
|
278
290
|
<li class={liSelectedClass}>Selected 1</li>
|
|
279
291
|
<li class={liSelectedClass}>Selected 2</li>
|
|
@@ -369,7 +381,7 @@ export default {
|
|
|
369
381
|
}
|
|
370
382
|
```
|
|
371
383
|
|
|
372
|
-
Here's a [Stackblitz example](https://stackblitz.com/fork/github/davipon/test-svelte-multiselect?initialPath=__vitest__) that also uses [`
|
|
384
|
+
Here's a [Stackblitz example](https://stackblitz.com/fork/github/davipon/test-svelte-multiselect?initialPath=__vitest__) that also uses [`vitest-svelte-kit`](https://github.com/nickbreaton/vitest-svelte-kit).
|
|
373
385
|
|
|
374
386
|
## Want to contribute?
|
|
375
387
|
|