svelte-multiselect 8.0.1 → 8.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MultiSelect.svelte +11 -12
- package/Wiggle.svelte +3 -3
- package/icons/Disabled.svelte +2 -2
- package/package.json +18 -16
- package/readme.md +23 -56
package/MultiSelect.svelte
CHANGED
|
@@ -63,11 +63,12 @@ const get_label = (op) => (op instanceof Object ? op.label : op);
|
|
|
63
63
|
$: value = maxSelect === 1 ? selected[0] ?? null : selected;
|
|
64
64
|
let wiggle = false; // controls wiggle animation when user tries to exceed maxSelect
|
|
65
65
|
if (!(options?.length > 0)) {
|
|
66
|
-
if (allowUserOptions) {
|
|
66
|
+
if (allowUserOptions || loading) {
|
|
67
67
|
options = []; // initializing as array avoids errors when component mounts
|
|
68
68
|
}
|
|
69
69
|
else {
|
|
70
|
-
// only error for empty options if user is not allowed to create custom
|
|
70
|
+
// only error for empty options if user is not allowed to create custom
|
|
71
|
+
// options and loading is false
|
|
71
72
|
console.error(`MultiSelect received no options`);
|
|
72
73
|
}
|
|
73
74
|
}
|
|
@@ -237,21 +238,19 @@ async function handle_keydown(event) {
|
|
|
237
238
|
// if no option is active and no options are matching, do nothing
|
|
238
239
|
return;
|
|
239
240
|
}
|
|
241
|
+
// if none of the abvove special cases apply, we make next/prev option
|
|
242
|
+
// active with wrap around at both ends
|
|
240
243
|
const increment = event.key === `ArrowUp` ? -1 : 1;
|
|
241
244
|
activeIndex = (activeIndex + increment) % matchingOptions.length;
|
|
242
|
-
//
|
|
245
|
+
// in JS % behaves like remainder operator, not real modulo, so negative numbers stay negative
|
|
243
246
|
// need to do manual wrap around at 0
|
|
244
247
|
if (activeIndex < 0)
|
|
245
248
|
activeIndex = matchingOptions.length - 1;
|
|
246
249
|
if (autoScroll) {
|
|
247
|
-
// TODO This ugly timeout hack is needed to properly scroll element into view when wrapping
|
|
248
|
-
// around start/end of option list. Find a better solution than waiting 10 ms.
|
|
249
250
|
await tick();
|
|
250
251
|
const li = document.querySelector(`ul.options > li.active`);
|
|
251
|
-
if (li)
|
|
252
|
-
li.parentNode?.scrollIntoView?.({ block: `center` });
|
|
252
|
+
if (li)
|
|
253
253
|
li.scrollIntoViewIfNeeded?.();
|
|
254
|
-
}
|
|
255
254
|
}
|
|
256
255
|
}
|
|
257
256
|
// on backspace key: remove last selected option
|
|
@@ -301,7 +300,8 @@ function on_click_outside(event) {
|
|
|
301
300
|
<!-- bind:value={selected} prevents form submission if required prop is true and no options are selected -->
|
|
302
301
|
<input
|
|
303
302
|
{required}
|
|
304
|
-
|
|
303
|
+
{name}
|
|
304
|
+
value={selected.length > 0 ? JSON.stringify(selected) : null}
|
|
305
305
|
tabindex="-1"
|
|
306
306
|
aria-hidden="true"
|
|
307
307
|
aria-label="ignore this, used only to prevent form submission if select is required but empty"
|
|
@@ -344,7 +344,6 @@ function on_click_outside(event) {
|
|
|
344
344
|
on:focus
|
|
345
345
|
on:focus={open_dropdown}
|
|
346
346
|
{id}
|
|
347
|
-
{name}
|
|
348
347
|
{disabled}
|
|
349
348
|
{inputmode}
|
|
350
349
|
{pattern}
|
|
@@ -373,7 +372,7 @@ function on_click_outside(event) {
|
|
|
373
372
|
{/if}
|
|
374
373
|
{#if disabled}
|
|
375
374
|
<slot name="disabled-icon">
|
|
376
|
-
<DisabledIcon width="
|
|
375
|
+
<DisabledIcon width="14pt" style="margin: 0 2pt;" data-name="disabled-icon" />
|
|
377
376
|
</slot>
|
|
378
377
|
{:else if selected.length > 0}
|
|
379
378
|
{#if maxSelect && (maxSelect > 1 || maxSelectMsg)}
|
|
@@ -480,7 +479,7 @@ function on_click_outside(event) {
|
|
|
480
479
|
padding: var(--sms-padding, 0 3pt);
|
|
481
480
|
color: var(--sms-text-color);
|
|
482
481
|
font-size: var(--sms-font-size, inherit);
|
|
483
|
-
min-height: var(--sms-min-height,
|
|
482
|
+
min-height: var(--sms-min-height, 22pt);
|
|
484
483
|
margin: var(--sms-margin);
|
|
485
484
|
}
|
|
486
485
|
:where(div.multiselect.open) {
|
package/Wiggle.svelte
CHANGED
|
@@ -9,9 +9,9 @@ export let dy = 0; // try 10
|
|
|
9
9
|
export let duration = 200;
|
|
10
10
|
export let stiffness = 0.05;
|
|
11
11
|
export let damping = 0.1;
|
|
12
|
-
let
|
|
13
|
-
let store = spring(
|
|
14
|
-
$: store.set(wiggle ? { scale, angle, dx, dy } :
|
|
12
|
+
let rest_state = { angle: 0, scale: 1, dx: 0, dy: 0 };
|
|
13
|
+
let store = spring(rest_state, { stiffness, damping });
|
|
14
|
+
$: store.set(wiggle ? { scale, angle, dx, dy } : rest_state);
|
|
15
15
|
$: if (wiggle)
|
|
16
16
|
setTimeout(() => (wiggle = false), duration);
|
|
17
17
|
</script>
|
package/icons/Disabled.svelte
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
<!-- https://api.iconify.design/fe:disabled.svg -->
|
|
1
2
|
<svg {...$$props} viewBox="0 0 24 24" fill="currentColor">
|
|
2
|
-
<path fill="none" d="M0 0h24v24H0V0z" />
|
|
3
3
|
<path
|
|
4
|
-
d="
|
|
4
|
+
d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2S2 6.477 2 12s4.477 10 10 10Zm-4.906-3.68L18.32 7.094A8 8 0 0 1 7.094 18.32ZM5.68 16.906A8 8 0 0 1 16.906 5.68L5.68 16.906Z"
|
|
5
5
|
/>
|
|
6
6
|
</svg>
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"homepage": "https://svelte-multiselect.netlify.app",
|
|
6
6
|
"repository": "https://github.com/janosh/svelte-multiselect",
|
|
7
7
|
"license": "MIT",
|
|
8
|
-
"version": "8.0.
|
|
8
|
+
"version": "8.0.3",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"svelte": "index.js",
|
|
11
11
|
"main": "index.js",
|
|
@@ -13,31 +13,33 @@
|
|
|
13
13
|
"devDependencies": {
|
|
14
14
|
"@iconify/svelte": "^3.0.0",
|
|
15
15
|
"@playwright/test": "^1.27.1",
|
|
16
|
-
"@sveltejs/adapter-static": "
|
|
17
|
-
"@sveltejs/kit": "
|
|
18
|
-
"@sveltejs/package": "
|
|
19
|
-
"@sveltejs/vite-plugin-svelte": "^1.
|
|
20
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
21
|
-
"@typescript-eslint/parser": "^5.
|
|
22
|
-
"
|
|
16
|
+
"@sveltejs/adapter-static": "1.0.0-next.48",
|
|
17
|
+
"@sveltejs/kit": "1.0.0-next.544",
|
|
18
|
+
"@sveltejs/package": "1.0.0-next.5",
|
|
19
|
+
"@sveltejs/vite-plugin-svelte": "^1.1.1",
|
|
20
|
+
"@typescript-eslint/eslint-plugin": "^5.42.1",
|
|
21
|
+
"@typescript-eslint/parser": "^5.42.1",
|
|
22
|
+
"@vitest/coverage-c8": "^0.25.1",
|
|
23
|
+
"eslint": "^8.27.0",
|
|
23
24
|
"eslint-plugin-svelte3": "^4.0.0",
|
|
24
25
|
"hastscript": "^7.1.0",
|
|
25
|
-
"
|
|
26
|
+
"highlight.js": "^11.6.0",
|
|
27
|
+
"jsdom": "^20.0.2",
|
|
26
28
|
"mdsvex": "^0.10.6",
|
|
27
29
|
"prettier": "^2.7.1",
|
|
28
30
|
"prettier-plugin-svelte": "^2.8.0",
|
|
29
31
|
"rehype-autolink-headings": "^6.1.1",
|
|
30
|
-
"rehype-slug": "^5.0
|
|
31
|
-
"svelte": "^3.
|
|
32
|
+
"rehype-slug": "^5.1.0",
|
|
33
|
+
"svelte": "^3.53.1",
|
|
32
34
|
"svelte-check": "^2.9.2",
|
|
33
35
|
"svelte-github-corner": "^0.1.0",
|
|
34
|
-
"svelte-preprocess": "^4.10.
|
|
35
|
-
"svelte-toc": "^0.4.
|
|
36
|
+
"svelte-preprocess": "^4.10.7",
|
|
37
|
+
"svelte-toc": "^0.4.1",
|
|
36
38
|
"svelte2tsx": "^0.5.20",
|
|
37
|
-
"tslib": "^2.4.
|
|
39
|
+
"tslib": "^2.4.1",
|
|
38
40
|
"typescript": "^4.8.4",
|
|
39
|
-
"vite": "^3.
|
|
40
|
-
"vitest": "^0.
|
|
41
|
+
"vite": "^3.2.3",
|
|
42
|
+
"vitest": "^0.25.1"
|
|
41
43
|
},
|
|
42
44
|
"keywords": [
|
|
43
45
|
"svelte",
|
package/readme.md
CHANGED
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
|
|
15
15
|
</h4>
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
<a href="https://svelte-multiselect.netlify.app">View the docs</a>
|
|
20
|
-
</
|
|
17
|
+
<p align="center"><strong>
|
|
18
|
+
Keyboard-friendly, accessible and highly customizable multi-select component.
|
|
19
|
+
<a class="hide-in-docs" href="https://svelte-multiselect.netlify.app">View the docs</a>
|
|
20
|
+
</strong></p>
|
|
21
21
|
|
|
22
22
|
<slot name="examples" />
|
|
23
23
|
|
|
@@ -36,14 +36,14 @@
|
|
|
36
36
|
|
|
37
37
|
## Recent breaking changes
|
|
38
38
|
|
|
39
|
-
- **v6.0.0** The prop `showOptions` which controls whether the list of dropdown options is currently open or closed was renamed to `open`.
|
|
40
|
-
- **v6.0.1** The prop `disabledTitle` which sets the title of the `<MultiSelect>` `<input>` node if in `disabled` mode was renamed to `disabledInputTitle`.
|
|
41
|
-
- **v6.0.1** The default margin of `1em 0` on the wrapper `div.multiselect` was removed. Instead, there is now a new CSS variable `--sms-margin`. Set it to `--sms-margin: 1em 0;` to restore the old appearance.
|
|
42
|
-
- **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.
|
|
43
|
-
- **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.
|
|
39
|
+
- **v6.0.0** The prop `showOptions` which controls whether the list of dropdown options is currently open or closed was renamed to `open`. [PR 103](https://github.com/janosh/svelte-multiselect/pull/103).
|
|
40
|
+
- **v6.0.1** The prop `disabledTitle` which sets the title of the `<MultiSelect>` `<input>` node if in `disabled` mode was renamed to `disabledInputTitle`. [PR 105](https://github.com/janosh/svelte-multiselect/pull/105).
|
|
41
|
+
- **v6.0.1** The default margin of `1em 0` on the wrapper `div.multiselect` was removed. Instead, there is now a new CSS variable `--sms-margin`. Set it to `--sms-margin: 1em 0;` to restore the old appearance. [PR 105](https://github.com/janosh/svelte-multiselect/pull/105).
|
|
42
|
+
- **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).
|
|
43
|
+
- **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).
|
|
44
44
|
- **8.0.0**
|
|
45
|
-
- 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.
|
|
46
|
-
- Prop `noOptionsMsg` was renamed to `noMatchingOptionsMsg`.
|
|
45
|
+
- 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)
|
|
46
|
+
- Prop `noOptionsMsg` was renamed to `noMatchingOptionsMsg`. [PR 133](https://github.com/janosh/svelte-multiselect/pull/133).
|
|
47
47
|
|
|
48
48
|
## Installation
|
|
49
49
|
|
|
@@ -98,7 +98,7 @@ Full list of props/bindable variables for this component. The `Option` type you
|
|
|
98
98
|
```
|
|
99
99
|
|
|
100
100
|
Whether users can enter values that are not in the dropdown list. `true` means add user-defined options to the selected list only, `'append'` means add to both options and selected.
|
|
101
|
-
If `allowUserOptions` is `true` or `'append'` then the type `object | number | string` of entered value is determined
|
|
101
|
+
If `allowUserOptions` is `true` or `'append'` then the type `object | number | string` of entered value is determined by `typeof options[0]` (i.e. the first option in the dropdown list) to keep type homogeneity.
|
|
102
102
|
|
|
103
103
|
1. ```ts
|
|
104
104
|
autocomplete: string = `off`
|
|
@@ -148,7 +148,7 @@ Full list of props/bindable variables for this component. The `Option` type you
|
|
|
148
148
|
duplicates: boolean = false
|
|
149
149
|
```
|
|
150
150
|
|
|
151
|
-
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 `
|
|
151
|
+
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.
|
|
152
152
|
|
|
153
153
|
1. ```ts
|
|
154
154
|
duplicateOptionMsg: string = `This option is already selected`
|
|
@@ -215,9 +215,9 @@ Full list of props/bindable variables for this component. The `Option` type you
|
|
|
215
215
|
|
|
216
216
|
1. ```ts
|
|
217
217
|
maxSelectMsg: ((current: number, max: number) => string) | null = (
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
218
|
+
current: number,
|
|
219
|
+
max: number
|
|
220
|
+
) => (max > 1 ? `${current}/${max}` : ``)
|
|
221
221
|
```
|
|
222
222
|
|
|
223
223
|
Inform users how many of the maximum allowed options they have already selected. Set `maxSelectMsg={null}` to not show a message. Defaults to `null` when `maxSelect={1}` or `maxSelect={null}`. Else if `maxSelect > 1`, defaults to:
|
|
@@ -230,7 +230,7 @@ Full list of props/bindable variables for this component. The `Option` type you
|
|
|
230
230
|
name: string | null = null
|
|
231
231
|
```
|
|
232
232
|
|
|
233
|
-
Applied to the `<input>` element. Sets the key of this field in a submitted form data object.
|
|
233
|
+
Applied to the `<input>` element. Sets the key of this field in a submitted form data object. See [form example](https://svelte-multiselect.netlify.app/form).
|
|
234
234
|
|
|
235
235
|
1. ```ts
|
|
236
236
|
noMatchingOptionsMsg: string = `No matching options`
|
|
@@ -410,7 +410,7 @@ For example, here's how you might annoy your users with an alert every time one
|
|
|
410
410
|
|
|
411
411
|
> Note: Depending on the data passed to the component the `options(s)` payload will either be objects or simple strings/numbers.
|
|
412
412
|
|
|
413
|
-
This component also forwards many DOM events from the `<input>` node: `blur`, `change`, `click`, `keydown`, `keyup`, `mousedown`, `mouseenter`, `mouseleave`, `touchcancel`, `touchend`, `touchmove`, `touchstart`.
|
|
413
|
+
The above list of events are [Svelte `dispatch` events](https://svelte.dev/tutorial/component-events). This component also forwards many DOM events from the `<input>` node: `blur`, `change`, `click`, `keydown`, `keyup`, `mousedown`, `mouseenter`, `mouseleave`, `touchcancel`, `touchend`, `touchmove`, `touchstart`. Registering listeners for these events works the same:
|
|
414
414
|
|
|
415
415
|
```svelte
|
|
416
416
|
<MultiSelect
|
|
@@ -425,7 +425,8 @@ TypeScript users can import the types used for internal type safety:
|
|
|
425
425
|
|
|
426
426
|
```svelte
|
|
427
427
|
<script lang="ts">
|
|
428
|
-
import MultiSelect
|
|
428
|
+
import MultiSelect from 'svelte-multiselect'
|
|
429
|
+
import type { Option, ObjectOption } from 'svelte-multiselect'
|
|
429
430
|
|
|
430
431
|
const myOptions: ObjectOption[] = [
|
|
431
432
|
{ label: 'foo', value: 42 },
|
|
@@ -463,7 +464,7 @@ If you only want to make small adjustments, you can pass the following CSS varia
|
|
|
463
464
|
- `padding: var(--sms-padding, 0 3pt)`
|
|
464
465
|
- `background: var(--sms-bg)`
|
|
465
466
|
- `color: var(--sms-text-color)`
|
|
466
|
-
- `min-height: var(--sms-min-height,
|
|
467
|
+
- `min-height: var(--sms-min-height, 22pt)`
|
|
467
468
|
- `width: var(--sms-width)`
|
|
468
469
|
- `max-width: var(--sms-max-width)`
|
|
469
470
|
- `margin: var(--sms-margin)`
|
|
@@ -585,40 +586,6 @@ Odd as it may seem, you get the most fine-grained control over the styling of ev
|
|
|
585
586
|
}
|
|
586
587
|
```
|
|
587
588
|
|
|
588
|
-
## Downstream testing
|
|
589
|
-
|
|
590
|
-
To test a Svelte component which imports `svelte-multiselect`, you need to configure your test runner to avoid [transpiling issues](https://github.com/janosh/svelte-multiselect/issues/48).
|
|
591
|
-
|
|
592
|
-
For Jest, exclude `svelte-multiselect` from `transformIgnorePatterns` in your `jest.config.json`:
|
|
593
|
-
|
|
594
|
-
```json
|
|
595
|
-
{
|
|
596
|
-
"transformIgnorePatterns": ["node_modules/?!(svelte-multiselect)"],
|
|
597
|
-
"transform": {
|
|
598
|
-
"^.+\\.[t|j]s?$": "esbuild-jest",
|
|
599
|
-
"^.+\\.svelte$": ["svelte-jester", { "preprocess": true }]
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
```
|
|
603
|
-
|
|
604
|
-
For Vitest, include `svelte-multiselect` in `deps.inline`:
|
|
605
|
-
|
|
606
|
-
```ts
|
|
607
|
-
// vite.config.ts
|
|
608
|
-
import { svelte } from '@sveltejs/vite-plugin-svelte'
|
|
609
|
-
|
|
610
|
-
export default {
|
|
611
|
-
plugins: [svelte({ hot: !process.env.VITEST })],
|
|
612
|
-
test: {
|
|
613
|
-
deps: {
|
|
614
|
-
inline: [/svelte-multiselect/],
|
|
615
|
-
},
|
|
616
|
-
},
|
|
617
|
-
}
|
|
618
|
-
```
|
|
619
|
-
|
|
620
|
-
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).
|
|
621
|
-
|
|
622
589
|
## Want to contribute?
|
|
623
590
|
|
|
624
591
|
To submit a PR, clone the repo, install dependencies and start the dev server to try out your changes.
|
|
@@ -626,6 +593,6 @@ To submit a PR, clone the repo, install dependencies and start the dev server to
|
|
|
626
593
|
```sh
|
|
627
594
|
git clone https://github.com/janosh/svelte-multiselect
|
|
628
595
|
cd svelte-multiselect
|
|
629
|
-
|
|
630
|
-
|
|
596
|
+
pnpm install
|
|
597
|
+
pnpm dev
|
|
631
598
|
```
|