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.
@@ -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 options
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
- // % in JS behaves like remainder operator, not real modulo, so negative numbers stay negative
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
- bind:value={selected}
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="15px" />
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, 19pt);
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 restState = { angle: 0, scale: 1, dx: 0, dy: 0 };
13
- let store = spring(restState, { stiffness, damping });
14
- $: store.set(wiggle ? { scale, angle, dx, dy } : restState);
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>
@@ -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="M14.48 11.95c.17.02.34.05.52.05 2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4c0 .18.03.35.05.52l3.43 3.43zm2.21 2.21L22.53 20H23v-2c0-2.14-3.56-3.5-6.31-3.84zM0 3.12l4 4V10H1v2h3v3h2v-3h2.88l2.51 2.51C9.19 15.11 7 16.3 7 18v2h9.88l4 4 1.41-1.41L1.41 1.71 0 3.12zM6.88 10H6v-.88l.88.88z"
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.1",
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": "^1.0.0-next.44",
17
- "@sveltejs/kit": "^1.0.0-next.516",
18
- "@sveltejs/package": "^1.0.0-next.5",
19
- "@sveltejs/vite-plugin-svelte": "^1.0.9",
20
- "@typescript-eslint/eslint-plugin": "^5.40.0",
21
- "@typescript-eslint/parser": "^5.40.0",
22
- "eslint": "^8.25.0",
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
- "jsdom": "^20.0.1",
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.1",
31
- "svelte": "^3.52.0",
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.6",
35
- "svelte-toc": "^0.4.0",
36
+ "svelte-preprocess": "^4.10.7",
37
+ "svelte-toc": "^0.4.1",
36
38
  "svelte2tsx": "^0.5.20",
37
- "tslib": "^2.4.0",
39
+ "tslib": "^2.4.1",
38
40
  "typescript": "^4.8.4",
39
- "vite": "^3.1.8",
40
- "vitest": "^0.24.3"
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
- **Keyboard-friendly, accessible and highly customizable multi-select component.**
18
- <span class="hide-in-docs">
19
- <a href="https://svelte-multiselect.netlify.app">View the docs</a>
20
- </span>
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**&nbsp; The prop `showOptions` which controls whether the list of dropdown options is currently open or closed was renamed to `open`. See [PR 103](https://github.com/janosh/svelte-multiselect/pull/103).
40
- - **v6.0.1**&nbsp; The prop `disabledTitle` which sets the title of the `<MultiSelect>` `<input>` node if in `disabled` mode was renamed to `disabledInputTitle`. See [PR 105](https://github.com/janosh/svelte-multiselect/pull/105).
41
- - **v6.0.1**&nbsp; 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. See [PR 105](https://github.com/janosh/svelte-multiselect/pull/105).
42
- - **6.1.0**&nbsp; 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. See [PR 120](https://github.com/janosh/svelte-multiselect/pull/120).
43
- - **v7.0.0**&nbsp; `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. See [PR 123](https://github.com/janosh/svelte-multiselect/pull/123).
39
+ - **v6.0.0**&nbsp; 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**&nbsp; 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**&nbsp; 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**&nbsp; 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**&nbsp; `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**&nbsp;
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. See [PR 138](https://github.com/janosh/svelte-multiselect/pull/138)
46
- - Prop `noOptionsMsg` was renamed to `noMatchingOptionsMsg`. See [PR 133](https://github.com/janosh/svelte-multiselect/pull/133).
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 from the first option of the list to keep type homogeneity.
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 ` 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.
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
- current: number,
219
- max: number
220
- ) => (max > 1 ? `${current}/${max}` : ``)
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. Not useful at the moment since the value is stored in Svelte state, not on the `<input>` node.
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`. You can register listeners for these just like for the above [Svelte `dispatch` events](https://svelte.dev/tutorial/component-events):
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, { Option, ObjectOption } from 'svelte-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, 19pt)`
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
- npm install
630
- npm run dev
596
+ pnpm install
597
+ pnpm dev
631
598
  ```