svelte-multiselect 11.4.0 โ†’ 11.5.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.
package/readme.md CHANGED
@@ -34,9 +34,9 @@
34
34
 
35
35
  ## ๐Ÿงช   Coverage
36
36
 
37
- | Statements | Branches | Lines |
38
- | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
39
- | ![Statements](https://img.shields.io/badge/statements-92.81%25-brightgreen.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-81.57%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-92.81%25-brightgreen.svg?style=flat) |
37
+ | Statements | Branches | Lines |
38
+ | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
39
+ | ![Statements](https://img.shields.io/badge/statements-85.65%25-yellow.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-82.47%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-85.51%25-yellow.svg?style=flat) |
40
40
 
41
41
  ## ๐Ÿ”จ   Installation
42
42
 
@@ -334,6 +334,85 @@ These are the core props you'll use in most cases:
334
334
 
335
335
  Configuration for portal rendering. When `active: true`, the dropdown is rendered at document.body level with fixed positioning. Useful for avoiding z-index and overflow issues.
336
336
 
337
+ ### Grouping Props
338
+
339
+ Group related options together with visual headers. Add a `group` key to your option objects:
340
+
341
+ ```svelte
342
+ <script>
343
+ const options = [
344
+ { label: `JavaScript`, group: `Frontend` },
345
+ { label: `TypeScript`, group: `Frontend` },
346
+ { label: `Python`, group: `Backend` },
347
+ { label: `Go`, group: `Backend` },
348
+ ]
349
+ </script>
350
+
351
+ <MultiSelect {options} collapsibleGroups groupSelectAll />
352
+ ```
353
+
354
+ See the [grouping demo](https://janosh.github.io/svelte-multiselect/grouping) for live examples.
355
+
356
+ 1. ```ts
357
+ collapsibleGroups: boolean = false
358
+ ```
359
+
360
+ Enable click-to-collapse groups. When `true`, users can click group headers to hide/show options in that group.
361
+
362
+ 1. ```ts
363
+ collapsedGroups: Set<string> = new Set()
364
+ ```
365
+
366
+ Bindable set of collapsed group names. Use `bind:collapsedGroups` to control which groups are collapsed externally or to persist collapse state.
367
+
368
+ 1. ```ts
369
+ groupSelectAll: boolean = false
370
+ ```
371
+
372
+ Add a "Select all" button to each group header, allowing users to select all options in a specific group at once.
373
+
374
+ 1. ```ts
375
+ ungroupedPosition: 'first' | 'last' = 'first'
376
+ ```
377
+
378
+ Where to render options that don't have a `group` key. `'first'` places them at the top, `'last'` at the bottom.
379
+
380
+ 1. ```ts
381
+ groupSortOrder: 'none' | 'asc' | 'desc' | ((a: string, b: string) => number) = 'none'
382
+ ```
383
+
384
+ Sort groups alphabetically (`'asc'` or `'desc'`) or with a custom comparator function. Default `'none'` preserves order of first occurrence.
385
+
386
+ 1. ```ts
387
+ searchExpandsCollapsedGroups: boolean = false
388
+ ```
389
+
390
+ When `true`, collapsed groups automatically expand when the search query matches options within them.
391
+
392
+ 1. ```ts
393
+ liGroupHeaderClass: string = ''
394
+ ```
395
+
396
+ CSS class applied to group header `<li>` elements.
397
+
398
+ 1. ```ts
399
+ liGroupHeaderStyle: string | null = null
400
+ ```
401
+
402
+ Inline style for group header elements.
403
+
404
+ 1. ```ts
405
+ groupHeader: Snippet<[{ group: string; options: T[]; collapsed: boolean }]>
406
+ ```
407
+
408
+ Custom snippet for rendering group headers. Receives the group name, array of options in that group, and whether the group is collapsed.
409
+
410
+ 1. ```ts
411
+ ongroupToggle: (data: { group: string; collapsed: boolean }) => void
412
+ ```
413
+
414
+ Callback fired when a group is collapsed or expanded. Receives the group name and its new collapsed state.
415
+
337
416
  ### Form & Accessibility Props
338
417
 
339
418
  1. ```ts
@@ -631,15 +710,16 @@ These reflect internal component state:
631
710
 
632
711
  1. `#snippet option({ option, idx })`: Customize rendering of dropdown options. Receives as props an `option` and the zero-indexed position (`idx`) it has in the dropdown.
633
712
  1. `#snippet selectedItem({ option, idx })`: Customize rendering of selected items. Receives as props an `option` and the zero-indexed position (`idx`) it has in the list of selected items.
713
+ 1. `#snippet children({ option, idx })`: Convenience snippet that applies to both dropdown options AND selected items. Use this when you want the same custom rendering for both. Takes precedence if `option` or `selectedItem` are not provided.
634
714
  1. `#snippet spinner()`: Custom spinner component to display when in `loading` state. Receives no props.
635
715
  1. `#snippet disabledIcon()`: Custom icon to display inside the input when in `disabled` state. Receives no props. Use an empty `{#snippet disabledIcon()}{/snippet}` to remove the default disabled icon.
636
- 1. `#snippet expandIcon()`: Allows setting a custom icon to indicate to users that the Multiselect text input field is expandable into a dropdown list. Receives prop `open: boolean` which is true if the Multiselect dropdown is visible and false if it's hidden.
716
+ 1. `#snippet expandIcon({ open })`: Allows setting a custom icon to indicate to users that the Multiselect text input field is expandable into a dropdown list. `open` is `true` if the dropdown is visible and `false` if hidden.
637
717
  1. `#snippet removeIcon()`: Custom icon to display as remove button. Will be used both by buttons to remove individual selected options and the 'remove all' button that clears all options at once. Receives no props.
638
718
  1. `#snippet userMsg({ searchText, msgType, msg })`: Displayed like a dropdown item when the list is empty and user is allowed to create custom options based on text input (or if the user's text input clashes with an existing option). Receives props:
639
719
  - `searchText`: The text user typed into search input.
640
720
  - `msgType: false | 'create' | 'dupe' | 'no-match'`: `'dupe'` means user input is a duplicate of an existing option. `'create'` means user is allowed to convert their input into a new option not previously in the dropdown. `'no-match'` means user input doesn't match any dropdown items and users are not allowed to create new options. `false` means none of the above.
641
721
  - `msg`: Will be `duplicateOptionMsg` or `createOptionMsg` (see [props](#๐Ÿ”ฃ-props)) based on whether user input is a duplicate or can be created as new option. Note this snippet replaces the default UI for displaying these messages so the snippet needs to render them instead (unless purposely not showing a message).
642
- 1. `snippet='after-input'`: Placed after the search input. For arbitrary content like icons or temporary messages. Receives props `selected: Option[]`, `disabled: boolean`, `invalid: boolean`, `id: string | null`, `placeholder: string`, `open: boolean`, `required: boolean`. Can serve as a more dynamic, more customizable alternative to the `placeholder` prop.
722
+ 1. `#snippet afterInput({ selected, disabled, invalid, id, placeholder, open, required })`: Placed after the search input. For arbitrary content like icons or temporary messages. Can serve as a more dynamic, more customizable alternative to the `placeholder` prop.
643
723
 
644
724
  Example using several snippets:
645
725
 
@@ -666,65 +746,79 @@ Example using several snippets:
666
746
 
667
747
  ## ๐ŸŽฌ &thinsp; Events
668
748
 
669
- `MultiSelect.svelte` dispatches the following events:
749
+ `MultiSelect.svelte` provides the following event callback props:
750
+
751
+ 1. ```ts
752
+ onadd={({ option }) => console.log(option)}
753
+ ```
754
+
755
+ Triggers when a new option is selected. The newly selected option is provided as `option`.
670
756
 
671
757
  1. ```ts
672
- onadd={(event) => console.log(event.detail.option)}
758
+ oncreate={({ option }) => console.log(option)}
673
759
  ```
674
760
 
675
- Triggers when a new option is selected. The newly selected option is provided as `event.detail.option`.
761
+ Triggers when a user creates a new option (when `allowUserOptions` is enabled). The created option is provided as `option`.
676
762
 
677
763
  1. ```ts
678
- oncreate={(event) => console.log(event.detail.option)}
764
+ onremove={({ option }) => console.log(option)}
679
765
  ```
680
766
 
681
- Triggers when a user creates a new option (when `allowUserOptions` is enabled). The created option is provided as `event.detail.option`.
767
+ Triggers when a single selected option is removed. The removed option is provided as `option`.
682
768
 
683
769
  1. ```ts
684
- onremove={(event) => console.log(event.detail.option)}`
770
+ onremoveAll={({ options }) => console.log(options)}
685
771
  ```
686
772
 
687
- Triggers when a single selected option is removed. The removed option is provided as `event.detail.option`.
773
+ Triggers when all selected options are removed. The `options` payload gives the options that were removed (might not be all if `minSelect` is set).
688
774
 
689
775
  1. ```ts
690
- onremoveAll={(event) => console.log(event.detail.options)}`
776
+ onselectAll={({ options }) => console.log(options)}
691
777
  ```
692
778
 
693
- Triggers when all selected options are removed. The payload `event.detail.options` gives the options that were removed (might not be all if `minSelect` is set).
779
+ Triggers when the "Select All" option is clicked (requires `selectAllOption` to be enabled). The `options` payload contains the options that were added.
694
780
 
695
781
  1. ```ts
696
- onchange={(event) => console.log(`${event.detail.type}: '${event.detail.option}'`)}
782
+ onreorder={({ options }) => console.log(options)}
697
783
  ```
698
784
 
699
- 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.
785
+ Triggers when selected options are reordered via drag-and-drop (enabled by default when `sortSelected` is false). The `options` payload is the newly ordered array of selected options.
700
786
 
701
787
  1. ```ts
702
- onopen={(event) => console.log(`Multiselect dropdown was opened by ${event}`)}
788
+ onchange={({ type, option, options }) => console.log(type, option ?? options)}
703
789
  ```
704
790
 
705
- Triggers when the dropdown list of options appears. Event is the DOM's `FocusEvent`,`KeyboardEvent` or `ClickEvent` that initiated this Svelte `dispatch` event.
791
+ Triggers when an option is either added (selected) or removed from selected, all selected options are removed at once, or selected options are reordered via drag-and-drop. `type` is one of `'add' | 'remove' | 'removeAll' | 'selectAll' | 'reorder'` and payload will be `option: Option` or `options: Option[]`, respectively.
706
792
 
707
793
  1. ```ts
708
- onclose={(event) => console.log(`Multiselect dropdown was closed by ${event}`)}
794
+ onopen={({ event }) => console.log(`Dropdown opened by`, event)}
709
795
  ```
710
796
 
711
- Triggers when the dropdown list of options disappears. Event is the DOM's `FocusEvent`, `KeyboardEvent` or `ClickEvent` that initiated this Svelte `dispatch` event.
797
+ Triggers when the dropdown list of options appears. `event` is the DOM's `FocusEvent`, `KeyboardEvent` or `ClickEvent` that triggered the open.
798
+
799
+ 1. ```ts
800
+ onclose={({ event }) => console.log(`Dropdown closed by`, event)}
801
+ ```
802
+
803
+ Triggers when the dropdown list of options disappears. `event` is the DOM's `FocusEvent`, `KeyboardEvent` or `ClickEvent` that triggered the close.
712
804
 
713
805
  For example, here's how you might annoy your users with an alert every time one or more options are added or removed:
714
806
 
715
807
  ```svelte
716
808
  <MultiSelect
717
- onchange={(event) => {
718
- if (event.detail.type === 'add') alert(`You added ${event.detail.option}`)
719
- if (event.detail.type === 'remove') alert(`You removed ${event.detail.option}`)
720
- if (event.detail.type === 'removeAll') alert(`You removed ${event.detail.options}`)
809
+ onchange={({ type, option, options }) => {
810
+ if (type === 'add') alert(`You added ${option}`)
811
+ if (type === 'remove') alert(`You removed ${option}`)
812
+ if (type === 'removeAll') alert(`You removed ${options}`)
813
+ if (type === 'selectAll') alert(`You selected all: ${options}`)
814
+ if (type === 'reorder') alert(`New order: ${options}`)
721
815
  }}
722
816
  />
723
817
  ```
724
818
 
725
- > Note: Depending on the data passed to the component the `options(s)` payload will either be objects or simple strings/numbers.
819
+ > Note: Depending on the data passed to the component the `option(s)` payload will either be objects or simple strings/numbers.
726
820
 
727
- The above list of events are [Svelte `dispatch` events](https://svelte.dev/tutorial/svelte/component-events). This component also forwards many DOM events from the `<input>` node: `blur`, `change`, `click`, `keydown`, `keyup`, `mousedown`, `mouseenter`, `mouseleave`, `touchcancel`, `touchend`, `touchmove`, `touchstart`. Registering listeners for these events works the same:
821
+ This component also forwards many DOM events from the `<input>` node: `blur`, `change`, `click`, `keydown`, `keyup`, `mousedown`, `mouseenter`, `mouseleave`, `touchcancel`, `touchend`, `touchmove`, `touchstart`. Registering listeners for these events works the same:
728
822
 
729
823
  ```svelte
730
824
  <MultiSelect
@@ -795,10 +889,10 @@ Minimal example that changes the background color of the options dropdown:
795
889
  ```
796
890
 
797
891
  - `div.multiselect`
798
- - `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.
892
+ - `border: var(--sms-border, 1pt solid light-dark(lightgray, #555))`: Change this to e.g. to `1px solid red` to indicate this form field is in an invalid state.
799
893
  - `border-radius: var(--sms-border-radius, 3pt)`
800
894
  - `padding: var(--sms-padding, 0 3pt)`
801
- - `background: var(--sms-bg)`
895
+ - `background: var(--sms-bg, light-dark(white, #1a1a1a))`
802
896
  - `color: var(--sms-text-color)`
803
897
  - `min-height: var(--sms-min-height, 22pt)`
804
898
  - `width: var(--sms-width)`
@@ -810,23 +904,23 @@ Minimal example that changes the background color of the options dropdown:
810
904
  - `div.multiselect:focus-within`
811
905
  - `border: var(--sms-focus-border, 1pt solid var(--sms-active-color, cornflowerblue))`: Border when component has focus. Defaults to `--sms-active-color` which in turn defaults to `cornflowerblue`.
812
906
  - `div.multiselect.disabled`
813
- - `background: var(--sms-disabled-bg, lightgray)`: Background when in disabled state.
907
+ - `background: var(--sms-disabled-bg, light-dark(lightgray, #444))`: Background when in disabled state.
814
908
  - `div.multiselect input::placeholder`
815
909
  - `color: var(--sms-placeholder-color)`
816
910
  - `opacity: var(--sms-placeholder-opacity)`
817
911
  - `div.multiselect > ul.selected > li`
818
- - `background: var(--sms-selected-bg, rgba(0, 0, 0, 0.15))`: Background of selected options.
912
+ - `background: var(--sms-selected-bg, light-dark(rgba(0, 0, 0, 0.15), rgba(255, 255, 255, 0.15)))`: Background of selected options.
819
913
  - `padding: var(--sms-selected-li-padding, 1pt 5pt)`: Height of selected options.
820
914
  - `color: var(--sms-selected-text-color, var(--sms-text-color))`: Text color for selected options.
821
915
  - `ul.selected > li button:hover, button.remove-all:hover, button:focus`
822
- - `color: var(--sms-remove-btn-hover-color, lightskyblue)`: Color of the remove-icon buttons for removing all or individual selected options when in `:focus` or `:hover` state.
823
- - `background: var(--sms-remove-btn-hover-bg, rgba(0, 0, 0, 0.2))`: Background for hovered remove buttons.
916
+ - `color: var(--sms-remove-btn-hover-color, light-dark(#0088cc, lightskyblue))`: Color of the remove-icon buttons for removing all or individual selected options when in `:focus` or `:hover` state.
917
+ - `background: var(--sms-remove-btn-hover-bg, light-dark(rgba(0, 0, 0, 0.2), rgba(255, 255, 255, 0.2)))`: Background for hovered remove buttons.
824
918
  - `div.multiselect > ul.options`
825
- - `background: var(--sms-options-bg, white)`: Background of dropdown list.
919
+ - `background: var(--sms-options-bg, light-dark(#fafafa, #1a1a1a))`: Background of dropdown list.
826
920
  - `max-height: var(--sms-options-max-height, 50vh)`: Maximum height of options dropdown.
827
921
  - `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).
828
922
  - `z-index: var(--sms-options-z-index, 3)`: Z-index for the dropdown options list.
829
- - `box-shadow: var(--sms-options-shadow, 0 0 14pt -8pt black)`: Box shadow of dropdown list.
923
+ - `box-shadow: var(--sms-options-shadow, light-dark(0 0 14pt -8pt black, 0 0 14pt -4pt rgba(0, 0, 0, 0.8)))`: Box shadow of dropdown list.
830
924
  - `border: var(--sms-options-border)`
831
925
  - `border-width: var(--sms-options-border-width)`
832
926
  - `border-radius: var(--sms-options-border-radius, 1ex)`
@@ -835,13 +929,13 @@ Minimal example that changes the background color of the options dropdown:
835
929
  - `div.multiselect > ul.options > li`
836
930
  - `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.
837
931
  - `div.multiselect > ul.options > li.selected`
838
- - `background: var(--sms-li-selected-bg)`: Background of selected list items in options pane.
839
- - `color: var(--sms-li-selected-color)`: Text color of selected list items in options pane.
932
+ - `background: var(--sms-li-selected-plain-bg, light-dark(rgba(0, 123, 255, 0.1), rgba(100, 180, 255, 0.2)))`: Background of selected list items in options pane.
933
+ - `border-left: var(--sms-li-selected-plain-border, 3px solid var(--sms-active-color, cornflowerblue))`: Left border of selected list items in options pane.
840
934
  - `div.multiselect > ul.options > li.active`
841
- - `background: var(--sms-li-active-bg, var(--sms-active-color, rgba(0, 0, 0, 0.15)))`: Background of active options. Options in the dropdown list become active either by mouseover or by navigating to them with arrow keys. Selected options become active when `selectedOptionsDraggable=true` and an option is being dragged to a new position. Note the active option in that case is not the dragged option but the option under it whose place it will take on drag end.
935
+ - `background: var(--sms-li-active-bg, var(--sms-active-color, light-dark(rgba(0, 0, 0, 0.15), rgba(255, 255, 255, 0.15))))`: Background of active options. Options in the dropdown list become active either by mouseover or by navigating to them with arrow keys. Selected options become active when `selectedOptionsDraggable=true` and an option is being dragged to a new position. Note the active option in that case is not the dragged option but the option under it whose place it will take on drag end.
842
936
  - `div.multiselect > ul.options > li.disabled`
843
- - `background: var(--sms-li-disabled-bg, #f5f5f6)`: Background of disabled options in the dropdown list.
844
- - `color: var(--sms-li-disabled-text, #b8b8b8)`: Text color of disabled option in the dropdown list.
937
+ - `background: var(--sms-li-disabled-bg, light-dark(#f5f5f6, #2a2a2a))`: Background of disabled options in the dropdown list.
938
+ - `color: var(--sms-li-disabled-text, light-dark(#b8b8b8, #666))`: Text color of disabled option in the dropdown list.
845
939
  - `::highlight(sms-search-matches)`: applies to search results in dropdown list that match the current search query if `highlightMatches=true`. These styles [cannot be set via CSS variables](https://stackoverflow.com/a/56799215). Instead, use a new rule set. For example:
846
940
 
847
941
  ```css
@@ -862,6 +956,7 @@ The second method allows you to pass in custom classes to the important DOM elem
862
956
  - `ulOptionsClass`: available options listed in the dropdown when component is in `open` state
863
957
  - `liOptionClass`: list items selectable from dropdown list
864
958
  - `liActiveOptionClass`: the currently active dropdown list item (i.e. hovered or navigated to with arrow keys)
959
+ - `liSelectAllClass`: the "Select All" option at the top of the dropdown (when `selectAllOption` is enabled)
865
960
  - `liUserMsgClass`: user message (last child of dropdown list when no options match user input)
866
961
  - `liActiveUserMsgClass`: user message when active (i.e. hovered or navigated to with arrow keys)
867
962
  - `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
@@ -877,6 +972,7 @@ This simplified version of the DOM structure of the component shows where these
877
972
  </ul>
878
973
  <span class="maxSelectMsgClass">2/5 selected</span>
879
974
  <ul class="options {ulOptionsClass}">
975
+ <li class="select-all {liSelectAllClass}">Select all</li>
880
976
  <li class={liOptionClass}>Option 1</li>
881
977
  <li class="{liOptionClass} {liActiveOptionClass}">
882
978
  Option 2 (currently active)
@@ -937,6 +1033,9 @@ Odd as it may seem, you get the most fine-grained control over the styling of ev
937
1033
  :global(div.multiselect > ul.options > li.disabled) {
938
1034
  /* options with disabled key set to true (see props above) */
939
1035
  }
1036
+ :global(div.multiselect > ul.options > li.select-all) {
1037
+ /* the "Select All" option at the top of the dropdown */
1038
+ }
940
1039
  ```
941
1040
 
942
1041
  ## ๐Ÿ†• &thinsp; Changelog