svelte-multiselect 6.0.1 → 6.0.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.
- package/MultiSelect.svelte +18 -28
- package/MultiSelect.svelte.d.ts +1 -1
- package/package.json +1 -1
- package/readme.md +61 -37
package/MultiSelect.svelte
CHANGED
|
@@ -37,12 +37,12 @@ export let options;
|
|
|
37
37
|
export let outerDiv = null;
|
|
38
38
|
export let outerDivClass = ``;
|
|
39
39
|
export let parseLabelsAsHtml = false; // should not be combined with allowUserOptions!
|
|
40
|
-
export let placeholder =
|
|
40
|
+
export let placeholder = null;
|
|
41
41
|
export let removeAllTitle = `Remove all`;
|
|
42
42
|
export let removeBtnTitle = `Remove`;
|
|
43
43
|
export let required = false;
|
|
44
44
|
export let searchText = ``;
|
|
45
|
-
export let selected = [];
|
|
45
|
+
export let selected = options?.filter((op) => op?.preselected) ?? [];
|
|
46
46
|
export let selectedLabels = [];
|
|
47
47
|
export let selectedValues = [];
|
|
48
48
|
export let sortSelected = false;
|
|
@@ -67,7 +67,7 @@ if (!Array.isArray(selected)) {
|
|
|
67
67
|
console.error(`selected prop must be an array, got ${selected}`);
|
|
68
68
|
}
|
|
69
69
|
const dispatch = createEventDispatcher();
|
|
70
|
-
let
|
|
70
|
+
let add_option_msg_is_active = false; // controls active state of <li>{addOptionMsg}</li>
|
|
71
71
|
let window_width;
|
|
72
72
|
let wiggle = false; // controls wiggle animation when user tries to exceed maxSelect
|
|
73
73
|
$: selectedLabels = selected.map(get_label);
|
|
@@ -78,9 +78,7 @@ $: formValue = selectedValues.join(`,`);
|
|
|
78
78
|
$: if (formValue)
|
|
79
79
|
invalid = false; // reset error status whenever component state changes
|
|
80
80
|
// options matching the current search text
|
|
81
|
-
$: matchingOptions = options.filter((op) => filterFunc(op, searchText) &&
|
|
82
|
-
!(op instanceof Object && op.disabled) &&
|
|
83
|
-
!selectedLabels.includes(get_label(op)) // remove already selected options from dropdown list
|
|
81
|
+
$: matchingOptions = options.filter((op) => filterFunc(op, searchText) && !selectedLabels.includes(get_label(op)) // remove already selected options from dropdown list
|
|
84
82
|
);
|
|
85
83
|
// raise if matchingOptions[activeIndex] does not yield a value
|
|
86
84
|
if (activeIndex !== null && !matchingOptions[activeIndex]) {
|
|
@@ -207,34 +205,26 @@ async function handle_keydown(event) {
|
|
|
207
205
|
// on up/down arrow keys: update active option
|
|
208
206
|
else if ([`ArrowDown`, `ArrowUp`].includes(event.key)) {
|
|
209
207
|
// if no option is active yet, but there are matching options, make first one active
|
|
210
|
-
if (
|
|
211
|
-
|
|
208
|
+
if (activeIndex === null && matchingOptions.length > 0) {
|
|
209
|
+
activeIndex = 0;
|
|
212
210
|
return;
|
|
213
211
|
}
|
|
214
212
|
else if (allowUserOptions && searchText.length > 0) {
|
|
215
213
|
// if allowUserOptions is truthy and user entered text but no options match, we make
|
|
216
214
|
// <li>{addUserMsg}</li> active on keydown (or toggle it if already active)
|
|
217
|
-
|
|
215
|
+
add_option_msg_is_active = !add_option_msg_is_active;
|
|
218
216
|
return;
|
|
219
217
|
}
|
|
220
|
-
else if (
|
|
218
|
+
else if (activeIndex === null) {
|
|
221
219
|
// if no option is active and no options are matching, do nothing
|
|
222
220
|
return;
|
|
223
221
|
}
|
|
224
222
|
const increment = event.key === `ArrowUp` ? -1 : 1;
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
else if (newActiveIdx === matchingOptions.length) {
|
|
231
|
-
// wrap around bottom
|
|
232
|
-
activeOption = matchingOptions[0];
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
// default case: select next/previous in item list
|
|
236
|
-
activeOption = matchingOptions[newActiveIdx];
|
|
237
|
-
}
|
|
223
|
+
activeIndex = (activeIndex + increment) % matchingOptions.length;
|
|
224
|
+
// % in JS behaves like remainder operator, not real modulo, so negative numbers stay negative
|
|
225
|
+
// need to do manual wrap around at 0
|
|
226
|
+
if (activeIndex < 0)
|
|
227
|
+
activeIndex = matchingOptions.length - 1;
|
|
238
228
|
if (autoScroll) {
|
|
239
229
|
// TODO This ugly timeout hack is needed to properly scroll element into view when wrapping
|
|
240
230
|
// around start/end of option list. Find a better solution than waiting 10 ms to.
|
|
@@ -419,11 +409,11 @@ function on_click_outside(event) {
|
|
|
419
409
|
on:mousedown|stopPropagation
|
|
420
410
|
on:mouseup|stopPropagation={() => add(searchText)}
|
|
421
411
|
title={addOptionMsg}
|
|
422
|
-
class:active={
|
|
423
|
-
on:mouseover={() => (
|
|
424
|
-
on:focus={() => (
|
|
425
|
-
on:mouseout={() => (
|
|
426
|
-
on:blur={() => (
|
|
412
|
+
class:active={add_option_msg_is_active}
|
|
413
|
+
on:mouseover={() => (add_option_msg_is_active = true)}
|
|
414
|
+
on:focus={() => (add_option_msg_is_active = true)}
|
|
415
|
+
on:mouseout={() => (add_option_msg_is_active = false)}
|
|
416
|
+
on:blur={() => (add_option_msg_is_active = false)}
|
|
427
417
|
aria-selected="false"
|
|
428
418
|
>
|
|
429
419
|
{addOptionMsg}
|
package/MultiSelect.svelte.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ declare const __propDef: {
|
|
|
32
32
|
outerDiv?: HTMLDivElement | null | undefined;
|
|
33
33
|
outerDivClass?: string | undefined;
|
|
34
34
|
parseLabelsAsHtml?: boolean | undefined;
|
|
35
|
-
placeholder?: string | undefined;
|
|
35
|
+
placeholder?: string | null | undefined;
|
|
36
36
|
removeAllTitle?: string | undefined;
|
|
37
37
|
removeBtnTitle?: string | undefined;
|
|
38
38
|
required?: boolean | undefined;
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -76,7 +76,7 @@ import type { Option } from 'svelte-multiselect'
|
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
1. ```ts
|
|
79
|
-
activeIndex:
|
|
79
|
+
activeIndex: number | null = null
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
Zero-based index of currently active option in the array of currently matching options, i.e. if the user typed a search string into the input and only a subset of options match, this index refers to the array position of the matching subset of options
|
|
@@ -88,22 +88,22 @@ import type { Option } from 'svelte-multiselect'
|
|
|
88
88
|
Currently active option, i.e. the one the user currently hovers or navigated to with arrow keys.
|
|
89
89
|
|
|
90
90
|
1. ```ts
|
|
91
|
-
addOptionMsg: string =
|
|
91
|
+
addOptionMsg: string = `Create this option...`
|
|
92
92
|
```
|
|
93
93
|
|
|
94
94
|
Message shown to users after entering text when no options match their query and `allowUserOptions` is truthy.
|
|
95
95
|
|
|
96
96
|
1. ```ts
|
|
97
|
-
allowUserOptions: boolean = false
|
|
97
|
+
allowUserOptions: boolean | 'append' = false
|
|
98
98
|
```
|
|
99
99
|
|
|
100
100
|
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.
|
|
101
101
|
|
|
102
102
|
1. ```ts
|
|
103
|
-
autocomplete: string =
|
|
103
|
+
autocomplete: string = `off`
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
-
Applied to the `<input>`. Specifies if browser is permitted to auto-fill this form field.
|
|
106
|
+
Applied to the `<input>`. Specifies if browser is permitted to auto-fill this form field. Should usually be one of `'on'` or `'off'` but see [MDN docs](https://developer.mozilla.org/docs/Web/HTML/Attributes/autocomplete) for other admissible values.
|
|
107
107
|
|
|
108
108
|
1. ```ts
|
|
109
109
|
autoScroll: boolean = true
|
|
@@ -112,13 +112,13 @@ import type { Option } from 'svelte-multiselect'
|
|
|
112
112
|
`false` disables keeping the active dropdown items in view when going up/down the list of options with arrow keys.
|
|
113
113
|
|
|
114
114
|
1. ```ts
|
|
115
|
-
breakpoint:
|
|
115
|
+
breakpoint: number = 800
|
|
116
116
|
```
|
|
117
117
|
|
|
118
118
|
Screens wider than `breakpoint` in pixels will be considered `'desktop'`, everything narrower as `'mobile'`.
|
|
119
119
|
|
|
120
120
|
1. ```ts
|
|
121
|
-
defaultDisabledTitle: string =
|
|
121
|
+
defaultDisabledTitle: string = `This option is disabled`
|
|
122
122
|
```
|
|
123
123
|
|
|
124
124
|
Title text to display when user hovers over a disabled option. Each option can override this through its `disabledTitle` attribute.
|
|
@@ -130,13 +130,13 @@ import type { Option } from 'svelte-multiselect'
|
|
|
130
130
|
Disable the component. It will still be rendered but users won't be able to interact with it.
|
|
131
131
|
|
|
132
132
|
1. ```ts
|
|
133
|
-
disabledInputTitle: string =
|
|
133
|
+
disabledInputTitle: string = `This input is disabled`
|
|
134
134
|
```
|
|
135
135
|
|
|
136
136
|
Tooltip text to display on hover when the component is in `disabled` state.
|
|
137
137
|
|
|
138
138
|
1. ```ts
|
|
139
|
-
filterFunc
|
|
139
|
+
filterFunc = (op: Option, searchText: string): boolean => {
|
|
140
140
|
if (!searchText) return true
|
|
141
141
|
return `${get_label(op)}`.toLowerCase().includes(searchText.toLowerCase())
|
|
142
142
|
}
|
|
@@ -187,7 +187,7 @@ import type { Option } from 'svelte-multiselect'
|
|
|
187
187
|
Positive integer to limit the number of options users can pick. `null` means no limit.
|
|
188
188
|
|
|
189
189
|
1. ```ts
|
|
190
|
-
maxSelectMsg: (current: number, max: number)
|
|
190
|
+
maxSelectMsg: ((current: number, max: number) => string) | null = null
|
|
191
191
|
```
|
|
192
192
|
|
|
193
193
|
Inform users how many of the maximum allowed options they have already selected. Set `maxSelectMsg={null}` to not show a message. Defaults to `null` when `maxSelect={1}` or `maxSelect={null}`. Else if `maxSelect > 1`, defaults to:
|
|
@@ -203,7 +203,7 @@ import type { Option } from 'svelte-multiselect'
|
|
|
203
203
|
Applied to the `<input>` element. 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>` node.
|
|
204
204
|
|
|
205
205
|
1. ```ts
|
|
206
|
-
noOptionsMsg: string =
|
|
206
|
+
noOptionsMsg: string = `No matching options`
|
|
207
207
|
```
|
|
208
208
|
|
|
209
209
|
What message to show if no options match the user-entered search string.
|
|
@@ -239,13 +239,13 @@ import type { Option } from 'svelte-multiselect'
|
|
|
239
239
|
String shown in the text input when no option is selected.
|
|
240
240
|
|
|
241
241
|
1. ```ts
|
|
242
|
-
removeAllTitle: string =
|
|
242
|
+
removeAllTitle: string = `Remove all`
|
|
243
243
|
```
|
|
244
244
|
|
|
245
245
|
Title text to display when user hovers over remove-all button.
|
|
246
246
|
|
|
247
247
|
1. ```ts
|
|
248
|
-
removeBtnTitle: string =
|
|
248
|
+
removeBtnTitle: string = `Remove`
|
|
249
249
|
```
|
|
250
250
|
|
|
251
251
|
Title text to display when user hovers over button to remove selected option (which defaults to a cross icon).
|
|
@@ -257,13 +257,13 @@ import type { Option } from 'svelte-multiselect'
|
|
|
257
257
|
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.
|
|
258
258
|
|
|
259
259
|
1. ```ts
|
|
260
|
-
searchText: string =
|
|
260
|
+
searchText: string = ``
|
|
261
261
|
```
|
|
262
262
|
|
|
263
263
|
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.
|
|
264
264
|
|
|
265
265
|
1. ```ts
|
|
266
|
-
selected: Option[] = []
|
|
266
|
+
selected: Option[] = options?.filter((op) => op?.preselected) ?? []
|
|
267
267
|
```
|
|
268
268
|
|
|
269
269
|
Array of currently selected options. Can be bound to `bind:selected={[1, 2, 3]}` to control component state externally or passed as prop to set pre-selected options that will already be populated when component mounts before any user interaction.
|
|
@@ -286,6 +286,13 @@ import type { Option } from 'svelte-multiselect'
|
|
|
286
286
|
|
|
287
287
|
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.
|
|
288
288
|
|
|
289
|
+
1. ```ts
|
|
290
|
+
userInputAs: 'string' | 'number' | 'object' =
|
|
291
|
+
options.length > 0 ? (typeof options[0] as 'object' | 'string' | 'number') : 'string'
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
What type new options created from user text input should be. Only relevant if `allowUserOptions=true | 'append'`. If not explicitly set, we default `userInputAs` to the type of the 1st option (if available, else `string`) to keep type homogeneity. E.g. if MultiSelect already contains at least one option and it's an object, new options from user-entered text will take the shape `{label: userText, value: userText}`. Likewise if the 1st existing option is a number of string. If MultiSelect starts out empty but you still want user-created custom options to be objects, pass `userInputAs='object'`.
|
|
295
|
+
|
|
289
296
|
## Slots
|
|
290
297
|
|
|
291
298
|
`MultiSelect.svelte` has 3 named slots:
|
|
@@ -321,34 +328,50 @@ Example:
|
|
|
321
328
|
|
|
322
329
|
`MultiSelect.svelte` dispatches the following events:
|
|
323
330
|
|
|
324
|
-
|
|
331
|
+
1. ```ts
|
|
332
|
+
on:add={(event) => console.log(event.detail.option)}
|
|
333
|
+
```
|
|
325
334
|
|
|
326
|
-
|
|
327
|
-
| ----------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
328
|
-
| `add` | `{ option: Option }` | Triggers when a new option is selected. |
|
|
329
|
-
| `remove` | `{ option: Option }` | Triggers when one selected option provided as `event.detail.option` is removed. |
|
|
330
|
-
| `removeAll` | `options: Option[]` | Triggers when all selected options are removed. The payload `event.detail.options` gives the options that were previously selected. |
|
|
331
|
-
| `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. |
|
|
332
|
-
| `blur` | none | Triggers when the input field looses focus. |
|
|
335
|
+
Triggers when a new option is selected.
|
|
333
336
|
|
|
334
|
-
|
|
337
|
+
1. ```ts
|
|
338
|
+
on:remove={(event) => console.log(event.detail.option)}`
|
|
339
|
+
```
|
|
335
340
|
|
|
336
|
-
|
|
341
|
+
Triggers when one selected option provided as `event.detail.option` is removed.
|
|
337
342
|
|
|
338
|
-
|
|
343
|
+
1. ```ts
|
|
344
|
+
on:removeAll={(event) => console.log(event.detail.options)}`
|
|
345
|
+
```
|
|
339
346
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
347
|
+
Triggers when all selected options are removed. The payload `event.detail.options` gives the options that were previously selected.
|
|
348
|
+
|
|
349
|
+
1. ```ts
|
|
350
|
+
on:change={(event) => console.log(`${event.detail.type}: '${event.detail.option}'`)}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
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.
|
|
354
|
+
|
|
355
|
+
1. ```ts
|
|
356
|
+
on:blur={() => console.log('Multiselect input lost focus')}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Triggers when the input field looses focus.
|
|
360
|
+
|
|
361
|
+
For example, here's how you might annoy your users with an alert every time one or more options are added or removed:
|
|
345
362
|
|
|
346
363
|
```svelte
|
|
347
364
|
<MultiSelect
|
|
348
|
-
on:change={(e) =>
|
|
365
|
+
on:change={(e) => {
|
|
366
|
+
if (e.detail.type === 'add') alert(`You added ${e.detail.option}`)
|
|
367
|
+
if (e.detail.type === 'remove') alert(`You removed ${e.detail.option}`)
|
|
368
|
+
if (e.detail.type === 'removeAll') alert(`You removed ${e.detail.options}`)
|
|
369
|
+
}}
|
|
349
370
|
/>
|
|
350
371
|
```
|
|
351
372
|
|
|
373
|
+
> Note: Depending on the data passed to the component the `options(s)` payload will either be objects or simple strings/numbers.
|
|
374
|
+
|
|
352
375
|
## TypeScript
|
|
353
376
|
|
|
354
377
|
TypeScript users can import the types used for internal type safety:
|
|
@@ -385,7 +408,7 @@ There are 3 ways to style this component. To understand which options do what, i
|
|
|
385
408
|
|
|
386
409
|
### With CSS variables
|
|
387
410
|
|
|
388
|
-
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. See [`app.css`](https://github.com/janosh/svelte-multiselect/blob/main/src/app.css) for how these variables are set
|
|
411
|
+
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. See [`app.css`](https://github.com/janosh/svelte-multiselect/blob/main/src/app.css) for how these variables are set on the demo site of this component.
|
|
389
412
|
|
|
390
413
|
- `div.multiselect`
|
|
391
414
|
- `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.
|
|
@@ -393,9 +416,10 @@ If you only want to make small adjustments, you can pass the following CSS varia
|
|
|
393
416
|
- `padding: var(--sms-padding, 0 3pt)`
|
|
394
417
|
- `background: var(--sms-bg)`
|
|
395
418
|
- `color: var(--sms-text-color)`
|
|
396
|
-
- `min-height: var(--sms-min-height)`
|
|
419
|
+
- `min-height: var(--sms-min-height, 19pt)`
|
|
397
420
|
- `max-width: var(--sms-max-width)`
|
|
398
421
|
- `margin: var(--sms-margin)`
|
|
422
|
+
- `font-size: var(--sms-font-size, inherit)`
|
|
399
423
|
- `div.multiselect.open`
|
|
400
424
|
- `z-index: var(--sms-open-z-index, 4)`: Increase this if needed to ensure the dropdown list is displayed atop all other page elements.
|
|
401
425
|
- `div.multiselect:focus-within`
|
|
@@ -404,10 +428,10 @@ If you only want to make small adjustments, you can pass the following CSS varia
|
|
|
404
428
|
- `background: var(--sms-disabled-bg, lightgray)`: Background when in disabled state.
|
|
405
429
|
- `div.multiselect input::placeholder`
|
|
406
430
|
- `color: var(--sms-placeholder-color)`
|
|
407
|
-
- `
|
|
431
|
+
- `opacity: var(--sms-placeholder-opacity)`
|
|
408
432
|
- `div.multiselect > ul.selected > li`
|
|
409
433
|
- `background: var(--sms-selected-bg, rgba(0, 0, 0, 0.15))`: Background of selected options.
|
|
410
|
-
- `padding: var(--sms-selected-li-padding, 5pt
|
|
434
|
+
- `padding: var(--sms-selected-li-padding, 1pt 5pt)`: Height of selected options.
|
|
411
435
|
- `color: var(--sms-selected-text-color, var(--sms-text-color))`: Text color for selected options.
|
|
412
436
|
- `ul.selected > li button:hover, button.remove-all:hover, button:focus`
|
|
413
437
|
- `color: var(--sms-button-hover-color, lightskyblue)`: Color of the remove-icon buttons for removing all or individual selected options when in `:focus` or `:hover` state.
|
|
@@ -415,7 +439,7 @@ If you only want to make small adjustments, you can pass the following CSS varia
|
|
|
415
439
|
- `background: var(--sms-options-bg, white)`: Background of dropdown list.
|
|
416
440
|
- `max-height: var(--sms-options-max-height, 50vh)`: Maximum height of options dropdown.
|
|
417
441
|
- `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/docs/Web/CSS/overscroll-behavior).
|
|
418
|
-
- `box-shadow: var(--sms-options-shadow, 0 0 14pt -8pt black)
|
|
442
|
+
- `box-shadow: var(--sms-options-shadow, 0 0 14pt -8pt black)`: Box shadow of dropdown list.
|
|
419
443
|
- `div.multiselect > ul.options > li`
|
|
420
444
|
- `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.
|
|
421
445
|
- `div.multiselect > ul.options > li.selected`
|