svelte-multiselect 4.0.6 → 5.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.
@@ -1,9 +1,8 @@
1
1
  <script >import { createEventDispatcher, tick } from 'svelte';
2
+ import './';
2
3
  import CircleSpinner from './CircleSpinner.svelte';
3
- import { CrossIcon, ExpandIcon, DisabledIcon } from './icons';
4
+ import { CrossIcon, DisabledIcon, ExpandIcon } from './icons';
4
5
  import Wiggle from './Wiggle.svelte';
5
- export let selectedLabels = [];
6
- export let selectedValues = [];
7
6
  export let searchText = ``;
8
7
  export let showOptions = false;
9
8
  export let maxSelect = null; // null means any number of options are selectable
@@ -11,7 +10,9 @@ export let maxSelectMsg = null;
11
10
  export let disabled = false;
12
11
  export let disabledTitle = `This field is disabled`;
13
12
  export let options;
14
- export let selected = options.filter((op) => op?.preselected) ?? [];
13
+ export let selected = [];
14
+ export let selectedLabels = [];
15
+ export let selectedValues = [];
15
16
  export let input = null;
16
17
  export let outerDiv = null;
17
18
  export let placeholder = undefined;
@@ -22,7 +23,7 @@ export let activeOption = null;
22
23
  export let filterFunc = (op, searchText) => {
23
24
  if (!searchText)
24
25
  return true;
25
- return `${op.label}`.toLowerCase().includes(searchText.toLowerCase());
26
+ return `${get_label(op)}`.toLowerCase().includes(searchText.toLowerCase());
26
27
  };
27
28
  export let outerDivClass = ``;
28
29
  export let ulSelectedClass = ``;
@@ -41,45 +42,32 @@ export let loading = false;
41
42
  export let required = false;
42
43
  export let autocomplete = `off`;
43
44
  export let invalid = false;
