svelte-multiselect 7.0.2 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MultiSelect.svelte +25 -7
- package/MultiSelect.svelte.d.ts +3 -0
- package/icons/Cross.svelte +3 -2
- package/package.json +1 -1
- package/readme.md +24 -3
package/MultiSelect.svelte
CHANGED
|
@@ -12,6 +12,10 @@ export let breakpoint = 800; // any screen with more horizontal pixels is consid
|
|
|
12
12
|
export let defaultDisabledTitle = `This option is disabled`;
|
|
13
13
|
export let disabled = false;
|
|
14
14
|
export let disabledInputTitle = `This input is disabled`;
|
|
15
|
+
// case-insensitive equality comparison after string coercion (looking only at the `label` key of object options)
|
|
16
|
+
export let duplicateFunc = (op1, op2) => `${get_label(op1)}`.toLowerCase() === `${get_label(op2)}`.toLowerCase();
|
|
17
|
+
export let duplicateOptionMsg = `This option is already selected`;
|
|
18
|
+
export let duplicates = false; // whether to allow duplicate options
|
|
15
19
|
export let filterFunc = (op, searchText) => {
|
|
16
20
|
if (!searchText)
|
|
17
21
|
return true;
|
|
@@ -104,8 +108,11 @@ $: activeOption = activeIndex !== null ? matchingOptions[activeIndex] : null;
|
|
|
104
108
|
function add(label, event) {
|
|
105
109
|
if (maxSelect && maxSelect > 1 && _selected.length >= maxSelect)
|
|
106
110
|
wiggle = true;
|
|
107
|
-
|
|
108
|
-
|
|
111
|
+
if (!isNaN(Number(label)) && typeof _selectedLabels[0] === `number`)
|
|
112
|
+
label = Number(label); // convert to number if possible
|
|
113
|
+
const is_duplicate = _selected.some((option) => duplicateFunc(option, label));
|
|
114
|
+
if ((maxSelect === null || maxSelect === 1 || _selected.length < maxSelect) &&
|
|
115
|
+
(duplicates || !is_duplicate)) {
|
|
109
116
|
// first check if we find option in the options list
|
|
110
117
|
let option = options.find((op) => get_label(op) === label);
|
|
111
118
|
if (!option && // this has the side-effect of not allowing to user to add the same
|
|
@@ -130,6 +137,9 @@ function add(label, event) {
|
|
|
130
137
|
if (allowUserOptions === `append`)
|
|
131
138
|
options = [...options, option];
|
|
132
139
|
}
|
|
140
|
+
if (option === undefined) {
|
|
141
|
+
throw `Run time error, option with label ${label} not found in options list`;
|
|
142
|
+
}
|
|
133
143
|
searchText = ``; // reset search string on selection
|
|
134
144
|
if ([``, undefined, null].includes(option)) {
|
|
135
145
|
console.error(`MultiSelect: encountered missing option with label ${label} (or option is poorly labeled)`);
|
|
@@ -325,7 +335,9 @@ function on_click_outside(event) {
|
|
|
325
335
|
type="button"
|
|
326
336
|
title="{removeBtnTitle} {get_label(option)}"
|
|
327
337
|
>
|
|
328
|
-
<slot name="remove-icon"
|
|
338
|
+
<slot name="remove-icon">
|
|
339
|
+
<CrossIcon width="15px" />
|
|
340
|
+
</slot>
|
|
329
341
|
</button>
|
|
330
342
|
{/if}
|
|
331
343
|
</li>
|
|
@@ -389,7 +401,9 @@ function on_click_outside(event) {
|
|
|
389
401
|
on:mouseup|stopPropagation={remove_all}
|
|
390
402
|
on:keydown={if_enter_or_space(remove_all)}
|
|
391
403
|
>
|
|
392
|
-
<slot name="remove-icon"
|
|
404
|
+
<slot name="remove-icon">
|
|
405
|
+
<CrossIcon width="15px" />
|
|
406
|
+
</slot>
|
|
393
407
|
</button>
|
|
394
408
|
{/if}
|
|
395
409
|
{/if}
|
|
@@ -449,7 +463,9 @@ function on_click_outside(event) {
|
|
|
449
463
|
on:blur={() => (add_option_msg_is_active = false)}
|
|
450
464
|
aria-selected="false"
|
|
451
465
|
>
|
|
452
|
-
{
|
|
466
|
+
{!duplicates && _selected.some((option) => duplicateFunc(option, searchText))
|
|
467
|
+
? duplicateOptionMsg
|
|
468
|
+
: addOptionMsg}
|
|
453
469
|
</li>
|
|
454
470
|
{:else}
|
|
455
471
|
<span>{noOptionsMsg}</span>
|
|
@@ -523,7 +539,8 @@ function on_click_outside(event) {
|
|
|
523
539
|
:where(div.multiselect) ul.selected > li button:hover,
|
|
524
540
|
:where(div.multiselect) button.remove-all:hover,
|
|
525
541
|
:where(div.multiselect) button:focus {
|
|
526
|
-
color: var(--sms-
|
|
542
|
+
color: var(--sms-remove-btn-hover-color, lightskyblue);
|
|
543
|
+
background: var(--sms-remove-btn-hover-bg, rgba(0, 0, 0, 0.2));
|
|
527
544
|
}
|
|
528
545
|
:where(div.multiselect) input {
|
|
529
546
|
margin: auto 0; /* CSS reset */
|
|
@@ -662,7 +679,8 @@ function on_click_outside(event) {
|
|
|
662
679
|
div.multiselect ul.selected > li button:hover,
|
|
663
680
|
div.multiselect button.remove-all:hover,
|
|
664
681
|
div.multiselect button:focus {
|
|
665
|
-
color: var(--sms-
|
|
682
|
+
color: var(--sms-remove-btn-hover-color, lightskyblue);
|
|
683
|
+
background: var(--sms-remove-btn-hover-bg, rgba(0, 0, 0, 0.2));
|
|
666
684
|
}
|
|
667
685
|
div.multiselect input {
|
|
668
686
|
margin: auto 0; /* CSS reset */
|
package/MultiSelect.svelte.d.ts
CHANGED
|
@@ -12,6 +12,9 @@ declare const __propDef: {
|
|
|
12
12
|
defaultDisabledTitle?: string | undefined;
|
|
13
13
|
disabled?: boolean | undefined;
|
|
14
14
|
disabledInputTitle?: string | undefined;
|
|
15
|
+
duplicateFunc?: ((op1: Option, op2: Option) => boolean) | undefined;
|
|
16
|
+
duplicateOptionMsg?: string | undefined;
|
|
17
|
+
duplicates?: boolean | undefined;
|
|
15
18
|
filterFunc?: ((op: Option, searchText: string) => boolean) | undefined;
|
|
16
19
|
focusInputOnSelect?: boolean | "desktop" | undefined;
|
|
17
20
|
id?: string | null | undefined;
|
package/icons/Cross.svelte
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
<svg {...$$props} viewBox="0 0
|
|
1
|
+
<svg {...$$props} viewBox="0 0 24 24" fill="currentColor">
|
|
2
2
|
<path
|
|
3
|
-
d="
|
|
3
|
+
d="M18.3 5.71a.996.996 0 0 0-1.41 0L12 10.59L7.11 5.7A.996.996 0 1 0 5.7 7.11L10.59 12L5.7 16.89a.996.996 0 1 0 1.41 1.41L12 13.41l4.89 4.89a.996.996 0 1 0 1.41-1.41L13.41 12l4.89-4.89c.38-.38.38-1.02 0-1.4z"
|
|
4
4
|
/>
|
|
5
5
|
</svg>
|
|
6
|
+
<!-- https://api.iconify.design/ic:round-clear.svg -->
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -138,6 +138,26 @@ import type { Option } from 'svelte-multiselect'
|
|
|
138
138
|
|
|
139
139
|
Tooltip text to display on hover when the component is in `disabled` state.
|
|
140
140
|
|
|
141
|
+
<!-- prettier-ignore -->
|
|
142
|
+
1. ```ts
|
|
143
|
+
duplicateFunc: (op1: Option, op2: Option) => boolean = (op1, op2) =>
|
|
144
|
+
`${get_label(op1)}`.toLowerCase() === `${get_label(op2)}`.toLowerCase()
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
This option determines when two options are considered duplicates. Defaults to case-insensitive equality comparison after string coercion (looking only at the `label` key of object options). I.e. the default `duplicateFunc` considers `'Foo' == 'foo'`, `'42' == 42` and ``{ label: `Foo`, value: 0 } == { label: `foo`, value: 42 }``.
|
|
148
|
+
|
|
149
|
+
1. ```ts
|
|
150
|
+
duplicates: boolean = false
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Whether to allow users to select duplicate options. Applies only to the selected item list, not the options dropdown. Keeping that free of duplicates is left to developer. The selected item list can have duplicates if `allowUserOptions` is truthy, `duplicates` is ` true` and users create the same option multiple times. Use `duplicateOptionMsg` to customize the message shown to user if `duplicates` is `false` and users attempt this and `duplicateFunc` to customize when a pair of options is considered a duplicate.
|
|
154
|
+
|
|
155
|
+
1. ```ts
|
|
156
|
+
duplicateOptionMsg: string = `This option is already selected`
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Text to display to users when `allowUserOptions` is truthy and they try to create a new option that's already selected.
|
|
160
|
+
|
|
141
161
|
1. ```ts
|
|
142
162
|
filterFunc = (op: Option, searchText: string): boolean => {
|
|
143
163
|
if (!searchText) return true
|
|
@@ -290,13 +310,13 @@ import type { Option } from 'svelte-multiselect'
|
|
|
290
310
|
selectedLabels: (string | number)[] | string | number | null = []
|
|
291
311
|
```
|
|
292
312
|
|
|
293
|
-
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`. If `maxSelect={1}`, selectedLabels will not be an array but a single `string | number` or `null` if no options are selected.
|
|
313
|
+
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 (or numbers), `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`. If `maxSelect={1}`, selectedLabels will not be an array but a single `string | number` or `null` if no options are selected.
|
|
294
314
|
|
|
295
315
|
1. ```ts
|
|
296
316
|
selectedValues: unknown[] | unknown | null = []
|
|
297
317
|
```
|
|
298
318
|
|
|
299
|
-
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`. If `maxSelect={1}`, selectedLabels will not be an array but a single value or `null` if no options are selected.
|
|
319
|
+
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 (or numbers), `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`. If `maxSelect={1}`, selectedLabels will not be an array but a single value or `null` if no options are selected.
|
|
300
320
|
|
|
301
321
|
1. ```ts
|
|
302
322
|
sortSelected: boolean | ((op1: Option, op2: Option) => number) = false
|
|
@@ -460,7 +480,8 @@ If you only want to make small adjustments, you can pass the following CSS varia
|
|
|
460
480
|
- `padding: var(--sms-selected-li-padding, 1pt 5pt)`: Height of selected options.
|
|
461
481
|
- `color: var(--sms-selected-text-color, var(--sms-text-color))`: Text color for selected options.
|
|
462
482
|
- `ul.selected > li button:hover, button.remove-all:hover, button:focus`
|
|
463
|
-
- `color: var(--sms-
|
|
483
|
+
- `color: var(--sms-remove-btn-hover-color, lightskyblue)`: Color of the remove-icon buttons for removing all or individual selected options when in `:focus` or `:hover` state.
|
|
484
|
+
- `background: var(--sms-remove-btn-hover-bg, rgba(0, 0, 0, 0.2))`: Background for hovered remove buttons.
|
|
464
485
|
- `div.multiselect > ul.options`
|
|
465
486
|
- `background: var(--sms-options-bg, white)`: Background of dropdown list.
|
|
466
487
|
- `max-height: var(--sms-options-max-height, 50vh)`: Maximum height of options dropdown.
|