svelte-multiselect 4.0.0 → 4.0.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/MultiSelect.svelte +56 -46
- package/MultiSelect.svelte.d.ts +4 -1
- package/icons/{ReadOnly.svelte → Disabled.svelte} +0 -0
- package/icons/{ReadOnly.svelte.d.ts → Disabled.svelte.d.ts} +4 -4
- package/icons/index.d.ts +2 -2
- package/icons/index.js +2 -2
- package/package.json +11 -6
- package/readme.md +78 -37
- package/actions.d.ts +0 -3
- package/actions.js +0 -16
package/MultiSelect.svelte
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
<script >import { createEventDispatcher, onMount, tick } from 'svelte';
|
|
2
2
|
import { fly } from 'svelte/transition';
|
|
3
|
-
import { onClickOutside } from './actions';
|
|
4
3
|
import CircleSpinner from './CircleSpinner.svelte';
|
|
5
|
-
import { CrossIcon, ExpandIcon,
|
|
4
|
+
import { CrossIcon, ExpandIcon, DisabledIcon } from './icons';
|
|
6
5
|
import Wiggle from './Wiggle.svelte';
|
|
7
6
|
export let selected = [];
|
|
8
7
|
export let selectedLabels = [];
|
|
@@ -11,7 +10,8 @@ export let searchText = ``;
|
|
|
11
10
|
export let showOptions = false;
|
|
12
11
|
export let maxSelect = null; // null means any number of options are selectable
|
|
13
12
|
export let maxSelectMsg = null;
|
|
14
|
-
export let
|
|
13
|
+
export let disabled = false;
|
|
14
|
+
export let disabledTitle = `This field is disabled`;
|
|
15
15
|
export let options;
|
|
16
16
|
export let input = null;
|
|
17
17
|
export let placeholder = undefined;
|
|
@@ -32,12 +32,12 @@ export let liOptionClass = ``;
|
|
|
32
32
|
export let liActiveOptionClass = ``;
|
|
33
33
|
export let removeBtnTitle = `Remove`;
|
|
34
34
|
export let removeAllTitle = `Remove all`;
|
|
35
|
-
// https://github.com/sveltejs/svelte/issues/6964
|
|
36
35
|
export let defaultDisabledTitle = `This option is disabled`;
|
|
37
36
|
export let allowUserOptions = false;
|
|
38
37
|
export let autoScroll = true;
|
|
39
38
|
export let loading = false;
|
|
40
39
|
export let required = false;
|
|
40
|
+
export let autocomplete = `off`;
|
|
41
41
|
if (maxSelect !== null && maxSelect < 0) {
|
|
42
42
|
console.error(`maxSelect must be null or positive integer, got ${maxSelect}`);
|
|
43
43
|
}
|
|
@@ -46,7 +46,7 @@ if (!(options?.length > 0))
|
|
|
46
46
|
if (!Array.isArray(selected))
|
|
47
47
|
console.error(`selected prop must be an array`);
|
|
48
48
|
onMount(() => {
|
|
49
|
-
selected = _options.filter((op) => op?.preselected);
|
|
49
|
+
selected = _options.filter((op) => op?.preselected) ?? [];
|
|
50
50
|
});
|
|
51
51
|
let wiggle = false;
|
|
52
52
|
// formValue binds to input.form-control to prevent form submission if required
|
|
@@ -85,8 +85,7 @@ $: matchingEnabledOptions = matchingOptions.filter((op) => !op.disabled);
|
|
|
85
85
|
function add(label) {
|
|
86
86
|
if (maxSelect && maxSelect > 1 && selected.length >= maxSelect)
|
|
87
87
|
wiggle = true;
|
|
88
|
-
if (!
|
|
89
|
-
!selectedLabels.includes(label) &&
|
|
88
|
+
if (!selectedLabels.includes(label) &&
|
|
90
89
|
// for maxselect = 1 we always replace current option with new selection
|
|
91
90
|
(maxSelect === null || maxSelect === 1 || selected.length < maxSelect)) {
|
|
92
91
|
searchText = ``; // reset search string on selection
|
|
@@ -108,7 +107,7 @@ function add(label) {
|
|
|
108
107
|
}
|
|
109
108
|
}
|
|
110
109
|
function remove(label) {
|
|
111
|
-
if (selected.length === 0
|
|
110
|
+
if (selected.length === 0)
|
|
112
111
|
return;
|
|
113
112
|
const option = _options.find((option) => option.label === label);
|
|
114
113
|
if (!option) {
|
|
@@ -119,6 +118,8 @@ function remove(label) {
|
|
|
119
118
|
dispatch(`change`, { option, type: `remove` });
|
|
120
119
|
}
|
|
121
120
|
function setOptionsVisible(show) {
|
|
121
|
+
if (disabled)
|
|
122
|
+
return;
|
|
122
123
|
showOptions = show;
|
|
123
124
|
if (show)
|
|
124
125
|
input?.focus();
|
|
@@ -204,13 +205,16 @@ const handleEnterAndSpaceKeys = (handler) => (event) => {
|
|
|
204
205
|
<!-- z-index: 2 when showOptions is true ensures the ul.selected of one <MultiSelect />
|
|
205
206
|
display above those of another following shortly after it -->
|
|
206
207
|
<div
|
|
207
|
-
class:
|
|
208
|
+
class:disabled
|
|
208
209
|
class:single={maxSelect === 1}
|
|
209
210
|
class:open={showOptions}
|
|
210
211
|
class="multiselect {outerDivClass}"
|
|
211
212
|
on:mouseup|stopPropagation={() => setOptionsVisible(true)}
|
|
212
|
-
|
|
213
|
-
|
|
213
|
+
on:focusout={() => {
|
|
214
|
+
setOptionsVisible(false)
|
|
215
|
+
dispatch(`blur`)
|
|
216
|
+
}}
|
|
217
|
+
title={disabled ? disabledTitle : null}
|
|
214
218
|
>
|
|
215
219
|
<!-- invisible input, used only to prevent form submission if required=true and no options selected -->
|
|
216
220
|
<input {required} bind:value={formValue} tabindex="-1" class="form-control" />
|
|
@@ -221,7 +225,7 @@ display above those of another following shortly after it -->
|
|
|
221
225
|
<slot name="selected" {option} {idx}>
|
|
222
226
|
{option.label}
|
|
223
227
|
</slot>
|
|
224
|
-
{#if !
|
|
228
|
+
{#if !disabled}
|
|
225
229
|
<button
|
|
226
230
|
on:mouseup|stopPropagation={() => remove(option.label)}
|
|
227
231
|
on:keydown={handleEnterAndSpaceKeys(() => remove(option.label))}
|
|
@@ -236,13 +240,15 @@ display above those of another following shortly after it -->
|
|
|
236
240
|
<li style="display: contents;">
|
|
237
241
|
<input
|
|
238
242
|
bind:this={input}
|
|
239
|
-
autocomplete
|
|
243
|
+
{autocomplete}
|
|
240
244
|
bind:value={searchText}
|
|
241
245
|
on:mouseup|self|stopPropagation={() => setOptionsVisible(true)}
|
|
242
246
|
on:keydown={handleKeydown}
|
|
243
247
|
on:focus={() => setOptionsVisible(true)}
|
|
248
|
+
on:blur={() => setOptionsVisible(false)}
|
|
244
249
|
{id}
|
|
245
250
|
{name}
|
|
251
|
+
{disabled}
|
|
246
252
|
placeholder={selectedLabels.length ? `` : placeholder}
|
|
247
253
|
/>
|
|
248
254
|
</li>
|
|
@@ -252,8 +258,10 @@ display above those of another following shortly after it -->
|
|
|
252
258
|
<CircleSpinner />
|
|
253
259
|
</slot>
|
|
254
260
|
{/if}
|
|
255
|
-
{#if
|
|
256
|
-
<
|
|
261
|
+
{#if disabled}
|
|
262
|
+
<slot name="disabled-icon">
|
|
263
|
+
<DisabledIcon height="14pt" />
|
|
264
|
+
</slot>
|
|
257
265
|
{:else if selected.length > 0}
|
|
258
266
|
{#if maxSelect && (maxSelect > 1 || maxSelectMsg)}
|
|
259
267
|
<Wiggle bind:wiggle angle={20}>
|
|
@@ -297,6 +305,16 @@ display above those of another following shortly after it -->
|
|
|
297
305
|
class:active
|
|
298
306
|
class:disabled
|
|
299
307
|
class="{liOptionClass} {active ? liActiveOptionClass : ``}"
|
|
308
|
+
on:mouseover={() => {
|
|
309
|
+
if (disabled) return
|
|
310
|
+
activeOption = option
|
|
311
|
+
}}
|
|
312
|
+
on:focus={() => {
|
|
313
|
+
if (disabled) return
|
|
314
|
+
activeOption = option
|
|
315
|
+
}}
|
|
316
|
+
on:mouseout={() => (activeOption = null)}
|
|
317
|
+
on:blur={() => (activeOption = null)}
|
|
300
318
|
>
|
|
301
319
|
<slot name="option" {option} {idx}>
|
|
302
320
|
{option.label}
|
|
@@ -318,9 +336,11 @@ display above those of another following shortly after it -->
|
|
|
318
336
|
cursor: text;
|
|
319
337
|
padding: 0 3pt;
|
|
320
338
|
border: var(--sms-border, 1pt solid lightgray);
|
|
321
|
-
border-radius: var(--sms-border-radius,
|
|
339
|
+
border-radius: var(--sms-border-radius, 3pt);
|
|
322
340
|
background: var(--sms-input-bg);
|
|
323
341
|
min-height: var(--sms-input-min-height, 22pt);
|
|
342
|
+
color: var(--sms-text-color);
|
|
343
|
+
font-size: var(--sms-font-size, inherit);
|
|
324
344
|
}
|
|
325
345
|
:where(div.multiselect.open) {
|
|
326
346
|
z-index: var(--sms-open-z-index, 4);
|
|
@@ -328,8 +348,9 @@ display above those of another following shortly after it -->
|
|
|
328
348
|
:where(div.multiselect:focus-within) {
|
|
329
349
|
border: var(--sms-focus-border, 1pt solid var(--sms-active-color, cornflowerblue));
|
|
330
350
|
}
|
|
331
|
-
:where(div.multiselect.
|
|
332
|
-
background: var(--sms-
|
|
351
|
+
:where(div.multiselect.disabled) {
|
|
352
|
+
background: var(--sms-disabled-bg, lightgray);
|
|
353
|
+
cursor: not-allowed;
|
|
333
354
|
}
|
|
334
355
|
|
|
335
356
|
:where(div.multiselect > ul.selected) {
|
|
@@ -345,26 +366,24 @@ display above those of another following shortly after it -->
|
|
|
345
366
|
display: flex;
|
|
346
367
|
margin: 2pt;
|
|
347
368
|
line-height: normal;
|
|
348
|
-
padding: 1pt
|
|
369
|
+
padding: 1pt 5pt;
|
|
349
370
|
transition: 0.3s;
|
|
350
371
|
white-space: nowrap;
|
|
351
|
-
background: var(--sms-selected-bg,
|
|
372
|
+
background: var(--sms-selected-bg, rgba(0, 0, 0, 0.15));
|
|
352
373
|
height: var(--sms-selected-li-height);
|
|
374
|
+
color: var(--sms-selected-text-color, var(--sms-text-color));
|
|
353
375
|
}
|
|
354
|
-
:where(div.multiselect
|
|
355
|
-
align-items: center;
|
|
376
|
+
:where(div.multiselect button) {
|
|
356
377
|
border-radius: 50%;
|
|
357
378
|
display: flex;
|
|
358
|
-
cursor: pointer;
|
|
359
379
|
transition: 0.2s;
|
|
360
|
-
}
|
|
361
|
-
:where(div.multiselect button) {
|
|
362
380
|
color: inherit;
|
|
363
381
|
background: transparent;
|
|
364
382
|
border: none;
|
|
365
383
|
cursor: pointer;
|
|
366
384
|
outline: none;
|
|
367
|
-
padding: 0
|
|
385
|
+
padding: 0;
|
|
386
|
+
margin: 0 0 0 4pt; /* CSS reset */
|
|
368
387
|
}
|
|
369
388
|
:where(ul.selected > li button:hover, button.remove-all:hover, button:focus) {
|
|
370
389
|
color: var(--sms-remove-x-hover-focus-color, lightskyblue);
|
|
@@ -373,16 +392,19 @@ display above those of another following shortly after it -->
|
|
|
373
392
|
transform: scale(1.04);
|
|
374
393
|
}
|
|
375
394
|
|
|
395
|
+
:where(div.multiselect input) {
|
|
396
|
+
margin: auto 0; /* CSS reset */
|
|
397
|
+
padding: 0; /* CSS reset */
|
|
398
|
+
}
|
|
376
399
|
:where(div.multiselect > ul.selected > li > input) {
|
|
377
400
|
border: none;
|
|
378
401
|
outline: none;
|
|
379
402
|
background: none;
|
|
380
403
|
flex: 1; /* this + next line fix issue #12 https://git.io/JiDe3 */
|
|
381
404
|
min-width: 2em;
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
color: var(--sms-text-color, inherit);
|
|
405
|
+
color: inherit;
|
|
406
|
+
font-size: inherit;
|
|
407
|
+
cursor: inherit; /* needed for disabled state */
|
|
386
408
|
}
|
|
387
409
|
:where(div.multiselect > input.form-control) {
|
|
388
410
|
width: 2em;
|
|
@@ -392,6 +414,7 @@ display above those of another following shortly after it -->
|
|
|
392
414
|
outline: none;
|
|
393
415
|
z-index: -1;
|
|
394
416
|
opacity: 0;
|
|
417
|
+
pointer-events: none;
|
|
395
418
|
}
|
|
396
419
|
|
|
397
420
|
:where(div.multiselect > ul.options) {
|
|
@@ -421,28 +444,15 @@ display above those of another following shortly after it -->
|
|
|
421
444
|
padding: 3pt 2ex;
|
|
422
445
|
}
|
|
423
446
|
:where(div.multiselect > ul.options > li.selected) {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
3pt solid var(--sms-selected-color, green)
|
|
427
|
-
);
|
|
428
|
-
background: var(--sms-li-selected-bg, inherit);
|
|
429
|
-
color: var(--sms-li-selected-color, inherit);
|
|
430
|
-
}
|
|
431
|
-
:where(div.multiselect > ul.options > li:not(.selected):hover) {
|
|
432
|
-
border-left: var(
|
|
433
|
-
--sms-li-not-selected-hover-border-left,
|
|
434
|
-
3pt solid var(--sms-active-color, cornflowerblue)
|
|
435
|
-
);
|
|
447
|
+
background: var(--sms-li-selected-bg);
|
|
448
|
+
color: var(--sms-li-selected-color);
|
|
436
449
|
}
|
|
437
450
|
:where(div.multiselect > ul.options > li.active) {
|
|
438
|
-
background: var(--sms-li-active-bg, var(--sms-active-color,
|
|
451
|
+
background: var(--sms-li-active-bg, var(--sms-active-color, rgba(0, 0, 0, 0.15)));
|
|
439
452
|
}
|
|
440
453
|
:where(div.multiselect > ul.options > li.disabled) {
|
|
441
454
|
cursor: not-allowed;
|
|
442
455
|
background: var(--sms-li-disabled-bg, #f5f5f6);
|
|
443
456
|
color: var(--sms-li-disabled-text, #b8b8b8);
|
|
444
457
|
}
|
|
445
|
-
:where(div.multiselect > ul.options > li.disabled:hover) {
|
|
446
|
-
border-left: unset;
|
|
447
|
-
}
|
|
448
458
|
</style>
|
package/MultiSelect.svelte.d.ts
CHANGED
|
@@ -9,7 +9,8 @@ declare const __propDef: {
|
|
|
9
9
|
showOptions?: boolean | undefined;
|
|
10
10
|
maxSelect?: number | null | undefined;
|
|
11
11
|
maxSelectMsg?: ((current: number, max: number) => string) | null | undefined;
|
|
12
|
-
|
|
12
|
+
disabled?: boolean | undefined;
|
|
13
|
+
disabledTitle?: string | undefined;
|
|
13
14
|
options: ProtoOption[];
|
|
14
15
|
input?: HTMLInputElement | null | undefined;
|
|
15
16
|
placeholder?: string | undefined;
|
|
@@ -31,6 +32,7 @@ declare const __propDef: {
|
|
|
31
32
|
autoScroll?: boolean | undefined;
|
|
32
33
|
loading?: boolean | undefined;
|
|
33
34
|
required?: boolean | undefined;
|
|
35
|
+
autocomplete?: string | undefined;
|
|
34
36
|
};
|
|
35
37
|
events: {
|
|
36
38
|
mouseup: MouseEvent;
|
|
@@ -43,6 +45,7 @@ declare const __propDef: {
|
|
|
43
45
|
idx: any;
|
|
44
46
|
};
|
|
45
47
|
spinner: {};
|
|
48
|
+
'disabled-icon': {};
|
|
46
49
|
option: {
|
|
47
50
|
option: Option;
|
|
48
51
|
idx: any;
|
|
File without changes
|
|
@@ -10,9 +10,9 @@ declare const __propDef: {
|
|
|
10
10
|
};
|
|
11
11
|
slots: {};
|
|
12
12
|
};
|
|
13
|
-
export declare type
|
|
14
|
-
export declare type
|
|
15
|
-
export declare type
|
|
16
|
-
export default class
|
|
13
|
+
export declare type DisabledProps = typeof __propDef.props;
|
|
14
|
+
export declare type DisabledEvents = typeof __propDef.events;
|
|
15
|
+
export declare type DisabledSlots = typeof __propDef.slots;
|
|
16
|
+
export default class Disabled extends SvelteComponentTyped<DisabledProps, DisabledEvents, DisabledSlots> {
|
|
17
17
|
}
|
|
18
18
|
export {};
|
package/icons/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { default as CrossIcon } from './Cross.svelte';
|
|
2
1
|
export { default as ExpandIcon } from './ChevronExpand.svelte';
|
|
3
|
-
export { default as
|
|
2
|
+
export { default as CrossIcon } from './Cross.svelte';
|
|
3
|
+
export { default as DisabledIcon } from './Disabled.svelte';
|
package/icons/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { default as CrossIcon } from './Cross.svelte';
|
|
2
1
|
export { default as ExpandIcon } from './ChevronExpand.svelte';
|
|
3
|
-
export { default as
|
|
2
|
+
export { default as CrossIcon } from './Cross.svelte';
|
|
3
|
+
export { default as DisabledIcon } from './Disabled.svelte';
|
package/package.json
CHANGED
|
@@ -5,18 +5,22 @@
|
|
|
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.1",
|
|
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.28",
|
|
14
|
-
"@sveltejs/kit": "^1.0.0-next.
|
|
15
|
-
"@
|
|
14
|
+
"@sveltejs/kit": "^1.0.0-next.287",
|
|
15
|
+
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.38",
|
|
16
|
+
"@testing-library/svelte": "^3.0.3",
|
|
17
|
+
"@typescript-eslint/eslint-plugin": "^5.12.1",
|
|
16
18
|
"@typescript-eslint/parser": "^5.12.0",
|
|
19
|
+
"@vitest/ui": "^0.5.7",
|
|
17
20
|
"eslint": "^8.9.0",
|
|
18
|
-
"eslint-plugin-svelte3": "^3.4.
|
|
21
|
+
"eslint-plugin-svelte3": "^3.4.1",
|
|
19
22
|
"hastscript": "^7.0.2",
|
|
23
|
+
"jsdom": "^19.0.0",
|
|
20
24
|
"mdsvex": "^0.10.5",
|
|
21
25
|
"prettier": "^2.5.1",
|
|
22
26
|
"prettier-plugin-svelte": "^2.6.0",
|
|
@@ -25,12 +29,13 @@
|
|
|
25
29
|
"svelte": "^3.46.4",
|
|
26
30
|
"svelte-check": "^2.4.5",
|
|
27
31
|
"svelte-github-corner": "^0.1.0",
|
|
28
|
-
"svelte-preprocess": "^4.10.
|
|
32
|
+
"svelte-preprocess": "^4.10.4",
|
|
29
33
|
"svelte-toc": "^0.2.6",
|
|
30
34
|
"svelte2tsx": "^0.5.5",
|
|
31
35
|
"tslib": "^2.3.1",
|
|
32
36
|
"typescript": "^4.5.5",
|
|
33
|
-
"vite": "^2.8.4"
|
|
37
|
+
"vite": "^2.8.4",
|
|
38
|
+
"vitest": "^0.5.7"
|
|
34
39
|
},
|
|
35
40
|
"keywords": [
|
|
36
41
|
"svelte",
|
package/readme.md
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
<h4 align="center">
|
|
7
7
|
|
|
8
|
+
[](https://github.com/janosh/svelte-multiselect/actions/workflows/test.yml)
|
|
8
9
|
[](https://app.netlify.com/sites/svelte-multiselect/deploys)
|
|
9
10
|
[](https://npmjs.com/package/svelte-multiselect)
|
|
10
11
|
[](https://results.pre-commit.ci/latest/github/janosh/svelte-multiselect/main)
|
|
@@ -38,16 +39,19 @@
|
|
|
38
39
|
- v3.0.0 changed the `event.detail` payload for `'add'`, `'remove'` and `'change'` events from `token` to `option`, e.g.
|
|
39
40
|
|
|
40
41
|
```js
|
|
41
|
-
on:add={(e) => console.log(e.detail.token.label)} // v2
|
|
42
|
-
on:add={(e) => console.log(e.detail.option.label)} // v3
|
|
42
|
+
on:add={(e) => console.log(e.detail.token.label)} // v2
|
|
43
|
+
on:add={(e) => console.log(e.detail.option.label)} // v3
|
|
43
44
|
```
|
|
44
45
|
|
|
45
46
|
It also added a separate event type `removeAll` for when the user removes all currently selected options at once which previously fired a normal `remove`. The props `ulTokensClass` and `liTokenClass` were renamed to `ulSelectedClass` and `liSelectedClass`. Similarly, the CSS variable `--sms-token-bg` changed to `--sms-selected-bg`.
|
|
46
47
|
|
|
47
48
|
- v4.0.0 renamed the slots for customizing how selected options and dropdown list items are rendered:
|
|
49
|
+
|
|
48
50
|
- old: `<slot name="renderOptions" />`, new: `<slot name="option" />`
|
|
49
51
|
- old: `<slot name="renderSelected" />`, new: `<slot name="selected" />`
|
|
50
52
|
|
|
53
|
+
- 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}`.
|
|
54
|
+
|
|
51
55
|
## Installation
|
|
52
56
|
|
|
53
57
|
```sh
|
|
@@ -74,7 +78,7 @@ yarn add -D svelte-multiselect
|
|
|
74
78
|
`Spring`,
|
|
75
79
|
]
|
|
76
80
|
|
|
77
|
-
let selected
|
|
81
|
+
let selected = []
|
|
78
82
|
</script>
|
|
79
83
|
|
|
80
84
|
Favorite Web Frameworks?
|
|
@@ -91,26 +95,31 @@ Full list of props/bindable variables for this component:
|
|
|
91
95
|
<div class="table">
|
|
92
96
|
|
|
93
97
|
<!-- prettier-ignore -->
|
|
94
|
-
| name
|
|
95
|
-
|
|
|
96
|
-
| `options`
|
|
97
|
-
| `showOptions`
|
|
98
|
-
| `searchText`
|
|
99
|
-
| `activeOption`
|
|
100
|
-
| `maxSelect`
|
|
101
|
-
| `selected`
|
|
102
|
-
| `selectedLabels`
|
|
103
|
-
| `selectedValues`
|
|
104
|
-
| `noOptionsMsg`
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
110
|
-
| `
|
|
111
|
-
| `
|
|
112
|
-
| `
|
|
113
|
-
| `
|
|
98
|
+
| name | default | description |
|
|
99
|
+
| :--------------------- | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
100
|
+
| `options` | required prop | Array of strings/numbers or `Option` objects that will be listed in the dropdown. See `src/lib/index.ts` for admissible fields. The `label` is the only mandatory one. It must also be unique. |
|
|
101
|
+
| `showOptions` | `false` | Bindable boolean that controls whether the options dropdown is visible. |
|
|
102
|
+
| `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. |
|
|
103
|
+
| `activeOption` | `null` | Currently active option, i.e. the one the user currently hovers or navigated to with arrow keys. |
|
|
104
|
+
| `maxSelect` | `null` | Positive integer to limit the number of options users can pick. `null` means no limit. |
|
|
105
|
+
| `selected` | `[]` | Array of currently/pre-selected options when binding/passing as props respectively. |
|
|
106
|
+
| `selectedLabels` | `[]` | Labels of currently selected options. |
|
|
107
|
+
| `selectedValues` | `[]` | Values of currently selected options. |
|
|
108
|
+
| `noOptionsMsg` | `'No matching options'` | What message to show if no options match the user-entered search string. |
|
|
109
|
+
| `disabled` | `false` | Disable the component. It will still be rendered but users won't be able to interact with it. |
|
|
110
|
+
| `disabledTitle` | `This field is disabled` | Tooltip text to display on hover when the component is in `disabled` state. |
|
|
111
|
+
| `placeholder` | `undefined` | String shown in the text input when no option is selected. |
|
|
112
|
+
| `input` | `undefined` | Handle to the `<input>` DOM node. |
|
|
113
|
+
| `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. |
|
|
114
|
+
| `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>`. |
|
|
115
|
+
| `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. |
|
|
116
|
+
| `autoScroll` | `true` | `false` disables keeping the active dropdown items in view when going up/down the list of options with arrow keys. |
|
|
117
|
+
| `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. |
|
|
118
|
+
| `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. |
|
|
119
|
+
| `removeBtnTitle` | `'Remove'` | Title text to display when user hovers over button (cross icon) to remove selected option. |
|
|
120
|
+
| `removeAllTitle` | `'Remove all'` | Title text to display when user hovers over remove-all button. |
|
|
121
|
+
| `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. |
|
|
122
|
+
| `autocomplete` | `'off'` | Applied to the `<input>`. Specifies if browser is permitted to auto-fill this form field. See [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete) for other admissible values. |
|
|
114
123
|
|
|
115
124
|
</div>
|
|
116
125
|
|
|
@@ -138,6 +147,7 @@ Full list of props/bindable variables for this component:
|
|
|
138
147
|
- `slot="option"`: Customize rendering of dropdown options. Receives as props the `option` object and the zero-indexed position (`idx`) it has in the dropdown.
|
|
139
148
|
- `slot="selected"`: Customize rendering selected tags. Receives as props the `option` object and the zero-indexed position (`idx`) it has in the list of selected items.
|
|
140
149
|
- `slot="spinner"`: Custom spinner component to display when in `loading` state. Receives no props.
|
|
150
|
+
- `slot="disabled-icon"`: Custom icon to display inside the input when in `disabled` state. Receives no props. Use an empty `<span slot="disabled-icon" />` or `div` to remove the default disabled icon.
|
|
141
151
|
|
|
142
152
|
Example:
|
|
143
153
|
|
|
@@ -225,20 +235,20 @@ If you only want to make small adjustments, you can pass the following CSS varia
|
|
|
225
235
|
|
|
226
236
|
- `div.multiselect`
|
|
227
237
|
- `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.
|
|
228
|
-
- `border-radius: var(--sms-border-radius,
|
|
238
|
+
- `border-radius: var(--sms-border-radius, 3pt)`
|
|
229
239
|
- `background: var(--sms-input-bg)`
|
|
230
240
|
- `height: var(--sms-input-height, 2em)`
|
|
241
|
+
- `color: var(--sms-text-color)`
|
|
231
242
|
- `div.multiselect.open`
|
|
232
243
|
- `z-index: var(--sms-open-z-index, 4)`: Increase this if needed to ensure the dropdown list is displayed atop all other page elements.
|
|
233
244
|
- `div.multiselect:focus-within`
|
|
234
245
|
- `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`.
|
|
235
|
-
- `div.multiselect.
|
|
236
|
-
- `background: var(--sms-
|
|
237
|
-
- `div.multiselect > ul.selected > li > input`
|
|
238
|
-
- `color: var(--sms-text-color, inherit)`: Input text color.
|
|
246
|
+
- `div.multiselect.disabled`
|
|
247
|
+
- `background: var(--sms-disabled-bg, lightgray)`: Background when in disabled state.
|
|
239
248
|
- `div.multiselect > ul.selected > li`
|
|
240
|
-
- `background: var(--sms-selected-bg,
|
|
249
|
+
- `background: var(--sms-selected-bg, rgba(0, 0, 0, 0.15))`: Background of selected options.
|
|
241
250
|
- `height: var(--sms-selected-li-height)`: Height of selected options.
|
|
251
|
+
- `color: var(--sms-selected-text-color, var(--sms-text-color))`: Text color for selected options.
|
|
242
252
|
- `ul.selected > li button:hover, button.remove-all:hover, button:focus`
|
|
243
253
|
- `color: var(--sms-remove-x-hover-focus-color, lightskyblue)`: Color of the cross-icon buttons for removing all or individual selected options when in `:focus` or `:hover` state.
|
|
244
254
|
- `div.multiselect > ul.options`
|
|
@@ -249,13 +259,10 @@ If you only want to make small adjustments, you can pass the following CSS varia
|
|
|
249
259
|
- `div.multiselect > ul.options > li`
|
|
250
260
|
- `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.
|
|
251
261
|
- `div.multiselect > ul.options > li.selected`
|
|
252
|
-
- `
|
|
253
|
-
- `
|
|
254
|
-
- `color: var(--sms-li-selected-color, inherit)`: Text color of selected list items in options pane.
|
|
255
|
-
- `div.multiselect > ul.options > li:not(.selected):hover`
|
|
256
|
-
- `border-left: var(--sms-li-not-selected-hover-border-left, 3pt solid var(--sms-active-color, cornflowerblue))`
|
|
262
|
+
- `background: var(--sms-li-selected-bg)`: Background of selected list items in options pane.
|
|
263
|
+
- `color: var(--sms-li-selected-color)`: Text color of selected list items in options pane.
|
|
257
264
|
- `div.multiselect > ul.options > li.active`
|
|
258
|
-
- `background: var(--sms-li-active-bg, var(--sms-active-color,
|
|
265
|
+
- `background: var(--sms-li-active-bg, var(--sms-active-color, rgba(0, 0, 0, 0.15)))`: Background of active dropdown item. Items become active either by mouseover or by navigating to them with arrow keys.
|
|
259
266
|
- `div.multiselect > ul.options > li.disabled`
|
|
260
267
|
- `background: var(--sms-li-disabled-bg, #f5f5f6)`: Background of disabled options in the dropdown list.
|
|
261
268
|
- `color: var(--sms-li-disabled-text, #b8b8b8)`: Text color of disabled option in the dropdown list.
|
|
@@ -305,8 +312,8 @@ You can alternatively style every part of this component with more fine-grained
|
|
|
305
312
|
:global(div.multiselect.open) {
|
|
306
313
|
/* top-level wrapper div when dropdown open */
|
|
307
314
|
}
|
|
308
|
-
:global(div.multiselect.
|
|
309
|
-
/* top-level wrapper div when in
|
|
315
|
+
:global(div.multiselect.disabled) {
|
|
316
|
+
/* top-level wrapper div when in disabled state */
|
|
310
317
|
}
|
|
311
318
|
:global(div.multiselect > ul.selected) {
|
|
312
319
|
/* selected list */
|
|
@@ -344,6 +351,40 @@ You can alternatively style every part of this component with more fine-grained
|
|
|
344
351
|
}
|
|
345
352
|
```
|
|
346
353
|
|
|
354
|
+
## Downstream testing
|
|
355
|
+
|
|
356
|
+
To test a Svelte component which imports `svelte-multiselect`, you need to configure your test runner to avoid [transpiling issues](https://github.com/EmilTholin/svelte-routing/issues/140#issuecomment-661682571).
|
|
357
|
+
|
|
358
|
+
For Jest, exclude `svelte-multiselect` from `transformIgnorePatterns` in your `jest.config.json`:
|
|
359
|
+
|
|
360
|
+
```json
|
|
361
|
+
{
|
|
362
|
+
"transformIgnorePatterns": ["node_modules/?!(svelte-multiselect)"],
|
|
363
|
+
"transform": {
|
|
364
|
+
"^.+\\.[t|j]s?$": "esbuild-jest",
|
|
365
|
+
"^.+\\.svelte$": ["svelte-jester", { "preprocess": true }]
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
For Vitest, include `svelte-multiselect` in `deps.inline`:
|
|
371
|
+
|
|
372
|
+
```ts
|
|
373
|
+
// vite.config.ts
|
|
374
|
+
import { svelte } from '@sveltejs/vite-plugin-svelte'
|
|
375
|
+
|
|
376
|
+
export default {
|
|
377
|
+
plugins: [svelte({ hot: !process.env.VITEST })],
|
|
378
|
+
test: {
|
|
379
|
+
deps: {
|
|
380
|
+
inline: [/svelte-multiselect/],
|
|
381
|
+
},
|
|
382
|
+
},
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
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).
|
|
387
|
+
|
|
347
388
|
## Want to contribute?
|
|
348
389
|
|
|
349
390
|
To submit a PR, clone the repo, install dependencies and start the dev server to try out your changes.
|
package/actions.d.ts
DELETED
package/actions.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export function onClickOutside(node, cb) {
|
|
2
|
-
const dispatchOnClickOutside = (event) => {
|
|
3
|
-
const clickWasOutside = node && !node.contains(event.target);
|
|
4
|
-
if (clickWasOutside && !event.defaultPrevented) {
|
|
5
|
-
node.dispatchEvent(new CustomEvent(`clickOutside`));
|
|
6
|
-
if (cb)
|
|
7
|
-
cb();
|
|
8
|
-
}
|
|
9
|
-
};
|
|
10
|
-
document.addEventListener(`click`, dispatchOnClickOutside);
|
|
11
|
-
return {
|
|
12
|
-
destroy() {
|
|
13
|
-
document.removeEventListener(`click`, dispatchOnClickOutside);
|
|
14
|
-
},
|
|
15
|
-
};
|
|
16
|
-
}
|