svelte-multiselect 5.0.0 → 5.0.3
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/CircleSpinner.svelte +1 -1
- package/MultiSelect.svelte +47 -28
- package/MultiSelect.svelte.d.ts +6 -7
- package/Wiggle.svelte +1 -1
- package/index.d.ts +6 -1
- package/index.js +4 -0
- package/package.json +22 -23
- package/readme.md +16 -18
package/CircleSpinner.svelte
CHANGED
package/MultiSelect.svelte
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
<script
|
|
2
|
-
import './';
|
|
1
|
+
<script>import { createEventDispatcher } from 'svelte';
|
|
2
|
+
import { get_label, get_value } from './';
|
|
3
3
|
import CircleSpinner from './CircleSpinner.svelte';
|
|
4
4
|
import { CrossIcon, DisabledIcon, ExpandIcon } from './icons';
|
|
5
5
|
import Wiggle from './Wiggle.svelte';
|
|
@@ -10,6 +10,7 @@ export let maxSelectMsg = null;
|
|
|
10
10
|
export let disabled = false;
|
|
11
11
|
export let disabledTitle = `This field is disabled`;
|
|
12
12
|
export let options;
|
|
13
|
+
export let matchingOptions = [];
|
|
13
14
|
export let selected = [];
|
|
14
15
|
export let selectedLabels = [];
|
|
15
16
|
export let selectedValues = [];
|
|
@@ -36,6 +37,7 @@ export let removeBtnTitle = `Remove`;
|
|
|
36
37
|
export let removeAllTitle = `Remove all`;
|
|
37
38
|
export let defaultDisabledTitle = `This option is disabled`;
|
|
38
39
|
export let allowUserOptions = false;
|
|
40
|
+
export let parseLabelsAsHtml = false; // should not be combined with allowUserOptions!
|
|
39
41
|
export let addOptionMsg = `Create this option...`;
|
|
40
42
|
export let autoScroll = true;
|
|
41
43
|
export let loading = false;
|
|
@@ -43,18 +45,17 @@ export let required = false;
|
|
|
43
45
|
export let autocomplete = `off`;
|
|
44
46
|
export let invalid = false;
|
|
45
47
|
export let sortSelected = false;
|
|
48
|
+
if (!(options?.length > 0))
|
|
49
|
+
console.error(`MultiSelect received no options`);
|
|
50
|
+
if (parseLabelsAsHtml && allowUserOptions)
|
|
51
|
+
console.warn(`You shouldn't combine parseLabelsAsHtml and allowUserOptions. It's susceptible to XSS attacks!`);
|
|
46
52
|
if (maxSelect !== null && maxSelect < 1) {
|
|
47
53
|
console.error(`maxSelect must be null or positive integer, got ${maxSelect}`);
|
|
48
54
|
}
|
|
49
|
-
if (!(options?.length > 0))
|
|
50
|
-
console.error(`MultiSelect is missing options`);
|
|
51
55
|
if (!Array.isArray(selected))
|
|
52
56
|
console.error(`selected prop must be an array`);
|
|
53
57
|
const dispatch = createEventDispatcher();
|
|
54
58
|
let activeMsg = false; // controls active state of <li>{addOptionMsg}</li>
|
|
55
|
-
// process proto options to full ones with mandatory labels
|
|
56
|
-
const get_value = (option) => (option instanceof Object ? option.value : option);
|
|
57
|
-
const get_label = (option) => (option instanceof Object ? option.label : option);
|
|
58
59
|
let wiggle = false; // controls wiggle animation when user tries to exceed maxSelect
|
|
59
60
|
$: selectedLabels = selected.map(get_label);
|
|
60
61
|
$: selectedValues = selected.map(get_value);
|
|
@@ -68,6 +69,9 @@ $: matchingOptions = options.filter((op) => filterFunc(op, searchText) &&
|
|
|
68
69
|
!(op instanceof Object && op.disabled) &&
|
|
69
70
|
!selectedLabels.includes(get_label(op)) // remove already selected options from dropdown list
|
|
70
71
|
);
|
|
72
|
+
// reset activeOption if it's no longer in the matchingOptions list
|
|
73
|
+
$: if (activeOption && !matchingOptions.includes(activeOption))
|
|
74
|
+
activeOption = null;
|
|
71
75
|
// add an option to selected list
|
|
72
76
|
function add(label) {
|
|
73
77
|
if (maxSelect && maxSelect > 1 && selected.length >= maxSelect)
|
|
@@ -75,13 +79,17 @@ function add(label) {
|
|
|
75
79
|
// to prevent duplicate selection, we could add `&& !selectedLabels.includes(label)`
|
|
76
80
|
if (maxSelect === null || maxSelect === 1 || selected.length < maxSelect) {
|
|
77
81
|
// first check if we find option in the options list
|
|
78
|
-
let option = options.find((op) =>
|
|
82
|
+
let option = options.find((op) => get_label(op) === label);
|
|
79
83
|
if (!option && // this has the side-effect of not allowing to user to add the same
|
|
80
84
|
// custom option twice in append mode
|
|
81
85
|
[true, `append`].includes(allowUserOptions) &&
|
|
82
86
|
searchText.length > 0) {
|
|
83
|
-
// user entered text but no options match, so if allowUserOptions=true | 'append', we create
|
|
84
|
-
|
|
87
|
+
// user entered text but no options match, so if allowUserOptions=true | 'append', we create
|
|
88
|
+
// a new option from the user-entered text
|
|
89
|
+
if (typeof options[0] === `string`)
|
|
90
|
+
option = searchText;
|
|
91
|
+
else
|
|
92
|
+
option = { label: searchText, value: searchText };
|
|
85
93
|
if (allowUserOptions === `append`)
|
|
86
94
|
options = [...options, option];
|
|
87
95
|
}
|
|
@@ -202,9 +210,12 @@ async function handleKeydown(event) {
|
|
|
202
210
|
activeOption = matchingOptions[newActiveIdx];
|
|
203
211
|
}
|
|
204
212
|
if (autoScroll) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
213
|
+
// TODO This ugly timeout hack is needed to properly scroll element into view when wrapping
|
|
214
|
+
// around start/end of option list. Find a better solution than waiting 10 ms to.
|
|
215
|
+
setTimeout(() => {
|
|
216
|
+
const li = document.querySelector(`ul.options > li.active`);
|
|
217
|
+
li?.scrollIntoView();
|
|
218
|
+
}, 10);
|
|
208
219
|
}
|
|
209
220
|
}
|
|
210
221
|
// on backspace key: remove last selected option
|
|
@@ -212,14 +223,14 @@ async function handleKeydown(event) {
|
|
|
212
223
|
remove(selectedLabels.at(-1));
|
|
213
224
|
}
|
|
214
225
|
}
|
|
215
|
-
|
|
226
|
+
function remove_all() {
|
|
216
227
|
dispatch(`removeAll`, { options: selected });
|
|
217
228
|
dispatch(`change`, { options: selected, type: `removeAll` });
|
|
218
229
|
selected = [];
|
|
219
230
|
searchText = ``;
|
|
220
|
-
}
|
|
221
|
-
$:
|
|
222
|
-
const
|
|
231
|
+
}
|
|
232
|
+
$: is_selected = (label) => selectedLabels.includes(label);
|
|
233
|
+
const if_enter_or_space = (handler) => (event) => {
|
|
223
234
|
if ([`Enter`, `Space`].includes(event.code)) {
|
|
224
235
|
event.preventDefault();
|
|
225
236
|
handler();
|
|
@@ -262,12 +273,16 @@ const handleEnterAndSpaceKeys = (handler) => (event) => {
|
|
|
262
273
|
{#each selected as option, idx}
|
|
263
274
|
<li class={liSelectedClass} aria-selected="true">
|
|
264
275
|
<slot name="selected" {option} {idx}>
|
|
265
|
-
{
|
|
276
|
+
{#if parseLabelsAsHtml}
|
|
277
|
+
{@html get_label(option)}
|
|
278
|
+
{:else}
|
|
279
|
+
{get_label(option)}
|
|
280
|
+
{/if}
|
|
266
281
|
</slot>
|
|
267
282
|
{#if !disabled}
|
|
268
283
|
<button
|
|
269
284
|
on:mouseup|stopPropagation={() => remove(get_label(option))}
|
|
270
|
-
on:keydown={
|
|
285
|
+
on:keydown={if_enter_or_space(() => remove(get_label(option)))}
|
|
271
286
|
type="button"
|
|
272
287
|
title="{removeBtnTitle} {get_label(option)}"
|
|
273
288
|
>
|
|
@@ -316,8 +331,8 @@ const handleEnterAndSpaceKeys = (handler) => (event) => {
|
|
|
316
331
|
type="button"
|
|
317
332
|
class="remove-all"
|
|
318
333
|
title={removeAllTitle}
|
|
319
|
-
on:mouseup|stopPropagation={
|
|
320
|
-
on:keydown={
|
|
334
|
+
on:mouseup|stopPropagation={remove_all}
|
|
335
|
+
on:keydown={if_enter_or_space(remove_all)}
|
|
321
336
|
>
|
|
322
337
|
<CrossIcon width="15px" />
|
|
323
338
|
</button>
|
|
@@ -337,10 +352,10 @@ const handleEnterAndSpaceKeys = (handler) => (event) => {
|
|
|
337
352
|
<li
|
|
338
353
|
on:mousedown|stopPropagation
|
|
339
354
|
on:mouseup|stopPropagation={() => {
|
|
340
|
-
if (!disabled)
|
|
355
|
+
if (!disabled) is_selected(label) ? remove(label) : add(label)
|
|
341
356
|
}}
|
|
342
|
-
title={disabled ? disabledTitle : (
|
|
343
|
-
class:selected={
|
|
357
|
+
title={disabled ? disabledTitle : (is_selected(label) && selectedTitle) || title}
|
|
358
|
+
class:selected={is_selected(label)}
|
|
344
359
|
class:active
|
|
345
360
|
class:disabled
|
|
346
361
|
class="{liOptionClass} {active ? liActiveOptionClass : ``}"
|
|
@@ -355,7 +370,11 @@ const handleEnterAndSpaceKeys = (handler) => (event) => {
|
|
|
355
370
|
aria-selected="false"
|
|
356
371
|
>
|
|
357
372
|
<slot name="option" {option} {idx}>
|
|
358
|
-
{
|
|
373
|
+
{#if parseLabelsAsHtml}
|
|
374
|
+
{@html get_label(option)}
|
|
375
|
+
{:else}
|
|
376
|
+
{get_label(option)}
|
|
377
|
+
{/if}
|
|
359
378
|
</slot>
|
|
360
379
|
</li>
|
|
361
380
|
{:else}
|
|
@@ -462,7 +481,9 @@ const handleEnterAndSpaceKeys = (handler) => (event) => {
|
|
|
462
481
|
cursor: inherit; /* needed for disabled state */
|
|
463
482
|
}
|
|
464
483
|
:where(div.multiselect > ul.selected > li > input)::placeholder {
|
|
484
|
+
padding-left: 5pt;
|
|
465
485
|
color: var(--sms-placeholder-color);
|
|
486
|
+
opacity: var(--sms-placeholder-opacity);
|
|
466
487
|
}
|
|
467
488
|
:where(div.multiselect > input.form-control) {
|
|
468
489
|
width: 2em;
|
|
@@ -477,7 +498,7 @@ const handleEnterAndSpaceKeys = (handler) => (event) => {
|
|
|
477
498
|
|
|
478
499
|
:where(div.multiselect > ul.options) {
|
|
479
500
|
list-style: none;
|
|
480
|
-
padding: 0;
|
|
501
|
+
padding: 4pt 0;
|
|
481
502
|
top: 100%;
|
|
482
503
|
left: 0;
|
|
483
504
|
width: 100%;
|
|
@@ -489,8 +510,6 @@ const handleEnterAndSpaceKeys = (handler) => (event) => {
|
|
|
489
510
|
overscroll-behavior: var(--sms-options-overscroll, none);
|
|
490
511
|
box-shadow: var(--sms-options-shadow, 0 0 14pt -8pt black);
|
|
491
512
|
transition: all 0.2s;
|
|
492
|
-
opacity: 1;
|
|
493
|
-
transform: translateY(0);
|
|
494
513
|
}
|
|
495
514
|
:where(div.multiselect > ul.options.hidden) {
|
|
496
515
|
visibility: hidden;
|
package/MultiSelect.svelte.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SvelteComponentTyped } from "svelte";
|
|
2
|
-
import { Option } from './';
|
|
2
|
+
import { MultiSelectEvents, Option } from './';
|
|
3
3
|
declare const __propDef: {
|
|
4
4
|
props: {
|
|
5
5
|
searchText?: string | undefined;
|
|
@@ -9,9 +9,10 @@ declare const __propDef: {
|
|
|
9
9
|
disabled?: boolean | undefined;
|
|
10
10
|
disabledTitle?: string | undefined;
|
|
11
11
|
options: Option[];
|
|
12
|
+
matchingOptions?: Option[] | undefined;
|
|
12
13
|
selected?: Option[] | undefined;
|
|
13
14
|
selectedLabels?: (string | number)[] | undefined;
|
|
14
|
-
selectedValues?:
|
|
15
|
+
selectedValues?: unknown[] | undefined;
|
|
15
16
|
input?: HTMLInputElement | null | undefined;
|
|
16
17
|
outerDiv?: HTMLDivElement | null | undefined;
|
|
17
18
|
placeholder?: string | undefined;
|
|
@@ -31,6 +32,7 @@ declare const __propDef: {
|
|
|
31
32
|
removeAllTitle?: string | undefined;
|
|
32
33
|
defaultDisabledTitle?: string | undefined;
|
|
33
34
|
allowUserOptions?: boolean | "append" | undefined;
|
|
35
|
+
parseLabelsAsHtml?: boolean | undefined;
|
|
34
36
|
addOptionMsg?: string | undefined;
|
|
35
37
|
autoScroll?: boolean | undefined;
|
|
36
38
|
loading?: boolean | undefined;
|
|
@@ -39,11 +41,6 @@ declare const __propDef: {
|
|
|
39
41
|
invalid?: boolean | undefined;
|
|
40
42
|
sortSelected?: boolean | ((op1: Option, op2: Option) => number) | undefined;
|
|
41
43
|
};
|
|
42
|
-
events: {
|
|
43
|
-
mousedown: MouseEvent;
|
|
44
|
-
} & {
|
|
45
|
-
[evt: string]: CustomEvent<any>;
|
|
46
|
-
};
|
|
47
44
|
slots: {
|
|
48
45
|
selected: {
|
|
49
46
|
option: Option;
|
|
@@ -56,6 +53,8 @@ declare const __propDef: {
|
|
|
56
53
|
idx: any;
|
|
57
54
|
};
|
|
58
55
|
};
|
|
56
|
+
getters: {};
|
|
57
|
+
events: MultiSelectEvents;
|
|
59
58
|
};
|
|
60
59
|
export declare type MultiSelectProps = typeof __propDef.props;
|
|
61
60
|
export declare type MultiSelectEvents = typeof __propDef.events;
|
package/Wiggle.svelte
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<script
|
|
1
|
+
<script>import { spring } from 'svelte/motion';
|
|
2
2
|
// bind to this state and set it to true from parent
|
|
3
3
|
export let wiggle = false;
|
|
4
4
|
// intended use case: set max value during wiggle for one of angle, scale, dx, dy through props
|
package/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { default } from './MultiSelect.svelte';
|
|
|
2
2
|
export declare type Option = string | number | ObjectOption;
|
|
3
3
|
export declare type ObjectOption = {
|
|
4
4
|
label: string | number;
|
|
5
|
-
value
|
|
5
|
+
value?: unknown;
|
|
6
6
|
title?: string;
|
|
7
7
|
disabled?: boolean;
|
|
8
8
|
preselected?: boolean;
|
|
@@ -28,3 +28,8 @@ export declare type DispatchEvents = {
|
|
|
28
28
|
focus: undefined;
|
|
29
29
|
blur: undefined;
|
|
30
30
|
};
|
|
31
|
+
export declare type MultiSelectEvents = {
|
|
32
|
+
[key in keyof DispatchEvents]: CustomEvent<DispatchEvents[key]>;
|
|
33
|
+
};
|
|
34
|
+
export declare const get_label: (op: Option) => string | number;
|
|
35
|
+
export declare const get_value: (op: Option) => unknown;
|
package/index.js
CHANGED
|
@@ -1 +1,5 @@
|
|
|
1
1
|
export { default } from './MultiSelect.svelte';
|
|
2
|
+
// get the label key from an option object or the option itself if it's a string or number
|
|
3
|
+
export const get_label = (op) => (op instanceof Object ? op.label : op);
|
|
4
|
+
// fallback on label if option is object and value is undefined
|
|
5
|
+
export const get_value = (op) => op instanceof Object ? op.value ?? op.label : op;
|
package/package.json
CHANGED
|
@@ -5,38 +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": "5.0.
|
|
8
|
+
"version": "5.0.3",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"svelte": "index.js",
|
|
11
|
+
"main": "index.js",
|
|
11
12
|
"bugs": "https://github.com/janosh/svelte-multiselect/issues",
|
|
12
13
|
"devDependencies": {
|
|
13
|
-
"@sveltejs/adapter-static": "^1.0.0-next.
|
|
14
|
-
"@sveltejs/kit": "
|
|
15
|
-
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.
|
|
16
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
17
|
-
"@typescript-eslint/parser": "^5.
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"eslint": "^8.12.0",
|
|
21
|
-
"eslint-plugin-svelte3": "^3.4.1",
|
|
14
|
+
"@sveltejs/adapter-static": "^1.0.0-next.34",
|
|
15
|
+
"@sveltejs/kit": "1.0.0-next.345",
|
|
16
|
+
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.49",
|
|
17
|
+
"@typescript-eslint/eslint-plugin": "^5.29.0",
|
|
18
|
+
"@typescript-eslint/parser": "^5.29.0",
|
|
19
|
+
"eslint": "^8.18.0",
|
|
20
|
+
"eslint-plugin-svelte3": "^4.0.0",
|
|
22
21
|
"hastscript": "^7.0.2",
|
|
23
|
-
"jsdom": "^
|
|
24
|
-
"mdsvex": "^0.10.
|
|
25
|
-
"playwright": "^1.
|
|
26
|
-
"prettier": "^2.
|
|
27
|
-
"prettier-plugin-svelte": "^2.
|
|
22
|
+
"jsdom": "^20.0.0",
|
|
23
|
+
"mdsvex": "^0.10.6",
|
|
24
|
+
"playwright": "^1.22.2",
|
|
25
|
+
"prettier": "^2.7.1",
|
|
26
|
+
"prettier-plugin-svelte": "^2.7.0",
|
|
28
27
|
"rehype-autolink-headings": "^6.1.1",
|
|
29
28
|
"rehype-slug": "^5.0.1",
|
|
30
|
-
"svelte": "^3.
|
|
31
|
-
"svelte-check": "^2.
|
|
29
|
+
"svelte": "^3.48.0",
|
|
30
|
+
"svelte-check": "^2.8.0",
|
|
32
31
|
"svelte-github-corner": "^0.1.0",
|
|
33
|
-
"svelte-preprocess": "^4.10.
|
|
32
|
+
"svelte-preprocess": "^4.10.6",
|
|
34
33
|
"svelte-toc": "^0.2.9",
|
|
35
|
-
"svelte2tsx": "^0.5.
|
|
36
|
-
"tslib": "^2.
|
|
37
|
-
"typescript": "^4.
|
|
38
|
-
"vite": "^2.9.
|
|
39
|
-
"vitest": "^0.
|
|
34
|
+
"svelte2tsx": "^0.5.11",
|
|
35
|
+
"tslib": "^2.4.0",
|
|
36
|
+
"typescript": "^4.7.4",
|
|
37
|
+
"vite": "^2.9.12",
|
|
38
|
+
"vitest": "^0.16.0"
|
|
40
39
|
},
|
|
41
40
|
"keywords": [
|
|
42
41
|
"svelte",
|
package/readme.md
CHANGED
|
@@ -22,29 +22,24 @@
|
|
|
22
22
|
|
|
23
23
|
## Key features
|
|
24
24
|
|
|
25
|
-
- **
|
|
25
|
+
- **Bindable:** `bind:selected` gives you an array of the currently selected options. Thanks to Svelte's 2-way binding, it can also control the component state externally through assignment `selected = ['foo', 42]`.
|
|
26
|
+
- **Keyboard friendly** for mouse-less form completion
|
|
27
|
+
- **No 3rd-party dependencies:** needs only Svelte as dev dependency
|
|
26
28
|
- **Dropdowns:** scrollable lists for large numbers of options
|
|
27
29
|
- **Searchable:** start typing to filter options
|
|
28
|
-
- **Tagging:** selected options are
|
|
29
|
-
- **
|
|
30
|
+
- **Tagging:** selected options are listed as tags within the input
|
|
31
|
+
- **Single / multiple select:** pass `maxSelect={1, 2, 3, ...}` prop to restrict the number of selectable options
|
|
30
32
|
- **Configurable:** see [props](#props)
|
|
31
|
-
- **No dependencies:** needs only Svelte as dev dependency
|
|
32
|
-
- **Keyboard friendly** for mouse-less form completion
|
|
33
33
|
|
|
34
34
|
<slot name="nav" />
|
|
35
35
|
|
|
36
36
|
## Recent breaking changes
|
|
37
37
|
|
|
38
|
-
- v4.0.0 renamed the slots for customizing how selected options and dropdown list items are rendered:
|
|
39
|
-
|
|
40
|
-
- old: `<slot name="renderOptions" />`, new: `<slot name="option" />`
|
|
41
|
-
- old: `<slot name="renderSelected" />`, new: `<slot name="selected" />`
|
|
42
|
-
|
|
43
38
|
- 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}`.
|
|
44
39
|
|
|
45
40
|
- 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`.
|
|
46
41
|
|
|
47
|
-
- v5.0.0 Support both simple and object options.Previously string or number options were converted to objects internally and returned by `bind:selected`. Now, if you pass in `string[]`, that's what you'll get from `bind:selected`.
|
|
42
|
+
- v5.0.0 Support both simple and object options. Previously string or number options were converted to objects internally and returned by `bind:selected`. Now, if you pass in `string[]`, that's what you'll get from `bind:selected`.
|
|
48
43
|
|
|
49
44
|
## Installation
|
|
50
45
|
|
|
@@ -79,18 +74,19 @@ Full list of props/bindable variables for this component:
|
|
|
79
74
|
<!-- prettier-ignore -->
|
|
80
75
|
| name | default | description |
|
|
81
76
|
| :--------------------- | :---------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
82
|
-
| `options` | required prop | Array of strings/numbers or `Option` objects
|
|
77
|
+
| `options` | required prop | Array of strings/numbers or `Option` objects to be listed in the dropdown. The only required key on objects is `label` which must also be unique. An object's `value` defaults to `label` if `undefined`. You can add arbitrary additional keys to your option objects. MultiSelect A few keys like `preselected` and `title` have special meaning though. See `src/lib/index.ts` for all special keys and their purpose. |
|
|
83
78
|
| `showOptions` | `false` | Bindable boolean that controls whether the options dropdown is visible. |
|
|
84
79
|
| `searchText` | `` | Text the user-entered to filter down on the list of options. Binds both ways, i.e. can also be used to set the input text. |
|
|
85
80
|
| `activeOption` | `null` | Currently active option, i.e. the one the user currently hovers or navigated to with arrow keys. |
|
|
86
81
|
| `maxSelect` | `null` | Positive integer to limit the number of options users can pick. `null` means no limit. |
|
|
87
|
-
| `selected` | `[]` | Array of currently
|
|
88
|
-
| `selectedLabels` | `[]` | Labels of currently selected options. Supports binding but is read-only, i.e. since this value is reactive to `selected`, you cannot control `selected` by changing `
|
|
89
|
-
| `selectedValues` | `[]` | Values of currently selected options. Supports binding but is read-only, i.e. since this value is reactive to `selected`, you cannot control `selected` by changing `selectedValues`.
|
|
82
|
+
| `selected` | `[]` | Array of currently selected options. Can be bound to `bind:selected={[1, 2, 3]}` to control component state externally or passed as prop to set pre-selected options that will already be populated when component mounts before any user interaction. |
|
|
83
|
+
| `selectedLabels` | `[]` | Labels of currently selected options. Exposed just for convenience, equivalent to `selected.map(op => op.label)` when options are objects. If options are simple strings, `selected === selectedLabels`. Supports binding but is read-only, i.e. since this value is reactive to `selected`, you cannot control `selected` by changing `bind:selectedLabels`. |
|
|
84
|
+
| `selectedValues` | `[]` | Values of currently selected options. Exposed just for convenience, equivalent to `selected.map(op => op.value)` when options are objects. If options are simple strings, `selected === selectedValues`. Supports binding but is read-only, i.e. since this value is reactive to `selected`, you cannot control `selected` by changing `bind:selectedValues`. |
|
|
85
|
+
| `matchingOptions` | `Option[]` | List of options currently displayed to the user. Same as `options` unless the user entered `searchText` in which case this array contains only those options for which `filterFunc = (op: Option, searchText: string) => boolean` returned `true` (see [exposed methods](#exposed-methods) below for details on `filterFunc`). |
|
|
90
86
|
| `sortSelected` | `boolean \| ((op1, op2) => number)` | Default behavior is to render selected items in the order they were chosen. `sortSelected={true}` uses default JS array sorting. A compare function enables custom logic for sorting selected options. See the [`/sort-selected`](https://svelte-multiselect.netlify.app/sort-selected) example. |
|
|
91
87
|
| `noOptionsMsg` | `'No matching options'` | What message to show if no options match the user-entered search string. |
|
|
92
88
|
| `disabled` | `false` | Disable the component. It will still be rendered but users won't be able to interact with it. |
|
|
93
|
-
| `disabledTitle` | `This field is disabled`
|
|
89
|
+
| `disabledTitle` | `'This field is disabled'` | Tooltip text to display on hover when the component is in `disabled` state. |
|
|
94
90
|
| `placeholder` | `undefined` | String shown in the text input when no option is selected. |
|
|
95
91
|
| `input` | `null` | Handle to the `<input>` DOM node. Only available after component mounts (`null` before then). |
|
|
96
92
|
| `outerDiv` | `null` | Handle to outer `<div class="multiselect">` that wraps the whole component. Only available after component mounts (`null` before then). |
|
|
@@ -99,12 +95,13 @@ Full list of props/bindable variables for this component:
|
|
|
99
95
|
| `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. |
|
|
100
96
|
| `autoScroll` | `true` | `false` disables keeping the active dropdown items in view when going up/down the list of options with arrow keys. |
|
|
101
97
|
| `allowUserOptions` | `false` | Whether users are allowed to enter values not in the dropdown list. `true` means add user-defined options to the selected list only, `'append'` means add to both options and selected. |
|
|
98
|
+
| `parseLabelsAsHtml` | `false` | Whether option labels should be passed to [Svelte's `@html` directive](https://svelte.dev/tutorial/html-tags) or inserted into the DOM as plain text. `true` will raise an error if `allowUserOptions` is also truthy as it makes your site susceptible to [cross-site scripting (XSS) attacks](https://wikipedia.org/wiki/Cross-site_scripting). |
|
|
102
99
|
| `addOptionMsg` | `'Create this option...'` | Message shown to users after entering text when no options match their query and `allowUserOptions` is truthy. |
|
|
103
100
|
| `loading` | `false` | Whether the component should display a spinner to indicate it's in loading state. Use `<slot name='spinner'>` to specify a custom spinner. |
|
|
104
101
|
| `removeBtnTitle` | `'Remove'` | Title text to display when user hovers over button (cross icon) to remove selected option. |
|
|
105
102
|
| `removeAllTitle` | `'Remove all'` | Title text to display when user hovers over remove-all button. |
|
|
106
103
|
| `defaultDisabledTitle` | `'This option is disabled'` | Title text to display when user hovers over a disabled option. Each option can override this through its `disabledTitle` attribute. |
|
|
107
|
-
| `autocomplete` | `'off'` | Applied to the `<input>`. Specifies if browser is permitted to auto-fill this form field. See [MDN docs](https://developer.mozilla.org/
|
|
104
|
+
| `autocomplete` | `'off'` | Applied to the `<input>`. Specifies if browser is permitted to auto-fill this form field. See [MDN docs](https://developer.mozilla.org/docs/Web/HTML/Attributes/autocomplete) for other admissible values. |
|
|
108
105
|
| `invalid` | `false` | If `required=true` and user tries to submit but `selected = []` is empty, `invalid` is automatically set to `true` and CSS class `invalid` applied to the top-level `div.multiselect`. `invalid` class is removed again as soon as the user selects an option. `invalid` can also be controlled externally by binding to it `<MultiSelect bind:invalid />` and setting it to `true` based on outside events or custom validation. |
|
|
109
106
|
|
|
110
107
|
</div>
|
|
@@ -239,6 +236,7 @@ If you only want to make small adjustments, you can pass the following CSS varia
|
|
|
239
236
|
- `background: var(--sms-disabled-bg, lightgray)`: Background when in disabled state.
|
|
240
237
|
- `div.multiselect input::placeholder`
|
|
241
238
|
- `color: var(--sms-placeholder-color)`
|
|
239
|
+
- `color: var(--sms-placeholder-opacity)`
|
|
242
240
|
- `div.multiselect > ul.selected > li`
|
|
243
241
|
- `background: var(--sms-selected-bg, rgba(0, 0, 0, 0.15))`: Background of selected options.
|
|
244
242
|
- `padding: var(--sms-selected-li-padding, 5pt 1pt)`: Height of selected options.
|
|
@@ -248,7 +246,7 @@ If you only want to make small adjustments, you can pass the following CSS varia
|
|
|
248
246
|
- `div.multiselect > ul.options`
|
|
249
247
|
- `background: var(--sms-options-bg, white)`: Background of dropdown list.
|
|
250
248
|
- `max-height: var(--sms-options-max-height, 50vh)`: Maximum height of options dropdown.
|
|
251
|
-
- `overscroll-behavior: var(--sms-options-overscroll, none)`: Whether scroll events bubble to parent elements when reaching the top/bottom of the options dropdown. See [MDN](https://developer.mozilla.org/
|
|
249
|
+
- `overscroll-behavior: var(--sms-options-overscroll, none)`: Whether scroll events bubble to parent elements when reaching the top/bottom of the options dropdown. See [MDN](https://developer.mozilla.org/docs/Web/CSS/overscroll-behavior).
|
|
252
250
|
- `box-shadow: var(--sms-options-shadow, 0 0 14pt -8pt black);`: Box shadow of dropdown list.
|
|
253
251
|
- `div.multiselect > ul.options > li`
|
|
254
252
|
- `scroll-margin: var(--sms-options-scroll-margin, 100px)`: Top/bottom margin to keep between dropdown list items and top/bottom screen edge when auto-scrolling list to keep items in view.
|