svelte-multiselect 8.2.3 โ 8.3.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 +40 -28
- package/MultiSelect.svelte.d.ts +13 -12
- package/changelog.md +32 -6
- package/icons/ChevronExpand.svelte.d.ts +2 -2
- package/icons/Cross.svelte.d.ts +2 -2
- package/icons/Disabled.svelte.d.ts +2 -2
- package/icons/Octocat.svelte.d.ts +2 -2
- package/index.d.ts +4 -1
- package/index.js +21 -15
- package/package.json +20 -21
- package/readme.md +53 -31
package/MultiSelect.svelte
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
<script>import { createEventDispatcher, tick } from 'svelte';
|
|
2
2
|
import { flip } from 'svelte/animate';
|
|
3
|
-
import CircleSpinner from '
|
|
3
|
+
import { CircleSpinner, Wiggle } from '.';
|
|
4
4
|
import { CrossIcon, DisabledIcon, ExpandIcon } from './icons';
|
|
5
|
-
import Wiggle from './Wiggle.svelte';
|
|
6
5
|
export let activeIndex = null;
|
|
7
6
|
export let activeOption = null;
|
|
8
|
-
export let
|
|
7
|
+
export let createOptionMsg = `Create this option...`;
|
|
9
8
|
export let allowUserOptions = false;
|
|
9
|
+
export let allowEmpty = false; // added for https://github.com/janosh/svelte-multiselect/issues/192
|
|
10
10
|
export let autocomplete = `off`;
|
|
11
11
|
export let autoScroll = true;
|
|
12
12
|
export let breakpoint = 800; // any screen with more horizontal pixels is considered desktop, below is mobile
|
|
@@ -14,6 +14,7 @@ export let defaultDisabledTitle = `This option is disabled`;
|
|
|
14
14
|
export let disabled = false;
|
|
15
15
|
export let disabledInputTitle = `This input is disabled`;
|
|
16
16
|
// case-insensitive equality comparison after string coercion (looking only at the `label` key of object options)
|
|
17
|
+
// prettier-ignore
|
|
17
18
|
export let duplicateFunc = (op1, op2) => `${get_label(op1)}`.toLowerCase() === `${get_label(op2)}`.toLowerCase();
|
|
18
19
|
export let duplicateOptionMsg = `This option is already selected`;
|
|
19
20
|
export let duplicates = false; // whether to allow duplicate options
|
|
@@ -53,7 +54,7 @@ export let required = false;
|
|
|
53
54
|
export let resetFilterOnAdd = true;
|
|
54
55
|
export let searchText = ``;
|
|
55
56
|
export let selected = options
|
|
56
|
-
?.filter((op) => op?.preselected)
|
|
57
|
+
?.filter((op) => op instanceof Object && op?.preselected)
|
|
57
58
|
.slice(0, maxSelect ?? undefined) ?? [];
|
|
58
59
|
export let sortSelected = false;
|
|
59
60
|
export let selectedOptionsDraggable = !sortSelected;
|
|
@@ -61,25 +62,30 @@ export let ulOptionsClass = ``;
|
|
|
61
62
|
export let ulSelectedClass = ``;
|
|
62
63
|
export let value = null;
|
|
63
64
|
// get the label key from an option object or the option itself if it's a string or number
|
|
64
|
-
const get_label = (op) =>
|
|
65
|
+
const get_label = (op) => {
|
|
66
|
+
if (op instanceof Object) {
|
|
67
|
+
if (op.label === undefined) {
|
|
68
|
+
console.error(`MultiSelect option ${JSON.stringify(op)} is an object but has no label key`);
|
|
69
|
+
}
|
|
70
|
+
return op.label;
|
|
71
|
+
}
|
|
72
|
+
return op;
|
|
73
|
+
};
|
|
65
74
|
// if maxSelect=1, value is the single item in selected (or null if selected is empty)
|
|
66
75
|
// this solves both https://github.com/janosh/svelte-multiselect/issues/86 and
|
|
67
76
|
// https://github.com/janosh/svelte-multiselect/issues/136
|
|
68
77
|
$: value = maxSelect === 1 ? selected[0] ?? null : selected;
|
|
69
78
|
let wiggle = false; // controls wiggle animation when user tries to exceed maxSelect
|
|
70
79
|
if (!(options?.length > 0)) {
|
|
71
|
-
if (allowUserOptions || loading || disabled) {
|
|
80
|
+
if (allowUserOptions || loading || disabled || allowEmpty) {
|
|
72
81
|
options = []; // initializing as array avoids errors when component mounts
|
|
73
82
|
}
|
|
74
83
|
else {
|
|
75
|
-
//
|
|
76
|
-
//
|
|
84
|
+
// error on empty options if user is not allowed to create custom options and loading is false
|
|
85
|
+
// and component is not disabled and allowEmpty is false
|
|
77
86
|
console.error(`MultiSelect received no options`);
|
|
78
87
|
}
|
|
79
88
|
}
|
|
80
|
-
if (parseLabelsAsHtml && allowUserOptions) {
|
|
81
|
-
console.warn(`Don't combine parseLabelsAsHtml and allowUserOptions. It's susceptible to XSS attacks!`);
|
|
82
|
-
}
|
|
83
89
|
if (maxSelect !== null && maxSelect < 1) {
|
|
84
90
|
console.error(`MultiSelect's maxSelect must be null or positive integer, got ${maxSelect}`);
|
|
85
91
|
}
|
|
@@ -89,11 +95,14 @@ if (!Array.isArray(selected)) {
|
|
|
89
95
|
if (maxSelect && typeof required === `number` && required > maxSelect) {
|
|
90
96
|
console.error(`MultiSelect maxSelect=${maxSelect} < required=${required}, makes it impossible for users to submit a valid form`);
|
|
91
97
|
}
|
|
98
|
+
if (parseLabelsAsHtml && allowUserOptions) {
|
|
99
|
+
console.warn(`Don't combine parseLabelsAsHtml and allowUserOptions. It's susceptible to XSS attacks!`);
|
|
100
|
+
}
|
|
92
101
|
if (sortSelected && selectedOptionsDraggable) {
|
|
93
102
|
console.warn(`MultiSelect's sortSelected and selectedOptionsDraggable should not be combined as any user re-orderings of selected options will be undone by sortSelected on component re-renders.`);
|
|
94
103
|
}
|
|
95
104
|
const dispatch = createEventDispatcher();
|
|
96
|
-
let add_option_msg_is_active = false; // controls active state of <li>{
|
|
105
|
+
let add_option_msg_is_active = false; // controls active state of <li>{createOptionMsg}</li>
|
|
97
106
|
let window_width;
|
|
98
107
|
// options matching the current search text
|
|
99
108
|
$: matchingOptions = options.filter((op) => filterFunc(op, searchText) && !selected.map(get_label).includes(get_label(op)) // remove already selected options from dropdown list
|
|
@@ -123,7 +132,7 @@ function add(label, event) {
|
|
|
123
132
|
// a new option from the user-entered text
|
|
124
133
|
if (typeof options[0] === `object`) {
|
|
125
134
|
// if 1st option is an object, we create new option as object to keep type homogeneity
|
|
126
|
-
option = { label: searchText
|
|
135
|
+
option = { label: searchText };
|
|
127
136
|
}
|
|
128
137
|
else {
|
|
129
138
|
if ([`number`, `undefined`].includes(typeof options[0]) &&
|
|
@@ -147,7 +156,7 @@ function add(label, event) {
|
|
|
147
156
|
return;
|
|
148
157
|
}
|
|
149
158
|
if (maxSelect === 1) {
|
|
150
|
-
// for
|
|
159
|
+
// for maxSelect = 1 we always replace current option with new one
|
|
151
160
|
selected = [option];
|
|
152
161
|
}
|
|
153
162
|
else {
|
|
@@ -179,16 +188,17 @@ function add(label, event) {
|
|
|
179
188
|
function remove(label) {
|
|
180
189
|
if (selected.length === 0)
|
|
181
190
|
return;
|
|
182
|
-
selected.
|
|
183
|
-
|
|
184
|
-
const option = options.find((option) => get_label(option) === label) ??
|
|
191
|
+
let option = selected.find((op) => get_label(op) === label);
|
|
192
|
+
if (option === undefined && allowUserOptions) {
|
|
185
193
|
// if option with label could not be found but allowUserOptions is truthy,
|
|
186
|
-
// assume it was created by user and create
|
|
194
|
+
// assume it was created by user and create corresponding option object
|
|
187
195
|
// on the fly for use as event payload
|
|
188
|
-
(
|
|
189
|
-
|
|
190
|
-
|
|
196
|
+
option = (typeof options[0] == `object` ? { label } : label);
|
|
197
|
+
}
|
|
198
|
+
if (option === undefined) {
|
|
199
|
+
return console.error(`Multiselect can't remove selected option ${label}, not found in selected list`);
|
|
191
200
|
}
|
|
201
|
+
selected = selected.filter((op) => get_label(op) !== label); // remove option from selected list
|
|
192
202
|
dispatch(`remove`, { option });
|
|
193
203
|
dispatch(`change`, { option, type: `remove` });
|
|
194
204
|
invalid = false; // reset error status whenever items are removed
|
|
@@ -212,7 +222,7 @@ function close_dropdown(event) {
|
|
|
212
222
|
}
|
|
213
223
|
// handle all keyboard events this component receives
|
|
214
224
|
async function handle_keydown(event) {
|
|
215
|
-
// on escape or tab out of input:
|
|
225
|
+
// on escape or tab out of input: close options dropdown and reset search text
|
|
216
226
|
if (event.key === `Escape` || event.key === `Tab`) {
|
|
217
227
|
close_dropdown(event);
|
|
218
228
|
searchText = ``;
|
|
@@ -297,7 +307,7 @@ const drop = (target_idx) => (event) => {
|
|
|
297
307
|
return;
|
|
298
308
|
event.dataTransfer.dropEffect = `move`;
|
|
299
309
|
const start_idx = parseInt(event.dataTransfer.getData(`text/plain`));
|
|
300
|
-
const new_selected = selected;
|
|
310
|
+
const new_selected = [...selected];
|
|
301
311
|
if (start_idx < target_idx) {
|
|
302
312
|
new_selected.splice(target_idx + 1, 0, new_selected[start_idx]);
|
|
303
313
|
new_selected.splice(start_idx, 1);
|
|
@@ -375,9 +385,10 @@ const dragstart = (idx) => (event) => {
|
|
|
375
385
|
on:dragstart={dragstart(idx)}
|
|
376
386
|
on:drop|preventDefault={drop(idx)}
|
|
377
387
|
on:dragenter={() => (drag_idx = idx)}
|
|
388
|
+
on:dragover|preventDefault
|
|
378
389
|
class:active={drag_idx === idx}
|
|
379
|
-
ondragover="return false"
|
|
380
390
|
>
|
|
391
|
+
<!-- on:dragover|preventDefault needed for the drop to succeed https://stackoverflow.com/a/31085796 -->
|
|
381
392
|
<slot name="selected" {option} {idx}>
|
|
382
393
|
{#if parseLabelsAsHtml}
|
|
383
394
|
{@html get_label(option)}
|
|
@@ -391,6 +402,7 @@ const dragstart = (idx) => (event) => {
|
|
|
391
402
|
on:keydown={if_enter_or_space(() => remove(get_label(option)))}
|
|
392
403
|
type="button"
|
|
393
404
|
title="{removeBtnTitle} {get_label(option)}"
|
|
405
|
+
class="remove"
|
|
394
406
|
>
|
|
395
407
|
<slot name="remove-icon">
|
|
396
408
|
<CrossIcon width="15px" />
|
|
@@ -452,7 +464,7 @@ const dragstart = (idx) => (event) => {
|
|
|
452
464
|
{#if maxSelect !== 1 && selected.length > 1}
|
|
453
465
|
<button
|
|
454
466
|
type="button"
|
|
455
|
-
class="remove-all"
|
|
467
|
+
class="remove remove-all"
|
|
456
468
|
title={removeAllTitle}
|
|
457
469
|
on:mouseup|stopPropagation={remove_all}
|
|
458
470
|
on:keydown={if_enter_or_space(remove_all)}
|
|
@@ -465,7 +477,7 @@ const dragstart = (idx) => (event) => {
|
|
|
465
477
|
{/if}
|
|
466
478
|
|
|
467
479
|
<!-- only render options dropdown if options or searchText is not empty needed to avoid briefly flashing empty dropdown -->
|
|
468
|
-
{#if searchText || options?.length > 0}
|
|
480
|
+
{#if (searchText && noMatchingOptionsMsg) || options?.length > 0}
|
|
469
481
|
<ul class:hidden={!open} class="options {ulOptionsClass}">
|
|
470
482
|
{#each matchingOptions as option, idx}
|
|
471
483
|
{@const {
|
|
@@ -511,7 +523,7 @@ const dragstart = (idx) => (event) => {
|
|
|
511
523
|
<li
|
|
512
524
|
on:mousedown|stopPropagation
|
|
513
525
|
on:mouseup|stopPropagation={(event) => add(searchText, event)}
|
|
514
|
-
title={
|
|
526
|
+
title={createOptionMsg}
|
|
515
527
|
class:active={add_option_msg_is_active}
|
|
516
528
|
on:mouseover={() => (add_option_msg_is_active = true)}
|
|
517
529
|
on:focus={() => (add_option_msg_is_active = true)}
|
|
@@ -521,7 +533,7 @@ const dragstart = (idx) => (event) => {
|
|
|
521
533
|
>
|
|
522
534
|
{!duplicates && selected.some((option) => duplicateFunc(option, searchText))
|
|
523
535
|
? duplicateOptionMsg
|
|
524
|
-
:
|
|
536
|
+
: createOptionMsg}
|
|
525
537
|
</li>
|
|
526
538
|
{:else}
|
|
527
539
|
<span>{noMatchingOptionsMsg}</span>
|
package/MultiSelect.svelte.d.ts
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { SvelteComponentTyped } from "svelte";
|
|
2
|
-
import type { MultiSelectEvents, Option } from './';
|
|
3
|
-
declare
|
|
4
|
-
props: {
|
|
2
|
+
import type { MultiSelectEvents, Option as GenericOption } from './';
|
|
3
|
+
declare class __sveltets_Render<Option extends GenericOption> {
|
|
4
|
+
props(): {
|
|
5
5
|
activeIndex?: number | null | undefined;
|
|
6
6
|
activeOption?: Option | null | undefined;
|
|
7
|
-
|
|
7
|
+
createOptionMsg?: string | undefined;
|
|
8
8
|
allowUserOptions?: boolean | "append" | undefined;
|
|
9
|
+
allowEmpty?: boolean | undefined;
|
|
9
10
|
autocomplete?: string | undefined;
|
|
10
11
|
autoScroll?: boolean | undefined;
|
|
11
12
|
breakpoint?: number | undefined;
|
|
12
13
|
defaultDisabledTitle?: string | undefined;
|
|
13
14
|
disabled?: boolean | undefined;
|
|
14
15
|
disabledInputTitle?: string | undefined;
|
|
15
|
-
duplicateFunc?: ((op1:
|
|
16
|
+
duplicateFunc?: ((op1: GenericOption, op2: GenericOption) => boolean) | undefined;
|
|
16
17
|
duplicateOptionMsg?: string | undefined;
|
|
17
18
|
duplicates?: boolean | undefined;
|
|
18
19
|
filterFunc?: ((op: Option, searchText: string) => boolean) | undefined;
|
|
@@ -53,7 +54,8 @@ declare const __propDef: {
|
|
|
53
54
|
ulSelectedClass?: string | undefined;
|
|
54
55
|
value?: Option | Option[] | null | undefined;
|
|
55
56
|
};
|
|
56
|
-
|
|
57
|
+
events(): MultiSelectEvents;
|
|
58
|
+
slots(): {
|
|
57
59
|
'expand-icon': {
|
|
58
60
|
open: boolean;
|
|
59
61
|
};
|
|
@@ -69,11 +71,10 @@ declare const __propDef: {
|
|
|
69
71
|
idx: any;
|
|
70
72
|
};
|
|
71
73
|
};
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
export type
|
|
75
|
-
export type
|
|
76
|
-
export
|
|
77
|
-
export default class MultiSelect extends SvelteComponentTyped<MultiSelectProps, MultiSelectEvents, MultiSelectSlots> {
|
|
74
|
+
}
|
|
75
|
+
export type MultiSelectProps<Option extends GenericOption> = ReturnType<__sveltets_Render<Option>['props']>;
|
|
76
|
+
export type MultiSelectEvents<Option extends GenericOption> = ReturnType<__sveltets_Render<Option>['events']>;
|
|
77
|
+
export type MultiSelectSlots<Option extends GenericOption> = ReturnType<__sveltets_Render<Option>['slots']>;
|
|
78
|
+
export default class MultiSelect<Option extends GenericOption> extends SvelteComponentTyped<MultiSelectProps<Option>, MultiSelectEvents<Option>, MultiSelectSlots<Option>> {
|
|
78
79
|
}
|
|
79
80
|
export {};
|
package/changelog.md
CHANGED
|
@@ -4,6 +4,32 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
4
4
|
|
|
5
5
|
<!-- auto-changelog-above -->
|
|
6
6
|
|
|
7
|
+
#### [v8.3.0](https://github.com/janosh/svelte-multiselect/compare/v8.2.4...v8.3.0)
|
|
8
|
+
|
|
9
|
+
> 25 January 2023
|
|
10
|
+
|
|
11
|
+
- Don't error on removing options that are in `selected` but not in `options` array [`#204`](https://github.com/janosh/svelte-multiselect/pull/204)
|
|
12
|
+
- Add class 'remove' to buttons that remove selected options [`#202`](https://github.com/janosh/svelte-multiselect/pull/202)
|
|
13
|
+
- Add prop allowEmpty: boolean = false [`#198`](https://github.com/janosh/svelte-multiselect/pull/198)
|
|
14
|
+
- Support `immutable` Svelte compiler option [`#197`](https://github.com/janosh/svelte-multiselect/pull/197)
|
|
15
|
+
- group demo routes [`e813e48`](https://github.com/janosh/svelte-multiselect/commit/e813e480716f29ab4bdd53f90afe56485507fb1c)
|
|
16
|
+
- breaking: rename addOptionMsg to createOptionMsg [`f24e025`](https://github.com/janosh/svelte-multiselect/commit/f24e0256fcdc32c90ed798edbb663a6be18ebe00)
|
|
17
|
+
|
|
18
|
+
#### [v8.2.4](https://github.com/janosh/svelte-multiselect/compare/v8.2.3...v8.2.4)
|
|
19
|
+
|
|
20
|
+
> 8 January 2023
|
|
21
|
+
|
|
22
|
+
- Coverage badges [`#190`](https://github.com/janosh/svelte-multiselect/pull/190)
|
|
23
|
+
- feat: add type inference for the `options` prop [`#189`](https://github.com/janosh/svelte-multiselect/pull/189)
|
|
24
|
+
- feat: add type inference for the `options` prop (#189) [`#78`](https://github.com/janosh/svelte-multiselect/issues/78)
|
|
25
|
+
- merge ExampleCode.svelte with CollapsibleCode.svelte [`56ff99b`](https://github.com/janosh/svelte-multiselect/commit/56ff99bcc378c5582b303aa1c03302cdbceb3076)
|
|
26
|
+
- pnpm add -D svelte-zoo to outsource some site components and icons [`6ee64f3`](https://github.com/janosh/svelte-multiselect/commit/6ee64f376dfe166b993c94a36d376d1dce5f44f5)
|
|
27
|
+
- restore reactive searchText block in loading example [`846da66`](https://github.com/janosh/svelte-multiselect/commit/846da66af058ac1f448c8aaa513d12fb4c2ac4cc)
|
|
28
|
+
- fix bunch of TS errors, add playwright test for dragging selected options to reorder [`a483217`](https://github.com/janosh/svelte-multiselect/commit/a4832176f6fceb5346af2d4cd8ecc01a5626ab43)
|
|
29
|
+
- add update-coverage package.json script [`1094f08`](https://github.com/janosh/svelte-multiselect/commit/1094f08cec9d6fd2f54b058af05022ab35ec4ac9)
|
|
30
|
+
- add vite alias $root to clean up package.json, readme|contributing|changelog.md imports [`c19cbe4`](https://github.com/janosh/svelte-multiselect/commit/c19cbe4e38413bbcd04d4e35eddcd4cd88c67662)
|
|
31
|
+
- mv src/components src/site [`3683ed7`](https://github.com/janosh/svelte-multiselect/commit/3683ed70f19498070ffe9e95c0261c688fb2f7c7)
|
|
32
|
+
|
|
7
33
|
#### [v8.2.3](https://github.com/janosh/svelte-multiselect/compare/v8.2.2...v8.2.3)
|
|
8
34
|
|
|
9
35
|
> 28 December 2022
|
|
@@ -116,7 +142,7 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
116
142
|
|
|
117
143
|
- Fix single select with arrow and enter keys [`#128`](https://github.com/janosh/svelte-multiselect/pull/128)
|
|
118
144
|
- Add SCSS preprocessing [`#126`](https://github.com/janosh/svelte-multiselect/pull/126)
|
|
119
|
-
-
|
|
145
|
+
- pre-commit autoupdate [`#124`](https://github.com/janosh/svelte-multiselect/pull/124)
|
|
120
146
|
- more unit tests [`1adbc99`](https://github.com/janosh/svelte-multiselect/commit/1adbc994b746b39c4ad081dc2573bf37f27c96c0)
|
|
121
147
|
- test required but empty MultiSelect fails form validity check (i.e. causes unsubmittable form) and filled one passes it [`fd8b377`](https://github.com/janosh/svelte-multiselect/commit/fd8b37782cd508aacfc8125c6647cefe56144b80)
|
|
122
148
|
|
|
@@ -186,7 +212,7 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
186
212
|
- Convert E2E tests from`vitest` to `@playwright/test` [`#95`](https://github.com/janosh/svelte-multiselect/pull/95)
|
|
187
213
|
- Allow empty Multiselect [`#94`](https://github.com/janosh/svelte-multiselect/pull/94)
|
|
188
214
|
- Add new slot `'remove-icon'` [`#93`](https://github.com/janosh/svelte-multiselect/pull/93)
|
|
189
|
-
-
|
|
215
|
+
- pre-commit autoupdate [`#92`](https://github.com/janosh/svelte-multiselect/pull/92)
|
|
190
216
|
|
|
191
217
|
#### [v5.0.3](https://github.com/janosh/svelte-multiselect/compare/v5.0.2...v5.0.3)
|
|
192
218
|
|
|
@@ -224,7 +250,7 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
224
250
|
|
|
225
251
|
- Fix backspace deleting multiple selected options if identical labels [`#72`](https://github.com/janosh/svelte-multiselect/pull/72)
|
|
226
252
|
- Several fixes for `allowUserOptions` [`#69`](https://github.com/janosh/svelte-multiselect/pull/69)
|
|
227
|
-
-
|
|
253
|
+
- pre-commit autoupdate [`#70`](https://github.com/janosh/svelte-multiselect/pull/70)
|
|
228
254
|
|
|
229
255
|
#### [v4.0.5](https://github.com/janosh/svelte-multiselect/compare/v4.0.4...v4.0.5)
|
|
230
256
|
|
|
@@ -347,7 +373,7 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
347
373
|
- favorite web framework show Confetti.svelte on:add Svelte [`8d109ee`](https://github.com/janosh/svelte-multiselect/commit/8d109ee5c7755e447fcb72419f3b7ecc19cac0b2)
|
|
348
374
|
- bump svelte@3.45.0 to silence warning: MultiSelect has unused export property 'defaultDisabledTitle' (sveltejs/svelte#6964) [`f80a7a6`](https://github.com/janosh/svelte-multiselect/commit/f80a7a622310005407585298f2611597c0941990)
|
|
349
375
|
- update readme + svelte-toc@0.2.0 [`40013ba`](https://github.com/janosh/svelte-multiselect/commit/40013badd61dd0fcade7ab295dabd26693e3cc51)
|
|
350
|
-
-
|
|
376
|
+
- pre-commit autoupdate [`0d05864`](https://github.com/janosh/svelte-multiselect/commit/0d05864d19987460dd30d667eb22deb91a520668)
|
|
351
377
|
- iOS Safari prevent zoom into page on focus MultiSelect input [`44f17be`](https://github.com/janosh/svelte-multiselect/commit/44f17be53378e38f4a8748b815737d25cdebc85f)
|
|
352
378
|
|
|
353
379
|
### [v3.0.0](https://github.com/janosh/svelte-multiselect/compare/v2.0.0...v3.0.0)
|
|
@@ -398,7 +424,7 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
398
424
|
|
|
399
425
|
- Add new prop disabledOptions [`#9`](https://github.com/janosh/svelte-multiselect/pull/9)
|
|
400
426
|
- add pre-commit hooks [`dfb6399`](https://github.com/janosh/svelte-multiselect/commit/dfb6399a77b705f8e5979eb887d345a5f52ff929)
|
|
401
|
-
-
|
|
427
|
+
- pre-commit autoupdate [`b69425d`](https://github.com/janosh/svelte-multiselect/commit/b69425d18473122f1af889d2f48c60d02e43b99f)
|
|
402
428
|
|
|
403
429
|
#### [v1.1.11](https://github.com/janosh/svelte-multiselect/compare/v1.1.10...v1.1.11)
|
|
404
430
|
|
|
@@ -490,7 +516,7 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
490
516
|
|
|
491
517
|
- remove hidden input for storing currently selected options as JSON [`802a219`](https://github.com/janosh/svelte-multiselect/commit/802a2195a28986c219298d7d9e7ca47f2aaf7db6)
|
|
492
518
|
|
|
493
|
-
|
|
519
|
+
### v1.0.0
|
|
494
520
|
|
|
495
521
|
> 7 May 2021
|
|
496
522
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/** @typedef {typeof __propDef.events} ChevronExpandEvents */
|
|
3
3
|
/** @typedef {typeof __propDef.slots} ChevronExpandSlots */
|
|
4
4
|
export default class ChevronExpand extends SvelteComponentTyped<{
|
|
5
|
-
[x: string]:
|
|
5
|
+
[x: string]: any;
|
|
6
6
|
}, {
|
|
7
7
|
[evt: string]: CustomEvent<any>;
|
|
8
8
|
}, {}> {
|
|
@@ -13,7 +13,7 @@ export type ChevronExpandSlots = typeof __propDef.slots;
|
|
|
13
13
|
import { SvelteComponentTyped } from "svelte";
|
|
14
14
|
declare const __propDef: {
|
|
15
15
|
props: {
|
|
16
|
-
[x: string]:
|
|
16
|
+
[x: string]: any;
|
|
17
17
|
};
|
|
18
18
|
events: {
|
|
19
19
|
[evt: string]: CustomEvent<any>;
|
package/icons/Cross.svelte.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/** @typedef {typeof __propDef.events} CrossEvents */
|
|
3
3
|
/** @typedef {typeof __propDef.slots} CrossSlots */
|
|
4
4
|
export default class Cross extends SvelteComponentTyped<{
|
|
5
|
-
[x: string]:
|
|
5
|
+
[x: string]: any;
|
|
6
6
|
}, {
|
|
7
7
|
[evt: string]: CustomEvent<any>;
|
|
8
8
|
}, {}> {
|
|
@@ -13,7 +13,7 @@ export type CrossSlots = typeof __propDef.slots;
|
|
|
13
13
|
import { SvelteComponentTyped } from "svelte";
|
|
14
14
|
declare const __propDef: {
|
|
15
15
|
props: {
|
|
16
|
-
[x: string]:
|
|
16
|
+
[x: string]: any;
|
|
17
17
|
};
|
|
18
18
|
events: {
|
|
19
19
|
[evt: string]: CustomEvent<any>;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/** @typedef {typeof __propDef.events} DisabledEvents */
|
|
3
3
|
/** @typedef {typeof __propDef.slots} DisabledSlots */
|
|
4
4
|
export default class Disabled extends SvelteComponentTyped<{
|
|
5
|
-
[x: string]:
|
|
5
|
+
[x: string]: any;
|
|
6
6
|
}, {
|
|
7
7
|
[evt: string]: CustomEvent<any>;
|
|
8
8
|
}, {}> {
|
|
@@ -13,7 +13,7 @@ export type DisabledSlots = typeof __propDef.slots;
|
|
|
13
13
|
import { SvelteComponentTyped } from "svelte";
|
|
14
14
|
declare const __propDef: {
|
|
15
15
|
props: {
|
|
16
|
-
[x: string]:
|
|
16
|
+
[x: string]: any;
|
|
17
17
|
};
|
|
18
18
|
events: {
|
|
19
19
|
[evt: string]: CustomEvent<any>;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/** @typedef {typeof __propDef.events} OctocatEvents */
|
|
3
3
|
/** @typedef {typeof __propDef.slots} OctocatSlots */
|
|
4
4
|
export default class Octocat extends SvelteComponentTyped<{
|
|
5
|
-
[x: string]:
|
|
5
|
+
[x: string]: any;
|
|
6
6
|
}, {
|
|
7
7
|
[evt: string]: CustomEvent<any>;
|
|
8
8
|
}, {}> {
|
|
@@ -13,7 +13,7 @@ export type OctocatSlots = typeof __propDef.slots;
|
|
|
13
13
|
import { SvelteComponentTyped } from "svelte";
|
|
14
14
|
declare const __propDef: {
|
|
15
15
|
props: {
|
|
16
|
-
[x: string]:
|
|
16
|
+
[x: string]: any;
|
|
17
17
|
};
|
|
18
18
|
events: {
|
|
19
19
|
[evt: string]: CustomEvent<any>;
|
package/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
export { default } from './
|
|
1
|
+
export { default as CircleSpinner } from './CircleSpinner.svelte';
|
|
2
|
+
export { default, default as MultiSelect } from './MultiSelect.svelte';
|
|
3
|
+
export { default as Wiggle } from './Wiggle.svelte';
|
|
2
4
|
export type Option = string | number | ObjectOption;
|
|
3
5
|
export type ObjectOption = {
|
|
4
6
|
label: string | number;
|
|
@@ -47,3 +49,4 @@ export type MultiSelectEvents = {
|
|
|
47
49
|
touchmove: TouchEvent;
|
|
48
50
|
touchstart: TouchEvent;
|
|
49
51
|
};
|
|
52
|
+
export declare function scroll_into_view_if_needed_polyfill(centerIfNeeded?: boolean): IntersectionObserver;
|
package/index.js
CHANGED
|
@@ -1,23 +1,29 @@
|
|
|
1
|
-
export { default } from './
|
|
1
|
+
export { default as CircleSpinner } from './CircleSpinner.svelte';
|
|
2
|
+
export { default, default as MultiSelect } from './MultiSelect.svelte';
|
|
3
|
+
export { default as Wiggle } from './Wiggle.svelte';
|
|
2
4
|
// Firefox lacks support for scrollIntoViewIfNeeded, see
|
|
3
5
|
// https://github.com/janosh/svelte-multiselect/issues/87
|
|
4
6
|
// this polyfill was copied from
|
|
5
7
|
// https://github.com/nuxodin/lazyfill/blob/a8e63/polyfills/Element/prototype/scrollIntoViewIfNeeded.js
|
|
8
|
+
// exported for testing
|
|
9
|
+
export function scroll_into_view_if_needed_polyfill(centerIfNeeded = true) {
|
|
10
|
+
const el = this;
|
|
11
|
+
const observer = new IntersectionObserver(function ([entry]) {
|
|
12
|
+
const ratio = entry.intersectionRatio;
|
|
13
|
+
if (ratio < 1) {
|
|
14
|
+
const place = ratio <= 0 && centerIfNeeded ? `center` : `nearest`;
|
|
15
|
+
el.scrollIntoView({
|
|
16
|
+
block: place,
|
|
17
|
+
inline: place,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
this.disconnect();
|
|
21
|
+
});
|
|
22
|
+
observer.observe(this);
|
|
23
|
+
return observer; // return for testing
|
|
24
|
+
}
|
|
6
25
|
if (typeof Element !== `undefined` &&
|
|
7
26
|
!Element.prototype?.scrollIntoViewIfNeeded &&
|
|
8
27
|
typeof IntersectionObserver !== `undefined`) {
|
|
9
|
-
Element.prototype.scrollIntoViewIfNeeded =
|
|
10
|
-
const el = this;
|
|
11
|
-
new IntersectionObserver(function ([entry]) {
|
|
12
|
-
const ratio = entry.intersectionRatio;
|
|
13
|
-
if (ratio < 1) {
|
|
14
|
-
const place = ratio <= 0 && centerIfNeeded ? `center` : `nearest`;
|
|
15
|
-
el.scrollIntoView({
|
|
16
|
-
block: place,
|
|
17
|
-
inline: place,
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
this.disconnect();
|
|
21
|
-
}).observe(this);
|
|
22
|
-
};
|
|
28
|
+
Element.prototype.scrollIntoViewIfNeeded = scroll_into_view_if_needed_polyfill;
|
|
23
29
|
}
|
package/package.json
CHANGED
|
@@ -5,42 +5,41 @@
|
|
|
5
5
|
"homepage": "https://janosh.github.io/svelte-multiselect",
|
|
6
6
|
"repository": "https://github.com/janosh/svelte-multiselect",
|
|
7
7
|
"license": "MIT",
|
|
8
|
-
"version": "8.
|
|
8
|
+
"version": "8.3.0",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"svelte": "index.js",
|
|
11
|
-
"main": "index.js",
|
|
12
11
|
"bugs": "https://github.com/janosh/svelte-multiselect/issues",
|
|
13
12
|
"devDependencies": {
|
|
14
13
|
"@iconify/svelte": "^3.0.1",
|
|
15
|
-
"@playwright/test": "^1.29.
|
|
16
|
-
"@sveltejs/adapter-static": "1.0.
|
|
17
|
-
"@sveltejs/kit": "1.
|
|
18
|
-
"@sveltejs/package": "1.0.
|
|
14
|
+
"@playwright/test": "^1.29.2",
|
|
15
|
+
"@sveltejs/adapter-static": "^1.0.5",
|
|
16
|
+
"@sveltejs/kit": "^1.2.2",
|
|
17
|
+
"@sveltejs/package": "1.0.2",
|
|
19
18
|
"@sveltejs/vite-plugin-svelte": "^2.0.2",
|
|
20
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
21
|
-
"@typescript-eslint/parser": "^5.
|
|
22
|
-
"@vitest/coverage-c8": "^0.
|
|
23
|
-
"eslint": "^8.
|
|
19
|
+
"@typescript-eslint/eslint-plugin": "^5.48.2",
|
|
20
|
+
"@typescript-eslint/parser": "^5.48.2",
|
|
21
|
+
"@vitest/coverage-c8": "^0.27.2",
|
|
22
|
+
"eslint": "^8.32.0",
|
|
24
23
|
"eslint-plugin-svelte3": "^4.0.0",
|
|
25
|
-
"hastscript": "^7.
|
|
24
|
+
"hastscript": "^7.2.0",
|
|
26
25
|
"highlight.js": "^11.7.0",
|
|
27
|
-
"jsdom": "^
|
|
26
|
+
"jsdom": "^21.0.0",
|
|
28
27
|
"mdsvex": "^0.10.6",
|
|
29
28
|
"mdsvexamples": "^0.3.3",
|
|
30
|
-
"prettier": "^2.8.
|
|
29
|
+
"prettier": "^2.8.3",
|
|
31
30
|
"prettier-plugin-svelte": "^2.9.0",
|
|
32
31
|
"rehype-autolink-headings": "^6.1.1",
|
|
33
32
|
"rehype-slug": "^5.1.0",
|
|
34
|
-
"svelte": "^3.55.
|
|
35
|
-
"svelte-check": "^
|
|
36
|
-
"svelte-
|
|
37
|
-
"svelte-
|
|
38
|
-
"svelte-
|
|
39
|
-
"svelte2tsx": "^0.
|
|
33
|
+
"svelte": "^3.55.1",
|
|
34
|
+
"svelte-check": "^3.0.2",
|
|
35
|
+
"svelte-preprocess": "^5.0.1",
|
|
36
|
+
"svelte-toc": "^0.5.2",
|
|
37
|
+
"svelte-zoo": "^0.2.3",
|
|
38
|
+
"svelte2tsx": "^0.6.0",
|
|
40
39
|
"tslib": "^2.4.1",
|
|
41
40
|
"typescript": "^4.9.4",
|
|
42
|
-
"vite": "^4.0.
|
|
43
|
-
"vitest": "^0.
|
|
41
|
+
"vite": "^4.0.4",
|
|
42
|
+
"vitest": "^0.27.2"
|
|
44
43
|
},
|
|
45
44
|
"keywords": [
|
|
46
45
|
"svelte",
|
package/readme.md
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
<slot name="examples" />
|
|
23
23
|
|
|
24
|
-
## ๐ก &
|
|
24
|
+
## ๐ก   Features
|
|
25
25
|
|
|
26
26
|
- **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]`.
|
|
27
27
|
- **Keyboard friendly** for mouse-less form completion
|
|
@@ -34,23 +34,28 @@
|
|
|
34
34
|
|
|
35
35
|
<slot name="nav" />
|
|
36
36
|
|
|
37
|
-
##
|
|
37
|
+
## ๐งช   Coverage
|
|
38
|
+
|
|
39
|
+
| Statements | Branches | Lines |
|
|
40
|
+
| ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- |
|
|
41
|
+
|  |  |  |
|
|
42
|
+
|
|
43
|
+
## ๐   Breaking changes
|
|
38
44
|
|
|
39
|
-
- **6.1.0** The `dispatch` events `focus` and `blur` were renamed to `open` and `close`, respectively. These actions refer to the dropdown list, i.e. `<MultiSelect on:open={(event) => console.log(event)}>` will trigger when the dropdown list opens. The focus and blur events are now regular DOM (not Svelte `dispatch`) events emitted by the `<input>` node. [PR 120](https://github.com/janosh/svelte-multiselect/pull/120).
|
|
40
|
-
- **v7.0.0** `selected` (as well `selectedLabels` and `selectedValues`) used to be arrays always. Now, if `maxSelect=1`, they will no longer be a length-1 array but simply a single a option (label/value respectively) or `null` if no option is selected. [PR 123](https://github.com/janosh/svelte-multiselect/pull/123).
|
|
41
45
|
- **8.0.0**
|
|
42
46
|
- Props `selectedLabels` and `selectedValues` were removed. If you were using them, they were equivalent to assigning `bind:selected` to a local variable and then running `selectedLabels = selected.map(option => option.label)` and `selectedValues = selected.map(option => option.value)` if your options were objects with `label` and `value` keys. If they were simple strings/numbers, there was no point in using `selected{Labels,Values}` anyway. [PR 138](https://github.com/janosh/svelte-multiselect/pull/138)
|
|
43
47
|
- Prop `noOptionsMsg` was renamed to `noMatchingOptionsMsg`. [PR 133](https://github.com/janosh/svelte-multiselect/pull/133).
|
|
48
|
+
- **v8.3.0** `addOptionMsg` was renamed to `createOptionMsg` (no major since version since it's rarely used) [sha](https://github.com/janosh/svelte-multiselect/commits).
|
|
44
49
|
|
|
45
|
-
## ๐จ &
|
|
50
|
+
## ๐จ   Installation
|
|
46
51
|
|
|
47
52
|
```sh
|
|
48
|
-
npm install
|
|
49
|
-
pnpm add
|
|
50
|
-
yarn add
|
|
53
|
+
npm install --dev svelte-multiselect
|
|
54
|
+
pnpm add --dev svelte-multiselect
|
|
55
|
+
yarn add --dev svelte-multiselect
|
|
51
56
|
```
|
|
52
57
|
|
|
53
|
-
## ๐ &
|
|
58
|
+
## ๐   Usage
|
|
54
59
|
|
|
55
60
|
```svelte
|
|
56
61
|
<script>
|
|
@@ -68,7 +73,7 @@ Favorite Frontend Tools?
|
|
|
68
73
|
<MultiSelect bind:selected options={ui_libs} />
|
|
69
74
|
```
|
|
70
75
|
|
|
71
|
-
## ๐ฃ &
|
|
76
|
+
## ๐ฃ   Props
|
|
72
77
|
|
|
73
78
|
Full list of props/bindable variables for this component. The `Option` type you see below is defined in [`src/lib/index.ts`](https://github.com/janosh/svelte-multiselect/blob/main/src/lib/index.ts) and can be imported as `import { type Option } from 'svelte-multiselect'`.
|
|
74
79
|
|
|
@@ -85,11 +90,17 @@ Full list of props/bindable variables for this component. The `Option` type you
|
|
|
85
90
|
Currently active option, i.e. the one the user currently hovers or navigated to with arrow keys.
|
|
86
91
|
|
|
87
92
|
1. ```ts
|
|
88
|
-
|
|
93
|
+
createOptionMsg: string = `Create this option...`
|
|
89
94
|
```
|
|
90
95
|
|
|
91
96
|
Message shown to users after entering text when no options match their query and `allowUserOptions` is truthy.
|
|
92
97
|
|
|
98
|
+
1. ```ts
|
|
99
|
+
allowEmpty: boolean = false
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Whether to `console.error` if dropdown list of options is empty. `allowEmpty={false}` will suppress errors. `allowEmpty={true}` will report a console error if component is not `disabled`, not in `loading` state and doesn't `allowUserOptions`.
|
|
103
|
+
|
|
93
104
|
1. ```ts
|
|
94
105
|
allowUserOptions: boolean | 'append' = false
|
|
95
106
|
```
|
|
@@ -135,7 +146,7 @@ Full list of props/bindable variables for this component. The `Option` type you
|
|
|
135
146
|
|
|
136
147
|
<!-- prettier-ignore -->
|
|
137
148
|
1. ```ts
|
|
138
|
-
duplicateFunc: (op1:
|
|
149
|
+
duplicateFunc: (op1: GenericOption, op2: GenericOption) => boolean = (op1, op2) =>
|
|
139
150
|
`${get_label(op1)}`.toLowerCase() === `${get_label(op2)}`.toLowerCase()
|
|
140
151
|
```
|
|
141
152
|
|
|
@@ -344,7 +355,7 @@ Full list of props/bindable variables for this component. The `Option` type you
|
|
|
344
355
|
|
|
345
356
|
If `maxSelect={1}`, `value` will be the single item in `selected` (or `null` if `selected` is empty). If `maxSelect != 1`, `maxSelect` and `selected` are equal. Warning: `value` supports 1-way binding only, meaning `bind:value` will update `value` when internal component state changes but changing `value` externally will not update internal component state. This is because `value` is already reactive to `selected` and making `selected` reactive to `value` would be cyclic. Suggestions for better solutions that solve both [#86](https://github.com/janosh/svelte-multiselect/issues/86) and [#136](https://github.com/janosh/svelte-multiselect/issues/136) welcome!
|
|
346
357
|
|
|
347
|
-
## ๐ฐ &
|
|
358
|
+
## ๐ฐ   Slots
|
|
348
359
|
|
|
349
360
|
`MultiSelect.svelte` accepts the following named slots:
|
|
350
361
|
|
|
@@ -376,7 +387,7 @@ Example:
|
|
|
376
387
|
</MultiSelect>
|
|
377
388
|
```
|
|
378
389
|
|
|
379
|
-
## ๐ฌ &
|
|
390
|
+
## ๐ฌ   Events
|
|
380
391
|
|
|
381
392
|
`MultiSelect.svelte` dispatches the following events:
|
|
382
393
|
|
|
@@ -439,25 +450,36 @@ The above list of events are [Svelte `dispatch` events](https://svelte.dev/tutor
|
|
|
439
450
|
/>
|
|
440
451
|
```
|
|
441
452
|
|
|
442
|
-
## ๐ฆบ &
|
|
453
|
+
## ๐ฆบ   TypeScript
|
|
443
454
|
|
|
444
|
-
|
|
455
|
+
The type of `options` is inferred automatically from the data you pass. E.g.
|
|
445
456
|
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
457
|
+
```ts
|
|
458
|
+
const options = [
|
|
459
|
+
{ label: `foo`, value: 42 }
|
|
460
|
+
{ label: `bar`, value: 69 }
|
|
461
|
+
]
|
|
462
|
+
// type Option = { label: string, value: number }
|
|
463
|
+
const options = [`foo`, `bar`]
|
|
464
|
+
// type Option = string
|
|
465
|
+
const options = [42, 69]
|
|
466
|
+
// type Option = number
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
The inferred type of `Option` is used to enforce type-safety on derived props like `selected` as well as slot components. E.g. you'll get an error when trying to use a slot component that expects a string if your options are objects (see [this comment](https://github.com/janosh/svelte-multiselect/pull/189/files#r1058853697) for example screenshots).
|
|
470
|
+
|
|
471
|
+
You can also import [the types this component uses](https://github.com/janosh/svelte-multiselect/blob/main/src/lib/index.ts) for downstream applications:
|
|
472
|
+
|
|
473
|
+
```ts
|
|
474
|
+
import {
|
|
475
|
+
Option,
|
|
476
|
+
ObjectOption,
|
|
477
|
+
DispatchEvents,
|
|
478
|
+
MultiSelectEvents,
|
|
479
|
+
} from 'svelte-multiselect'
|
|
458
480
|
```
|
|
459
481
|
|
|
460
|
-
## โจ &
|
|
482
|
+
## โจ   Styling
|
|
461
483
|
|
|
462
484
|
There are 3 ways to style this component. To understand which options do what, it helps to keep in mind this simplified DOM structure of the component:
|
|
463
485
|
|
|
@@ -608,10 +630,10 @@ Odd as it may seem, you get the most fine-grained control over the styling of ev
|
|
|
608
630
|
}
|
|
609
631
|
```
|
|
610
632
|
|
|
611
|
-
## ๐ &
|
|
633
|
+
## ๐   Changelog
|
|
612
634
|
|
|
613
635
|
[View the changelog](changelog.md).
|
|
614
636
|
|
|
615
|
-
## ๐ &
|
|
637
|
+
## ๐   Contributing
|
|
616
638
|
|
|
617
639
|
Here are some steps to [get you started](contributing.md) if you'd like to contribute to this project!
|