44
- if (maxSelect !== null && maxSelect < 0) {
45
+ export let sortSelected = false;
46
+ if (maxSelect !== null && maxSelect < 1) {
45
47
  console.error(`maxSelect must be null or positive integer, got ${maxSelect}`);
46
48
  }
47
49
  if (!(options?.length > 0))
48
- console.error(`MultiSelect missing options`);
50
+ console.error(`MultiSelect is missing options`);
49
51
  if (!Array.isArray(selected))
50
52
  console.error(`selected prop must be an array`);
51
53
  const dispatch = createEventDispatcher();
52
54
  let activeMsg = false; // controls active state of <li>{addOptionMsg}</li>
53
- function is_object(item) {
54
- return typeof item === `object` && !Array.isArray(item) && item !== null;
55
- }
56
55
  // process proto options to full ones with mandatory labels
57
- $: _options = options.map((raw_op) => {
58
- if (is_object(raw_op)) {
59
- const option = { ...raw_op };
60
- if (option.value === undefined)
61
- option.value = option.label;
62
- return option;
63
- }
64
- else {
65
- if (![`string`, `number`].includes(typeof raw_op)) {
66
- console.warn(`MultiSelect options must be objects, strings or numbers, got ${typeof raw_op}`);
67
- }
68
- // even if we logged error above, try to proceed hoping user knows what they're doing
69
- return { label: raw_op, value: raw_op };
70
- }
71
- });
72
- let wiggle = false;
73
- $: selectedLabels = selected.map((op) => op.label);
74
- $: selectedValues = selected.map((op) => op.value);
56
+ const get_value = (option) => (option instanceof Object ? option.value : option);
57
+ const get_label = (option) => (option instanceof Object ? option.label : option);
58
+ let wiggle = false; // controls wiggle animation when user tries to exceed maxSelect
59
+ $: selectedLabels = selected.map(get_label);
60
+ $: selectedValues = selected.map(get_value);
75
61
  // formValue binds to input.form-control to prevent form submission if required
76
62
  // prop is true and no options are selected
77
63
  $: formValue = selectedValues.join(`,`);
78
64
  $: if (formValue)
79
65
  invalid = false; // reset error status whenever component state changes
80
66
  // options matching the current search text
81
- $: matchingOptions = _options.filter((op) => filterFunc(op, searchText) && !selectedLabels.includes(op.label));
82
- $: matchingEnabledOptions = matchingOptions.filter((op) => !op.disabled);
67
+ $: matchingOptions = options.filter((op) => filterFunc(op, searchText) &&
68
+ !(op instanceof Object && op.disabled) &&
69
+ !selectedLabels.includes(get_label(op)) // remove already selected options from dropdown list
70
+ );
83
71
  // add an option to selected list
84
72
  function add(label) {
85
73
  if (maxSelect && maxSelect > 1 && selected.length >= maxSelect)
@@ -87,7 +75,7 @@ function add(label) {
87
75
  // to prevent duplicate selection, we could add `&& !selectedLabels.includes(label)`
88
76
  if (maxSelect === null || maxSelect === 1 || selected.length < maxSelect) {
89
77
  // first check if we find option in the options list
90
- let option = _options.find((op) => op.label === label);
78
+ let option = options.find((op) => get_value(op) === label);
91
79
  if (!option && // this has the side-effect of not allowing to user to add the same
92
80
  // custom option twice in append mode
93
81
  [true, `append`].includes(allowUserOptions) &&
@@ -95,7 +83,7 @@ function add(label) {
95
83
  // user entered text but no options match, so if allowUserOptions=true | 'append', we create new option
96
84
  option = { label: searchText, value: searchText };
97
85
  if (allowUserOptions === `append`)
98
- _options = [..._options, option];
86
+ options = [...options, option];
99
87
  }
100
88
  searchText = ``; // reset search string on selection
101
89
  if (!option) {
@@ -108,6 +96,16 @@ function add(label) {
108
96
  }
109
97
  else {
110
98
  selected = [...selected, option];
99
+ if (sortSelected === true) {
100
+ selected = selected.sort((op1, op2) => {
101
+ const [label1, label2] = [get_label(op1), get_label(op2)];
102
+ // coerce to string if labels are numbers
103
+ return `${label1}`.localeCompare(`${label2}`);
104
+ });
105
+ }
106
+ else if (typeof sortSelected === `function`) {
107
+ selected = selected.sort(sortSelected);
108
+ }
111
109
  }
112
110
  if (selected.length === maxSelect)
113
111
  setOptionsVisible(false);
@@ -123,7 +121,7 @@ function remove(label) {
123
121
  return;
124
122
  selected.splice(selectedLabels.lastIndexOf(label), 1);
125
123
  selected = selected; // Svelte rerender after in-place splice
126
- const option = _options.find((option) => option.label === label) ??
124
+ const option = options.find((option) => get_label(option) === label) ??
127
125
  // if option with label could not be found but allowUserOptions is truthy,
128
126
  // assume it was created by user and create correspondidng option object
129
127
  // on the fly for use as event payload
@@ -159,7 +157,7 @@ async function handleKeydown(event) {
159
157
  else if (event.key === `Enter`) {
160
158
  event.preventDefault(); // prevent enter key from triggering form submission
161
159
  if (activeOption) {
162
- const { label } = activeOption;
160
+ const label = get_label(activeOption);
163
161
  selectedLabels.includes(label) ? remove(label) : add(label);
164
162
  searchText = ``;
165
163
  }
@@ -175,8 +173,8 @@ async function handleKeydown(event) {
175
173
  // on up/down arrow keys: update active option
176
174
  else if ([`ArrowDown`, `ArrowUp`].includes(event.key)) {
177
175
  // if no option is active yet, but there are matching options, make first one active
178
- if (activeOption === null && matchingEnabledOptions.length > 0) {
179
- activeOption = matchingEnabledOptions[0];
176
+ if (activeOption === null && matchingOptions.length > 0) {
177
+ activeOption = matchingOptions[0];
180
178
  return;
181
179
  }
182
180
  else if (allowUserOptions && searchText.length > 0) {
@@ -185,19 +183,23 @@ async function handleKeydown(event) {
185
183
  activeMsg = !activeMsg;
186
184
  return;
187
185
  }
186
+ else if (activeOption === null) {
187
+ // if no option is active and no options are matching, do nothing
188
+ return;
189
+ }
188
190
  const increment = event.key === `ArrowUp` ? -1 : 1;
189
- const newActiveIdx = matchingEnabledOptions.indexOf(activeOption) + increment;
191
+ const newActiveIdx = matchingOptions.indexOf(activeOption) + increment;
190
192
  if (newActiveIdx < 0) {
191
193
  // wrap around top
192
- activeOption = matchingEnabledOptions[matchingEnabledOptions.length - 1];
194
+ activeOption = matchingOptions[matchingOptions.length - 1];
193
195
  }
194
- else if (newActiveIdx === matchingEnabledOptions.length) {
196
+ else if (newActiveIdx === matchingOptions.length) {
195
197
  // wrap around bottom
196
- activeOption = matchingEnabledOptions[0];
198
+ activeOption = matchingOptions[0];
197
199
  }
198
200
  else {
199
201
  // default case: select next/previous in item list
200
- activeOption = matchingEnabledOptions[newActiveIdx];
202
+ activeOption = matchingOptions[newActiveIdx];
201
203
  }
202
204
  if (autoScroll) {
203
205
  await tick();
@@ -233,8 +235,6 @@ const handleEnterAndSpaceKeys = (handler) => (event) => {
233
235
  }}
234
236
  />
235
237
 
236
- <!-- z-index: 2 when showOptions is true ensures the ul.selected of one <MultiSelect />
237
- display above those of another following shortly after it -->
238
238
  <div
239
239
  bind:this={outerDiv}
240
240
  class:disabled
@@ -262,14 +262,14 @@ display above those of another following shortly after it -->
262
262
  {#each selected as option, idx}
263
263
  <li class={liSelectedClass} aria-selected="true">
264
264
  <slot name="selected" {option} {idx}>
265
- {option.label}
265
+ {get_label(option)}
266
266
  </slot>
267
267
  {#if !disabled}
268
268
  <button
269
- on:mouseup|stopPropagation={() => remove(option.label)}
270
- on:keydown={handleEnterAndSpaceKeys(() => remove(option.label))}
269
+ on:mouseup|stopPropagation={() => remove(get_label(option))}
270
+ on:keydown={handleEnterAndSpaceKeys(() => remove(get_label(option)))}
271
271
  type="button"
272
- title="{removeBtnTitle} {option.label}"
272
+ title="{removeBtnTitle} {get_label(option)}"
273
273
  >
274
274
  <CrossIcon width="15px" />
275
275
  </button>
@@ -326,9 +326,14 @@ display above those of another following shortly after it -->
326
326
 
327
327
  <ul class:hidden={!showOptions} class="options {ulOptionsClass}">
328
328
  {#each matchingOptions as option, idx}
329
- {@const { label, disabled, title = null, selectedTitle } = option}
330
- {@const { disabledTitle = defaultDisabledTitle } = option}
331
- {@const active = activeOption?.label === label}
329
+ {@const {
330
+ label,
331
+ disabled = null,
332
+ title = null,
333
+ selectedTitle = null,
334
+ disabledTitle = defaultDisabledTitle,
335
+ } = option instanceof Object ? option : { label: option }}
336
+ {@const active = activeOption && get_label(activeOption) === label}
332
337
  <li
333
338
  on:mousedown|stopPropagation
334
339
  on:mouseup|stopPropagation={() => {
@@ -350,7 +355,7 @@ display above those of another following shortly after it -->
350
355
  aria-selected="false"
351
356
  >
352
357
  <slot name="option" {option} {idx}>
353
- {option.label}
358
+ {get_label(option)}
354
359
  </slot>
355
360
  </li>
356
361
  {:else}
@@ -392,6 +397,8 @@ display above those of another following shortly after it -->
392
397
  min-height: var(--sms-min-height, 19pt);
393
398
  }
394
399
  :where(div.multiselect.open) {
400
+ /* increase z-index when open to ensure the dropdown of one <MultiSelect />
401
+ displays above that of another slightly below it on the page */
395
402
  z-index: var(--sms-open-z-index, 4);
396
403
  }
397
404
  :where(div.multiselect:focus-within) {
@@ -1,17 +1,17 @@
1
1
  import { SvelteComponentTyped } from "svelte";
2
- import type { Option, Primitive, ProtoOption } from './';
2
+ import { Option } from './';
3
3
  declare const __propDef: {
4
4
  props: {
5
- selectedLabels?: Primitive[] | undefined;
6
- selectedValues?: Primitive[] | undefined;
7
5
  searchText?: string | undefined;
8
6
  showOptions?: boolean | undefined;
9
7
  maxSelect?: number | null | undefined;
10
8
  maxSelectMsg?: ((current: number, max: number) => string) | null | undefined;
11
9
  disabled?: boolean | undefined;
12
10
  disabledTitle?: string | undefined;
13
- options: ProtoOption[];
11
+ options: Option[];
14
12
  selected?: Option[] | undefined;
13
+ selectedLabels?: (string | number)[] | undefined;
14
+ selectedValues?: (string | number)[] | undefined;
15
15
  input?: HTMLInputElement | null | undefined;
16
16
  outerDiv?: HTMLDivElement | null | undefined;
17
17
  placeholder?: string | undefined;
@@ -37,6 +37,7 @@ declare const __propDef: {
37
37
  required?: boolean | undefined;
38
38
  autocomplete?: string | undefined;
39
39
  invalid?: boolean | undefined;
40
+ sortSelected?: boolean | ((op1: Option, op2: Option) => number) | undefined;
40
41
  };
41
42
  events: {
42
43
  mousedown: MouseEvent;
package/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  export { default } from './MultiSelect.svelte';
2
- export declare type Primitive = string | number;
3
- export declare type Option = {
4
- label: Primitive;
5
- value: Primitive;
2
+ export declare type Option = string | number | ObjectOption;
3
+ export declare type ObjectOption = {
4
+ label: string | number;
5
+ value: string | number;
6
6
  title?: string;
7
7
  disabled?: boolean;
8
8
  preselected?: boolean;
@@ -10,9 +10,6 @@ export declare type Option = {
10
10
  selectedTitle?: string;
11
11
  [key: string]: unknown;
12
12
  };
13
- export declare type ProtoOption = Primitive | (Omit<Option, `value`> & {
14
- value?: Primitive;
15
- });
16
13
  export declare type DispatchEvents = {
17
14
  add: {
18
15
  option: Option;
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": "4.0.6",
8
+ "version": "5.0.0",
9
9
  "type": "module",
10
10
  "svelte": "index.js",
11
11
  "bugs": "https://github.com/janosh/svelte-multiselect/issues",
package/readme.md CHANGED
@@ -35,15 +35,6 @@
35
35
 
36
36
  ## Recent breaking changes
37
37
 
38
- - v3.0.0 changed the `event.detail` payload for `'add'`, `'remove'` and `'change'` events from `token` to `option`, e.g.
39
-
40
- ```js
41
- on:add={(e) => console.log(e.detail.token.label)} // v2
42
- on:add={(e) => console.log(e.detail.option.label)} // v3
43
- ```
44
-
45
- It also added a separate event type `removeAll` for when the user removes all currently selected options at once which previously fired a normal `remove`. The props `ulTokensClass` and `liTokenClass` were renamed to `ulSelectedClass` and `liSelectedClass`. Similarly, the CSS variable `--sms-token-bg` changed to `--sms-selected-bg`.
46
-
47
38
  - v4.0.0 renamed the slots for customizing how selected options and dropdown list items are rendered:
48
39
 
49
40
  - old: `<slot name="renderOptions" />`, new: `<slot name="option" />`
@@ -53,6 +44,8 @@
53
44
 
54
45
  - v4.0.3 CSS variables starting with `--sms-input-<attr>` were renamed to just `--sms-<attr>`. E.g. `--sms-input-min-height` is now `--sms-min-height`.
55
46
 
47
+ - v5.0.0 Support both simple and object options.Previously string or number options were converted to objects internally and returned by `bind:selected`. Now, if you pass in `string[]`, that's what you'll get from `bind:selected`.
48
+
56
49
  ## Installation
57
50
 
58
51
  ```sh
@@ -84,34 +77,35 @@ Full list of props/bindable variables for this component:
84
77
  <div class="table">
85
78
 
86
79
  <!-- prettier-ignore -->
87
- | name | default | description |
88
- | :--------------------- | :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
89
- | `options` | required prop | Array of strings/numbers or `Option` objects that will be listed in the dropdown. See `src/lib/index.ts` for admissible fields. The `label` is the only mandatory one. It must also be unique. |
90
- | `showOptions` | `false` | Bindable boolean that controls whether the options dropdown is visible. |
91
- | `searchText` | `` | Text the user-entered to filter down on the list of options. Binds both ways, i.e. can also be used to set the input text. |
92
- | `activeOption` | `null` | Currently active option, i.e. the one the user currently hovers or navigated to with arrow keys. |
93
- | `maxSelect` | `null` | Positive integer to limit the number of options users can pick. `null` means no limit. |
94
- | `selected` | `[]` | Array of currently/pre-selected options when binding/passing as props respectively. |
95
- | `selectedLabels` | `[]` | Labels of currently selected options. |
96
- | `selectedValues` | `[]` | Values of currently selected options. |
97
- | `noOptionsMsg` | `'No matching options'` | What message to show if no options match the user-entered search string. |
98
- | `disabled` | `false` | Disable the component. It will still be rendered but users won't be able to interact with it. |
99
- | `disabledTitle` | `This field is disabled` | Tooltip text to display on hover when the component is in `disabled` state. |
100
- | `placeholder` | `undefined` | String shown in the text input when no option is selected. |
101
- | `input` | `null` | Handle to the `<input>` DOM node. Only available after component mounts (`null` before then). |
102
- | `outerDiv` | `null` | Handle to outer `<div class="multiselect">` that wraps the whole component. Only available after component mounts (`null` before then). |
103
- | `id` | `undefined` | Applied to the `<input>` element for associating HTML form `<label>`s with this component for accessibility. Also, clicking a `<label>` with same `for` attribute as `id` will focus this component. |
104
- | `name` | `id` | Applied to the `<input>` element. If not provided, will be set to the value of `id`. 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>`. |
105
- | `required` | `false` | Whether forms can be submitted without selecting any options. Aborts submission, is scrolled into view and shows help "Please fill out" message when true and user tries to submit with no options selected. |
106
- | `autoScroll` | `true` | `false` disables keeping the active dropdown items in view when going up/down the list of options with arrow keys. |
107
- | `allowUserOptions` | `false` | Whether users are allowed to enter values not in the dropdown list. `true` means add user-defined options to the selected list only, `'append'` means add to both options and selected. |
108
- | `addOptionMsg` | `'Create this option...'` | Message shown to users after entering text when no options match their query and `allowUserOptions` is truthy. |
109
- | `loading` | `false` | Whether the component should display a spinner to indicate it's in loading state. Use `<slot name='spinner'>` to specify a custom spinner. |
110
- | `removeBtnTitle` | `'Remove'` | Title text to display when user hovers over button (cross icon) to remove selected option. |
111
- | `removeAllTitle` | `'Remove all'` | Title text to display when user hovers over remove-all button. |
112
- | `defaultDisabledTitle` | `'This option is disabled'` | Title text to display when user hovers over a disabled option. Each option can override this through its `disabledTitle` attribute. |
113
- | `autocomplete` | `'off'` | Applied to the `<input>`. Specifies if browser is permitted to auto-fill this form field. See [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete) for other admissible values. |
114
- | `invalid` | `false` | If `required=true` and user tries to submit but `selected = []` is empty, `invalid` is automatically set to `true` and CSS class `invalid` applied to the top-level `div.multiselect`. `invalid` class is removed again as soon as the user selects an option. `invalid` can also be controlled externally by binding to it `<MultiSelect bind:invalid />` and setting it to `true` based on outside events or custom validation. |
80
+ | name | default | description |
81
+ | :--------------------- | :---------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
82
+ | `options` | required prop | Array of strings/numbers or `Option` objects that will be listed in the dropdown. See `src/lib/index.ts` for admissible fields. The `label` is the only mandatory one. It must also be unique. |
83
+ | `showOptions` | `false` | Bindable boolean that controls whether the options dropdown is visible. |
84
+ | `searchText` | `` | Text the user-entered to filter down on the list of options. Binds both ways, i.e. can also be used to set the input text. |
85
+ | `activeOption` | `null` | Currently active option, i.e. the one the user currently hovers or navigated to with arrow keys. |
86
+ | `maxSelect` | `null` | Positive integer to limit the number of options users can pick. `null` means no limit. |
87
+ | `selected` | `[]` | Array of currently/pre-selected options when binding/passing as props respectively. |
88
+ | `selectedLabels` | `[]` | Labels of currently selected options. Supports binding but is read-only, i.e. since this value is reactive to `selected`, you cannot control `selected` by changing `selectedValues`. |
89
+ | `selectedValues` | `[]` | Values of currently selected options. Supports binding but is read-only, i.e. since this value is reactive to `selected`, you cannot control `selected` by changing `selectedValues`. |
90
+ | `sortSelected` | `boolean \| ((op1, op2) => number)` | Default behavior is to render selected items in the order they were chosen. `sortSelected={true}` uses default JS array sorting. A compare function enables custom logic for sorting selected options. See the [`/sort-selected`](https://svelte-multiselect.netlify.app/sort-selected) example. |
91
+ | `noOptionsMsg` | `'No matching options'` | What message to show if no options match the user-entered search string. |
92
+ | `disabled` | `false` | Disable the component. It will still be rendered but users won't be able to interact with it. |
93
+ | `disabledTitle` | `This field is disabled` | Tooltip text to display on hover when the component is in `disabled` state. |
94
+ | `placeholder` | `undefined` | String shown in the text input when no option is selected. |
95
+ | `input` | `null` | Handle to the `<input>` DOM node. Only available after component mounts (`null` before then). |
96
+ | `outerDiv` | `null` | Handle to outer `<div class="multiselect">` that wraps the whole component. Only available after component mounts (`null` before then). |
97
+ | `id` | `undefined` | Applied to the `<input>` element for associating HTML form `<label>`s with this component for accessibility. Also, clicking a `<label>` with same `for` attribute as `id` will focus this component. |
98
+ | `name` | `id` | Applied to the `<input>` element. If not provided, will be set to the value of `id`. 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>`. |
99
+ | `required` | `false` | Whether forms can be submitted without selecting any options. Aborts submission, is scrolled into view and shows help "Please fill out" message when true and user tries to submit with no options selected. |
100
+ | `autoScroll` | `true` | `false` disables keeping the active dropdown items in view when going up/down the list of options with arrow keys. |
101
+ | `allowUserOptions` | `false` | Whether users are allowed to enter values not in the dropdown list. `true` means add user-defined options to the selected list only, `'append'` means add to both options and selected. |
102
+ | `addOptionMsg` | `'Create this option...'` | Message shown to users after entering text when no options match their query and `allowUserOptions` is truthy. |
103
+ | `loading` | `false` | Whether the component should display a spinner to indicate it's in loading state. Use `<slot name='spinner'>` to specify a custom spinner. |
104
+ | `removeBtnTitle` | `'Remove'` | Title text to display when user hovers over button (cross icon) to remove selected option. |
105
+ | `removeAllTitle` | `'Remove all'` | Title text to display when user hovers over remove-all button. |
106
+ | `defaultDisabledTitle` | `'This option is disabled'` | Title text to display when user hovers over a disabled option. Each option can override this through its `disabledTitle` attribute. |
107
+ | `autocomplete` | `'off'` | Applied to the `<input>`. Specifies if browser is permitted to auto-fill this form field. See [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete) for other admissible values. |
108
+ | `invalid` | `false` | If `required=true` and user tries to submit but `selected = []` is empty, `invalid` is automatically set to `true` and CSS class `invalid` applied to the top-level `div.multiselect`. `invalid` class is removed again as soon as the user selects an option. `invalid` can also be controlled externally by binding to it `<MultiSelect bind:invalid />` and setting it to `true` based on outside events or custom validation. |
115
109
 
116
110
  </div>
117
111
 
@@ -136,8 +130,8 @@ Full list of props/bindable variables for this component:
136
130
 
137
131
  `MultiSelect.svelte` has 3 named slots:
138
132
 
139
- - `slot="option"`: Customize rendering of dropdown options. Receives as props the `option` object and the zero-indexed position (`idx`) it has in the dropdown.
140
- - `slot="selected"`: Customize rendering selected tags. Receives as props the `option` object and the zero-indexed position (`idx`) it has in the list of selected items.
133
+ - `slot="option"`: Customize rendering of dropdown options. Receives as props an `option` and the zero-indexed position (`idx`) it has in the dropdown.
134
+ - `slot="selected"`: Customize rendering of selected items. Receives as props an `option` and the zero-indexed position (`idx`) it has in the list of selected items.
141
135
  - `slot="spinner"`: Custom spinner component to display when in `loading` state. Receives no props.
142
136
  - `slot="disabled-icon"`: Custom icon to display inside the input when in `disabled` state. Receives no props. Use an empty `<span slot="disabled-icon" />` or `div` to remove the default disabled icon.
143
137
 
@@ -165,25 +159,27 @@ Example:
165
159
 
166
160
  `MultiSelect.svelte` dispatches the following events:
167
161
 
168
- | name | detail | description |
169
- | ----------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
170
- | `add` | `{ option: Option }` | Triggers when a new option is selected. |
171
- | `remove` | `{ option: Option }` | Triggers when one selected option provided as `event.detail.option` is removed. |
172
- | `removeAll` | `options: Option[]` | Triggers when all selected options are removed. The payload `event.detail.options` gives the options that were previously selected. |
173
- | `change` | `type: 'add' \| 'remove' \| 'removeAll'` | Triggers when a option is either added or removed, or all options are removed at once. Payload will be a single or an aarray of `Option` objects, respectively. |
174
- | `blur` | none | Triggers when the input field looses focus. |
162
+ | name | detail | description |
163
+ | ----------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
164
+ | `add` | `{ option: Option }` | Triggers when a new option is selected. |
165
+ | `remove` | `{ option: Option }` | Triggers when one selected option provided as `event.detail.option` is removed. |
166
+ | `removeAll` | `options: Option[]` | Triggers when all selected options are removed. The payload `event.detail.options` gives the options that were previously selected. |
167
+ | `change` | `type: 'add' \| 'remove' \| 'removeAll'` | Triggers when a option is either added or removed, or all options are removed at once. Payload will be a single or an array of `Option`s, respectively. |
168
+ | `blur` | none | Triggers when the input field looses focus. |
169
+
170
+ Depending on the data passed to the component the `options(s)` payload will either be objects or simple strings/numbers.
175
171
 
176
172
  ### Examples
177
173
 
178
174
  <!-- prettier-ignore -->
179
- - `on:add={(event) => console.log(event.detail.option.label)}`
180
- - `on:remove={(event) => console.log(event.detail.option.label)}`.
181
- - ``on:change={(event) => console.log(`${event.detail.type}: '${event.detail.option.label}'`)}``
182
- - `on:blur={yourFunctionHere}`
175
+ - `on:add={(event) => console.log(event.detail.option)}`
176
+ - `on:remove={(event) => console.log(event.detail.option)}`.
177
+ - ``on:change={(event) => console.log(`${event.detail.type}: '${event.detail.option}'`)}``
178
+ - `on:blur={myFunction}`
183
179
 
184
180
  ```svelte
185
181
  <MultiSelect
186
- on:change={(e) => alert(`You ${e.detail.type}ed '${e.detail.option.label}'`)}
182
+ on:change={(e) => alert(`You ${e.detail.type}ed '${e.detail.option}'`)}
187
183
  />
188
184
  ```
189
185