svelte-multiselect 10.2.0 → 11.0.0-rc.1

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,14 +1,16 @@
1
1
  import { SvelteComponent } from "svelte";
2
2
  declare const __propDef: {
3
3
  props: {
4
- color?: string | undefined;
5
- duration?: string | undefined;
6
- size?: string | undefined;
4
+ color?: string;
5
+ duration?: string;
6
+ size?: string;
7
7
  };
8
8
  events: {
9
9
  [evt: string]: CustomEvent<any>;
10
10
  };
11
11
  slots: {};
12
+ exports?: {} | undefined;
13
+ bindings?: string | undefined;
12
14
  };
13
15
  export type CircleSpinnerProps = typeof __propDef.props;
14
16
  export type CircleSpinnerEvents = typeof __propDef.events;
@@ -28,8 +28,8 @@ function close_if_outside(event) {
28
28
  open = false;
29
29
  }
30
30
  }
31
- function run_and_close(event) {
32
- event.detail.option.action();
31
+ function trigger_action_and_close(event) {
32
+ event.detail.option.action(event.detail.option.label);
33
33
  open = false;
34
34
  }
35
35
  </script>
@@ -42,7 +42,7 @@ function run_and_close(event) {
42
42
  options={actions}
43
43
  bind:input
44
44
  {placeholder}
45
- on:add={run_and_close}
45
+ on:add={trigger_action_and_close}
46
46
  on:keydown={toggle}
47
47
  {...$$restProps}
48
48
  let:option
@@ -12,8 +12,8 @@ declare const __propDef: {
12
12
  style?: string | undefined;
13
13
  span_style?: string | undefined;
14
14
  open?: boolean | undefined;
15
- dialog?: HTMLDialogElement | null | undefined;
16
- input?: HTMLInputElement | null | undefined;
15
+ dialog?: (HTMLDialogElement | null) | undefined;
16
+ input?: (HTMLInputElement | null) | undefined;
17
17
  placeholder?: string | undefined;
18
18
  };
19
19
  events: {
@@ -22,6 +22,8 @@ declare const __propDef: {
22
22
  slots: {
23
23
  default: {};
24
24
  };
25
+ exports?: undefined;
26
+ bindings?: undefined;
25
27
  };
26
28
  export type CmdPaletteProps = typeof __propDef.props;
27
29
  export type CmdPaletteEvents = typeof __propDef.events;
@@ -32,11 +32,16 @@ export let highlightMatches = true;
32
32
  export let id = null;
33
33
  export let input = null;
34
34
  export let inputClass = ``;
35
+ export let inputStyle = null;
35
36
  export let inputmode = null;
36
37
  export let invalid = false;
37
38
  export let liActiveOptionClass = ``;
39
+ export let liActiveUserMsgClass = ``;
38
40
  export let liOptionClass = ``;
41
+ export let liOptionStyle = null;
39
42
  export let liSelectedClass = ``;
43
+ export let liSelectedStyle = null;
44
+ export let liUserMsgClass = ``;
40
45
  export let loading = false;
41
46
  export let matchingOptions = [];
42
47
  export let maxOptions = undefined;
@@ -63,8 +68,11 @@ export let selected = options
63
68
  .slice(0, maxSelect ?? undefined) ?? []; // don't allow more than maxSelect preselected options
64
69
  export let sortSelected = false;
65
70
  export let selectedOptionsDraggable = !sortSelected;
71
+ export let style = null;
66
72
  export let ulOptionsClass = ``;
67
73
  export let ulSelectedClass = ``;
74
+ export let ulSelectedStyle = null;
75
+ export let ulOptionsStyle = null;
68
76
  export let value = null;
69
77
  const selected_to_value = (selected) => {
70
78
  value = maxSelect === 1 ? selected[0] ?? null : selected;
@@ -353,7 +361,7 @@ let ul_options;
353
361
  // highlight text matching user-entered search text in available options
354
362
  function highlight_matching_options(event) {
355
363
  if (!highlightMatches || typeof CSS == `undefined` || !CSS.highlights)
356
- return; // don't try if CSS highlight API not supported
364
+ return; // abort if CSS highlight API not supported
357
365
  // clear previous ranges from HighlightRegistry
358
366
  CSS.highlights.clear();
359
367
  // get input's search query
@@ -395,9 +403,11 @@ function highlight_matching_options(event) {
395
403
  });
396
404
  });
397
405
  // create Highlight object from ranges and add to registry
398
- // eslint-disable-next-line no-undef
399
406
  CSS.highlights.set(`sms-search-matches`, new Highlight(...ranges.flat()));
400
407
  }
408
+ // reset form validation when required prop changes
409
+ // https://github.com/janosh/svelte-multiselect/issues/285
410
+ $: required, form_input?.setCustomValidity(``);
401
411
  </script>
402
412
 
403
413
  <svelte:window
@@ -418,12 +428,13 @@ function highlight_matching_options(event) {
418
428
  data-id={id}
419
429
  role="searchbox"
420
430
  tabindex="-1"
431
+ {style}
421
432
  >
422
433
  <!-- form control input invisible to the user, only purpose is to abort form submission if this component fails data validation -->
423
434
  <!-- bind:value={selected} prevents form submission if required prop is true and no options are selected -->
424
435
  <input
425
436
  {name}
426
- required={Boolean(required)}
437
+ {required}
427
438
  value={selected.length >= Number(required) ? JSON.stringify(selected) : null}
428
439
  tabindex="-1"
429
440
  aria-hidden="true"
@@ -446,7 +457,11 @@ function highlight_matching_options(event) {
446
457
  <slot name="expand-icon" {open}>
447
458
  <ExpandIcon width="15px" style="min-width: 1em; padding: 0 1pt; cursor: pointer;" />
448
459
  </slot>
449
- <ul class="selected {ulSelectedClass}" aria-label="selected options">
460
+ <ul
461
+ class="selected {ulSelectedClass}"
462
+ aria-label="selected options"
463
+ style={ulSelectedStyle}
464
+ >
450
465
  {#each selected as option, idx (duplicates ? [key(option), idx] : key(option))}
451
466
  <li
452
467
  class={liSelectedClass}
@@ -459,7 +474,7 @@ function highlight_matching_options(event) {
459
474
  on:dragenter={() => (drag_idx = idx)}
460
475
  on:dragover|preventDefault
461
476
  class:active={drag_idx === idx}
462
- style={get_style(option, `selected`)}
477
+ style="{get_style(option, `selected`)} {liSelectedStyle}"
463
478
  >
464
479
  <!-- on:dragover|preventDefault needed for the drop to succeed https://stackoverflow.com/a/31085796 -->
465
480
  <slot name="selected" {option} {idx}>
@@ -488,6 +503,7 @@ function highlight_matching_options(event) {
488
503
  {/each}
489
504
  <input
490
505
  class={inputClass}
506
+ style={inputStyle}
491
507
  bind:this={input}
492
508
  bind:value={searchText}
493
509
  on:mouseup|self|stopPropagation={open_dropdown}
@@ -502,7 +518,7 @@ function highlight_matching_options(event) {
502
518
  {pattern}
503
519
  placeholder={selected.length == 0 ? placeholder : null}
504
520
  aria-invalid={invalid ? `true` : null}
505
- ondrop="return false"
521
+ on:drop={() => false}
506
522
  on:blur
507
523
  on:change
508
524
  on:click
@@ -570,6 +586,7 @@ function highlight_matching_options(event) {
570
586
  aria-expanded={open}
571
587
  aria-disabled={disabled ? `true` : null}
572
588
  bind:this={ul_options}
589
+ style={ulOptionsStyle}
573
590
  >
574
591
  {#each matchingOptions.slice(0, Math.max(0, maxOptions ?? 0) || Infinity) as option, idx}
575
592
  {@const {
@@ -602,7 +619,7 @@ function highlight_matching_options(event) {
602
619
  on:blur={() => (activeIndex = null)}
603
620
  role="option"
604
621
  aria-selected="false"
605
- style={get_style(option, `option`)}
622
+ style="{get_style(option, `option`)} {liOptionStyle}"
606
623
  >
607
624
  <slot name="option" {option} {idx}>
608
625
  <slot {option} {idx}>
@@ -641,7 +658,9 @@ function highlight_matching_options(event) {
641
658
  on:blur={() => (option_msg_is_active = false)}
642
659
  role="option"
643
660
  aria-selected="false"
644
- class="user-msg"
661
+ class="user-msg {liUserMsgClass} {option_msg_is_active
662
+ ? liActiveUserMsgClass
663
+ : ``}"
645
664
  style:cursor={{
646
665
  dupe: `not-allowed`,
647
666
  create: `pointer`,
@@ -2,58 +2,66 @@ 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(): {
5
- activeIndex?: number | null | undefined;
5
+ activeIndex?: number | null;
6
6
  activeOption?: Option | null | undefined;
7
- createOptionMsg?: string | null | undefined;
8
- allowUserOptions?: boolean | "append" | undefined;
9
- allowEmpty?: boolean | undefined;
10
- autocomplete?: string | undefined;
11
- autoScroll?: boolean | undefined;
12
- breakpoint?: number | undefined;
13
- defaultDisabledTitle?: string | undefined;
14
- disabled?: boolean | undefined;
15
- disabledInputTitle?: string | undefined;
16
- duplicateOptionMsg?: string | undefined;
17
- duplicates?: boolean | undefined;
7
+ createOptionMsg?: string | null;
8
+ allowUserOptions?: boolean | "append";
9
+ allowEmpty?: boolean;
10
+ autocomplete?: string;
11
+ autoScroll?: boolean;
12
+ breakpoint?: number;
13
+ defaultDisabledTitle?: string;
14
+ disabled?: boolean;
15
+ disabledInputTitle?: string;
16
+ duplicateOptionMsg?: string;
17
+ duplicates?: boolean;
18
18
  key?: ((opt: T) => unknown) | undefined;
19
19
  filterFunc?: ((opt: Option, searchText: string) => boolean) | undefined;
20
- closeDropdownOnSelect?: boolean | "desktop" | undefined;
21
- form_input?: HTMLInputElement | null | undefined;
22
- highlightMatches?: boolean | undefined;
23
- id?: string | null | undefined;
24
- input?: HTMLInputElement | null | undefined;
25
- inputClass?: string | undefined;
26
- inputmode?: string | null | undefined;
27
- invalid?: boolean | undefined;
28
- liActiveOptionClass?: string | undefined;
29
- liOptionClass?: string | undefined;
30
- liSelectedClass?: string | undefined;
31
- loading?: boolean | undefined;
20
+ closeDropdownOnSelect?: boolean | "desktop";
21
+ form_input?: HTMLInputElement | null;
22
+ highlightMatches?: boolean;
23
+ id?: string | null;
24
+ input?: HTMLInputElement | null;
25
+ inputClass?: string;
26
+ inputStyle?: string | null;
27
+ inputmode?: string | null;
28
+ invalid?: boolean;
29
+ liActiveOptionClass?: string;
30
+ liActiveUserMsgClass?: string;
31
+ liOptionClass?: string;
32
+ liOptionStyle?: string | null;
33
+ liSelectedClass?: string;
34
+ liSelectedStyle?: string | null;
35
+ liUserMsgClass?: string;
36
+ loading?: boolean;
32
37
  matchingOptions?: Option[] | undefined;
33
38
  maxOptions?: number | undefined;
34
- maxSelect?: number | null | undefined;
39
+ maxSelect?: number | null;
35
40
  maxSelectMsg?: ((current: number, max: number) => string) | null | undefined;
36
- maxSelectMsgClass?: string | undefined;
37
- name?: string | null | undefined;
38
- noMatchingOptionsMsg?: string | undefined;
39
- open?: boolean | undefined;
41
+ maxSelectMsgClass?: string;
42
+ name?: string | null;
43
+ noMatchingOptionsMsg?: string;
44
+ open?: boolean;
40
45
  options: Option[];
41
- outerDiv?: HTMLDivElement | null | undefined;
42
- outerDivClass?: string | undefined;
43
- parseLabelsAsHtml?: boolean | undefined;
44
- pattern?: string | null | undefined;
45
- placeholder?: string | null | undefined;
46
- removeAllTitle?: string | undefined;
47
- removeBtnTitle?: string | undefined;
48
- minSelect?: number | null | undefined;
49
- required?: number | boolean | undefined;
50
- resetFilterOnAdd?: boolean | undefined;
51
- searchText?: string | undefined;
46
+ outerDiv?: HTMLDivElement | null;
47
+ outerDivClass?: string;
48
+ parseLabelsAsHtml?: boolean;
49
+ pattern?: string | null;
50
+ placeholder?: string | null;
51
+ removeAllTitle?: string;
52
+ removeBtnTitle?: string;
53
+ minSelect?: number | null;
54
+ required?: boolean | number;
55
+ resetFilterOnAdd?: boolean;
56
+ searchText?: string;
52
57
  selected?: Option[] | undefined;
53
58
  sortSelected?: boolean | ((op1: Option, op2: Option) => number) | undefined;
54
- selectedOptionsDraggable?: boolean | undefined;
55
- ulOptionsClass?: string | undefined;
56
- ulSelectedClass?: string | undefined;
59
+ selectedOptionsDraggable?: boolean;
60
+ style?: string | null;
61
+ ulOptionsClass?: string;
62
+ ulSelectedClass?: string;
63
+ ulSelectedStyle?: string | null;
64
+ ulOptionsStyle?: string | null;
57
65
  value?: Option | Option[] | null | undefined;
58
66
  };
59
67
  events(): MultiSelectEvents;
@@ -93,8 +101,8 @@ declare class __sveltets_Render<Option extends T> {
93
101
  };
94
102
  }
95
103
  export type MultiSelectProps<Option extends T> = ReturnType<__sveltets_Render<Option>['props']>;
96
- export type MultiSelectEvents<Option extends T> = ReturnType<__sveltets_Render<Option>['events']>;
104
+ export type MultiSelectEvents_<Option extends T> = ReturnType<__sveltets_Render<Option>['events']>;
97
105
  export type MultiSelectSlots<Option extends T> = ReturnType<__sveltets_Render<Option>['slots']>;
98
- export default class MultiSelect<Option extends T> extends SvelteComponent<MultiSelectProps<Option>, MultiSelectEvents<Option>, MultiSelectSlots<Option>> {
106
+ export default class MultiSelect<Option extends T> extends SvelteComponent<MultiSelectProps<Option>, MultiSelectEvents_<Option>, MultiSelectSlots<Option>> {
99
107
  }
100
108
  export {};
@@ -1,14 +1,14 @@
1
1
  import { SvelteComponent } from "svelte";
2
2
  declare const __propDef: {
3
3
  props: {
4
- wiggle?: boolean | undefined;
5
- angle?: number | undefined;
6
- scale?: number | undefined;
7
- dx?: number | undefined;
8
- dy?: number | undefined;
9
- duration?: number | undefined;
10
- stiffness?: number | undefined;
11
- damping?: number | undefined;
4
+ wiggle?: boolean;
5
+ angle?: number;
6
+ scale?: number;
7
+ dx?: number;
8
+ dy?: number;
9
+ duration?: number;
10
+ stiffness?: number;
11
+ damping?: number;
12
12
  };
13
13
  events: {
14
14
  [evt: string]: CustomEvent<any>;
@@ -16,6 +16,8 @@ declare const __propDef: {
16
16
  slots: {
17
17
  default: {};
18
18
  };
19
+ exports?: {} | undefined;
20
+ bindings?: string | undefined;
19
21
  };
20
22
  export type WiggleProps = typeof __propDef.props;
21
23
  export type WiggleEvents = typeof __propDef.events;
@@ -19,5 +19,7 @@ declare const __propDef: {
19
19
  [evt: string]: CustomEvent<any>;
20
20
  };
21
21
  slots: {};
22
+ exports?: undefined;
23
+ bindings?: undefined;
22
24
  };
23
25
  export {};
@@ -19,5 +19,7 @@ declare const __propDef: {
19
19
  [evt: string]: CustomEvent<any>;
20
20
  };
21
21
  slots: {};
22
+ exports?: undefined;
23
+ bindings?: undefined;
22
24
  };
23
25
  export {};
@@ -19,5 +19,7 @@ declare const __propDef: {
19
19
  [evt: string]: CustomEvent<any>;
20
20
  };
21
21
  slots: {};
22
+ exports?: undefined;
23
+ bindings?: undefined;
22
24
  };
23
25
  export {};
@@ -19,5 +19,7 @@ declare const __propDef: {
19
19
  [evt: string]: CustomEvent<any>;
20
20
  };
21
21
  slots: {};
22
+ exports?: undefined;
23
+ bindings?: undefined;
22
24
  };
23
25
  export {};
package/dist/utils.d.ts CHANGED
@@ -3,4 +3,4 @@ export declare const get_label: (opt: Option) => string | number;
3
3
  export declare function get_style(option: {
4
4
  style?: OptionStyle;
5
5
  [key: string]: unknown;
6
- } | string | number, key?: 'selected' | 'option' | null): string | null | undefined;
6
+ } | string | number, key?: 'selected' | 'option' | null): string;
package/dist/utils.js CHANGED
@@ -8,23 +8,27 @@ export const get_label = (opt) => {
8
8
  }
9
9
  return `${opt}`;
10
10
  };
11
+ // this function is used extract CSS strings from a {selected, option} style object to be used in the style attribute of the option
12
+ // if the style is a string, it will be returned as is
11
13
  export function get_style(option, key = null) {
12
- if (!option?.style)
13
- return null;
14
+ let css_str = ``;
14
15
  if (![`selected`, `option`, null].includes(key)) {
15
16
  console.error(`MultiSelect: Invalid key=${key} for get_style`);
16
- return;
17
17
  }
18
18
  if (typeof option == `object` && option.style) {
19
19
  if (typeof option.style == `string`) {
20
- return option.style;
20
+ css_str = option.style;
21
21
  }
22
22
  if (typeof option.style == `object`) {
23
23
  if (key && key in option.style)
24
- return option.style[key];
24
+ return option.style[key] ?? ``;
25
25
  else {
26
26
  console.error(`Invalid style object for option=${JSON.stringify(option)}`);
27
27
  }
28
28
  }
29
29
  }
30
+ // ensure css_str ends with a semicolon
31
+ if (css_str.trim() && !css_str.trim().endsWith(`;`))
32
+ css_str += `;`;
33
+ return css_str;
30
34
  }
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.2.0",
8
+ "version": "11.0.0-rc.1",
9
9
  "type": "module",
10
10
  "svelte": "./dist/index.js",
11
11
  "bugs": "https://github.com/janosh/svelte-multiselect/issues",
@@ -23,37 +23,38 @@
23
23
  "update-coverage": "vitest tests/unit --run --coverage && npx istanbul-badges-readme"
24
24
  },
25
25
  "dependencies": {
26
- "svelte": "^4.2.0"
26
+ "svelte": "4.2.12"
27
27
  },
28
28
  "devDependencies": {
29
- "@iconify/svelte": "^3.1.4",
30
- "@playwright/test": "^1.37.1",
31
- "@sveltejs/adapter-static": "^2.0.3",
32
- "@sveltejs/kit": "^1.24.1",
33
- "@sveltejs/package": "2.2.2",
34
- "@sveltejs/vite-plugin-svelte": "2.4.5",
35
- "@typescript-eslint/eslint-plugin": "^6.7.0",
36
- "@typescript-eslint/parser": "^6.7.0",
37
- "@vitest/coverage-v8": "^0.34.4",
38
- "eslint": "^8.49.0",
39
- "eslint-plugin-svelte": "^2.33.1",
40
- "hastscript": "^8.0.0",
41
- "highlight.js": "^11.8.0",
42
- "jsdom": "^22.1.0",
43
- "mdsvex": "^0.11.0",
29
+ "@iconify/svelte": "^4.0.2",
30
+ "@playwright/test": "^1.48.1",
31
+ "@sveltejs/adapter-static": "^3.0.5",
32
+ "@sveltejs/kit": "^2.7.2",
33
+ "@sveltejs/package": "2.3.5",
34
+ "@sveltejs/vite-plugin-svelte": "3.0.2",
35
+ "@vitest/coverage-v8": "^2.1.3",
36
+ "eslint": "^9.13.0",
37
+ "eslint-plugin-svelte": "^2.46.0",
38
+ "globals": "^15.11.0",
39
+ "hastscript": "^9.0.0",
40
+ "highlight.js": "^11.10.0",
41
+ "jsdom": "^25.0.1",
42
+ "mdsvex": "^0.12.3",
44
43
  "mdsvexamples": "^0.4.1",
45
- "prettier": "^3.0.3",
46
- "prettier-plugin-svelte": "^3.0.3",
47
- "rehype-autolink-headings": "^7.0.0",
44
+ "prettier": "^3.3.3",
45
+ "prettier-plugin-svelte": "^3.2.7",
46
+ "rehype-autolink-headings": "^7.1.0",
48
47
  "rehype-slug": "^6.0.0",
49
- "svelte-check": "^3.5.1",
50
- "svelte-preprocess": "^5.0.4",
51
- "svelte-toc": "^0.5.6",
52
- "svelte-zoo": "^0.4.9",
53
- "svelte2tsx": "^0.6.21",
54
- "typescript": "5.2.2",
55
- "vite": "^4.4.9",
56
- "vitest": "^0.34.4"
48
+ "svelte-check": "^4.0.5",
49
+ "svelte-multiselect": "^10.3.0",
50
+ "svelte-preprocess": "^6.0.3",
51
+ "svelte-toc": "^0.5.9",
52
+ "svelte-zoo": "^0.4.13",
53
+ "svelte2tsx": "^0.7.22",
54
+ "typescript": "5.6.3",
55
+ "typescript-eslint": "^8.11.0",
56
+ "vite": "^5.4.9",
57
+ "vitest": "^2.1.3"
57
58
  },
58
59
  "keywords": [
59
60
  "svelte",
package/readme.md CHANGED
@@ -229,12 +229,30 @@ Full list of props/bindable variables for this component. The `Option` type you
229
229
 
230
230
  The `inputmode` attribute hints at the type of data the user may enter. Values like `'numeric' | 'tel' | 'email'` allow mobile browsers to display an appropriate virtual on-screen keyboard. See [MDN](https://developer.mozilla.org/docs/Web/HTML/Global_attributes/inputmode) for details. If you want to suppress the on-screen keyboard to leave full-screen real estate for the dropdown list of options, set `inputmode="none"`.
231
231
 
232
+ 1. ```ts
233
+ inputStyle: string | null = null
234
+ ```
235
+
236
+ One-off CSS rules applied to the `<input>` element.
237
+
232
238
  1. ```ts
233
239
  invalid: boolean = false
234
240
  ```
235
241
 
236
242
  If `required = true, 1, 2, ...` and user tries to submit form but `selected = []` is empty/`selected.length < required`, `invalid` is automatically set to `true` and CSS class `invalid` applied to the top-level `div.multiselect`. `invalid` class is removed as soon as any change to `selected` is registered. `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.
237
243
 
244
+ 1. ```ts
245
+ liOptionStyle: string | null = null
246
+ ```
247
+
248
+ One-off CSS rules applied to the `<li>` elements that wrap the dropdown options.
249
+
250
+ 1. ```ts
251
+ liSelectedStyle: string | null = null
252
+ ```
253
+
254
+ One-off CSS rules applied to the `<li>` elements that wrap the selected options.
255
+
238
256
  1. ```ts
239
257
  loading: boolean = false
240
258
  ```
@@ -304,7 +322,7 @@ Full list of props/bindable variables for this component. The `Option` type you
304
322
  options: Option[]
305
323
  ```
306
324
 
307
- **The only required prop** (no default). Array of strings/numbers or `Option` objects to be listed in the dropdown. The only required key on objects is `label` which must also be unique. An object's `value` defaults to `label` if `undefined`. You can add arbitrary additional keys to your option objects. A few keys like `preselected` and `title` have special meaning though. See type `ObjectOption` in [`src/lib/index.ts`](https://github.com/janosh/svelte-multiselect/blob/main/src/lib/index.ts) for all special keys and their purpose.
325
+ **The only required prop** (no default). Array of strings/numbers or `Option` objects to be listed in the dropdown. The only required key on objects is `label` which must also be unique. An object's `value` defaults to `label` if `undefined`. You can add arbitrary additional keys to your option objects. A few keys like `preselected` and `title` have special meaning though. See type `ObjectOption` in [`/src/lib/types.ts`](https://github.com/janosh/svelte-multiselect/blob/main/src/lib/types.ts) for all special keys and their purpose.
308
326
 
309
327
  1. ```ts
310
328
  outerDiv: HTMLDivElement | null = null
@@ -381,6 +399,24 @@ Full list of props/bindable variables for this component. The `Option` type you
381
399
 
382
400
  Whether selected options are draggable so users can change their order.
383
401
 
402
+ 1. ```ts
403
+ style: string | null = null
404
+ ```
405
+
406
+ One-off CSS rules applied to the outer `<div class="multiselect">` that wraps the whole component for passing one-off CSS.
407
+
408
+ 1. ```ts
409
+ ulSelectedStyle: string | null = null
410
+ ```
411
+
412
+ One-off CSS rules applied to the `<ul class="selected">` that wraps the list of selected options.
413
+
414
+ 1. ```ts
415
+ ulOptionsStyle: string | null = null
416
+ ```
417
+
418
+ One-off CSS rules applied to the `<ul class="options">` that wraps the list of selected options.
419
+
384
420
  1. ```ts
385
421
  value: Option | Option[] | null = null
386
422
  ```
@@ -427,13 +463,13 @@ Example using several slots:
427
463
  on:add={(event) => console.log(event.detail.option)}
428
464
  ```
429
465
 
430
- Triggers when a new option is selected.
466
+ Triggers when a new option is selected. The newly selected option is provided as `event.detail.option`.
431
467
 
432
468
  1. ```ts
433
469
  on:remove={(event) => console.log(event.detail.option)}`
434
470
  ```
435
471
 
436
- Triggers when one selected option provided as `event.detail.option` is removed.
472
+ Triggers when a single selected option is removed. The removed option is provided as `event.detail.option`.
437
473
 
438
474
  1. ```ts
439
475
  on:removeAll={(event) => console.log(event.detail.options)}`
@@ -445,7 +481,7 @@ Example using several slots:
445
481
  on:change={(event) => console.log(`${event.detail.type}: '${event.detail.option}'`)}
446
482
  ```
447
483
 
448
- Triggers when an option is either added or removed, or all options are removed at once. `type` is one of `'add' | 'remove' | 'removeAll'` and payload will be `option: Option` or `options: Option[]`, respectively.
484
+ Triggers when an option is either added (selected) or removed from selected, or all selected options are removed at once. `type` is one of `'add' | 'remove' | 'removeAll'` and payload will be `option: Option` or `options: Option[]`, respectively.
449
485
 
450
486
  1. ```ts
451
487
  on:open={(event) => console.log(`Multiselect dropdown was opened by ${event}`)}
@@ -605,6 +641,8 @@ The second method allows you to pass in custom classes to the important DOM elem
605
641
  - `ulOptionsClass`: available options listed in the dropdown when component is in `open` state
606
642
  - `liOptionClass`: list items selectable from dropdown list
607
643
  - `liActiveOptionClass`: the currently active dropdown list item (i.e. hovered or navigated to with arrow keys)
644
+ - `liUserMsgClass`: user message (last child of dropdown list when no options match user input)
645
+ - `liActiveUserMsgClass`: user message when active (i.e. hovered or navigated to with arrow keys)
608
646
  - `maxSelectMsgClass`: small span towards the right end of the input field displaying to the user how many of the allowed number of options they've already selected
609
647
 
610
648
  This simplified version of the DOM structure of the component shows where these classes are inserted:
@@ -622,6 +660,10 @@ This simplified version of the DOM structure of the component shows where these
622
660
  <li class="{liOptionClass} {liActiveOptionClass}">
623
661
  Option 2 (currently active)
624
662
  </li>
663
+ ...
664
+ <li class="{liUserMsgClass} {liActiveUserMsgClass}">
665
+ Create this option...
666
+ </li>
625
667
  </ul>
626
668
  </div>
627
669
  ```