svelte-multiselect 5.0.3 → 5.0.6
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 +93 -65
- package/MultiSelect.svelte.d.ts +2 -1
- package/icons/Octocat.svelte +5 -0
- package/icons/Octocat.svelte.d.ts +23 -0
- package/icons/index.d.ts +1 -0
- package/icons/index.js +1 -0
- package/index.js +21 -0
- package/package.json +13 -13
- package/readme.md +13 -7
package/MultiSelect.svelte
CHANGED
|
@@ -45,15 +45,24 @@ export let required = false;
|
|
|
45
45
|
export let autocomplete = `off`;
|
|
46
46
|
export let invalid = false;
|
|
47
47
|
export let sortSelected = false;
|
|
48
|
-
if (!(options?.length > 0))
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
if (!(options?.length > 0)) {
|
|
49
|
+
if (allowUserOptions) {
|
|
50
|
+
options = []; // initializing as array avoids errors when component mounts
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
// only error for empty options if user is not allowed to create custom options
|
|
54
|
+
console.error(`MultiSelect received no options`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (parseLabelsAsHtml && allowUserOptions) {
|
|
58
|
+
console.warn(`Don't combine parseLabelsAsHtml and allowUserOptions. It's susceptible to XSS attacks!`);
|
|
59
|
+
}
|
|
52
60
|
if (maxSelect !== null && maxSelect < 1) {
|
|
53
61
|
console.error(`maxSelect must be null or positive integer, got ${maxSelect}`);
|
|
54
62
|
}
|
|
55
|
-
if (!Array.isArray(selected))
|
|
56
|
-
console.error(`selected prop must be an array`);
|
|
63
|
+
if (!Array.isArray(selected)) {
|
|
64
|
+
console.error(`selected prop must be an array, got ${selected}`);
|
|
65
|
+
}
|
|
57
66
|
const dispatch = createEventDispatcher();
|
|
58
67
|
let activeMsg = false; // controls active state of <li>{addOptionMsg}</li>
|
|
59
68
|
let wiggle = false; // controls wiggle animation when user tries to exceed maxSelect
|
|
@@ -86,10 +95,19 @@ function add(label) {
|
|
|
86
95
|
searchText.length > 0) {
|
|
87
96
|
// user entered text but no options match, so if allowUserOptions=true | 'append', we create
|
|
88
97
|
// a new option from the user-entered text
|
|
89
|
-
if (typeof options[0] === `
|
|
90
|
-
option
|
|
91
|
-
else
|
|
98
|
+
if (typeof options[0] === `object`) {
|
|
99
|
+
// if 1st option is an object, we create new option as object to keep type homogeneity
|
|
92
100
|
option = { label: searchText, value: searchText };
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
if ([`number`, `undefined`].includes(typeof options[0]) &&
|
|
104
|
+
!isNaN(Number(searchText))) {
|
|
105
|
+
// create new option as number if it parses to a number and 1st option is also number or missing
|
|
106
|
+
option = Number(searchText);
|
|
107
|
+
}
|
|
108
|
+
else
|
|
109
|
+
option = searchText; // else create custom option as string
|
|
110
|
+
}
|
|
93
111
|
if (allowUserOptions === `append`)
|
|
94
112
|
options = [...options, option];
|
|
95
113
|
}
|
|
@@ -214,7 +232,10 @@ async function handleKeydown(event) {
|
|
|
214
232
|
// around start/end of option list. Find a better solution than waiting 10 ms to.
|
|
215
233
|
setTimeout(() => {
|
|
216
234
|
const li = document.querySelector(`ul.options > li.active`);
|
|
217
|
-
li
|
|
235
|
+
if (li) {
|
|
236
|
+
li.parentNode?.scrollIntoView({ block: `center` });
|
|
237
|
+
li.scrollIntoViewIfNeeded();
|
|
238
|
+
}
|
|
218
239
|
}, 10);
|
|
219
240
|
}
|
|
220
241
|
}
|
|
@@ -286,7 +307,7 @@ const if_enter_or_space = (handler) => (event) => {
|
|
|
286
307
|
type="button"
|
|
287
308
|
title="{removeBtnTitle} {get_label(option)}"
|
|
288
309
|
>
|
|
289
|
-
<CrossIcon width="15px"
|
|
310
|
+
<slot name="remove-icon"><CrossIcon width="15px" /></slot>
|
|
290
311
|
</button>
|
|
291
312
|
{/if}
|
|
292
313
|
</li>
|
|
@@ -334,69 +355,74 @@ const if_enter_or_space = (handler) => (event) => {
|
|
|
334
355
|
on:mouseup|stopPropagation={remove_all}
|
|
335
356
|
on:keydown={if_enter_or_space(remove_all)}
|
|
336
357
|
>
|
|
337
|
-
<CrossIcon width="15px"
|
|
358
|
+
<slot name="remove-icon"><CrossIcon width="15px" /></slot>
|
|
338
359
|
</button>
|
|
339
360
|
{/if}
|
|
340
361
|
{/if}
|
|
341
362
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
on:mouseup|stopPropagation={() => {
|
|
355
|
-
if (!disabled) is_selected(label) ? remove(label) : add(label)
|
|
356
|
-
}}
|
|
357
|
-
title={disabled ? disabledTitle : (is_selected(label) && selectedTitle) || title}
|
|
358
|
-
class:selected={is_selected(label)}
|
|
359
|
-
class:active
|
|
360
|
-
class:disabled
|
|
361
|
-
class="{liOptionClass} {active ? liActiveOptionClass : ``}"
|
|
362
|
-
on:mouseover={() => {
|
|
363
|
-
if (!disabled) activeOption = option
|
|
364
|
-
}}
|
|
365
|
-
on:focus={() => {
|
|
366
|
-
if (!disabled) activeOption = option
|
|
367
|
-
}}
|
|
368
|
-
on:mouseout={() => (activeOption = null)}
|
|
369
|
-
on:blur={() => (activeOption = null)}
|
|
370
|
-
aria-selected="false"
|
|
371
|
-
>
|
|
372
|
-
<slot name="option" {option} {idx}>
|
|
373
|
-
{#if parseLabelsAsHtml}
|
|
374
|
-
{@html get_label(option)}
|
|
375
|
-
{:else}
|
|
376
|
-
{get_label(option)}
|
|
377
|
-
{/if}
|
|
378
|
-
</slot>
|
|
379
|
-
</li>
|
|
380
|
-
{:else}
|
|
381
|
-
{#if allowUserOptions && searchText}
|
|
363
|
+
<!-- only render options dropdown if options or searchText is not empty needed to avoid briefly flashing empty dropdown -->
|
|
364
|
+
{#if searchText || options?.length > 0}
|
|
365
|
+
<ul class:hidden={!showOptions} class="options {ulOptionsClass}">
|
|
366
|
+
{#each matchingOptions as option, idx}
|
|
367
|
+
{@const {
|
|
368
|
+
label,
|
|
369
|
+
disabled = null,
|
|
370
|
+
title = null,
|
|
371
|
+
selectedTitle = null,
|
|
372
|
+
disabledTitle = defaultDisabledTitle,
|
|
373
|
+
} = option instanceof Object ? option : { label: option }}
|
|
374
|
+
{@const active = activeOption && get_label(activeOption) === label}
|
|
382
375
|
<li
|
|
383
376
|
on:mousedown|stopPropagation
|
|
384
|
-
on:mouseup|stopPropagation={() =>
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
377
|
+
on:mouseup|stopPropagation={() => {
|
|
378
|
+
if (!disabled) is_selected(label) ? remove(label) : add(label)
|
|
379
|
+
}}
|
|
380
|
+
title={disabled
|
|
381
|
+
? disabledTitle
|
|
382
|
+
: (is_selected(label) && selectedTitle) || title}
|
|
383
|
+
class:selected={is_selected(label)}
|
|
384
|
+
class:active
|
|
385
|
+
class:disabled
|
|
386
|
+
class="{liOptionClass} {active ? liActiveOptionClass : ``}"
|
|
387
|
+
on:mouseover={() => {
|
|
388
|
+
if (!disabled) activeOption = option
|
|
389
|
+
}}
|
|
390
|
+
on:focus={() => {
|
|
391
|
+
if (!disabled) activeOption = option
|
|
392
|
+
}}
|
|
393
|
+
on:mouseout={() => (activeOption = null)}
|
|
394
|
+
on:blur={() => (activeOption = null)}
|
|
391
395
|
aria-selected="false"
|
|
392
396
|
>
|
|
393
|
-
{
|
|
397
|
+
<slot name="option" {option} {idx}>
|
|
398
|
+
{#if parseLabelsAsHtml}
|
|
399
|
+
{@html get_label(option)}
|
|
400
|
+
{:else}
|
|
401
|
+
{get_label(option)}
|
|
402
|
+
{/if}
|
|
403
|
+
</slot>
|
|
394
404
|
</li>
|
|
395
405
|
{:else}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
406
|
+
{#if allowUserOptions && searchText}
|
|
407
|
+
<li
|
|
408
|
+
on:mousedown|stopPropagation
|
|
409
|
+
on:mouseup|stopPropagation={() => add(searchText)}
|
|
410
|
+
title={addOptionMsg}
|
|
411
|
+
class:active={activeMsg}
|
|
412
|
+
on:mouseover={() => (activeMsg = true)}
|
|
413
|
+
on:focus={() => (activeMsg = true)}
|
|
414
|
+
on:mouseout={() => (activeMsg = false)}
|
|
415
|
+
on:blur={() => (activeMsg = false)}
|
|
416
|
+
aria-selected="false"
|
|
417
|
+
>
|
|
418
|
+
{addOptionMsg}
|
|
419
|
+
</li>
|
|
420
|
+
{:else}
|
|
421
|
+
<span>{noOptionsMsg}</span>
|
|
422
|
+
{/if}
|
|
423
|
+
{/each}
|
|
424
|
+
</ul>
|
|
425
|
+
{/if}
|
|
400
426
|
</div>
|
|
401
427
|
|
|
402
428
|
<style>
|
|
@@ -476,9 +502,11 @@ const if_enter_or_space = (handler) => (event) => {
|
|
|
476
502
|
background: none;
|
|
477
503
|
flex: 1; /* this + next line fix issue #12 https://git.io/JiDe3 */
|
|
478
504
|
min-width: 2em;
|
|
479
|
-
color
|
|
505
|
+
/* ensure input uses text color and not --sms-selected-text-color */
|
|
506
|
+
color: var(--sms-text-color);
|
|
480
507
|
font-size: inherit;
|
|
481
508
|
cursor: inherit; /* needed for disabled state */
|
|
509
|
+
border-radius: 0; /* reset ul.selected > li */
|
|
482
510
|
}
|
|
483
511
|
:where(div.multiselect > ul.selected > li > input)::placeholder {
|
|
484
512
|
padding-left: 5pt;
|
package/MultiSelect.svelte.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SvelteComponentTyped } from "svelte";
|
|
2
|
-
import { MultiSelectEvents, Option } from './';
|
|
2
|
+
import type { MultiSelectEvents, Option } from './';
|
|
3
3
|
declare const __propDef: {
|
|
4
4
|
props: {
|
|
5
5
|
searchText?: string | undefined;
|
|
@@ -46,6 +46,7 @@ declare const __propDef: {
|
|
|
46
46
|
option: Option;
|
|
47
47
|
idx: any;
|
|
48
48
|
};
|
|
49
|
+
'remove-icon': {};
|
|
49
50
|
spinner: {};
|
|
50
51
|
'disabled-icon': {};
|
|
51
52
|
option: {
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg {...$$props} viewBox="0 0 24 24" fill="currentColor">
|
|
2
|
+
<path
|
|
3
|
+
d="M8.422 20.081c0 .896.01 1.753.016 2.285a.617.617 0 0 0 .422.58c2.078.686 4.317.718 6.414.091l.292-.087a.67.67 0 0 0 .478-.638c.005-.733.017-2.017.017-3.53c0-1.372-.477-2.25-1.031-2.707c3.399-.366 6.97-1.61 6.97-7.227c0-1.61-.592-2.91-1.566-3.934c.153-.366.688-1.866-.153-3.878c0 0-1.28-.403-4.201 1.5a14.76 14.76 0 0 0-3.82-.494c-1.298 0-2.597.165-3.819.494C5.52.65 4.24 1.036 4.24 1.036c-.84 2.012-.306 3.512-.153 3.878a5.565 5.565 0 0 0-1.566 3.934c0 5.598 3.552 6.86 6.951 7.227c-.439.366-.84 1.006-.973 1.957c-.879.384-3.075 1.006-4.45-1.207c-.286-.44-1.146-1.519-2.349-1.5c-1.28.018-.516.695.02.97c.648.347 1.393 1.646 1.565 2.067c.306.823 1.299 2.396 5.137 1.72Z"
|
|
4
|
+
/>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** @typedef {typeof __propDef.props} OctocatProps */
|
|
2
|
+
/** @typedef {typeof __propDef.events} OctocatEvents */
|
|
3
|
+
/** @typedef {typeof __propDef.slots} OctocatSlots */
|
|
4
|
+
export default class Octocat extends SvelteComponentTyped<{
|
|
5
|
+
[x: string]: any;
|
|
6
|
+
}, {
|
|
7
|
+
[evt: string]: CustomEvent<any>;
|
|
8
|
+
}, {}> {
|
|
9
|
+
}
|
|
10
|
+
export type OctocatProps = typeof __propDef.props;
|
|
11
|
+
export type OctocatEvents = typeof __propDef.events;
|
|
12
|
+
export type OctocatSlots = typeof __propDef.slots;
|
|
13
|
+
import { SvelteComponentTyped } from "svelte";
|
|
14
|
+
declare const __propDef: {
|
|
15
|
+
props: {
|
|
16
|
+
[x: string]: any;
|
|
17
|
+
};
|
|
18
|
+
events: {
|
|
19
|
+
[evt: string]: CustomEvent<any>;
|
|
20
|
+
};
|
|
21
|
+
slots: {};
|
|
22
|
+
};
|
|
23
|
+
export {};
|
package/icons/index.d.ts
CHANGED
package/icons/index.js
CHANGED
package/index.js
CHANGED
|
@@ -3,3 +3,24 @@ export { default } from './MultiSelect.svelte';
|
|
|
3
3
|
export const get_label = (op) => (op instanceof Object ? op.label : op);
|
|
4
4
|
// fallback on label if option is object and value is undefined
|
|
5
5
|
export const get_value = (op) => op instanceof Object ? op.value ?? op.label : op;
|
|
6
|
+
// Firefox lacks support for scrollIntoViewIfNeeded, see
|
|
7
|
+
// https://github.com/janosh/svelte-multiselect/issues/87
|
|
8
|
+
// this polyfill was copied from
|
|
9
|
+
// https://github.com/nuxodin/lazyfill/blob/a8e63/polyfills/Element/prototype/scrollIntoViewIfNeeded.js
|
|
10
|
+
if (typeof Element !== `undefined` &&
|
|
11
|
+
!Element.prototype?.scrollIntoViewIfNeeded) {
|
|
12
|
+
Element.prototype.scrollIntoViewIfNeeded = function (centerIfNeeded = true) {
|
|
13
|
+
const el = this;
|
|
14
|
+
new IntersectionObserver(function ([entry]) {
|
|
15
|
+
const ratio = entry.intersectionRatio;
|
|
16
|
+
if (ratio < 1) {
|
|
17
|
+
const place = ratio <= 0 && centerIfNeeded ? `center` : `nearest`;
|
|
18
|
+
el.scrollIntoView({
|
|
19
|
+
block: place,
|
|
20
|
+
inline: place,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
this.disconnect();
|
|
24
|
+
}).observe(this);
|
|
25
|
+
};
|
|
26
|
+
}
|
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": "5.0.
|
|
8
|
+
"version": "5.0.6",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"svelte": "index.js",
|
|
11
11
|
"main": "index.js",
|
|
12
12
|
"bugs": "https://github.com/janosh/svelte-multiselect/issues",
|
|
13
13
|
"devDependencies": {
|
|
14
|
-
"@
|
|
15
|
-
"@sveltejs/
|
|
16
|
-
"@sveltejs/
|
|
17
|
-
"@
|
|
18
|
-
"@typescript-eslint/
|
|
19
|
-
"eslint": "^
|
|
14
|
+
"@playwright/test": "^1.24.1",
|
|
15
|
+
"@sveltejs/adapter-static": "^1.0.0-next.38",
|
|
16
|
+
"@sveltejs/kit": "^1.0.0-next.396",
|
|
17
|
+
"@sveltejs/vite-plugin-svelte": "^1.0.1",
|
|
18
|
+
"@typescript-eslint/eslint-plugin": "^5.31.0",
|
|
19
|
+
"@typescript-eslint/parser": "^5.31.0",
|
|
20
|
+
"eslint": "^8.20.0",
|
|
20
21
|
"eslint-plugin-svelte3": "^4.0.0",
|
|
21
22
|
"hastscript": "^7.0.2",
|
|
22
23
|
"jsdom": "^20.0.0",
|
|
23
24
|
"mdsvex": "^0.10.6",
|
|
24
|
-
"playwright": "^1.22.2",
|
|
25
25
|
"prettier": "^2.7.1",
|
|
26
26
|
"prettier-plugin-svelte": "^2.7.0",
|
|
27
27
|
"rehype-autolink-headings": "^6.1.1",
|
|
28
28
|
"rehype-slug": "^5.0.1",
|
|
29
|
-
"svelte": "^3.
|
|
29
|
+
"svelte": "^3.49.0",
|
|
30
30
|
"svelte-check": "^2.8.0",
|
|
31
31
|
"svelte-github-corner": "^0.1.0",
|
|
32
32
|
"svelte-preprocess": "^4.10.6",
|
|
33
|
-
"svelte-toc": "^0.2.
|
|
34
|
-
"svelte2tsx": "^0.5.
|
|
33
|
+
"svelte-toc": "^0.2.10",
|
|
34
|
+
"svelte2tsx": "^0.5.13",
|
|
35
35
|
"tslib": "^2.4.0",
|
|
36
36
|
"typescript": "^4.7.4",
|
|
37
|
-
"vite": "^
|
|
38
|
-
"vitest": "^0.
|
|
37
|
+
"vite": "^3.0.4",
|
|
38
|
+
"vitest": "^0.19.1"
|
|
39
39
|
},
|
|
40
40
|
"keywords": [
|
|
41
41
|
"svelte",
|
package/readme.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
</h4>
|
|
15
15
|
|
|
16
|
-
**Keyboard-friendly, accessible multi-select
|
|
16
|
+
**Keyboard-friendly, accessible and highly customizable multi-select component.**
|
|
17
17
|
<strong class="hide-in-docs">
|
|
18
18
|
<a href="https://svelte-multiselect.netlify.app">Docs</a>
|
|
19
19
|
</strong>
|
|
@@ -24,7 +24,7 @@
|
|
|
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
26
|
- **Keyboard friendly** for mouse-less form completion
|
|
27
|
-
- **No
|
|
27
|
+
- **No run-time deps:** needs only Svelte as dev dependency
|
|
28
28
|
- **Dropdowns:** scrollable lists for large numbers of options
|
|
29
29
|
- **Searchable:** start typing to filter options
|
|
30
30
|
- **Tagging:** selected options are listed as tags within the input
|
|
@@ -35,15 +35,17 @@
|
|
|
35
35
|
|
|
36
36
|
## Recent breaking changes
|
|
37
37
|
|
|
38
|
-
- v4.0.1 renamed the `readonly` prop to `disabled` which now prevents all form
|
|
38
|
+
- v4.0.1 renamed the `readonly` prop to `disabled` which now prevents all form of 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 `div.multiselect.{readonly=>disabled}`.
|
|
39
39
|
|
|
40
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`.
|
|
41
41
|
|
|
42
|
-
- v5.0.0 Support both simple and object options. Previously
|
|
42
|
+
- v5.0.0 Support both simple and object options. Previously strings and numbers were converted to `{ value, label }` objects internally and returned by `bind:selected`. Now, if you pass in `string[]`, that's exactly what you'll get from `bind:selected`.
|
|
43
43
|
|
|
44
44
|
## Installation
|
|
45
45
|
|
|
46
46
|
```sh
|
|
47
|
+
npm install -D svelte-multiselect
|
|
48
|
+
pnpm install -D svelte-multiselect
|
|
47
49
|
yarn add -D svelte-multiselect
|
|
48
50
|
```
|
|
49
51
|
|
|
@@ -98,7 +100,7 @@ Full list of props/bindable variables for this component:
|
|
|
98
100
|
| `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). |
|
|
99
101
|
| `addOptionMsg` | `'Create this option...'` | Message shown to users after entering text when no options match their query and `allowUserOptions` is truthy. |
|
|
100
102
|
| `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. |
|
|
101
|
-
| `removeBtnTitle` | `'Remove'` | Title text to display when user hovers over button (
|
|
103
|
+
| `removeBtnTitle` | `'Remove'` | Title text to display when user hovers over button to remove selected option (which defaults to a cross icon). |
|
|
102
104
|
| `removeAllTitle` | `'Remove all'` | Title text to display when user hovers over remove-all button. |
|
|
103
105
|
| `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. |
|
|
104
106
|
| `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,9 +110,11 @@ Full list of props/bindable variables for this component:
|
|
|
108
110
|
|
|
109
111
|
## Exposed methods
|
|
110
112
|
|
|
111
|
-
1. `filterFunc = (op: Option, searchText: string) => boolean`:
|
|
113
|
+
1. `filterFunc = (op: Option, searchText: string) => boolean`: Customize how dropdown options are filtered when user enters search string into `<MultiSelect />`. Defaults to:
|
|
112
114
|
|
|
113
115
|
```ts
|
|
116
|
+
import type { Option } from 'svelte-multiselect'
|
|
117
|
+
|
|
114
118
|
filterFunc = (op: Option, searchText: string) => {
|
|
115
119
|
if (!searchText) return true
|
|
116
120
|
return `${op.label}`.toLowerCase().includes(searchText.toLowerCase())
|
|
@@ -131,6 +135,7 @@ Full list of props/bindable variables for this component:
|
|
|
131
135
|
- `slot="selected"`: Customize rendering of selected items. Receives as props an `option` and the zero-indexed position (`idx`) it has in the list of selected items.
|
|
132
136
|
- `slot="spinner"`: Custom spinner component to display when in `loading` state. Receives no props.
|
|
133
137
|
- `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.
|
|
138
|
+
- `slot="remove-icon"`: Custom icon to display as remove button. Will be used both by buttons to remove individual selected options and the 'remove all' button that clears all options at once. Receives no props.
|
|
134
139
|
|
|
135
140
|
Example:
|
|
136
141
|
|
|
@@ -149,6 +154,7 @@ Example:
|
|
|
149
154
|
</span>
|
|
150
155
|
|
|
151
156
|
<CustomSpinner slot="spinner">
|
|
157
|
+
<strong slot="remove-icon">X</strong>
|
|
152
158
|
</MultiSelect>
|
|
153
159
|
```
|
|
154
160
|
|
|
@@ -242,7 +248,7 @@ If you only want to make small adjustments, you can pass the following CSS varia
|
|
|
242
248
|
- `padding: var(--sms-selected-li-padding, 5pt 1pt)`: Height of selected options.
|
|
243
249
|
- `color: var(--sms-selected-text-color, var(--sms-text-color))`: Text color for selected options.
|
|
244
250
|
- `ul.selected > li button:hover, button.remove-all:hover, button:focus`
|
|
245
|
-
- `color: var(--sms-button-hover-color, lightskyblue)`: Color of the
|
|
251
|
+
- `color: var(--sms-button-hover-color, lightskyblue)`: Color of the remove-icon buttons for removing all or individual selected options when in `:focus` or `:hover` state.
|
|
246
252
|
- `div.multiselect > ul.options`
|
|
247
253
|
- `background: var(--sms-options-bg, white)`: Background of dropdown list.
|
|
248
254
|
- `max-height: var(--sms-options-max-height, 50vh)`: Maximum height of options dropdown.
|