svelte-multiselect 4.0.2 → 4.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 +21 -11
- package/MultiSelect.svelte.d.ts +2 -0
- package/package.json +12 -12
- package/readme.md +32 -17
package/MultiSelect.svelte
CHANGED
|
@@ -14,6 +14,7 @@ export let disabled = false;
|
|
|
14
14
|
export let disabledTitle = `This field is disabled`;
|
|
15
15
|
export let options;
|
|
16
16
|
export let input = null;
|
|
17
|
+
export let outerDiv = null;
|
|
17
18
|
export let placeholder = undefined;
|
|
18
19
|
export let id = undefined;
|
|
19
20
|
export let name = id;
|
|
@@ -30,6 +31,7 @@ export let liSelectedClass = ``;
|
|
|
30
31
|
export let ulOptionsClass = ``;
|
|
31
32
|
export let liOptionClass = ``;
|
|
32
33
|
export let liActiveOptionClass = ``;
|
|
34
|
+
export let inputClass = ``;
|
|
33
35
|
export let removeBtnTitle = `Remove`;
|
|
34
36
|
export let removeAllTitle = `Remove all`;
|
|
35
37
|
export let defaultDisabledTitle = `This option is disabled`;
|
|
@@ -136,8 +138,8 @@ function setOptionsVisible(show) {
|
|
|
136
138
|
}
|
|
137
139
|
// handle all keyboard events this component receives
|
|
138
140
|
async function handleKeydown(event) {
|
|
139
|
-
// on escape: dismiss options dropdown and reset search text
|
|
140
|
-
if (event.key === `Escape`) {
|
|
141
|
+
// on escape or tab out of input: dismiss options dropdown and reset search text
|
|
142
|
+
if (event.key === `Escape` || event.key === `Tab`) {
|
|
141
143
|
setOptionsVisible(false);
|
|
142
144
|
searchText = ``;
|
|
143
145
|
}
|
|
@@ -208,9 +210,18 @@ const handleEnterAndSpaceKeys = (handler) => (event) => {
|
|
|
208
210
|
};
|
|
209
211
|
</script>
|
|
210
212
|
|
|
213
|
+
<svelte:window
|
|
214
|
+
on:click={(event) => {
|
|
215
|
+
if (outerDiv && !outerDiv.contains(event.target)) {
|
|
216
|
+
setOptionsVisible(false)
|
|
217
|
+
}
|
|
218
|
+
}}
|
|
219
|
+
/>
|
|
220
|
+
|
|
211
221
|
<!-- z-index: 2 when showOptions is true ensures the ul.selected of one <MultiSelect />
|
|
212
222
|
display above those of another following shortly after it -->
|
|
213
223
|
<div
|
|
224
|
+
bind:this={outerDiv}
|
|
214
225
|
class:disabled
|
|
215
226
|
class:single={maxSelect === 1}
|
|
216
227
|
class:open={showOptions}
|
|
@@ -219,16 +230,15 @@ display above those of another following shortly after it -->
|
|
|
219
230
|
class:invalid
|
|
220
231
|
class="multiselect {outerDivClass}"
|
|
221
232
|
on:mouseup|stopPropagation={() => setOptionsVisible(true)}
|
|
222
|
-
on:focusout={() => setOptionsVisible(false)}
|
|
223
233
|
title={disabled ? disabledTitle : null}
|
|
224
234
|
aria-disabled={disabled ? `true` : null}
|
|
225
235
|
>
|
|
226
|
-
<!-- invisible input, used only to prevent form submission if required=true and no options selected -->
|
|
227
236
|
<input
|
|
228
237
|
{required}
|
|
229
238
|
bind:value={formValue}
|
|
230
239
|
tabindex="-1"
|
|
231
240
|
aria-hidden="true"
|
|
241
|
+
aria-label="ignore this, used only to prevent form submission if select is required but empty"
|
|
232
242
|
class="form-control"
|
|
233
243
|
on:invalid={() => (invalid = true)}
|
|
234
244
|
/>
|
|
@@ -253,13 +263,13 @@ display above those of another following shortly after it -->
|
|
|
253
263
|
{/each}
|
|
254
264
|
<li style="display: contents;">
|
|
255
265
|
<input
|
|
266
|
+
class={inputClass}
|
|
256
267
|
bind:this={input}
|
|
257
268
|
{autocomplete}
|
|
258
269
|
bind:value={searchText}
|
|
259
270
|
on:mouseup|self|stopPropagation={() => setOptionsVisible(true)}
|
|
260
271
|
on:keydown={handleKeydown}
|
|
261
272
|
on:focus={() => setOptionsVisible(true)}
|
|
262
|
-
on:blur={() => setOptionsVisible(false)}
|
|
263
273
|
{id}
|
|
264
274
|
{name}
|
|
265
275
|
{disabled}
|
|
@@ -350,13 +360,14 @@ display above those of another following shortly after it -->
|
|
|
350
360
|
align-items: center;
|
|
351
361
|
display: flex;
|
|
352
362
|
cursor: text;
|
|
353
|
-
padding: 0 3pt;
|
|
354
363
|
border: var(--sms-border, 1pt solid lightgray);
|
|
355
364
|
border-radius: var(--sms-border-radius, 3pt);
|
|
356
|
-
background: var(--sms-
|
|
357
|
-
|
|
365
|
+
background: var(--sms-bg);
|
|
366
|
+
max-width: var(--sms-max-width);
|
|
367
|
+
padding: var(--sms-padding, 0 3pt);
|
|
358
368
|
color: var(--sms-text-color);
|
|
359
369
|
font-size: var(--sms-font-size, inherit);
|
|
370
|
+
min-height: var(--sms-min-height, 19pt);
|
|
360
371
|
}
|
|
361
372
|
:where(div.multiselect.open) {
|
|
362
373
|
z-index: var(--sms-open-z-index, 4);
|
|
@@ -378,15 +389,14 @@ display above those of another following shortly after it -->
|
|
|
378
389
|
}
|
|
379
390
|
:where(div.multiselect > ul.selected > li) {
|
|
380
391
|
align-items: center;
|
|
381
|
-
border-radius:
|
|
392
|
+
border-radius: 3pt;
|
|
382
393
|
display: flex;
|
|
383
394
|
margin: 2pt;
|
|
384
395
|
line-height: normal;
|
|
385
|
-
padding: 1pt 5pt;
|
|
386
396
|
transition: 0.3s;
|
|
387
397
|
white-space: nowrap;
|
|
388
398
|
background: var(--sms-selected-bg, rgba(0, 0, 0, 0.15));
|
|
389
|
-
|
|
399
|
+
padding: var(--sms-selected-li-padding, 1pt 5pt);
|
|
390
400
|
color: var(--sms-selected-text-color, var(--sms-text-color));
|
|
391
401
|
}
|
|
392
402
|
:where(div.multiselect button) {
|
package/MultiSelect.svelte.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ declare const __propDef: {
|
|
|
13
13
|
disabledTitle?: string | undefined;
|
|
14
14
|
options: ProtoOption[];
|
|
15
15
|
input?: HTMLInputElement | null | undefined;
|
|
16
|
+
outerDiv?: HTMLDivElement | null | undefined;
|
|
16
17
|
placeholder?: string | undefined;
|
|
17
18
|
id?: string | undefined;
|
|
18
19
|
name?: string | undefined;
|
|
@@ -25,6 +26,7 @@ declare const __propDef: {
|
|
|
25
26
|
ulOptionsClass?: string | undefined;
|
|
26
27
|
liOptionClass?: string | undefined;
|
|
27
28
|
liActiveOptionClass?: string | undefined;
|
|
29
|
+
inputClass?: string | undefined;
|
|
28
30
|
removeBtnTitle?: string | undefined;
|
|
29
31
|
removeAllTitle?: string | undefined;
|
|
30
32
|
defaultDisabledTitle?: string | undefined;
|
package/package.json
CHANGED
|
@@ -5,37 +5,37 @@
|
|
|
5
5
|
"homepage": "https://svelte-multiselect.netlify.app",
|
|
6
6
|
"repository": "https://github.com/janosh/svelte-multiselect",
|
|
7
7
|
"license": "MIT",
|
|
8
|
-
"version": "4.0.
|
|
8
|
+
"version": "4.0.3",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"svelte": "index.js",
|
|
11
11
|
"bugs": "https://github.com/janosh/svelte-multiselect/issues",
|
|
12
12
|
"devDependencies": {
|
|
13
13
|
"@sveltejs/adapter-static": "^1.0.0-next.29",
|
|
14
|
-
"@sveltejs/kit": "^1.0.0-next.
|
|
15
|
-
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.
|
|
16
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
17
|
-
"@typescript-eslint/parser": "^5.
|
|
18
|
-
"@vitest/ui": "^0.
|
|
14
|
+
"@sveltejs/kit": "^1.0.0-next.302",
|
|
15
|
+
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.40",
|
|
16
|
+
"@typescript-eslint/eslint-plugin": "^5.16.0",
|
|
17
|
+
"@typescript-eslint/parser": "^5.16.0",
|
|
18
|
+
"@vitest/ui": "^0.7.9",
|
|
19
19
|
"eslint": "^8.11.0",
|
|
20
20
|
"eslint-plugin-svelte3": "^3.4.1",
|
|
21
21
|
"hastscript": "^7.0.2",
|
|
22
22
|
"jsdom": "^19.0.0",
|
|
23
23
|
"mdsvex": "^0.10.5",
|
|
24
|
-
"playwright": "^1.
|
|
25
|
-
"prettier": "^2.
|
|
24
|
+
"playwright": "^1.20.0",
|
|
25
|
+
"prettier": "^2.6.0",
|
|
26
26
|
"prettier-plugin-svelte": "^2.6.0",
|
|
27
27
|
"rehype-autolink-headings": "^6.1.1",
|
|
28
28
|
"rehype-slug": "^5.0.1",
|
|
29
29
|
"svelte": "^3.46.4",
|
|
30
|
-
"svelte-check": "^2.4.
|
|
30
|
+
"svelte-check": "^2.4.6",
|
|
31
31
|
"svelte-github-corner": "^0.1.0",
|
|
32
32
|
"svelte-preprocess": "^4.10.4",
|
|
33
|
-
"svelte-toc": "^0.2.
|
|
34
|
-
"svelte2tsx": "^0.5.
|
|
33
|
+
"svelte-toc": "^0.2.8",
|
|
34
|
+
"svelte2tsx": "^0.5.6",
|
|
35
35
|
"tslib": "^2.3.1",
|
|
36
36
|
"typescript": "^4.6.2",
|
|
37
37
|
"vite": "^2.8.6",
|
|
38
|
-
"vitest": "^0.
|
|
38
|
+
"vitest": "^0.7.9"
|
|
39
39
|
},
|
|
40
40
|
"keywords": [
|
|
41
41
|
"svelte",
|
package/readme.md
CHANGED
|
@@ -13,7 +13,13 @@
|
|
|
13
13
|
|
|
14
14
|
</h4>
|
|
15
15
|
|
|
16
|
-
**Keyboard-friendly, zero-dependency multi-select Svelte component.**
|
|
16
|
+
**Keyboard-friendly, zero-dependency multi-select Svelte component.**
|
|
17
|
+
<strong class="hide-in-docs">
|
|
18
|
+
<a href="https://svelte-multiselect.netlify.app">Docs</a> •
|
|
19
|
+
</strong>
|
|
20
|
+
<strong>
|
|
21
|
+
<a href="https://svelte.dev/repl/a5a14b8f15d64cb083b567292480db05">REPL</a>
|
|
22
|
+
</strong>
|
|
17
23
|
|
|
18
24
|
<slot name="examples" />
|
|
19
25
|
|
|
@@ -48,6 +54,8 @@
|
|
|
48
54
|
|
|
49
55
|
- v4.0.1 renamed the `readonly` prop to `disabled` which now prevents all form or user interaction with this component including opening the dropdown list which was still possible before. See [#45](https://github.com/janosh/svelte-multiselect/issues/45) for details. The associated CSS class applied to the outer `div` was likewise renamed to `div.multiselect.{readonly=>disabled}`.
|
|
50
56
|
|
|
57
|
+
- v4.0.3 CSS variables starting with `--sms-input-<attr>` were renamed to just `--sms-<attr>`. E.g. `--sms-input-min-height` is now `--sms-min-height`.
|
|
58
|
+
|
|
51
59
|
## Installation
|
|
52
60
|
|
|
53
61
|
```sh
|
|
@@ -93,7 +101,8 @@ Full list of props/bindable variables for this component:
|
|
|
93
101
|
| `disabled` | `false` | Disable the component. It will still be rendered but users won't be able to interact with it. |
|
|
94
102
|
| `disabledTitle` | `This field is disabled` | Tooltip text to display on hover when the component is in `disabled` state. |
|
|
95
103
|
| `placeholder` | `undefined` | String shown in the text input when no option is selected. |
|
|
96
|
-
| `input` | `
|
|
104
|
+
| `input` | `null` | Handle to the `<input>` DOM node. Only available after component mounts (`null` before then). |
|
|
105
|
+
| `outerDiv` | `null` | Handle to outer `<div class="multiselect">` that wraps the whole component. Only available after component mounts (`null` before then). |
|
|
97
106
|
| `id` | `undefined` | Applied to the `<input>` element for associating HTML form `<label>`s with this component for accessibility. Also, clicking a `<label>` with same `for` attribute as `id` will focus this component. |
|
|
98
107
|
| `name` | `id` | Applied to the `<input>` element. If not provided, will be set to the value of `id`. Sets the key of this field in a submitted form data object. Not useful at the moment since the value is stored in Svelte state, not on the `<input>`. |
|
|
99
108
|
| `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. |
|
|
@@ -137,15 +146,17 @@ Full list of props/bindable variables for this component:
|
|
|
137
146
|
Example:
|
|
138
147
|
|
|
139
148
|
```svelte
|
|
140
|
-
<MultiSelect options={[`
|
|
149
|
+
<MultiSelect options={[`Red`, `Green`, `Blue`, `Yellow`, `Purple`]}>
|
|
141
150
|
<span let:idx let:option slot="option">
|
|
142
|
-
{idx + 1}
|
|
143
|
-
{option.label
|
|
151
|
+
{idx + 1}
|
|
152
|
+
{option.label}
|
|
153
|
+
<span style:background={option.label} style=" width: 1em; height: 1em;" />
|
|
144
154
|
</span>
|
|
145
155
|
|
|
146
156
|
<span let:idx let:option slot="selected">
|
|
147
|
-
|
|
157
|
+
{idx + 1}
|
|
148
158
|
{option.label}
|
|
159
|
+
<span style:background={option.label} style=" width: 1em; height: 1em;" />
|
|
149
160
|
</span>
|
|
150
161
|
|
|
151
162
|
<CustomSpinner slot="spinner">
|
|
@@ -156,13 +167,13 @@ Example:
|
|
|
156
167
|
|
|
157
168
|
`MultiSelect.svelte` dispatches the following events:
|
|
158
169
|
|
|
159
|
-
| name | detail
|
|
160
|
-
| ----------- |
|
|
161
|
-
| `add` | `{ option: Option }`
|
|
162
|
-
| `remove` | `{ option: Option }`
|
|
163
|
-
| `removeAll` | `options: Option[]`
|
|
164
|
-
| `change` | `
|
|
165
|
-
| `blur` | none
|
|
170
|
+
| name | detail | description |
|
|
171
|
+
| ----------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
172
|
+
| `add` | `{ option: Option }` | Triggers when a new option is selected. |
|
|
173
|
+
| `remove` | `{ option: Option }` | Triggers when one selected option provided as `event.detail.option` is removed. |
|
|
174
|
+
| `removeAll` | `options: Option[]` | Triggers when all selected options are removed. The payload `event.detail.options` gives the options that were previously selected. |
|
|
175
|
+
| `change` | `type: 'add' \| 'remove' \| 'removeAll'` | Triggers when a option is either added or removed, or all options are removed at once. Payload will be a single or an aarray of `Option` objects, respectively. |
|
|
176
|
+
| `blur` | none | Triggers when the input field looses focus. |
|
|
166
177
|
|
|
167
178
|
### Examples
|
|
168
179
|
|
|
@@ -221,19 +232,22 @@ If you only want to make small adjustments, you can pass the following CSS varia
|
|
|
221
232
|
- `div.multiselect`
|
|
222
233
|
- `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.
|
|
223
234
|
- `border-radius: var(--sms-border-radius, 3pt)`
|
|
224
|
-
- `
|
|
225
|
-
- `
|
|
235
|
+
- `padding: var(--sms-padding, 0 3pt)`
|
|
236
|
+
- `background: var(--sms-bg)`
|
|
226
237
|
- `color: var(--sms-text-color)`
|
|
227
|
-
- `
|
|
238
|
+
- `min-height: var(--sms-min-height)`
|
|
239
|
+
- `max-width: var(--sms-max-width)`
|
|
228
240
|
- `div.multiselect.open`
|
|
229
241
|
- `z-index: var(--sms-open-z-index, 4)`: Increase this if needed to ensure the dropdown list is displayed atop all other page elements.
|
|
230
242
|
- `div.multiselect:focus-within`
|
|
231
243
|
- `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`.
|
|
232
244
|
- `div.multiselect.disabled`
|
|
233
245
|
- `background: var(--sms-disabled-bg, lightgray)`: Background when in disabled state.
|
|
246
|
+
- `div.multiselect input::placeholder`
|
|
247
|
+
- `color: var(--sms-placeholder-color)`
|
|
234
248
|
- `div.multiselect > ul.selected > li`
|
|
235
249
|
- `background: var(--sms-selected-bg, rgba(0, 0, 0, 0.15))`: Background of selected options.
|
|
236
|
-
- `
|
|
250
|
+
- `padding: var(--sms-selected-li-padding, 5pt 1pt)`: Height of selected options.
|
|
237
251
|
- `color: var(--sms-selected-text-color, var(--sms-text-color))`: Text color for selected options.
|
|
238
252
|
- `ul.selected > li button:hover, button.remove-all:hover, button:focus`
|
|
239
253
|
- `color: var(--sms-button-hover-color, lightskyblue)`: Color of the cross-icon buttons for removing all or individual selected options when in `:focus` or `:hover` state.
|
|
@@ -274,6 +288,7 @@ This simplified version of the DOM structure of this component shows where these
|
|
|
274
288
|
|
|
275
289
|
```svelte
|
|
276
290
|
<div class="multiselect {outerDivClass}">
|
|
291
|
+
<input class={inputClass} />
|
|
277
292
|
<ul class="selected {ulSelectedClass}">
|
|
278
293
|
<li class={liSelectedClass}>Selected 1</li>
|
|
279
294
|
<li class={liSelectedClass}>Selected 2</li>
|