@stackline/react-multiselect-dropdown 19.1.1 → 19.1.2
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 +7 -7
- package/dist/index.cjs +14 -5
- package/dist/index.js +14 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
<img src="https://alexandro.net/images/public/2026/06/dropdownlist.gif" alt="@stackline/react-multiselect-dropdown live dropdown preview" width="420">
|
|
16
16
|
</p>
|
|
17
17
|
|
|
18
|
-
**Latest React 19 release:** `19.1.
|
|
18
|
+
**Latest React 19 release:** `19.1.2` for React `19.x`
|
|
19
19
|
|
|
20
20
|
---
|
|
21
21
|
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
|
|
30
30
|
The package is built around a controlled React API: pass `data`, bind `selectedItems`, receive updates through `onChange`, and customize behavior through a `settings` object. It also supports a Slots API for replacing component structure without losing ARIA/focus behavior, a headless `useMultiSelectDropdown` hook, a lower-level `useMultiSelectState` hook, custom React render functions for option rows and selected badges, lazy loading callbacks, imperative `ref` methods, and body-overlay positioning for dialogs or clipped containers.
|
|
31
31
|
|
|
32
|
-
The current stable React 19 release is `19.1.
|
|
32
|
+
The current stable React 19 release is `19.1.2`. It adds guided structural slots, a headless hook, a state hook, a type-safe factory helper, and a strengthened combobox contract while keeping the styled `<MultiSelectDropdown />` component compatible with the existing visual contract.
|
|
33
33
|
|
|
34
34
|
## Features
|
|
35
35
|
|
|
@@ -86,9 +86,9 @@ Each package family installs on its matching React family. Keep the package fami
|
|
|
86
86
|
|
|
87
87
|
| Package family | React family | Peer range | Tested release window | Demo link |
|
|
88
88
|
| :---: | :---: | :---: | :---: | :--- |
|
|
89
|
-
| **17.x** | **React 17 only** | **`>=17.0.0 <18.0.0`** | **17.0.
|
|
90
|
-
| **18.x** | **React 18 only** | **`>=18.0.0 <19.0.0`** | **18.0.
|
|
91
|
-
| **19.x** | **React 19 only** | **`>=19.0.0 <20.0.0`** | **19.1.
|
|
89
|
+
| **17.x** | **React 17 only** | **`>=17.0.0 <18.0.0`** | **17.0.1 -> 17.0.2** | [React 17 family docs](https://alexandro.net/docs/react/multiselect/react-17/) |
|
|
90
|
+
| **18.x** | **React 18 only** | **`>=18.0.0 <19.0.0`** | **18.0.1 -> 18.3.1** | [React 18 family docs](https://alexandro.net/docs/react/multiselect/react-18/) |
|
|
91
|
+
| **19.x** | **React 19 only** | **`>=19.0.0 <20.0.0`** | **19.1.2 -> 19.2.4** | [React 19 family docs](https://alexandro.net/docs/react/multiselect/react-19/) |
|
|
92
92
|
|
|
93
93
|
## Installation
|
|
94
94
|
|
|
@@ -96,7 +96,7 @@ Each package family installs on its matching React family. Keep the package fami
|
|
|
96
96
|
npm install @stackline/react-multiselect-dropdown@19.1.1 --save-exact
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
Install `19.1.
|
|
99
|
+
Install `19.1.2` for React 19.x applications. The styled component includes its component styles and injects them at runtime. The headless hook does not inject CSS and lets your application own the markup and styling.
|
|
100
100
|
|
|
101
101
|
## Setup
|
|
102
102
|
|
|
@@ -481,7 +481,7 @@ The styled component remains available for drop-in usage. The headless hooks are
|
|
|
481
481
|
|
|
482
482
|
## Combobox Contract
|
|
483
483
|
|
|
484
|
-
Version `19.1.
|
|
484
|
+
Version `19.1.2` tightens the interaction details that usually matter most in production forms:
|
|
485
485
|
|
|
486
486
|
| Behavior | Contract |
|
|
487
487
|
| :--- | :--- |
|
package/dist/index.cjs
CHANGED
|
@@ -1518,6 +1518,11 @@ function InnerMultiSelectDropdown({
|
|
|
1518
1518
|
return;
|
|
1519
1519
|
}
|
|
1520
1520
|
if (isSelected(item)) {
|
|
1521
|
+
if (settings.singleSelection) {
|
|
1522
|
+
closeDropdown(true);
|
|
1523
|
+
focusAfterSelectionChange("trigger");
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1521
1526
|
removeItem(item, focusTarget);
|
|
1522
1527
|
return;
|
|
1523
1528
|
}
|
|
@@ -1928,7 +1933,7 @@ function InnerMultiSelectDropdown({
|
|
|
1928
1933
|
}
|
|
1929
1934
|
if (event.key === "Enter" || isSpaceKey(event.key)) {
|
|
1930
1935
|
event.preventDefault();
|
|
1931
|
-
const willClose = !isSelected(item) &&
|
|
1936
|
+
const willClose = settings.singleSelection || !isSelected(item) && settings.closeDropDownOnSelection;
|
|
1932
1937
|
const currentOptionId = event.currentTarget.id;
|
|
1933
1938
|
const moveToNextOption = isSpaceKey(event.key) && settings.keyboard.spaceOptionAction === "toggle-and-next";
|
|
1934
1939
|
selectItem(item, willClose ? "trigger" : "none");
|
|
@@ -2120,7 +2125,7 @@ function InnerMultiSelectDropdown({
|
|
|
2120
2125
|
if (disabled) {
|
|
2121
2126
|
return;
|
|
2122
2127
|
}
|
|
2123
|
-
const willClose = !isSelected(item) &&
|
|
2128
|
+
const willClose = settings.singleSelection || !isSelected(item) && settings.closeDropDownOnSelection;
|
|
2124
2129
|
selectItem(item, willClose ? "trigger" : "none");
|
|
2125
2130
|
if (!willClose) {
|
|
2126
2131
|
focusOptionAfterPointerSelection(optionId, optionIndex);
|
|
@@ -2610,6 +2615,10 @@ function useMultiSelectState({
|
|
|
2610
2615
|
return;
|
|
2611
2616
|
}
|
|
2612
2617
|
if (isSelected(item)) {
|
|
2618
|
+
if (settings.singleSelection) {
|
|
2619
|
+
onSelectionShouldClose?.();
|
|
2620
|
+
return;
|
|
2621
|
+
}
|
|
2613
2622
|
removeItem(item);
|
|
2614
2623
|
return;
|
|
2615
2624
|
}
|
|
@@ -2934,7 +2943,7 @@ function useMultiSelectDropdown({
|
|
|
2934
2943
|
};
|
|
2935
2944
|
const selectItem = (item, focusTarget = "search") => {
|
|
2936
2945
|
const wasSelected = state.isSelected(item);
|
|
2937
|
-
const willClose =
|
|
2946
|
+
const willClose = settings.singleSelection || !wasSelected && settings.closeDropDownOnSelection;
|
|
2938
2947
|
state.selectItem(item);
|
|
2939
2948
|
focusAfterSelectionChange(willClose ? "trigger" : focusTarget);
|
|
2940
2949
|
};
|
|
@@ -3049,7 +3058,7 @@ function useMultiSelectDropdown({
|
|
|
3049
3058
|
event.preventDefault();
|
|
3050
3059
|
const enabledOptions2 = options.filter((currentOption) => !currentOption.disabled);
|
|
3051
3060
|
const currentEnabledIndex2 = enabledOptions2.findIndex((currentOption) => currentOption.id === option.id);
|
|
3052
|
-
const willClose = !state.isSelected(option.item) &&
|
|
3061
|
+
const willClose = settings.singleSelection || !state.isSelected(option.item) && settings.closeDropDownOnSelection;
|
|
3053
3062
|
const moveToNextOption = isSpaceKey2(event.key) && settings.keyboard.spaceOptionAction === "toggle-and-next";
|
|
3054
3063
|
selectItem(option.item, willClose ? "trigger" : "none");
|
|
3055
3064
|
if (!willClose) {
|
|
@@ -3286,7 +3295,7 @@ function useMultiSelectDropdown({
|
|
|
3286
3295
|
if (option.disabled) {
|
|
3287
3296
|
return;
|
|
3288
3297
|
}
|
|
3289
|
-
const willClose = !state.isSelected(option.item) &&
|
|
3298
|
+
const willClose = settings.singleSelection || !state.isSelected(option.item) && settings.closeDropDownOnSelection;
|
|
3290
3299
|
selectItem(option.item, willClose ? "trigger" : "none");
|
|
3291
3300
|
if (!willClose) {
|
|
3292
3301
|
setActiveOptionIndex(option.index);
|
package/dist/index.js
CHANGED
|
@@ -1497,6 +1497,11 @@ function InnerMultiSelectDropdown({
|
|
|
1497
1497
|
return;
|
|
1498
1498
|
}
|
|
1499
1499
|
if (isSelected(item)) {
|
|
1500
|
+
if (settings.singleSelection) {
|
|
1501
|
+
closeDropdown(true);
|
|
1502
|
+
focusAfterSelectionChange("trigger");
|
|
1503
|
+
return;
|
|
1504
|
+
}
|
|
1500
1505
|
removeItem(item, focusTarget);
|
|
1501
1506
|
return;
|
|
1502
1507
|
}
|
|
@@ -1907,7 +1912,7 @@ function InnerMultiSelectDropdown({
|
|
|
1907
1912
|
}
|
|
1908
1913
|
if (event.key === "Enter" || isSpaceKey(event.key)) {
|
|
1909
1914
|
event.preventDefault();
|
|
1910
|
-
const willClose = !isSelected(item) &&
|
|
1915
|
+
const willClose = settings.singleSelection || !isSelected(item) && settings.closeDropDownOnSelection;
|
|
1911
1916
|
const currentOptionId = event.currentTarget.id;
|
|
1912
1917
|
const moveToNextOption = isSpaceKey(event.key) && settings.keyboard.spaceOptionAction === "toggle-and-next";
|
|
1913
1918
|
selectItem(item, willClose ? "trigger" : "none");
|
|
@@ -2099,7 +2104,7 @@ function InnerMultiSelectDropdown({
|
|
|
2099
2104
|
if (disabled) {
|
|
2100
2105
|
return;
|
|
2101
2106
|
}
|
|
2102
|
-
const willClose = !isSelected(item) &&
|
|
2107
|
+
const willClose = settings.singleSelection || !isSelected(item) && settings.closeDropDownOnSelection;
|
|
2103
2108
|
selectItem(item, willClose ? "trigger" : "none");
|
|
2104
2109
|
if (!willClose) {
|
|
2105
2110
|
focusOptionAfterPointerSelection(optionId, optionIndex);
|
|
@@ -2595,6 +2600,10 @@ function useMultiSelectState({
|
|
|
2595
2600
|
return;
|
|
2596
2601
|
}
|
|
2597
2602
|
if (isSelected(item)) {
|
|
2603
|
+
if (settings.singleSelection) {
|
|
2604
|
+
onSelectionShouldClose?.();
|
|
2605
|
+
return;
|
|
2606
|
+
}
|
|
2598
2607
|
removeItem(item);
|
|
2599
2608
|
return;
|
|
2600
2609
|
}
|
|
@@ -2919,7 +2928,7 @@ function useMultiSelectDropdown({
|
|
|
2919
2928
|
};
|
|
2920
2929
|
const selectItem = (item, focusTarget = "search") => {
|
|
2921
2930
|
const wasSelected = state.isSelected(item);
|
|
2922
|
-
const willClose =
|
|
2931
|
+
const willClose = settings.singleSelection || !wasSelected && settings.closeDropDownOnSelection;
|
|
2923
2932
|
state.selectItem(item);
|
|
2924
2933
|
focusAfterSelectionChange(willClose ? "trigger" : focusTarget);
|
|
2925
2934
|
};
|
|
@@ -3034,7 +3043,7 @@ function useMultiSelectDropdown({
|
|
|
3034
3043
|
event.preventDefault();
|
|
3035
3044
|
const enabledOptions2 = options.filter((currentOption) => !currentOption.disabled);
|
|
3036
3045
|
const currentEnabledIndex2 = enabledOptions2.findIndex((currentOption) => currentOption.id === option.id);
|
|
3037
|
-
const willClose = !state.isSelected(option.item) &&
|
|
3046
|
+
const willClose = settings.singleSelection || !state.isSelected(option.item) && settings.closeDropDownOnSelection;
|
|
3038
3047
|
const moveToNextOption = isSpaceKey2(event.key) && settings.keyboard.spaceOptionAction === "toggle-and-next";
|
|
3039
3048
|
selectItem(option.item, willClose ? "trigger" : "none");
|
|
3040
3049
|
if (!willClose) {
|
|
@@ -3271,7 +3280,7 @@ function useMultiSelectDropdown({
|
|
|
3271
3280
|
if (option.disabled) {
|
|
3272
3281
|
return;
|
|
3273
3282
|
}
|
|
3274
|
-
const willClose = !state.isSelected(option.item) &&
|
|
3283
|
+
const willClose = settings.singleSelection || !state.isSelected(option.item) && settings.closeDropDownOnSelection;
|
|
3275
3284
|
selectItem(option.item, willClose ? "trigger" : "none");
|
|
3276
3285
|
if (!willClose) {
|
|
3277
3286
|
setActiveOptionIndex(option.index);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackline/react-multiselect-dropdown",
|
|
3
|
-
"version": "19.1.
|
|
3
|
+
"version": "19.1.2",
|
|
4
4
|
"description": "Maintained React 19 multiselect dropdown with accessibility-focused and keyboard/ARIA tested support, controlled state, slots, headless/state hooks, Stackline skins, body overlays, live docs, search, grouping, lazy loading, and custom renderers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|