svelte-multiselect 3.1.1 → 3.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,10 +12,15 @@ export let readonly = false;
12
12
  export let options;
13
13
  export let input = null;
14
14
  export let placeholder = undefined;
15
- export let name = undefined;
16
15
  export let id = undefined;
16
+ export let name = id;
17
17
  export let noOptionsMsg = `No matching options`;
18
18
  export let activeOption = null;
19
+ export let filterFunc = (op, searchText) => {
20
+ if (!searchText)
21
+ return true;
22
+ return `${op.label}`.toLowerCase().includes(searchText.toLowerCase());
23
+ };
19
24
  export let outerDivClass = ``;
20
25
  export let ulSelectedClass = ``;
21
26
  export let liSelectedClass = ``;
@@ -66,11 +71,7 @@ const dispatch = createEventDispatcher();
66
71
  let searchText = ``;
67
72
  let showOptions = false;
68
73
  // options matching the current search text
69
- $: matchingOptions = _options.filter((op) => {
70
- if (!searchText)
71
- return true;
72
- return `${op.label}`.toLowerCase().includes(searchText.toLowerCase());
73
- });
74
+ $: matchingOptions = _options.filter((op) => filterFunc(op, searchText));
74
75
  $: matchingEnabledOptions = matchingOptions.filter((op) => !op.disabled);
