svelte-multiselect 8.6.1 โ†’ 9.0.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.
@@ -2,26 +2,26 @@
2
2
  // https://github.com/sveltejs/eslint-plugin-svelte3/issues/201
3
3
  import { tick } from 'svelte';
4
4
  import { fade } from 'svelte/transition';
5
- import Select from '.';
5
+ import Select from './MultiSelect.svelte';
6
6
  export let actions;
7
- export let trigger = `k`;
7
+ export let triggers = [`k`];
8
+ export let close_keys = [`Escape`];
8
9
  export let fade_duration = 200; // in ms
9
10
  export let style = ``; // for dialog
10
- // for span in option slot, has no effect when passing slot="option"
11
+ // for span in option slot, has no effect when passing a slot
11
12
  export let span_style = ``;
12
13
  export let open = false;
13
14
  export let dialog = null;
14
15
  export let input = null;
15
16
  export let placeholder = `Filter actions...`;
16
17
  async function toggle(event) {
17
- if (event.key === trigger && event.metaKey && !open) {
18
+ if (triggers.includes(event.key) && event.metaKey && !open) {
18
19
  // open on cmd+trigger
19
20
  open = true;
20
21
  await tick(); // wait for dialog to open and input to be mounted
21
22
  input?.focus();
22
23
  }
23
- else if (event.key === `Escape` && open) {
24
- // close on escape
24
+ else if (close_keys.includes(event.key) && open) {
25
25
  open = false;
26
26
  }
27
27
  }
@@ -39,22 +39,18 @@ function run_and_close(event) {
39
39
  <svelte:window on:keydown={toggle} on:click={close_if_outside} />
40
40
 
41
41
  {#if open}
42
- <dialog
43
- class:open
44
- bind:this={dialog}
45
- transition:fade={{ duration: fade_duration }}
46
- {style}
47
- >
42
+ <dialog open bind:this={dialog} transition:fade={{ duration: fade_duration }} {style}>
48
43
  <Select
49
44
  options={actions}
50
45
  bind:input
51
46
  {placeholder}
52
47
  on:add={run_and_close}
53
48
  on:keydown={toggle}
54
- {...$$props}
49
+ {...$$restProps}
50
+ let:option
55
51
  >
56
52
  <!-- wait for https://github.com/sveltejs/svelte/pull/8304 -->
57
- <slot slot="option" name="option" let:option>
53
+ <slot>
58
54
  <span style={span_style}>{option.label}</span>
59
55
  </slot>
60
56
  </Select>
@@ -6,7 +6,8 @@ declare const __propDef: {
6
6
  label: string;
7
7
  action: () => void;
8
8
  }[];
9
- trigger?: string | undefined;
9
+ triggers?: string[] | undefined;
10
+ close_keys?: string[] | undefined;
10
11
  fade_duration?: number | undefined;
11
12
  style?: string | undefined;
12
13
  span_style?: string | undefined;
@@ -19,9 +20,7 @@ declare const __propDef: {
19
20
  [evt: string]: CustomEvent<any>;
20
21
  };
21
22
  slots: {
22
- option: {
23
- slot: string;
24
- };
23
+ default: {};
25
24
  };
26
25
  };
27
26
  export type CmdPaletteProps = typeof __propDef.props;
@@ -1,6 +1,7 @@
1
1
  <script>import { createEventDispatcher, tick } from 'svelte';
2
2
  import { flip } from 'svelte/animate';
3
- import { CircleSpinner, Wiggle } from '.';
3
+ import CircleSpinner from './CircleSpinner.svelte';
4
+ import Wiggle from './Wiggle.svelte';
4
5
  import { CrossIcon, DisabledIcon, ExpandIcon } from './icons';
5
6
  export let activeIndex = null;
6
7
  export let activeOption = null;
@@ -63,7 +64,7 @@ export let ulOptionsClass = ``;
63
64
  export let ulSelectedClass = ``;
64
65
  export let value = null;
65
66
  // get the label key from an option object or the option itself if it's a string or number
66
- const get_label = (op) => {
67
+ export const get_label = (op) => {
67
68
  if (op instanceof Object) {
68
69
  if (op.label === undefined) {
69
70
  console.error(`MultiSelect option ${JSON.stringify(op)} is an object but has no label key`);
@@ -103,8 +104,8 @@ if (sortSelected && selectedOptionsDraggable) {
103
104
  console.warn(`MultiSelect's sortSelected and selectedOptionsDraggable should not be combined as any ` +
104
105
  `user re-orderings of selected options will be undone by sortSelected on component re-renders.`);
105
106
  }
106
- if (allowUserOptions && !createOptionMsg) {
107
- console.error(`MultiSelect's allowUserOptions=${allowUserOptions} but createOptionMsg=${createOptionMsg} is falsy. ` +
107
+ if (allowUserOptions && !createOptionMsg && createOptionMsg !== null) {
108
+ console.error(`MultiSelect has allowUserOptions=${allowUserOptions} but createOptionMsg=${createOptionMsg} is falsy. ` +
108
109
  `This prevents the "Add option" <span> from showing up, resulting in a confusing user experience.`);
109
110
  }
110
111
  const dispatch = createEventDispatcher();
@@ -190,20 +191,22 @@ function add(option, event) {
190
191
  }
191
192
  }
192
193
  // remove an option from selected list
193
- function remove(label) {
194
+ function remove(to_remove) {
194
195
  if (selected.length === 0)
195
196
  return;
196
- let option = selected.find((op) => get_label(op) === label);
197
+ const idx = selected.findIndex((op) => JSON.stringify(op) === JSON.stringify(to_remove));
198
+ let [option] = selected.splice(idx, 1); // remove option from selected list
197
199
  if (option === undefined && allowUserOptions) {
198
200
  // if option with label could not be found but allowUserOptions is truthy,
199
201
  // assume it was created by user and create corresponding option object
200
202
  // on the fly for use as event payload
201
- option = (typeof options[0] == `object` ? { label } : label);
203
+ const other_ops_type = typeof options[0];
204
+ option = (other_ops_type ? { label: to_remove } : to_remove);
202
205
  }
203
206
  if (option === undefined) {
204
- return console.error(`Multiselect can't remove selected option ${label}, not found in selected list`);
207
+ return console.error(`Multiselect can't remove selected option ${JSON.stringify(to_remove)}, not found in selected list`);
205
208
  }
206
- selected = selected.filter((op) => get_label(op) !== label); // remove option from selected list
209
+ selected = [...selected]; // trigger Svelte rerender
207
210
  invalid = false; // reset error status whenever items are removed
208
211
  form_input?.setCustomValidity(``);
209
212
  dispatch(`remove`, { option });
@@ -336,6 +339,7 @@ const dragstart = (idx) => (event) => {
336
339
  event.dataTransfer.setData(`text/plain`, `${idx}`);
337
340
  };
338
341
  let ul_options;
342
+ // highlight text matching user-entered search text in available options
339
343
  function highlight_matching_options(event) {
340
344
  if (!highlightMatches || typeof CSS == `undefined` || !CSS.highlights)
341
345
  return; // don't try if CSS highlight API not supported
@@ -401,6 +405,8 @@ function highlight_matching_options(event) {
401
405
  on:mouseup|stopPropagation={open_dropdown}
402
406
  title={disabled ? disabledInputTitle : null}
403
407
  data-id={id}
408
+ role="searchbox"
409
+ tabindex="-1"
404
410
  >
405
411
  <!-- bind:value={selected} prevents form submission if required prop is true and no options are selected -->
406
412
  <input
@@ -432,6 +438,8 @@ function highlight_matching_options(event) {
432
438
  {#each selected as option, idx (option)}
433
439
  <li
434
440
  class={liSelectedClass}
441
+ role="option"
442
+ aria-selected="true"
435
443
  animate:flip={{ duration: 100 }}
436
444
  draggable={selectedOptionsDraggable && !disabled && selected.length > 1}
437
445
  on:dragstart={dragstart(idx)}
@@ -442,10 +450,13 @@ function highlight_matching_options(event) {
442
450
  >
443
451
  <!-- on:dragover|preventDefault needed for the drop to succeed https://stackoverflow.com/a/31085796 -->
444
452
  <slot name="selected" {option} {idx}>
445
- {#if parseLabelsAsHtml}
446
- {@html get_label(option)}
447
- {:else}
448
- {get_label(option)}{/if}
453
+ <slot {option} {idx}>
454
+ {#if parseLabelsAsHtml}
455
+ {@html get_label(option)}
456
+ {:else}
457
+ {get_label(option)}
458
+ {/if}
459
+ </slot>
449
460
  </slot>
450
461
  {#if !disabled && (minSelect === null || selected.length > minSelect)}
451
462
  <button
@@ -528,7 +539,15 @@ function highlight_matching_options(event) {
528
539
 
529
540
  <!-- only render options dropdown if options or searchText is not empty needed to avoid briefly flashing empty dropdown -->
530
541
  {#if (searchText && noMatchingOptionsMsg) || options?.length > 0}
531
- <ul class:hidden={!open} class="options {ulOptionsClass}" bind:this={ul_options}>
542
+ <ul
543
+ class:hidden={!open}
544
+ class="options {ulOptionsClass}"
545
+ role="listbox"
546
+ aria-multiselectable={maxSelect === null || maxSelect > 1}
547
+ aria-expanded={open}
548
+ aria-disabled={disabled ? `true` : null}
549
+ bind:this={ul_options}
550
+ >
532
551
  {#each matchingOptions as option, idx}
533
552
  {@const {
534
553
  label,
@@ -558,13 +577,17 @@ function highlight_matching_options(event) {
558
577
  }}
559
578
  on:mouseout={() => (activeIndex = null)}
560
579
  on:blur={() => (activeIndex = null)}
580
+ role="option"
581
+ aria-selected="false"
561
582
  >
562
583
  <slot name="option" {option} {idx}>
563
- {#if parseLabelsAsHtml}
564
- {@html get_label(option)}
565
- {:else}
566
- {get_label(option)}
567
- {/if}
584
+ <slot {option} {idx}>
585
+ {#if parseLabelsAsHtml}
586
+ {@html get_label(option)}
587
+ {:else}
588
+ {get_label(option)}
589
+ {/if}
590
+ </slot>
568
591
  </slot>
569
592
  </li>
570
593
  {:else}
@@ -583,6 +606,8 @@ function highlight_matching_options(event) {
583
606
  on:focus={() => (option_msg_is_active = true)}
584
607
  on:mouseout={() => (option_msg_is_active = false)}
585
608
  on:blur={() => (option_msg_is_active = false)}
609
+ role="option"
610
+ aria-selected="false"
586
611
  class="user-msg"
587
612
  >
588
613
  {msg}
@@ -1,10 +1,10 @@
1
1
  import { SvelteComponentTyped } from "svelte";
2
- import type { Option as GenericOption, MultiSelectEvents } from './';
3
- declare class __sveltets_Render<Option extends GenericOption> {
2
+ import type { MultiSelectEvents, Option as T } from './types';
3
+ declare class __sveltets_Render<Option extends T> {
4
4
  props(): {
5
5
  activeIndex?: number | null | undefined;
6
6
  activeOption?: Option | null | undefined;
7
- createOptionMsg?: string | undefined;
7
+ createOptionMsg?: string | null | undefined;
8
8
  allowUserOptions?: boolean | "append" | undefined;
9
9
  allowEmpty?: boolean | undefined;
10
10
  autocomplete?: string | undefined;
@@ -13,7 +13,7 @@ declare class __sveltets_Render<Option extends GenericOption> {
13
13
  defaultDisabledTitle?: string | undefined;
14
14
  disabled?: boolean | undefined;
15
15
  disabledInputTitle?: string | undefined;
16
- duplicateFunc?: ((op1: GenericOption, op2: GenericOption) => boolean) | undefined;
16
+ duplicateFunc?: ((op1: T, op2: T) => boolean) | undefined;
17
17
  duplicateOptionMsg?: string | undefined;
18
18
  duplicates?: boolean | undefined;
19
19
  filterFunc?: ((op: Option, searchText: string) => boolean) | undefined;
@@ -54,6 +54,7 @@ declare class __sveltets_Render<Option extends GenericOption> {
54
54
  ulOptionsClass?: string | undefined;
55
55
  ulSelectedClass?: string | undefined;
56
56
  value?: Option | Option[] | null | undefined;
57
+ get_label?: ((op: T) => string | number) | undefined;
57
58
  };
58
59
  events(): MultiSelectEvents;
59
60
  slots(): {
@@ -64,6 +65,10 @@ declare class __sveltets_Render<Option extends GenericOption> {
64
65
  option: Option;
65
66
  idx: any;
66
67
  };
68
+ default: {
69
+ option: Option;
70
+ idx: any;
71
+ };
67
72
  'remove-icon': {};
68
73
  spinner: {};
69
74
  'disabled-icon': {};
@@ -73,9 +78,10 @@ declare class __sveltets_Render<Option extends GenericOption> {
73
78
  };
74
79
  };
75
80
  }
76
- export type MultiSelectProps<Option extends GenericOption> = ReturnType<__sveltets_Render<Option>['props']>;
77
- export type MultiSelectEvents<Option extends GenericOption> = ReturnType<__sveltets_Render<Option>['events']>;
78
- export type MultiSelectSlots<Option extends GenericOption> = ReturnType<__sveltets_Render<Option>['slots']>;
79
- export default class MultiSelect<Option extends GenericOption> extends SvelteComponentTyped<MultiSelectProps<Option>, MultiSelectEvents<Option>, MultiSelectSlots<Option>> {
81
+ export type MultiSelectProps<Option extends T> = ReturnType<__sveltets_Render<Option>['props']>;
82
+ export type MultiSelectEvents<Option extends T> = ReturnType<__sveltets_Render<Option>['events']>;
83
+ export type MultiSelectSlots<Option extends T> = ReturnType<__sveltets_Render<Option>['slots']>;
84
+ export default class MultiSelect<Option extends T> extends SvelteComponentTyped<MultiSelectProps<Option>, MultiSelectEvents<Option>, MultiSelectSlots<Option>> {
85
+ get get_label(): (op: T) => string | number;
80
86
  }
81
87
  export {};
package/dist/index.d.ts CHANGED
@@ -2,55 +2,5 @@ export { default as CircleSpinner } from './CircleSpinner.svelte';
2
2
  export { default as CmdPalette } from './CmdPalette.svelte';
3
3
  export { default as MultiSelect, default } from './MultiSelect.svelte';
4
4
  export { default as Wiggle } from './Wiggle.svelte';
5
- export type Option = string | number | ObjectOption;
6
- export type ObjectOption = {
7
- label: string | number;
8
- value?: unknown;
9
- title?: string;
10
- disabled?: boolean;
11
- preselected?: boolean;
12
- disabledTitle?: string;
13
- selectedTitle?: string;
14
- [key: string]: unknown;
15
- };
16
- export type DispatchEvents<T = Option> = {
17
- add: {
18
- option: T;
19
- };
20
- create: {
21
- option: T;
22
- };
23
- remove: {
24
- option: T;
25
- };
26
- removeAll: {
27
- options: T[];
28
- };
29
- change: {
30
- option?: T;
31
- options?: T[];
32
- type: 'add' | 'remove' | 'removeAll';
33
- };
34
- open: {
35
- event: Event;
36
- };
37
- close: {
38
- event: Event;
39
- };
40
- };
41
- export type MultiSelectEvents = {
42
- [key in keyof DispatchEvents]: CustomEvent<DispatchEvents[key]>;
43
- } & {
44
- blur: FocusEvent;
45
- click: MouseEvent;
46
- focus: FocusEvent;
47
- keydown: KeyboardEvent;
48
- keyup: KeyboardEvent;
49
- mouseenter: MouseEvent;
50
- mouseleave: MouseEvent;
51
- touchcancel: TouchEvent;
52
- touchend: TouchEvent;
53
- touchmove: TouchEvent;
54
- touchstart: TouchEvent;
55
- };
5
+ export * from './types';
56
6
  export declare function scroll_into_view_if_needed_polyfill(centerIfNeeded?: boolean): IntersectionObserver;
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@ export { default as CircleSpinner } from './CircleSpinner.svelte';
2
2
  export { default as CmdPalette } from './CmdPalette.svelte';
3
3
  export { default as MultiSelect, default } from './MultiSelect.svelte';
4
4
  export { default as Wiggle } from './Wiggle.svelte';
5
+ export * from './types';
5
6
  // Firefox lacks support for scrollIntoViewIfNeeded (https://caniuse.com/scrollintoviewifneeded).
6
7
  // See https://github.com/janosh/svelte-multiselect/issues/87
7
8
  // Polyfill copied from
@@ -0,0 +1,51 @@
1
+ export type Option = string | number | ObjectOption;
2
+ export type ObjectOption = {
3
+ label: string | number;
4
+ value?: unknown;
5
+ title?: string;
6
+ disabled?: boolean;
7
+ preselected?: boolean;
8
+ disabledTitle?: string;
9
+ selectedTitle?: string;
10
+ [key: string]: unknown;
11
+ };
12
+ export type DispatchEvents<T = Option> = {
13
+ add: {
14
+ option: T;
15
+ };
16
+ create: {
17
+ option: T;
18
+ };
19
+ remove: {
20
+ option: T;
21
+ };
22
+ removeAll: {
23
+ options: T[];
24
+ };
25
+ change: {
26
+ option?: T;
27
+ options?: T[];
28
+ type: 'add' | 'remove' | 'removeAll';
29
+ };
30
+ open: {
31
+ event: Event;
32
+ };
33
+ close: {
34
+ event: Event;
35
+ };
36
+ };
37
+ export type MultiSelectEvents = {
38
+ [key in keyof DispatchEvents]: CustomEvent<DispatchEvents[key]>;
39
+ } & {
40
+ blur: FocusEvent;
41
+ click: MouseEvent;
42
+ focus: FocusEvent;
43
+ keydown: KeyboardEvent;
44
+ keyup: KeyboardEvent;
45
+ mouseenter: MouseEvent;
46
+ mouseleave: MouseEvent;
47
+ touchcancel: TouchEvent;
48
+ touchend: TouchEvent;
49
+ touchmove: TouchEvent;
50
+ touchstart: TouchEvent;
51
+ };
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -5,7 +5,7 @@
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.6.1",
8
+ "version": "9.0.0",
9
9
  "type": "module",
10
10
  "svelte": "./dist/index.js",
11
11
  "bugs": "https://github.com/janosh/svelte-multiselect/issues",
@@ -19,41 +19,41 @@
19
19
  "test": "vitest --run --coverage tests/unit/*.ts && playwright test tests/*.test.ts",
20
20
  "test:unit": "vitest tests/unit/*.ts",
21
21
  "test:e2e": "playwright test tests/*.test.ts",
22
- "changelog": "npx auto-changelog --package --output changelog.md --unreleased-only --hide-credit --commit-limit false",
22
+ "changelog": "npx auto-changelog --package --output changelog.md --hide-empty-releases --hide-credit --commit-limit false",
23
23
  "update-coverage": "vitest tests/unit --run --coverage && npx istanbul-badges-readme"
24
24
  },
25
25
  "dependencies": {
26
- "svelte": "^3.58.0"
26
+ "svelte": "^4.0.0-next.0"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@iconify/svelte": "^3.1.3",
30
- "@playwright/test": "^1.33.0",
30
+ "@playwright/test": "^1.34.3",
31
31
  "@sveltejs/adapter-static": "^2.0.2",
32
- "@sveltejs/kit": "^1.15.9",
32
+ "@sveltejs/kit": "^1.19.0",
33
33
  "@sveltejs/package": "2.0.2",
34
- "@sveltejs/vite-plugin-svelte": "^2.1.1",
35
- "@typescript-eslint/eslint-plugin": "^5.59.1",
36
- "@typescript-eslint/parser": "^5.59.1",
37
- "@vitest/coverage-c8": "^0.30.1",
38
- "eslint": "^8.39.0",
34
+ "@sveltejs/vite-plugin-svelte": "1.0.0-next.29",
35
+ "@typescript-eslint/eslint-plugin": "^5.59.7",
36
+ "@typescript-eslint/parser": "^5.59.7",
37
+ "@vitest/coverage-c8": "^0.31.1",
38
+ "eslint": "^8.41.0",
39
39
  "eslint-plugin-svelte3": "^4.0.0",
40
40
  "hastscript": "^7.2.0",
41
41
  "highlight.js": "^11.8.0",
42
- "jsdom": "^21.1.1",
42
+ "jsdom": "^22.0.0",
43
43
  "mdsvex": "^0.10.6",
44
44
  "mdsvexamples": "^0.3.3",
45
45
  "prettier": "^2.8.8",
46
46
  "prettier-plugin-svelte": "^2.10.0",
47
47
  "rehype-autolink-headings": "^6.1.1",
48
48
  "rehype-slug": "^5.1.0",
49
- "svelte-check": "^3.2.0",
49
+ "svelte-check": "^3.4.2",
50
50
  "svelte-preprocess": "^5.0.3",
51
51
  "svelte-toc": "^0.5.5",
52
- "svelte-zoo": "^0.4.5",
53
- "svelte2tsx": "^0.6.11",
52
+ "svelte-zoo": "^0.4.6",
53
+ "svelte2tsx": "^0.6.15",
54
54
  "typescript": "5.0.4",
55
- "vite": "^4.3.3",
56
- "vitest": "^0.30.1"
55
+ "vite": "^4.3.9",
56
+ "vitest": "^0.31.1"
57
57
  },
58
58
  "keywords": [
59
59
  "svelte",
package/readme.md CHANGED
@@ -36,22 +36,34 @@
36
36
 
37
37
  ## ๐Ÿงช &thinsp; Coverage
38
38
 
39
- | Statements | Branches | Lines |
40
- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- |
41
- | ![Statements](https://img.shields.io/badge/statements-92.95%25-brightgreen.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-100%25-brightgreen.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-92.95%25-brightgreen.svg?style=flat) |
39
+ | Statements | Branches | Lines |
40
+ | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- |
41
+ | ![Statements](https://img.shields.io/badge/statements-97.94%25-brightgreen.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-79.39%25-red.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-97.94%25-brightgreen.svg?style=flat) |
42
42
 
43
43
  ## ๐Ÿ“œ &thinsp; Breaking changes
44
44
 
45
- - **8.0.0**&nbsp;
45
+ - **8.0.0** (2022-10-22)&nbsp;
46
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)
47
47
  - Prop `noOptionsMsg` was renamed to `noMatchingOptionsMsg`. [PR 133](https://github.com/janosh/svelte-multiselect/pull/133).
48
- - **v8.3.0**&nbsp; `addOptionMsg` was renamed to `createOptionMsg` (no major since version since it's rarely used) [sha](https://github.com/janosh/svelte-multiselect/commits).
48
+ - **v8.3.0** (2023-01-25)&nbsp; `addOptionMsg` was renamed to `createOptionMsg` (no major since version since it's rarely used) [sha](https://github.com/janosh/svelte-multiselect/commits).
49
+ - **v9.0.0** (2023-06-01)&nbsp; Svelte bumped from v3 to v4. Also, not breaking but noteworthy: MultiSelect received a default slot that functions as both `"option"` and `"selected"`. If you previously had two identical slots for `"option"` and `"selected"`, you can now remove the `name` from one of them and drop the other:
50
+
51
+ ```diff
52
+ <MultiSelect
53
+ {options}
54
+ + let:option
55
+ >
56
+ - <SlotComponent let:option {option} slot="selected" />
57
+ - <SlotComponent let:option {option} slot="option" />
58
+ + <SlotComponent {option} />
59
+ </MultiSelect>
60
+ ```
49
61
 
50
62
  ## ๐Ÿ”จ &thinsp; Installation
51
63
 
52
64
  ```sh
53
65
  npm install --dev svelte-multiselect
54
- pnpm add --dev svelte-multiselect
66
+ pnpm add -D svelte-multiselect
55
67
  yarn add --dev svelte-multiselect
56
68
  ```
57
69
 
@@ -90,10 +102,10 @@ Full list of props/bindable variables for this component. The `Option` type you
90
102
  Currently active option, i.e. the one the user currently hovers or navigated to with arrow keys.
91
103
 
92
104
  1. ```ts
93
- createOptionMsg: string = `Create this option...`
105
+ createOptionMsg: string | null = `Create this option...`
94
106
  ```
95
107
 
96
- Message shown to users after entering text when no options match their query and `allowUserOptions` is truthy.
108
+ The message shown to users when `allowUserOptions` is truthy and they entered text that doesn't match any existing options to suggest creating a new option from the entered text. Emits `console.error` if `allowUserOptions` is `true` or `'append'` and `createOptionMsg=''` to since users might be unaware they can create new option. The error can be silenced by setting `createOptionMsg=null` indicating developer intent is to e.g. use MultiSelect as a tagging component where a user message might be unwanted.
97
109
 
98
110
  1. ```ts
99
111
  allowEmpty: boolean = false
@@ -146,7 +158,7 @@ Full list of props/bindable variables for this component. The `Option` type you
146
158
 
147
159
  <!-- prettier-ignore -->
148
160
  1. ```ts
149
- duplicateFunc: (op1: GenericOption, op2: GenericOption) => boolean = (op1, op2) =>
161
+ duplicateFunc: (op1: T, op2: T) => boolean = (op1, op2) =>
150
162
  `${get_label(op1)}`.toLowerCase() === `${get_label(op2)}`.toLowerCase()
151
163
  ```
152
164
 
@@ -189,7 +201,7 @@ Full list of props/bindable variables for this component. The `Option` type you
189
201
  highlightMatches: boolean = true
190
202
  ```
191
203
 
192
- Whether to highlight text in the dropdown options that matches the current user-entered search query. Uses the [CSS Custom Highlight API](https://developer.mozilla.org/docs/Web/API/CSS_Custom_Highlight_API) with limited browser support and [styling options](https://developer.mozilla.org/docs/Web/CSS/::highlight). See `::highlight(sms-search-matches)` below for available CSS variables.
204
+ Whether to highlight text in the dropdown options that matches the current user-entered search query. Uses the [CSS Custom Highlight API](https://developer.mozilla.org/docs/Web/API/CSS_Custom_Highlight_API) with [limited browser support](https://caniuse.com/mdn-api_css_highlights) (70% as of May 2023) and [styling options](https://developer.mozilla.org/docs/Web/CSS/::highlight). See `::highlight(sms-search-matches)` below for available CSS variables.
193
205
 
194
206
  1. ```ts
195
207
  id: string | null = null
@@ -375,14 +387,9 @@ Full list of props/bindable variables for this component. The `Option` type you
375
387
  Example:
376
388
 
377
389
  ```svelte
378
- <MultiSelect options={[`Red`, `Green`, `Blue`, `Yellow`, `Purple`]}>
379
- <span let:idx let:option slot="option">
380
- {idx + 1}
381
- {option.label}
382
- <span style:background={option.label} style=" width: 1em; height: 1em;" />
383
- </span>
384
-
385
- <span let:idx let:option slot="selected">
390
+ <MultiSelect options={[`Red`, `Green`, `Blue`, `Yellow`, `Purple`]} let:idx let:option>
391
+ <!-- default slot overrides rendering of both dropdown-listed and selected options -->
392
+ <span>
386
393
  {idx + 1}
387
394
  {option.label}
388
395
  <span style:background={option.label} style=" width: 1em; height: 1em;" />