svelte-multiselect 10.0.0 → 10.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { SvelteComponentTyped } from "svelte";
1
+ import { SvelteComponent } from "svelte";
2
2
  declare const __propDef: {
3
3
  props: {
4
4
  color?: string | undefined;
@@ -13,6 +13,6 @@ declare const __propDef: {
13
13
  export type CircleSpinnerProps = typeof __propDef.props;
14
14
  export type CircleSpinnerEvents = typeof __propDef.events;
15
15
  export type CircleSpinnerSlots = typeof __propDef.slots;
16
- export default class CircleSpinner extends SvelteComponentTyped<CircleSpinnerProps, CircleSpinnerEvents, CircleSpinnerSlots> {
16
+ export default class CircleSpinner extends SvelteComponent<CircleSpinnerProps, CircleSpinnerEvents, CircleSpinnerSlots> {
17
17
  }
18
18
  export {};
@@ -1,4 +1,4 @@
1
- import { SvelteComponentTyped } from "svelte";
1
+ import { SvelteComponent } from "svelte";
2
2
  declare const __propDef: {
3
3
  props: {
4
4
  [x: string]: any;
@@ -26,6 +26,6 @@ declare const __propDef: {
26
26
  export type CmdPaletteProps = typeof __propDef.props;
27
27
  export type CmdPaletteEvents = typeof __propDef.events;
28
28
  export type CmdPaletteSlots = typeof __propDef.slots;
29
- export default class CmdPalette extends SvelteComponentTyped<CmdPaletteProps, CmdPaletteEvents, CmdPaletteSlots> {
29
+ export default class CmdPalette extends SvelteComponent<CmdPaletteProps, CmdPaletteEvents, CmdPaletteSlots> {
30
30
  }
31
31
  export {};
@@ -3,6 +3,7 @@ import { flip } from 'svelte/animate';
3
3
  import CircleSpinner from './CircleSpinner.svelte';
4
4
  import Wiggle from './Wiggle.svelte';
5
5
  import { CrossIcon, DisabledIcon, ExpandIcon } from './icons';
6
+ import { get_label, get_style } from './utils';
6
7
  export let activeIndex = null;
7
8
  export let activeOption = null;
8
9
  export let createOptionMsg = `Create this option...`;
@@ -59,26 +60,26 @@ export let resetFilterOnAdd = true;
59
60
  export let searchText = ``;
60
61
  export let selected = options
61
62
  ?.filter((opt) => opt instanceof Object && opt?.preselected)
62
- .slice(0, maxSelect ?? undefined) ?? [];
63
+ .slice(0, maxSelect ?? undefined) ?? []; // don't allow more than maxSelect preselected options
63
64
  export let sortSelected = false;
64
65
  export let selectedOptionsDraggable = !sortSelected;
65
66
  export let ulOptionsClass = ``;
66
67
  export let ulSelectedClass = ``;
67
68
  export let value = null;
68
- // get the label key from an option object or the option itself if it's a string or number
69
- export const get_label = (opt) => {
70
- if (opt instanceof Object) {
71
- if (opt.label === undefined) {
72
- console.error(`MultiSelect option ${JSON.stringify(opt)} is an object but has no label key`);
73
- }
74
- return opt.label;
75
- }
76
- return `${opt}`;
69
+ const selected_to_value = (selected) => {
70
+ value = maxSelect === 1 ? selected[0] ?? null : selected;
71
+ };
72
+ const value_to_selected = (value) => {
73
+ if (maxSelect === 1)
74
+ selected = value ? [value] : [];
75
+ else
76
+ selected = value ?? [];
77
77
  };
78
78
  // if maxSelect=1, value is the single item in selected (or null if selected is empty)
79
79
  // this solves both https://github.com/janosh/svelte-multiselect/issues/86 and
80
80
  // https://github.com/janosh/svelte-multiselect/issues/136
81
- $: value = maxSelect === 1 ? selected[0] ?? null : selected;
81
+ $: selected_to_value(selected);
82
+ $: value_to_selected(value);
82
83
  let wiggle = false; // controls wiggle animation when user tries to exceed maxSelect
83
84
  if (!(options?.length > 0)) {
84
85
  if (allowUserOptions || loading || disabled || allowEmpty) {
@@ -455,6 +456,7 @@ function highlight_matching_options(event) {
455
456
  on:dragenter={() => (drag_idx = idx)}
456
457
  on:dragover|preventDefault
457
458
  class:active={drag_idx === idx}
459
+ style={get_style(option, `selected`)}
458
460
  >
459
461
  <!-- on:dragover|preventDefault needed for the drop to succeed https://stackoverflow.com/a/31085796 -->
460
462
  <slot name="selected" {option} {idx}>
@@ -597,6 +599,7 @@ function highlight_matching_options(event) {
597
599
  on:blur={() => (activeIndex = null)}
598
600
  role="option"
599
601
  aria-selected="false"
602
+ style={get_style(option, `option`)}
600
603
  >
601
604
  <slot name="option" {option} {idx}>
602
605
  <slot {option} {idx}>
@@ -608,16 +611,25 @@ function highlight_matching_options(event) {
608
611
  </slot>
609
612
  </slot>
610
613
  </li>
611
- {:else}
612
- {@const textInputIsDuplicate = selected.map(get_label).includes(searchText)}
613
- <!-- set msg to duplicateOptionMsg if duplicates are not allowed and the user-entered
614
- searchText is a duplicate, else set to createOptionMsg -->
615
- {@const msg =
616
- !duplicates && textInputIsDuplicate ? duplicateOptionMsg : createOptionMsg}
617
- {#if allowUserOptions && searchText && msg}
614
+ {/each}
615
+ {#if searchText}
616
+ {@const text_input_is_duplicate = selected.map(get_label).includes(searchText)}
617
+ {@const is_dupe = !duplicates && text_input_is_duplicate && `dupe`}
618
+ {@const can_create = allowUserOptions && createOptionMsg && `create`}
619
+ {@const no_match =
620
+ matchingOptions?.length == 0 && noMatchingOptionsMsg && `no-match`}
621
+ {@const msgType = is_dupe || can_create || no_match}
622
+ {#if msgType}
623
+ {@const msg = {
624
+ dupe: duplicateOptionMsg,
625
+ create: createOptionMsg,
626
+ 'no-match': noMatchingOptionsMsg,
627
+ }[msgType]}
618
628
  <li
619
629
  on:mousedown|stopPropagation
620
- on:mouseup|stopPropagation={(event) => add(searchText, event)}
630
+ on:mouseup|stopPropagation={(event) => {
631
+ if (allowUserOptions) add(searchText, event)
632
+ }}
621
633
  title={createOptionMsg}
622
634
  class:active={option_msg_is_active}
623
635
  on:mouseover={() => (option_msg_is_active = true)}
@@ -627,24 +639,18 @@ function highlight_matching_options(event) {
627
639
  role="option"
628
640
  aria-selected="false"
629
641
  class="user-msg"
642
+ style:cursor={{
643
+ dupe: `not-allowed`,
644
+ create: `pointer`,
645
+ 'no-match': `default`,
646
+ }[msgType]}
630
647
  >
631
- <slot
632
- name="user-msg"
633
- {duplicateOptionMsg}
634
- {createOptionMsg}
635
- {textInputIsDuplicate}
636
- {searchText}
637
- {msg}
638
- >
648
+ <slot name="user-msg" {searchText} {msgType} {msg}>
639
649
  {msg}
640
650
  </slot>
641
651
  </li>
642
- {:else if noMatchingOptionsMsg}
643
- <!-- use span to not have cursor: pointer -->
644
- <span class="user-msg">{noMatchingOptionsMsg}</span>
645
652
  {/if}
646
- <!-- Show nothing if all messages are empty -->
647
- {/each}
653
+ {/if}
648
654
  </ul>
649
655
  {/if}
650
656
  </div>
@@ -1,4 +1,4 @@
1
- import { SvelteComponentTyped } from "svelte";
1
+ import { SvelteComponent } from "svelte";
2
2
  import type { MultiSelectEvents, Option as T } from './types';
3
3
  declare class __sveltets_Render<Option extends T> {
4
4
  props(): {
@@ -55,7 +55,6 @@ declare class __sveltets_Render<Option extends T> {
55
55
  ulOptionsClass?: string | undefined;
56
56
  ulSelectedClass?: string | undefined;
57
57
  value?: Option | Option[] | null | undefined;
58
- get_label?: ((opt: T) => string | number) | undefined;
59
58
  };
60
59
  events(): MultiSelectEvents;
61
60
  slots(): {
@@ -87,10 +86,8 @@ declare class __sveltets_Render<Option extends T> {
87
86
  idx: any;
88
87
  };
89
88
  'user-msg': {
90
- duplicateOptionMsg: string;
91
- createOptionMsg: string | null;
92
- textInputIsDuplicate: any;
93
89
  searchText: string;
90
+ msgType: any;
94
91
  msg: any;
95
92
  };
96
93
  };
@@ -98,7 +95,6 @@ declare class __sveltets_Render<Option extends T> {
98
95
  export type MultiSelectProps<Option extends T> = ReturnType<__sveltets_Render<Option>['props']>;
99
96
  export type MultiSelectEvents<Option extends T> = ReturnType<__sveltets_Render<Option>['events']>;
100
97
  export type MultiSelectSlots<Option extends T> = ReturnType<__sveltets_Render<Option>['slots']>;
101
- export default class MultiSelect<Option extends T> extends SvelteComponentTyped<MultiSelectProps<Option>, MultiSelectEvents<Option>, MultiSelectSlots<Option>> {
102
- get get_label(): (opt: T) => string | number;
98
+ export default class MultiSelect<Option extends T> extends SvelteComponent<MultiSelectProps<Option>, MultiSelectEvents<Option>, MultiSelectSlots<Option>> {
103
99
  }
104
100
  export {};
@@ -1,4 +1,4 @@
1
- import { SvelteComponentTyped } from "svelte";
1
+ import { SvelteComponent } from "svelte";
2
2
  declare const __propDef: {
3
3
  props: {
4
4
  wiggle?: boolean | undefined;
@@ -20,6 +20,6 @@ declare const __propDef: {
20
20
  export type WiggleProps = typeof __propDef.props;
21
21
  export type WiggleEvents = typeof __propDef.events;
22
22
  export type WiggleSlots = typeof __propDef.slots;
23
- export default class Wiggle extends SvelteComponentTyped<WiggleProps, WiggleEvents, WiggleSlots> {
23
+ export default class Wiggle extends SvelteComponent<WiggleProps, WiggleEvents, WiggleSlots> {
24
24
  }
25
25
  export {};
@@ -1,7 +1,7 @@
1
1
  /** @typedef {typeof __propDef.props} ChevronExpandProps */
2
2
  /** @typedef {typeof __propDef.events} ChevronExpandEvents */
3
3
  /** @typedef {typeof __propDef.slots} ChevronExpandSlots */
4
- export default class ChevronExpand extends SvelteComponentTyped<{
4
+ export default class ChevronExpand extends SvelteComponent<{
5
5
  [x: string]: any;
6
6
  }, {
7
7
  [evt: string]: CustomEvent<any>;
@@ -10,7 +10,7 @@ export default class ChevronExpand extends SvelteComponentTyped<{
10
10
  export type ChevronExpandProps = typeof __propDef.props;
11
11
  export type ChevronExpandEvents = typeof __propDef.events;
12
12
  export type ChevronExpandSlots = typeof __propDef.slots;
13
- import { SvelteComponentTyped } from "svelte";
13
+ import { SvelteComponent } from "svelte";
14
14
  declare const __propDef: {
15
15
  props: {
16
16
  [x: string]: any;
@@ -1,7 +1,7 @@
1
1
  /** @typedef {typeof __propDef.props} CrossProps */
2
2
  /** @typedef {typeof __propDef.events} CrossEvents */
3
3
  /** @typedef {typeof __propDef.slots} CrossSlots */
4
- export default class Cross extends SvelteComponentTyped<{
4
+ export default class Cross extends SvelteComponent<{
5
5
  [x: string]: any;
6
6
  }, {
7
7
  [evt: string]: CustomEvent<any>;
@@ -10,7 +10,7 @@ export default class Cross extends SvelteComponentTyped<{
10
10
  export type CrossProps = typeof __propDef.props;
11
11
  export type CrossEvents = typeof __propDef.events;
12
12
  export type CrossSlots = typeof __propDef.slots;
13
- import { SvelteComponentTyped } from "svelte";
13
+ import { SvelteComponent } from "svelte";
14
14
  declare const __propDef: {
15
15
  props: {
16
16
  [x: string]: any;
@@ -1,7 +1,7 @@
1
1
  /** @typedef {typeof __propDef.props} DisabledProps */
2
2
  /** @typedef {typeof __propDef.events} DisabledEvents */
3
3
  /** @typedef {typeof __propDef.slots} DisabledSlots */
4
- export default class Disabled extends SvelteComponentTyped<{
4
+ export default class Disabled extends SvelteComponent<{
5
5
  [x: string]: any;
6
6
  }, {
7
7
  [evt: string]: CustomEvent<any>;
@@ -10,7 +10,7 @@ export default class Disabled extends SvelteComponentTyped<{
10
10
  export type DisabledProps = typeof __propDef.props;
11
11
  export type DisabledEvents = typeof __propDef.events;
12
12
  export type DisabledSlots = typeof __propDef.slots;
13
- import { SvelteComponentTyped } from "svelte";
13
+ import { SvelteComponent } from "svelte";
14
14
  declare const __propDef: {
15
15
  props: {
16
16
  [x: string]: any;
@@ -1,7 +1,7 @@
1
1
  /** @typedef {typeof __propDef.props} OctocatProps */
2
2
  /** @typedef {typeof __propDef.events} OctocatEvents */
3
3
  /** @typedef {typeof __propDef.slots} OctocatSlots */
4
- export default class Octocat extends SvelteComponentTyped<{
4
+ export default class Octocat extends SvelteComponent<{
5
5
  [x: string]: any;
6
6
  }, {
7
7
  [evt: string]: CustomEvent<any>;
@@ -10,7 +10,7 @@ export default class Octocat extends SvelteComponentTyped<{
10
10
  export type OctocatProps = typeof __propDef.props;
11
11
  export type OctocatEvents = typeof __propDef.events;
12
12
  export type OctocatSlots = typeof __propDef.slots;
13
- import { SvelteComponentTyped } from "svelte";
13
+ import { SvelteComponent } from "svelte";
14
14
  declare const __propDef: {
15
15
  props: {
16
16
  [x: string]: any;
package/dist/types.d.ts CHANGED
@@ -1,4 +1,8 @@
1
1
  export type Option = string | number | ObjectOption;
2
+ export type OptionStyle = string | {
3
+ option: string;
4
+ selected: string;
5
+ };
2
6
  export type ObjectOption = {
3
7
  label: string | number;
4
8
  value?: unknown;
@@ -7,6 +11,7 @@ export type ObjectOption = {
7
11
  preselected?: boolean;
8
12
  disabledTitle?: string;
9
13
  selectedTitle?: string;
14
+ style?: OptionStyle;
10
15
  [key: string]: unknown;
11
16
  };
12
17
  export type DispatchEvents<T = Option> = {
@@ -0,0 +1,6 @@
1
+ import type { Option, OptionStyle } from './types';
2
+ export declare const get_label: (opt: Option) => string | number;
3
+ export declare function get_style(option: {
4
+ style?: OptionStyle;
5
+ [key: string]: unknown;
6
+ } | string | number, key?: 'selected' | 'option' | null): string | null | undefined;
package/dist/utils.js ADDED
@@ -0,0 +1,30 @@
1
+ // get the label key from an option object or the option itself if it's a string or number
2
+ export const get_label = (opt) => {
3
+ if (opt instanceof Object) {
4
+ if (opt.label === undefined) {
5
+ console.error(`MultiSelect option ${JSON.stringify(opt)} is an object but has no label key`);
6
+ }
7
+ return opt.label;
8
+ }
9
+ return `${opt}`;
10
+ };
11
+ export function get_style(option, key = null) {
12
+ if (!option?.style)
13
+ return null;
14
+ if (![`selected`, `option`, null].includes(key)) {
15
+ console.error(`MultiSelect: Invalid key=${key} for get_style`);
16
+ return;
17
+ }
18
+ if (typeof option == `object` && option.style) {
19
+ if (typeof option.style == `string`) {
20
+ return option.style;
21
+ }
22
+ if (typeof option.style == `object`) {
23
+ if (key && key in option.style)
24
+ return option.style[key];
25
+ else {
26
+ console.error(`Invalid style object for option=${JSON.stringify(option)}`);
27
+ }
28
+ }
29
+ }
30
+ }
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": "10.0.0",
8
+ "version": "10.1.0",
9
9
  "type": "module",
10
10
  "svelte": "./dist/index.js",
11
11
  "bugs": "https://github.com/janosh/svelte-multiselect/issues",
@@ -23,19 +23,19 @@
23
23
  "update-coverage": "vitest tests/unit --run --coverage && npx istanbul-badges-readme"
24
24
  },
25
25
  "dependencies": {
26
- "svelte": "^4.0.1"
26
+ "svelte": "^4.0.5"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@iconify/svelte": "^3.1.4",
30
- "@playwright/test": "^1.35.1",
30
+ "@playwright/test": "^1.36.1",
31
31
  "@sveltejs/adapter-static": "^2.0.2",
32
- "@sveltejs/kit": "^1.21.0",
33
- "@sveltejs/package": "2.1.0",
32
+ "@sveltejs/kit": "^1.22.3",
33
+ "@sveltejs/package": "2.2.0",
34
34
  "@sveltejs/vite-plugin-svelte": "2.4.2",
35
- "@typescript-eslint/eslint-plugin": "^5.60.1",
36
- "@typescript-eslint/parser": "^5.60.1",
37
- "@vitest/coverage-v8": "^0.32.2",
38
- "eslint": "^8.44.0",
35
+ "@typescript-eslint/eslint-plugin": "^6.1.0",
36
+ "@typescript-eslint/parser": "^6.1.0",
37
+ "@vitest/coverage-v8": "^0.33.0",
38
+ "eslint": "^8.45.0",
39
39
  "eslint-plugin-svelte": "^2.32.2",
40
40
  "hastscript": "^7.2.0",
41
41
  "highlight.js": "^11.8.0",
@@ -46,14 +46,14 @@
46
46
  "prettier-plugin-svelte": "^2.10.1",
47
47
  "rehype-autolink-headings": "^6.1.1",
48
48
  "rehype-slug": "^5.1.0",
49
- "svelte-check": "^3.4.4",
49
+ "svelte-check": "^3.4.6",
50
50
  "svelte-preprocess": "^5.0.4",
51
51
  "svelte-toc": "^0.5.5",
52
- "svelte-zoo": "^0.4.8",
53
- "svelte2tsx": "^0.6.16",
52
+ "svelte-zoo": "^0.4.9",
53
+ "svelte2tsx": "^0.6.19",
54
54
  "typescript": "5.1.6",
55
- "vite": "^4.3.9",
56
- "vitest": "^0.32.2"
55
+ "vite": "^4.4.4",
56
+ "vitest": "^0.33.0"
57
57
  },
58
58
  "keywords": [
59
59
  "svelte",
package/readme.md CHANGED
@@ -385,7 +385,7 @@ Full list of props/bindable variables for this component. The `Option` type you
385
385
  value: Option | Option[] | null = null
386
386
  ```
387
387
 
388
- 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!
388
+ 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: Setting `value` does not rendered state on initial mount, meaning `bind:value` will update local variable `value` whenever internal component state changes but passing a `value` when component first mounts won't be reflected in UI. This is because the source of truth for rendering is `bind:selected`. `selected` is reactive to `value` internally but only on reassignment from initial value. Suggestions for better solutions than [#249](https://github.com/janosh/svelte-multiselect/issues/249) welcome!
389
389
 
390
390
  ## 🎰 &thinsp; Slots
391
391