75
76
  $: if (
76
77
  // if there was an active option but it's not in the filtered list of options
@@ -81,7 +82,7 @@ $: if (
81
82
  // make the first filtered option active
82
83
  activeOption = matchingEnabledOptions[0];
83
84
  function add(label) {
84
- if (selected.length - (maxSelect ?? 0) < 1)
85
+ if (maxSelect && maxSelect > 1 && selected.length >= maxSelect)
85
86
  wiggle = true;
86
87
  if (!readonly &&
87
88
  !selectedLabels.includes(label) &&
@@ -114,9 +115,6 @@ function remove(label) {
114
115
  dispatch(`change`, { option, type: `remove` });
115
116
  }
116
117
  function setOptionsVisible(show) {
117
- // nothing to do if visibility is already as intended
118
- if (readonly || show === showOptions)
119
- return;
120
118
  showOptions = show;
121
119
  if (show)
122
120
  input?.focus();
@@ -200,16 +198,15 @@ const handleEnterAndSpaceKeys = (handler) => (event) => {
200
198
  <!-- z-index: 2 when showOptions is true ensures the ul.selected of one <MultiSelect />
201
199
  display above those of another following shortly after it -->
202
200
  <div
203
- {id}
204
201
  class="multiselect {outerDivClass}"
205
202
  class:readonly
206
203
  class:single={maxSelect == 1}
207
- style={showOptions ? `z-index: 2;` : undefined}
204
+ class:open={showOptions}
208
205
  on:mouseup|stopPropagation={() => setOptionsVisible(true)}
209
206
  use:onClickOutside={() => setOptionsVisible(false)}
210
207
  use:onClickOutside={() => dispatch(`blur`)}
211
208
  >
212
- <ExpandIcon height="14pt" style="padding: 0 3pt 0 1pt;" />
209
+ <ExpandIcon style="min-width: 1em; padding: 0 1pt;" />
213
210
  <ul class="selected {ulSelectedClass}">
214
211
  {#each selected as option, idx}
215
212
  <li class={liSelectedClass}>
@@ -228,21 +225,24 @@ display above those of another following shortly after it -->
228
225
  {/if}
229
226
  </li>
230
227
  {/each}
231
- <input
232
- bind:this={input}
233
- autocomplete="off"
234
- bind:value={searchText}
235
- on:mouseup|self|stopPropagation={() => setOptionsVisible(true)}
236
- on:keydown={handleKeydown}
237
- on:focus={() => setOptionsVisible(true)}
238
- {name}
239
- placeholder={selectedLabels.length ? `` : placeholder}
240
- />
228
+ <li style="display: contents;">
229
+ <input
230
+ bind:this={input}
231
+ autocomplete="off"
232
+ bind:value={searchText}
233
+ on:mouseup|self|stopPropagation={() => setOptionsVisible(true)}
234
+ on:keydown={handleKeydown}
235
+ on:focus={() => setOptionsVisible(true)}
236
+ {id}
237
+ {name}
238
+ placeholder={selectedLabels.length ? `` : placeholder}
239
+ />
240
+ </li>
241
241
  </ul>
242
242
  {#if readonly}
243
243
  <ReadOnlyIcon height="14pt" />
244
244
  {:else if selected.length > 0}
245
- {#if maxSelect !== null && maxSelect > 1}
245
+ {#if maxSelect !== null && maxSelectMsg !== null}
246
246
  <Wiggle bind:wiggle angle={20}>
247
247
  <span style="padding: 0 3pt;">{maxSelectMsg(selected.length, maxSelect)}</span>
248
248
  </Wiggle>
@@ -284,7 +284,7 @@ display above those of another following shortly after it -->
284
284
  </slot>
285
285
  </li>
286
286
  {:else}
287
- {noOptionsMsg}
287
+ <span>{noOptionsMsg}</span>
288
288
  {/each}
289
289
  </ul>
290
290
  {/key}
@@ -294,15 +294,17 @@ display above those of another following shortly after it -->
294
294
  :where(div.multiselect) {
295
295
  position: relative;
296
296
  margin: 1em 0;
297
- border: var(--sms-border, 1pt solid lightgray);
298
- border-radius: var(--sms-border-radius, 5pt);
299
- background: var(--sms-input-bg);
300
- height: var(--sms-input-height, 2em);
301
297
  align-items: center;
302
- min-height: 18pt;
303
298
  display: flex;
304
299
  cursor: text;
305
300
  padding: 0 3pt;
301
+ border: var(--sms-border, 1pt solid lightgray);
302
+ border-radius: var(--sms-border-radius, 5pt);
303
+ background: var(--sms-input-bg);
304
+ min-height: var(--sms-input-min-height, 22pt);
305
+ }
306
+ :where(div.multiselect.open) {
307
+ z-index: var(--sms-open-z-index, 4);
306
308
  }
307
309
  :where(div.multiselect:focus-within) {
308
310
  border: var(--sms-focus-border, 1pt solid var(--sms-active-color, cornflowerblue));
@@ -311,25 +313,33 @@ display above those of another following shortly after it -->
311
313
  background: var(--sms-readonly-bg, lightgray);
312
314
  }
313
315
 
314
- :where(ul.selected > li) {
315
- background: var(--sms-selected-bg, var(--sms-active-color, cornflowerblue));
316
+ :where(div.multiselect > ul.selected) {
317
+ display: flex;
318
+ flex: 1;
319
+ padding: 0;
320
+ margin: 0;
321
+ flex-wrap: wrap;
322
+ }
323
+ :where(div.multiselect > ul.selected > li) {
316
324
  align-items: center;
317
325
  border-radius: 4pt;
318
326
  display: flex;
319
327
  margin: 2pt;
320
- padding: 0 0 0 1ex;
328
+ line-height: normal;
329
+ padding: 1pt 2pt 1pt 5pt;
321
330
  transition: 0.3s;
322
331
  white-space: nowrap;
323
- height: 16pt;
332
+ background: var(--sms-selected-bg, var(--sms-active-color, cornflowerblue));
333
+ height: var(--sms-selected-li-height);
324
334
  }
325
- :where(ul.selected > li button, button.remove-all) {
335
+ :where(div.multiselect > ul.selected > li button, button.remove-all) {
326
336
  align-items: center;
327
337
  border-radius: 50%;
328
338
  display: flex;
329
339
  cursor: pointer;
330
340
  transition: 0.2s;
331
341
  }
332
- :where(button) {
342
+ :where(div.multiselect button) {
333
343
  color: inherit;
334
344
  background: transparent;
335
345
  border: none;
@@ -340,32 +350,23 @@ display above those of another following shortly after it -->
340
350
  :where(ul.selected > li button:hover, button.remove-all:hover, button:focus) {
341
351
  color: var(--sms-remove-x-hover-focus-color, lightskyblue);
342
352
  }
343
- :where(button:focus) {
353
+ :where(div.multiselect > button:focus) {
344
354
  transform: scale(1.04);
345
355
  }
346
356
 
347
- :where(div.multiselect input) {
357
+ :where(div.multiselect > ul.selected > li > input) {
348
358
  border: none;
349
359
  outline: none;
350
360
  background: none;
351
- color: var(--sms-text-color, inherit);
352
361
  flex: 1; /* this + next line fix issue #12 https://git.io/JiDe3 */
353
362
  min-width: 2em;
354
363
  /* minimum font-size > 16px ensures iOS doesn't zoom in when focusing input */
355
364
  /* https://stackoverflow.com/a/6394497 */
356
365
  font-size: calc(16px + 0.1vw);
366
+ color: var(--sms-text-color, inherit);
357
367
  }
358
368
 
359
- :where(ul.selected) {
360
- display: flex;
361
- padding: 0;
362
- margin: 0;
363
- flex-wrap: wrap;
364
- flex: 1;
365
- overscroll-behavior: none;
366
- }
367
-
368
- :where(ul.options) {
369
+ :where(div.multiselect > ul.options) {
369
370
  list-style: none;
370
371
  max-height: 50vh;
371
372
  padding: 0;
@@ -375,15 +376,20 @@ display above those of another following shortly after it -->
375
376
  border-radius: 1ex;
376
377
  overflow: auto;
377
378
  background: var(--sms-options-bg, white);
379
+ overscroll-behavior: var(--sms-options-overscroll, none);
378
380
  }
379
- :where(ul.options.hidden) {
381
+ :where(div.multiselect > ul.options.hidden) {
380
382
  visibility: hidden;
381
383
  }
382
- :where(ul.options li) {
384
+ :where(div.multiselect > ul.options > li) {
383
385
  padding: 3pt 2ex;
384
386
  cursor: pointer;
385
387
  }
386
- :where(ul.options li.selected) {
388
+ /* for noOptionsMsg */
389
+ :where(div.multiselect > ul.options span) {
390
+ padding: 3pt 2ex;
391
+ }
392
+ :where(div.multiselect > ul.options > li.selected) {
387
393
  border-left: var(
388
394
  --sms-li-selected-border-left,
389
395
  3pt solid var(--sms-selected-color, green)
@@ -391,22 +397,21 @@ display above those of another following shortly after it -->
391
397
  background: var(--sms-li-selected-bg, inherit);
392
398
  color: var(--sms-li-selected-color, inherit);
393
399
  }
394
- :where(ul.options li:not(.selected):hover) {
400
+ :where(div.multiselect > ul.options > li:not(.selected):hover) {
395
401
  border-left: var(
396
402
  --sms-li-not-selected-hover-border-left,
397
403
  3pt solid var(--sms-active-color, cornflowerblue)
398
404
  );
399
- border-left: 3pt solid var(--blue);
400
405
  }
401
- :where(ul.options li.active) {
406
+ :where(div.multiselect > ul.options > li.active) {
402
407
  background: var(--sms-li-active-bg, var(--sms-active-color, cornflowerblue));
403
408
  }
404
- :where(ul.options li.disabled) {
409
+ :where(div.multiselect > ul.options > li.disabled) {
410
+ cursor: not-allowed;
405
411
  background: var(--sms-li-disabled-bg, #f5f5f6);
406
412
  color: var(--sms-li-disabled-text, #b8b8b8);
407
- cursor: not-allowed;
408
413
  }
409
- :where(ul.options li.disabled:hover) {
414
+ :where(div.multiselect > ul.options > li.disabled:hover) {
410
415
  border-left: unset;
411
416
  }
412
417
  </style>
@@ -11,10 +11,11 @@ declare const __propDef: {
11
11
  options: ProtoOption[];
12
12
  input?: HTMLInputElement | null | undefined;
13
13
  placeholder?: string | undefined;
14
- name?: string | undefined;
15
14
  id?: string | undefined;
15
+ name?: string | undefined;
16
16
  noOptionsMsg?: string | undefined;
17
17
  activeOption?: Option | null | undefined;
18
+ filterFunc?: ((op: Option, searchText: string) => boolean) | undefined;
18
19
  outerDivClass?: string | undefined;
19
20
  ulSelectedClass?: string | undefined;
20
21
  liSelectedClass?: string | undefined;
package/package.json CHANGED
@@ -5,32 +5,32 @@
5
5
  "homepage": "https://svelte-multiselect.netlify.app",
6
6
  "repository": "https://github.com/janosh/svelte-multiselect",
7
7
  "license": "MIT",
8
- "version": "3.1.1",
8
+ "version": "3.2.3",
9
9
  "type": "module",
10
10
  "svelte": "index.js",
11
11
  "bugs": "https://github.com/janosh/svelte-multiselect/issues",
12
12
  "devDependencies": {
13
- "@sveltejs/adapter-static": "^1.0.0-next.26",
14
- "@sveltejs/kit": "^1.0.0-next.239",
15
- "@typescript-eslint/eslint-plugin": "^5.10.0",
16
- "@typescript-eslint/parser": "^5.10.0",
17
- "eslint": "^8.7.0",
13
+ "@sveltejs/adapter-static": "^1.0.0-next.28",
14
+ "@sveltejs/kit": "^1.0.0-next.269",
15
+ "@typescript-eslint/eslint-plugin": "^5.12.0",
16
+ "@typescript-eslint/parser": "^5.12.0",
17
+ "eslint": "^8.9.0",
18
18
  "eslint-plugin-svelte3": "^3.4.0",
19
19
  "hastscript": "^7.0.2",
20
- "mdsvex": "^0.9.8",
20
+ "mdsvex": "^0.10.5",
21
21
  "prettier": "^2.5.1",
22
22
  "prettier-plugin-svelte": "^2.6.0",
23
23
  "rehype-autolink-headings": "^6.1.1",
24
24
  "rehype-slug": "^5.0.1",
25
- "svelte": "^3.46.2",
26
- "svelte-check": "^2.3.0",
25
+ "svelte": "^3.46.4",
26
+ "svelte-check": "^2.4.3",
27
27
  "svelte-github-corner": "^0.1.0",
28
- "svelte-preprocess": "^4.10.2",
29
- "svelte-toc": "^0.2.2",
30
- "svelte2tsx": "^0.4.14",
28
+ "svelte-preprocess": "^4.10.3",
29
+ "svelte-toc": "^0.2.5",
30
+ "svelte2tsx": "^0.5.3",
31
31
  "tslib": "^2.3.1",
32
32
  "typescript": "^4.5.5",
33
- "vite": "^2.7.13"
33
+ "vite": "^2.8.2"
34
34
  },
35
35
  "keywords": [
36
36
  "svelte",
package/readme.md CHANGED
@@ -8,6 +8,7 @@
8
8
  [![Netlify Status](https://api.netlify.com/api/v1/badges/a45b62c3-ea45-4cfd-9912-77ec4fc8d7e8/deploy-status)](https://app.netlify.com/sites/svelte-multiselect/deploys)
9
9
  [![NPM version](https://img.shields.io/npm/v/svelte-multiselect?color=blue&logo=NPM)](https://npmjs.com/package/svelte-multiselect)
10
10
  [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/janosh/svelte-multiselect/main.svg)](https://results.pre-commit.ci/latest/github/janosh/svelte-multiselect/main)
11
+ ![Needs Svelte version](https://img.shields.io/npm/dependency-version/svelte-multiselect/dev/svelte)
11
12
 
12
13
  </h4>
13
14
 
@@ -21,7 +22,7 @@
21
22
 
22
23
  <slot />
23
24
 
24
- ## Key Features
25
+ ## Key features
25
26
 
26
27
  - **Single / multiple select:** pass `maxSelect={1}` prop to only allow one selection
27
28
  - **Dropdowns:** scrollable lists for large numbers of options
@@ -87,23 +88,39 @@ Full list of props/bindable variables for this component:
87
88
  <div class="table">
88
89
 
89
90
  <!-- prettier-ignore -->
90
- | name | default | description |
91
- | :--------------- | :--------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
92
- | `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. |
93
- | `activeOption` | `null` | Currently active option, i.e. the one the user currently hovers or navigated to with arrow keys. |
94
- | `maxSelect` | `null` | Positive integer to limit the number of options users can pick. `null` means no limit. |
95
- | `maxSelectMsg` | ``(current: number, max: number) => `${current}/${max}` `` | Function that returns a string informing the user how many of the maximum allowed options they have currently selected. Return empty string to disable, i.e. `() => ''`. |
96
- | `selected` | `[]` | Array of currently/pre-selected options when binding/passing as props respectively. |
97
- | `selectedLabels` | `[]` | Labels of currently selected options. |
98
- | `selectedValues` | `[]` | Values of currently selected options. |
99
- | `readonly` | `false` | Disable the component. It will still be rendered but users won't be able to interact with it. |
100
- | `placeholder` | `undefined` | String shown in the text input when no option is selected. |
101
- | `input` | `undefined` | Handle to the `<input>` DOM node. |
102
- | `name` | `undefined` | Passed to the `<input>` for associating HTML form `<label>`s with this component. E.g. clicking a `<label>` with same name will focus this component. |
103
- | `id` | `undefined` | Applied to the top-level `<div>` e.g. for `document.getElementById()`. |
91
+ | name | default | description |
92
+ | :--------------- | :------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
93
+ | `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. |
94
+ | `activeOption` | `null` | Currently active option, i.e. the one the user currently hovers or navigated to with arrow keys. |
95
+ | `maxSelect` | `null` | Positive integer to limit the number of options users can pick. `null` means no limit. |
96
+ | `selected` | `[]` | Array of currently/pre-selected options when binding/passing as props respectively. |
97
+ | `selectedLabels` | `[]` | Labels of currently selected options. |
98
+ | `selectedValues` | `[]` | Values of currently selected options. |
99
+ | `readonly` | `false` | Disable the component. It will still be rendered but users won't be able to interact with it. |
100
+ | `placeholder` | `undefined` | String shown in the text input when no option is selected. |
101
+ | `input` | `undefined` | Handle to the `<input>` DOM node. |
102
+ | `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. |
103
+ | `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>`. |
104
104
 
105
105
  </div>
106
106
 
107
+ ## Exposed methods
108
+
109
+ 1. `filterFunc = (op: Option, searchText: string) => boolean`: Determine what options are shown when user enters search string to filter dropdown list. Defaults to:
110
+
111
+ ```ts
112
+ filterFunc = (op: Option, searchText: string) => {
113
+ if (!searchText) return true
114
+ return `${op.label}`.toLowerCase().includes(searchText.toLowerCase())
115
+ }
116
+ ```
117
+
118
+ 2. `maxSelectMsg = (current: number, max: number) => string`: Inform the user how many of the maximum allowed options they have currently selected. Return empty string to disable, i.e. `() => ''`. Is automatically disabled when `maxSelect === null`. Defaults to:
119
+
120
+ ```ts
121
+ maxSelectMsg = (current: number, max: number) => `${current}/${max}`
122
+ ```
123
+
107
124
  ## Slots
108
125
 
109
126
  `MultiSelect.svelte` accepts two named slots
@@ -193,27 +210,36 @@ There are 3 ways to style this component. To understand which options do what, i
193
210
 
194
211
  If you only want to make small adjustments, you can pass the following CSS variables directly to the component as props or define them in a `:global()` CSS context.
195
212
 
196
- - `div.multiselect`:
213
+ - `div.multiselect`
197
214
  - `border: var(--sms-border, 1pt solid lightgray)`: Change this to e.g. to `1px solid red` to indicate this form field is in an invalid state.
198
- - `border-radius: var(--sms-border-radius, 5pt)`: Input border radius.
199
- - `background: var(--sms-input-bg)`: Input background.
200
- - `height: var(--sms-input-height, 2em)`: Input height.
201
- - `border: var(--sms-focus-border, 1pt solid var(--sms-active-color, cornflowerblue))`: Border when focused. Falls back to `--sms-active-color` if not set which in turn falls back on `cornflowerblue`.
215
+ - `border-radius: var(--sms-border-radius, 5pt)`
216
+ - `background: var(--sms-input-bg)`
217
+ - `height: var(--sms-input-height, 2em)`
218
+ - `div.multiselect.open`
219
+ - `z-index: var(--sms-open-z-index, 4)`: Increase this if needed to ensure the dropdown list is displayed atop all other page elements.
220
+ - `div.multiselect:focus-within`
221
+ - `border: var(--sms-focus-border, 1pt solid var(--sms-active-color, cornflowerblue))`: Border when component has focus. Defaults to `--sms-active-color` if not set which defaults to `cornflowerblue`.
222
+ - `div.multiselect.readonly`
202
223
  - `background: var(--sms-readonly-bg, lightgray)`: Background when in readonly state.
203
- - `div.multiselect input`
224
+ - `div.multiselect > ul.selected > li > input`
204
225
  - `color: var(--sms-text-color, inherit)`: Input text color.
205
- - `ul.selected > li`:
226
+ - `div.multiselect > ul.selected > li`
206
227
  - `background: var(--sms-selected-bg, var(--sms-active-color, cornflowerblue))`: Background of selected options.
207
- - `ul.selected > li button:hover, button.remove-all:hover`
228
+ - `height: var(--sms-selected-li-height)`: Height of selected options.
229
+ - `ul.selected > li button:hover, button.remove-all:hover, button:focus`
208
230
  - `color: var(--sms-remove-x-hover-focus-color, lightskyblue)`: Color of the cross-icon buttons for removing all or individual selected options when in `:focus` or `:hover` state.
209
- - `ul.options`
210
- - `background: var(--sms-options-bg, white)`: Background of options list.
211
- - `ul.options > li.selected`
231
+ - `div.multiselect > ul.options`
232
+ - `background: var(--sms-options-bg, white)`: Background of dropdown list.
233
+ - `overscroll-behavior: var(--sms-options-overscroll, none)`: Whether scroll events bubble to parent elements when reaching the top/bottom of the options dropdown. See [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior).
234
+ - `div.multiselect > ul.options > li.selected`
235
+ - `border-left: var(--sms-li-selected-border-left, 3pt solid var(--sms-selected-color, green))`
212
236
  - `background: var(--sms-li-selected-bg, inherit)`: Background of selected list items in options pane.
213
237
  - `color: var(--sms-li-selected-color, inherit)`: Text color of selected list items in options pane.
214
- - `ul.options > li.active`
238
+ - `div.multiselect > ul.options > li:not(.selected):hover`
239
+ - `border-left: var(--sms-li-not-selected-hover-border-left, 3pt solid var(--sms-active-color, cornflowerblue))`
240
+ - `div.multiselect > ul.options > li.active`
215
241
  - `background: var(--sms-li-active-bg, var(--sms-active-color, cornflowerblue))`: Background of active (currently with arrow keys highlighted) list item.
216
- - `ul.options > li.disabled`
242
+ - `div.multiselect > ul.options > li.disabled`
217
243
  - `background: var(--sms-li-disabled-bg, #f5f5f6)`: Background of disabled options in the dropdown list.
218
244
  - `color: var(--sms-li-disabled-text, #b8b8b8)`: Text color of disabled option in the dropdown list.
219
245
 
@@ -253,40 +279,47 @@ This simplified version of the DOM structure of this component shows where these
253
279
  You can alternatively style every part of this component with more fine-grained control by using the following `:global()` CSS selectors. `ul.selected` is the list of currently selected options rendered inside the component's input whereas `ul.options` is the list of available options that slides out when the component has focus.
254
280
 
255
281
  ```css
256
- :global(.multiselect) {
282
+ :global(div.multiselect) {
257
283
  /* top-level wrapper div */
258
284
  }
259
- :global(.multiselect ul.selected > li) {
260
- /* selected options */
285
+ :global(div.multiselect.open) {
286
+ /* top-level wrapper div when dropdown open */
287
+ }
288
+ :global(div.multiselect.readonly) {
289
+ /* top-level wrapper div when in readonly state */
261
290
  }
262
- :global(.multiselect ul.selected > li button),
263
- :global(.multiselect button.remove-all) {
291
+ :global(div.multiselect > ul.selected) {
292
+ /* selected list */
293
+ }
294
+ :global(div.multiselect > ul.selected > li) {
295
+ /* selected list items */
296
+ }
297
+ :global(div.multiselect button) {
298
+ /* target all buttons in this component */
299
+ }
300
+ :global(div.multiselect > ul.selected > li button, button.remove-all) {
264
301
  /* buttons to remove a single or all selected options at once */
265
302
  }
266
- :global(.multiselect ul.options) {
303
+ :global(div.multiselect > ul.selected > li > input) {
304
+ /* input inside the top-level wrapper div */
305
+ }
306
+ :global(div.multiselect > ul.options) {
267
307
  /* dropdown options */
268
308
  }
269
- :global(.multiselect ul.options li) {
270
- /* dropdown list of available options */
309
+ :global(div.multiselect > ul.options > li) {
310
+ /* dropdown list items */
271
311
  }
272
- :global(.multiselect ul.options li.selected) {
312
+ :global(div.multiselect > ul.options > li.selected) {
273
313
  /* selected options in the dropdown list */
274
314
  }
275
- :global(.multiselect ul.options li:not(.selected):hover) {
315
+ :global(div.multiselect > ul.options > li:not(.selected):hover) {
276
316
  /* unselected but hovered options in the dropdown list */
277
317
  }
278
- :global(.multiselect ul.options li.selected:hover) {
279
- /* selected and hovered options in the dropdown list */
280
- /* probably not necessary to style this state in most cases */
281
- }
282
- :global(.multiselect ul.options li.active) {
318
+ :global(div.multiselect > ul.options > li.active) {
283
319
  /* active means item was navigated to with up/down arrow keys */
284
320
  /* ready to be selected by pressing enter */
285
321
  }
286
- :global(.multiselect ul.options li.selected.active) {
287
- /* both active and already selected, pressing enter now will deselect the item */
288
- }
289
- :global(.multiselect ul.options li.disabled) {
322
+ :global(div.multiselect > ul.options > li.disabled) {
290
323
  /* options with disabled key set to true (see props above) */
291
324
  }
292
325
  ```