svelte-multiselect 3.3.0 → 4.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.
- package/CircleSpinner.svelte +29 -0
- package/CircleSpinner.svelte.d.ts +18 -0
- package/MultiSelect.svelte +50 -28
- package/MultiSelect.svelte.d.ts +7 -2
- package/index.d.ts +17 -0
- package/package.json +1 -1
- package/readme.md +36 -23
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<script >export let color = `cornflowerblue`;
|
|
2
|
+
export let duration = `1.5s`;
|
|
3
|
+
export let size = `1em`;
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<div
|
|
7
|
+
style="--duration: {duration}"
|
|
8
|
+
style:border-color="{color} transparent {color}
|
|
9
|
+
{color}"
|
|
10
|
+
style:width={size}
|
|
11
|
+
style:height={size}
|
|
12
|
+
/>
|
|
13
|
+
|
|
14
|
+
<style>
|
|
15
|
+
div {
|
|
16
|
+
display: inline-block;
|
|
17
|
+
vertical-align: middle;
|
|
18
|
+
margin: 0 3pt;
|
|
19
|
+
border-width: calc(1em / 5);
|
|
20
|
+
border-style: solid;
|
|
21
|
+
border-radius: 50%;
|
|
22
|
+
animation: var(--duration) infinite rotate;
|
|
23
|
+
}
|
|
24
|
+
@keyframes rotate {
|
|
25
|
+
100% {
|
|
26
|
+
transform: rotate(360deg);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
</style>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { SvelteComponentTyped } from "svelte";
|
|
2
|
+
declare const __propDef: {
|
|
3
|
+
props: {
|
|
4
|
+
color?: string | undefined;
|
|
5
|
+
duration?: string | undefined;
|
|
6
|
+
size?: string | undefined;
|
|
7
|
+
};
|
|
8
|
+
events: {
|
|
9
|
+
[evt: string]: CustomEvent<any>;
|
|
10
|
+
};
|
|
11
|
+
slots: {};
|
|
12
|
+
};
|
|
13
|
+
export declare type CircleSpinnerProps = typeof __propDef.props;
|
|
14
|
+
export declare type CircleSpinnerEvents = typeof __propDef.events;
|
|
15
|
+
export declare type CircleSpinnerSlots = typeof __propDef.slots;
|
|
16
|
+
export default class CircleSpinner extends SvelteComponentTyped<CircleSpinnerProps, CircleSpinnerEvents, CircleSpinnerSlots> {
|
|
17
|
+
}
|
|
18
|
+
export {};
|
package/MultiSelect.svelte
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
<script >import { createEventDispatcher, onMount } from 'svelte';
|
|
1
|
+
<script >import { createEventDispatcher, onMount, tick } from 'svelte';
|
|
2
2
|
import { fly } from 'svelte/transition';
|
|
3
3
|
import { onClickOutside } from './actions';
|
|
4
|
+
import CircleSpinner from './CircleSpinner.svelte';
|
|
4
5
|
import { CrossIcon, ExpandIcon, ReadOnlyIcon } from './icons';
|
|
5
6
|
import Wiggle from './Wiggle.svelte';
|
|
6
7
|
export let selected = [];
|
|
@@ -33,6 +34,10 @@ export let removeBtnTitle = `Remove`;
|
|
|
33
34
|
export let removeAllTitle = `Remove all`;
|
|
34
35
|
// https://github.com/sveltejs/svelte/issues/6964
|
|
35
36
|
export let defaultDisabledTitle = `This option is disabled`;
|
|
37
|
+
export let allowUserOptions = false;
|
|
38
|
+
export let autoScroll = true;
|
|
39
|
+
export let loading = false;
|
|
40
|
+
export let required = false;
|
|
36
41
|
if (maxSelect !== null && maxSelect < 0) {
|
|
37
42
|
console.error(`maxSelect must be null or positive integer, got ${maxSelect}`);
|
|
38
43
|
}
|
|
@@ -44,6 +49,9 @@ onMount(() => {
|
|
|
44
49
|
selected = _options.filter((op) => op?.preselected);
|
|
45
50
|
});
|
|
46
51
|
let wiggle = false;
|
|
52
|
+
// formValue binds to input.form-control to prevent form submission if required
|
|
53
|
+
// prop is true and no options are selected
|
|
54
|
+
$: formValue = selectedValues.join(`,`);
|
|
47
55
|
const dispatch = createEventDispatcher();
|
|
48
56
|
function isObject(item) {
|
|
49
57
|
return typeof item === `object` && !Array.isArray(item) && item !== null;
|
|
@@ -72,16 +80,8 @@ $: if (new Set(labels).size !== options.length) {
|
|
|
72
80
|
$: selectedLabels = selected.map((op) => op.label);
|
|
73
81
|
$: selectedValues = selected.map((op) => op.value);
|
|
74
82
|
// options matching the current search text
|
|
75
|
-
$: matchingOptions = _options.filter((op) => filterFunc(op, searchText));
|
|
83
|
+
$: matchingOptions = _options.filter((op) => filterFunc(op, searchText) && !selectedLabels.includes(op.label));
|
|
76
84
|
$: matchingEnabledOptions = matchingOptions.filter((op) => !op.disabled);
|
|
77
|
-
$: if (
|
|
78
|
-
// if there was an active option but it's not in the filtered list of options
|
|
79
|
-
(activeOption &&
|
|
80
|
-
!matchingEnabledOptions.map((op) => op.label).includes(activeOption.label)) ||
|
|
81
|
-
// or there's no active option but the user entered search text
|
|
82
|
-
(!activeOption && searchText))
|
|
83
|
-
// make the first filtered option active
|
|
84
|
-
activeOption = matchingEnabledOptions[0];
|
|
85
85
|
function add(label) {
|
|
86
86
|
if (maxSelect && maxSelect > 1 && selected.length >= maxSelect)
|
|
87
87
|
wiggle = true;
|
|
@@ -110,8 +110,11 @@ function add(label) {
|
|
|
110
110
|
function remove(label) {
|
|
111
111
|
if (selected.length === 0 || readonly)
|
|
112
112
|
return;
|
|
113
|
-
selected = selected.filter((option) => label !== option.label);
|
|
114
113
|
const option = _options.find((option) => option.label === label);
|
|
114
|
+
if (!option) {
|
|
115
|
+
return console.error(`MultiSelect: option with label ${label} not found`);
|
|
116
|
+
}
|
|
117
|
+
selected = selected.filter((option) => label !== option.label);
|
|
115
118
|
dispatch(`remove`, { option });
|
|
116
119
|
dispatch(`change`, { option, type: `remove` });
|
|
117
120
|
}
|
|
@@ -125,7 +128,7 @@ function setOptionsVisible(show) {
|
|
|
125
128
|
}
|
|
126
129
|
}
|
|
127
130
|
// handle all keyboard events this component receives
|
|
128
|
-
function handleKeydown(event) {
|
|
131
|
+
async function handleKeydown(event) {
|
|
129
132
|
// on escape: dismiss options dropdown and reset search text
|
|
130
133
|
if (event.key === `Escape`) {
|
|
131
134
|
setOptionsVisible(false);
|
|
@@ -137,7 +140,15 @@ function handleKeydown(event) {
|
|
|
137
140
|
const { label } = activeOption;
|
|
138
141
|
selectedLabels.includes(label) ? remove(label) : add(label);
|
|
139
142
|
searchText = ``;
|
|
140
|
-
}
|
|
143
|
+
}
|
|
144
|
+
else if ([true, `append`].includes(allowUserOptions)) {
|
|
145
|
+
selected = [...selected, { label: searchText, value: searchText }];
|
|
146
|
+
if (allowUserOptions === `append`)
|
|
147
|
+
options = [...options, { label: searchText, value: searchText }];
|
|
148
|
+
searchText = ``;
|
|
149
|
+
}
|
|
150
|
+
// no active option and no search text means the options dropdown is closed
|
|
151
|
+
// in which case enter means open it
|
|
141
152
|
else
|
|
142
153
|
setOptionsVisible(true);
|
|
143
154
|
}
|
|
@@ -150,31 +161,25 @@ function handleKeydown(event) {
|
|
|
150
161
|
}
|
|
151
162
|
const increment = event.key === `ArrowUp` ? -1 : 1;
|
|
152
163
|
const newActiveIdx = matchingEnabledOptions.indexOf(activeOption) + increment;
|
|
153
|
-
const ulOps = document.querySelector(`ul.options`);
|
|
154
164
|
if (newActiveIdx < 0) {
|
|
155
165
|
// wrap around top
|
|
156
166
|
activeOption = matchingEnabledOptions[matchingEnabledOptions.length - 1];
|
|
157
|
-
if (ulOps)
|
|
158
|
-
ulOps.scrollTop = ulOps.scrollHeight;
|
|
159
167
|
}
|
|
160
168
|
else if (newActiveIdx === matchingEnabledOptions.length) {
|
|
161
169
|
// wrap around bottom
|
|
162
170
|
activeOption = matchingEnabledOptions[0];
|
|
163
|
-
if (ulOps)
|
|
164
|
-
ulOps.scrollTop = 0;
|
|
165
171
|
}
|
|
166
172
|
else {
|
|
167
|
-
// default case
|
|
173
|
+
// default case: select next/previous in item list
|
|
168
174
|
activeOption = matchingEnabledOptions[newActiveIdx];
|
|
175
|
+
}
|
|
176
|
+
if (autoScroll) {
|
|
177
|
+
await tick();
|
|
169
178
|
const li = document.querySelector(`ul.options > li.active`);
|
|
170
|
-
|
|
171
|
-
// downwards, we scroll to next sibling to make element fully visible
|
|
172
|
-
if (increment === 1)
|
|
173
|
-
li?.nextSibling?.scrollIntoViewIfNeeded();
|
|
174
|
-
else
|
|
175
|
-
li?.scrollIntoViewIfNeeded();
|
|
179
|
+
li?.scrollIntoViewIfNeeded();
|
|
176
180
|
}
|
|
177
181
|
}
|
|
182
|
+
// on backspace key: remove last selected option
|
|
178
183
|
else if (event.key === `Backspace`) {
|
|
179
184
|
const label = selectedLabels.pop();
|
|
180
185
|
if (label && !searchText)
|
|
@@ -207,11 +212,13 @@ display above those of another following shortly after it -->
|
|
|
207
212
|
use:onClickOutside={() => setOptionsVisible(false)}
|
|
208
213
|
use:onClickOutside={() => dispatch(`blur`)}
|
|
209
214
|
>
|
|
215
|
+
<!-- invisible input, used only to prevent form submission if required=true and no options selected -->
|
|
216
|
+
<input {required} bind:value={formValue} tabindex="-1" class="form-control" />
|
|
210
217
|
<ExpandIcon style="min-width: 1em; padding: 0 1pt;" />
|
|
211
218
|
<ul class="selected {ulSelectedClass}">
|
|
212
219
|
{#each selected as option, idx}
|
|
213
220
|
<li class={liSelectedClass}>
|
|
214
|
-
<slot name="
|
|
221
|
+
<slot name="selected" {option} {idx}>
|
|
215
222
|
{option.label}
|
|
216
223
|
</slot>
|
|
217
224
|
{#if !readonly}
|
|
@@ -240,6 +247,11 @@ display above those of another following shortly after it -->
|
|
|
240
247
|
/>
|
|
241
248
|
</li>
|
|
242
249
|
</ul>
|
|
250
|
+
{#if loading}
|
|
251
|
+
<slot name="spinner">
|
|
252
|
+
<CircleSpinner />
|
|
253
|
+
</slot>
|
|
254
|
+
{/if}
|
|
243
255
|
{#if readonly}
|
|
244
256
|
<ReadOnlyIcon height="14pt" />
|
|
245
257
|
{:else if selected.length > 0}
|
|
@@ -286,7 +298,7 @@ display above those of another following shortly after it -->
|
|
|
286
298
|
class:disabled
|
|
287
299
|
class="{liOptionClass} {active ? liActiveOptionClass : ``}"
|
|
288
300
|
>
|
|
289
|
-
<slot name="
|
|
301
|
+
<slot name="option" {option} {idx}>
|
|
290
302
|
{option.label}
|
|
291
303
|
</slot>
|
|
292
304
|
</li>
|
|
@@ -372,10 +384,18 @@ display above those of another following shortly after it -->
|
|
|
372
384
|
font-size: calc(16px + 0.1vw);
|
|
373
385
|
color: var(--sms-text-color, inherit);
|
|
374
386
|
}
|
|
387
|
+
:where(div.multiselect > input.form-control) {
|
|
388
|
+
width: 2em;
|
|
389
|
+
position: absolute;
|
|
390
|
+
background: transparent;
|
|
391
|
+
border: none;
|
|
392
|
+
outline: none;
|
|
393
|
+
z-index: -1;
|
|
394
|
+
opacity: 0;
|
|
395
|
+
}
|
|
375
396
|
|
|
376
397
|
:where(div.multiselect > ul.options) {
|
|
377
398
|
list-style: none;
|
|
378
|
-
max-height: 50vh;
|
|
379
399
|
padding: 0;
|
|
380
400
|
top: 100%;
|
|
381
401
|
left: 0;
|
|
@@ -384,6 +404,7 @@ display above those of another following shortly after it -->
|
|
|
384
404
|
border-radius: 1ex;
|
|
385
405
|
overflow: auto;
|
|
386
406
|
background: var(--sms-options-bg, white);
|
|
407
|
+
max-height: var(--sms-options-max-height, 50vh);
|
|
387
408
|
overscroll-behavior: var(--sms-options-overscroll, none);
|
|
388
409
|
box-shadow: var(--sms-options-shadow, 0 0 14pt -8pt black);
|
|
389
410
|
}
|
|
@@ -393,6 +414,7 @@ display above those of another following shortly after it -->
|
|
|
393
414
|
:where(div.multiselect > ul.options > li) {
|
|
394
415
|
padding: 3pt 2ex;
|
|
395
416
|
cursor: pointer;
|
|
417
|
+
scroll-margin: var(--sms-options-scroll-margin, 100px);
|
|
396
418
|
}
|
|
397
419
|
/* for noOptionsMsg */
|
|
398
420
|
:where(div.multiselect > ul.options span) {
|
package/MultiSelect.svelte.d.ts
CHANGED
|
@@ -27,6 +27,10 @@ declare const __propDef: {
|
|
|
27
27
|
removeBtnTitle?: string | undefined;
|
|
28
28
|
removeAllTitle?: string | undefined;
|
|
29
29
|
defaultDisabledTitle?: string | undefined;
|
|
30
|
+
allowUserOptions?: boolean | "append" | undefined;
|
|
31
|
+
autoScroll?: boolean | undefined;
|
|
32
|
+
loading?: boolean | undefined;
|
|
33
|
+
required?: boolean | undefined;
|
|
30
34
|
};
|
|
31
35
|
events: {
|
|
32
36
|
mouseup: MouseEvent;
|
|
@@ -34,11 +38,12 @@ declare const __propDef: {
|
|
|
34
38
|
[evt: string]: CustomEvent<any>;
|
|
35
39
|
};
|
|
36
40
|
slots: {
|
|
37
|
-
|
|
41
|
+
selected: {
|
|
38
42
|
option: Option;
|
|
39
43
|
idx: any;
|
|
40
44
|
};
|
|
41
|
-
|
|
45
|
+
spinner: {};
|
|
46
|
+
option: {
|
|
42
47
|
option: Option;
|
|
43
48
|
idx: any;
|
|
44
49
|
};
|
package/index.d.ts
CHANGED
|
@@ -13,3 +13,20 @@ export declare type Option = {
|
|
|
13
13
|
export declare type ProtoOption = Primitive | (Omit<Option, `value`> & {
|
|
14
14
|
value?: Primitive;
|
|
15
15
|
});
|
|
16
|
+
export declare type DispatchEvents = {
|
|
17
|
+
add: {
|
|
18
|
+
option: Option;
|
|
19
|
+
};
|
|
20
|
+
remove: {
|
|
21
|
+
option: Option;
|
|
22
|
+
};
|
|
23
|
+
removeAll: {
|
|
24
|
+
options: Option[];
|
|
25
|
+
};
|
|
26
|
+
change: {
|
|
27
|
+
option?: Option;
|
|
28
|
+
options?: Option[];
|
|
29
|
+
type: 'add' | 'remove' | 'removeAll';
|
|
30
|
+
};
|
|
31
|
+
blur: undefined;
|
|
32
|
+
};
|
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": "
|
|
8
|
+
"version": "4.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,7 +35,6 @@
|
|
|
35
35
|
|
|
36
36
|
## Recent breaking changes
|
|
37
37
|
|
|
38
|
-
- v2.0.0 added the ability to pass options as objects. As a result, `bind:selected` no longer returns simple strings but objects, even if you still pass in `options` as strings. To get the same stuff you would have gotten from `bind:selected` before, there's now `bind:selectedLabels` (and `bind:selectedValues`).
|
|
39
38
|
- v3.0.0 changed the `event.detail` payload for `'add'`, `'remove'` and `'change'` events from `token` to `option`, e.g.
|
|
40
39
|
|
|
41
40
|
```js
|
|
@@ -45,6 +44,10 @@
|
|
|
45
44
|
|
|
46
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`.
|
|
47
46
|
|
|
47
|
+
- v4.0.0 renamed the slots for customizing how selected options and dropdown list items are rendered:
|
|
48
|
+
- old: `<slot name="renderOptions" />`, new: `<slot name="option" />`
|
|
49
|
+
- old: `<slot name="renderSelected" />`, new: `<slot name="selected" />`
|
|
50
|
+
|
|
48
51
|
## Installation
|
|
49
52
|
|
|
50
53
|
```sh
|
|
@@ -88,22 +91,26 @@ Full list of props/bindable variables for this component:
|
|
|
88
91
|
<div class="table">
|
|
89
92
|
|
|
90
93
|
<!-- prettier-ignore -->
|
|
91
|
-
| name
|
|
92
|
-
|
|
|
93
|
-
| `options`
|
|
94
|
-
| `showOptions`
|
|
95
|
-
| `searchText`
|
|
96
|
-
| `activeOption`
|
|
97
|
-
| `maxSelect`
|
|
98
|
-
| `selected`
|
|
99
|
-
| `selectedLabels`
|
|
100
|
-
| `selectedValues`
|
|
101
|
-
| `noOptionsMsg`
|
|
102
|
-
| `readonly`
|
|
103
|
-
| `placeholder`
|
|
104
|
-
| `input`
|
|
105
|
-
| `id`
|
|
106
|
-
| `name`
|
|
94
|
+
| name | default | description |
|
|
95
|
+
| :----------------- | :---------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
96
|
+
| `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. |
|
|
97
|
+
| `showOptions` | `false` | Bindable boolean that controls whether the options dropdown is visible. |
|
|
98
|
+
| `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. |
|
|
99
|
+
| `activeOption` | `null` | Currently active option, i.e. the one the user currently hovers or navigated to with arrow keys. |
|
|
100
|
+
| `maxSelect` | `null` | Positive integer to limit the number of options users can pick. `null` means no limit. |
|
|
101
|
+
| `selected` | `[]` | Array of currently/pre-selected options when binding/passing as props respectively. |
|
|
102
|
+
| `selectedLabels` | `[]` | Labels of currently selected options. |
|
|
103
|
+
| `selectedValues` | `[]` | Values of currently selected options. |
|
|
104
|
+
| `noOptionsMsg` | `'No matching options'` | What message to show if no options match the user-entered search string. |
|
|
105
|
+
| `readonly` | `false` | Disable the component. It will still be rendered but users won't be able to interact with it. |
|
|
106
|
+
| `placeholder` | `undefined` | String shown in the text input when no option is selected. |
|
|
107
|
+
| `input` | `undefined` | Handle to the `<input>` DOM node. |
|
|
108
|
+
| `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. |
|
|
109
|
+
| `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>`. |
|
|
110
|
+
| `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. |
|
|
111
|
+
| `autoScroll` | `true` | `false` disables keeping the active dropdown items in view when going up/down the list of options with arrow keys. |
|
|
112
|
+
| `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. |
|
|
113
|
+
| `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. |
|
|
107
114
|
|
|
108
115
|
</div>
|
|
109
116
|
|
|
@@ -126,24 +133,27 @@ Full list of props/bindable variables for this component:
|
|
|
126
133
|
|
|
127
134
|
## Slots
|
|
128
135
|
|
|
129
|
-
`MultiSelect.svelte`
|
|
136
|
+
`MultiSelect.svelte` has 3 named slots:
|
|
130
137
|
|
|
131
|
-
- `slot="
|
|
132
|
-
- `slot="
|
|
138
|
+
- `slot="option"`: Customize rendering of dropdown options. Receives as props the `option` object and the zero-indexed position (`idx`) it has in the dropdown.
|
|
139
|
+
- `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.
|
|
140
|
+
- `slot="spinner"`: Custom spinner component to display when in `loading` state. Receives no props.
|
|
133
141
|
|
|
134
|
-
|
|
142
|
+
Example:
|
|
135
143
|
|
|
136
144
|
```svelte
|
|
137
145
|
<MultiSelect options={[`Banana`, `Apple`, `Mango`]}>
|
|
138
|
-
<span let:idx let:option slot="
|
|
146
|
+
<span let:idx let:option slot="option">
|
|
139
147
|
{idx + 1}. {option.label}
|
|
140
148
|
{option.label === `Mango` ? `🎉` : ``}
|
|
141
149
|
</span>
|
|
142
150
|
|
|
143
|
-
<span let:idx let:option slot="
|
|
151
|
+
<span let:idx let:option slot="selected">
|
|
144
152
|
#{idx + 1}
|
|
145
153
|
{option.label}
|
|
146
154
|
</span>
|
|
155
|
+
|
|
156
|
+
<CustomSpinner slot="spinner">
|
|
147
157
|
</MultiSelect>
|
|
148
158
|
```
|
|
149
159
|
|
|
@@ -233,8 +243,11 @@ If you only want to make small adjustments, you can pass the following CSS varia
|
|
|
233
243
|
- `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.
|
|
234
244
|
- `div.multiselect > ul.options`
|
|
235
245
|
- `background: var(--sms-options-bg, white)`: Background of dropdown list.
|
|
246
|
+
- `max-height: var(--sms-options-max-height, 50vh)`: Maximum height of options dropdown.
|
|
236
247
|
- `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).
|
|
237
248
|
- `box-shadow: var(--sms-options-shadow, 0 0 14pt -8pt black);`: Box shadow of dropdown list.
|
|
249
|
+
- `div.multiselect > ul.options > li`
|
|
250
|
+
- `scroll-margin: var(--sms-options-scroll-margin, 100px)`: Top/bottom margin to keep between dropdown list items and top/bottom screen edge when auto-scrolling list to keep items in view.
|
|
238
251
|
- `div.multiselect > ul.options > li.selected`
|
|
239
252
|
- `border-left: var(--sms-li-selected-border-left, 3pt solid var(--sms-selected-color, green))`
|
|
240
253
|
- `background: var(--sms-li-selected-bg, inherit)`: Background of selected list items in options pane.
|