@stackline/react-multiselect-dropdown 17.0.0 → 17.0.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 +384 -30
- package/dist/index.cjs +1789 -429
- package/dist/index.d.cts +348 -6
- package/dist/index.d.ts +348 -6
- package/dist/index.js +1775 -411
- package/package.json +48 -7
package/dist/index.cjs
CHANGED
|
@@ -21,14 +21,207 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
MultiSelectDropdown: () => MultiSelectDropdown,
|
|
24
|
-
ReactMultiSelectDropdown: () => ReactMultiSelectDropdown
|
|
24
|
+
ReactMultiSelectDropdown: () => ReactMultiSelectDropdown,
|
|
25
|
+
createMultiSelectDropdown: () => createMultiSelectDropdown,
|
|
26
|
+
useMultiSelectDropdown: () => useMultiSelectDropdown,
|
|
27
|
+
useMultiSelectState: () => useMultiSelectState
|
|
25
28
|
});
|
|
26
29
|
module.exports = __toCommonJS(index_exports);
|
|
27
30
|
|
|
28
31
|
// src/MultiSelectDropdown.tsx
|
|
29
|
-
var
|
|
32
|
+
var import_react2 = require("react");
|
|
30
33
|
var import_react_dom = require("react-dom");
|
|
31
34
|
|
|
35
|
+
// src/itemUtils.ts
|
|
36
|
+
function isPrimitiveItem(item) {
|
|
37
|
+
return typeof item === "string" || typeof item === "number" || typeof item === "boolean";
|
|
38
|
+
}
|
|
39
|
+
function getLabel(item, settings) {
|
|
40
|
+
if (isPrimitiveItem(item)) {
|
|
41
|
+
return String(item);
|
|
42
|
+
}
|
|
43
|
+
const keys = [settings.labelKey, "itemName", "name", "label", "title", "value"].filter(Boolean);
|
|
44
|
+
for (const key of keys) {
|
|
45
|
+
if (key && item[key] != null) {
|
|
46
|
+
return String(item[key]);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return JSON.stringify(item);
|
|
50
|
+
}
|
|
51
|
+
function getPrimaryValue(item, settings) {
|
|
52
|
+
if (isPrimitiveItem(item)) {
|
|
53
|
+
return String(item);
|
|
54
|
+
}
|
|
55
|
+
const keys = [settings.primaryKey, "id", "value", "key"].filter(Boolean);
|
|
56
|
+
for (const key of keys) {
|
|
57
|
+
if (key && item[key] != null) {
|
|
58
|
+
return String(item[key]);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return getLabel(item, settings);
|
|
62
|
+
}
|
|
63
|
+
function sanitizeId(value) {
|
|
64
|
+
return value.replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 56) || "option";
|
|
65
|
+
}
|
|
66
|
+
function normalizeSkinName(value) {
|
|
67
|
+
return sanitizeId(value.toLowerCase()) || "classic";
|
|
68
|
+
}
|
|
69
|
+
function itemMatchesQuery(item, query, settings) {
|
|
70
|
+
if (!query.trim()) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
const needle = query.trim().toLowerCase();
|
|
74
|
+
const haystack = /* @__PURE__ */ new Set([getLabel(item, settings).toLowerCase()]);
|
|
75
|
+
if (!isPrimitiveItem(item)) {
|
|
76
|
+
const objectItem = item;
|
|
77
|
+
const searchKeys = settings.searchBy.length ? settings.searchBy : [settings.labelKey];
|
|
78
|
+
for (const key of searchKeys) {
|
|
79
|
+
if (key && objectItem[key] != null) {
|
|
80
|
+
haystack.add(String(objectItem[key]).toLowerCase());
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
for (const value of haystack) {
|
|
85
|
+
if (value.includes(needle)) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
function isDisabledItem(item) {
|
|
92
|
+
return !isPrimitiveItem(item) && Boolean(item.disabled);
|
|
93
|
+
}
|
|
94
|
+
function getGroupName(item, settings) {
|
|
95
|
+
if (!settings.groupBy) {
|
|
96
|
+
return "";
|
|
97
|
+
}
|
|
98
|
+
if (typeof settings.groupBy === "function") {
|
|
99
|
+
return settings.groupBy(item);
|
|
100
|
+
}
|
|
101
|
+
if (!isPrimitiveItem(item)) {
|
|
102
|
+
const objectItem = item;
|
|
103
|
+
if (settings.groupBy in objectItem) {
|
|
104
|
+
return String(objectItem[settings.groupBy] ?? "");
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return "";
|
|
108
|
+
}
|
|
109
|
+
function mergeUniqueItems(base, extra, settings) {
|
|
110
|
+
const itemsById = /* @__PURE__ */ new Map();
|
|
111
|
+
for (const item of [...base, ...extra]) {
|
|
112
|
+
itemsById.set(getPrimaryValue(item, settings), item);
|
|
113
|
+
}
|
|
114
|
+
return Array.from(itemsById.values());
|
|
115
|
+
}
|
|
116
|
+
function createItemFromQuery(query, settings, sample) {
|
|
117
|
+
if (sample && !isPrimitiveItem(sample)) {
|
|
118
|
+
return {
|
|
119
|
+
[settings.primaryKey]: query.toLowerCase().replace(/\s+/g, "-"),
|
|
120
|
+
[settings.labelKey]: query
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return query;
|
|
124
|
+
}
|
|
125
|
+
function buildGroups(items, settings) {
|
|
126
|
+
if (!settings.groupBy) {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
const itemsByGroup = /* @__PURE__ */ new Map();
|
|
130
|
+
for (const item of items) {
|
|
131
|
+
const groupName = getGroupName(item, settings) || "Ungrouped";
|
|
132
|
+
const currentItems = itemsByGroup.get(groupName) || [];
|
|
133
|
+
currentItems.push(item);
|
|
134
|
+
itemsByGroup.set(groupName, currentItems);
|
|
135
|
+
}
|
|
136
|
+
return Array.from(itemsByGroup.entries()).map(([name, groupedItems]) => ({
|
|
137
|
+
name,
|
|
138
|
+
items: groupedItems
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
function getVisibleBadgeLimit(selectedCount, rawLimit) {
|
|
142
|
+
if (!Number.isFinite(rawLimit)) {
|
|
143
|
+
return selectedCount;
|
|
144
|
+
}
|
|
145
|
+
return Math.min(selectedCount, Math.max(0, Math.floor(rawLimit)));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/settings.ts
|
|
149
|
+
var DEFAULT_KEYBOARD_SETTINGS = {
|
|
150
|
+
space: true,
|
|
151
|
+
spaceOptionAction: "toggle",
|
|
152
|
+
tab: true,
|
|
153
|
+
arrows: true,
|
|
154
|
+
escape: true,
|
|
155
|
+
backspaceRemovesLastWhenSearchEmpty: false,
|
|
156
|
+
deleteRemovesFocusedBadge: true,
|
|
157
|
+
backspace: false
|
|
158
|
+
};
|
|
159
|
+
var DEFAULT_SETTINGS = {
|
|
160
|
+
singleSelection: false,
|
|
161
|
+
text: "Select",
|
|
162
|
+
enableCheckAll: true,
|
|
163
|
+
selectAllText: "Select All",
|
|
164
|
+
unSelectAllText: "Unselect All",
|
|
165
|
+
filterSelectAllText: "Select filtered",
|
|
166
|
+
filterUnSelectAllText: "Unselect filtered",
|
|
167
|
+
enableFilterSelectAll: true,
|
|
168
|
+
enableSearchFilter: false,
|
|
169
|
+
searchBy: [],
|
|
170
|
+
maxHeight: 300,
|
|
171
|
+
badgeShowLimit: Number.MAX_SAFE_INTEGER,
|
|
172
|
+
classes: "",
|
|
173
|
+
limitSelection: 0,
|
|
174
|
+
disabled: false,
|
|
175
|
+
searchPlaceholderText: "Search",
|
|
176
|
+
groupBy: "",
|
|
177
|
+
showCheckbox: true,
|
|
178
|
+
noDataLabel: "No Data Available",
|
|
179
|
+
searchAutofocus: true,
|
|
180
|
+
lazyLoading: false,
|
|
181
|
+
labelKey: "itemName",
|
|
182
|
+
primaryKey: "id",
|
|
183
|
+
position: "bottom",
|
|
184
|
+
autoPosition: true,
|
|
185
|
+
loading: false,
|
|
186
|
+
selectGroup: false,
|
|
187
|
+
addNewItemOnFilter: false,
|
|
188
|
+
addNewButtonText: "Add",
|
|
189
|
+
escapeToClose: true,
|
|
190
|
+
clearAll: true,
|
|
191
|
+
closeDropDownOnSelection: false,
|
|
192
|
+
tagToBody: false,
|
|
193
|
+
appendToBody: false,
|
|
194
|
+
theme: "",
|
|
195
|
+
skin: "classic",
|
|
196
|
+
ariaLabel: "Multiselect dropdown",
|
|
197
|
+
listboxAriaLabel: "Dropdown options",
|
|
198
|
+
searchAriaLabel: "Search options",
|
|
199
|
+
clearSearchAriaLabel: "Clear search",
|
|
200
|
+
clearAllAriaLabel: "Clear selected options",
|
|
201
|
+
removeItemAriaLabel: "Remove selected option",
|
|
202
|
+
openDropdownAriaLabel: "Open dropdown",
|
|
203
|
+
closeDropdownAriaLabel: "Close dropdown",
|
|
204
|
+
loadingText: "Loading options",
|
|
205
|
+
keyboard: DEFAULT_KEYBOARD_SETTINGS
|
|
206
|
+
};
|
|
207
|
+
function resolveDropdownSettings(incomingSettings) {
|
|
208
|
+
const escapeToClose = incomingSettings?.escapeToClose ?? DEFAULT_SETTINGS.escapeToClose;
|
|
209
|
+
const incomingKeyboard = incomingSettings?.keyboard;
|
|
210
|
+
const keyboard = {
|
|
211
|
+
...DEFAULT_KEYBOARD_SETTINGS,
|
|
212
|
+
...incomingKeyboard,
|
|
213
|
+
backspaceRemovesLastWhenSearchEmpty: incomingKeyboard?.backspaceRemovesLastWhenSearchEmpty ?? incomingKeyboard?.backspace ?? DEFAULT_KEYBOARD_SETTINGS.backspaceRemovesLastWhenSearchEmpty,
|
|
214
|
+
deleteRemovesFocusedBadge: incomingKeyboard?.deleteRemovesFocusedBadge ?? DEFAULT_KEYBOARD_SETTINGS.deleteRemovesFocusedBadge,
|
|
215
|
+
escape: escapeToClose && (incomingKeyboard?.escape ?? DEFAULT_KEYBOARD_SETTINGS.escape)
|
|
216
|
+
};
|
|
217
|
+
return {
|
|
218
|
+
...DEFAULT_SETTINGS,
|
|
219
|
+
...incomingSettings,
|
|
220
|
+
escapeToClose,
|
|
221
|
+
keyboard
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
32
225
|
// src/styles.ts
|
|
33
226
|
var STYLE_ID = "stackline-react-multiselect-dropdown-styles";
|
|
34
227
|
var styles = `
|
|
@@ -71,6 +264,7 @@ var styles = `
|
|
|
71
264
|
position: relative;
|
|
72
265
|
display: flex;
|
|
73
266
|
align-items: center;
|
|
267
|
+
align-content: center;
|
|
74
268
|
flex-wrap: wrap;
|
|
75
269
|
width: 100%;
|
|
76
270
|
min-height: 56px;
|
|
@@ -107,16 +301,26 @@ var styles = `
|
|
|
107
301
|
display: flex;
|
|
108
302
|
flex: 1 1 auto;
|
|
109
303
|
min-width: 0;
|
|
304
|
+
min-height: 1.45em;
|
|
110
305
|
align-items: center;
|
|
306
|
+
align-content: center;
|
|
111
307
|
gap: 8px;
|
|
112
308
|
flex-wrap: wrap;
|
|
113
309
|
}
|
|
114
310
|
|
|
115
311
|
.rmsd-placeholder,
|
|
116
312
|
.rmsd-single-value {
|
|
313
|
+
display: inline-flex;
|
|
314
|
+
align-items: center;
|
|
315
|
+
align-self: center;
|
|
316
|
+
justify-content: flex-start;
|
|
117
317
|
min-width: 0;
|
|
318
|
+
min-height: 1.45em;
|
|
319
|
+
max-width: 100%;
|
|
118
320
|
color: var(--rmsd-muted);
|
|
119
321
|
font-size: 0.95rem;
|
|
322
|
+
line-height: 1.25;
|
|
323
|
+
text-align: left;
|
|
120
324
|
overflow: hidden;
|
|
121
325
|
text-overflow: ellipsis;
|
|
122
326
|
white-space: nowrap;
|
|
@@ -195,33 +399,25 @@ var styles = `
|
|
|
195
399
|
display: inline-flex;
|
|
196
400
|
align-items: center;
|
|
197
401
|
justify-content: center;
|
|
198
|
-
|
|
199
|
-
min-
|
|
402
|
+
flex: 0 0 auto;
|
|
403
|
+
min-width: 24px;
|
|
404
|
+
min-height: 20px;
|
|
200
405
|
color: var(--rmsd-muted);
|
|
201
406
|
font-size: 0.8rem;
|
|
202
407
|
font-weight: 600;
|
|
408
|
+
line-height: 1;
|
|
409
|
+
white-space: nowrap;
|
|
410
|
+
text-align: center;
|
|
203
411
|
}
|
|
204
412
|
|
|
205
|
-
.rmsd-root.rmsd-has-overflow
|
|
413
|
+
.rmsd-root.rmsd-has-overflow .rmsd-trigger {
|
|
206
414
|
padding-right: 104px;
|
|
207
415
|
}
|
|
208
416
|
|
|
209
|
-
.rmsd-root.rmsd-has-overflow:not(.
|
|
417
|
+
.rmsd-root.rmsd-has-overflow:not(.rmsd-has-clear) .rmsd-trigger {
|
|
210
418
|
padding-right: 74px;
|
|
211
419
|
}
|
|
212
420
|
|
|
213
|
-
.rmsd-root.rmsd-has-overflow:not(.skin-classic) .rmsd-overflow {
|
|
214
|
-
position: absolute;
|
|
215
|
-
top: 50%;
|
|
216
|
-
right: 76px;
|
|
217
|
-
transform: translateY(-50%);
|
|
218
|
-
z-index: 1;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
.rmsd-root.rmsd-has-overflow:not(.skin-classic):not(.rmsd-has-clear) .rmsd-overflow {
|
|
222
|
-
right: 42px;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
421
|
.rmsd-actions {
|
|
226
422
|
position: absolute;
|
|
227
423
|
top: 50%;
|
|
@@ -703,6 +899,7 @@ var styles = `
|
|
|
703
899
|
|
|
704
900
|
.theme-classic .rmsd-trigger,
|
|
705
901
|
.skin-classic .rmsd-trigger {
|
|
902
|
+
align-content: center;
|
|
706
903
|
flex-wrap: nowrap;
|
|
707
904
|
gap: 6px;
|
|
708
905
|
min-height: 42px;
|
|
@@ -785,9 +982,12 @@ var styles = `
|
|
|
785
982
|
|
|
786
983
|
.theme-classic .rmsd-overflow,
|
|
787
984
|
.skin-classic .rmsd-overflow {
|
|
985
|
+
min-width: 24px;
|
|
986
|
+
min-height: 20px;
|
|
788
987
|
color: #333333;
|
|
789
988
|
font-size: 14px;
|
|
790
989
|
font-weight: 400;
|
|
990
|
+
line-height: 1;
|
|
791
991
|
}
|
|
792
992
|
|
|
793
993
|
.theme-classic .rmsd-actions,
|
|
@@ -1055,7 +1255,7 @@ var styles = `
|
|
|
1055
1255
|
|
|
1056
1256
|
@media (max-width: 720px) {
|
|
1057
1257
|
.rmsd-trigger {
|
|
1058
|
-
align-items:
|
|
1258
|
+
align-items: center;
|
|
1059
1259
|
padding-right: 54px;
|
|
1060
1260
|
}
|
|
1061
1261
|
}
|
|
@@ -1073,56 +1273,30 @@ function ensureDropdownStyles() {
|
|
|
1073
1273
|
document.head.appendChild(tag);
|
|
1074
1274
|
}
|
|
1075
1275
|
|
|
1276
|
+
// src/useControllableSelection.ts
|
|
1277
|
+
var import_react = require("react");
|
|
1278
|
+
function useControllableSelection(controlledValue, defaultValue, onChange) {
|
|
1279
|
+
const [internalValue, setInternalValue] = (0, import_react.useState)(defaultValue ?? []);
|
|
1280
|
+
const isControlled = controlledValue !== void 0;
|
|
1281
|
+
const value = isControlled ? controlledValue : internalValue;
|
|
1282
|
+
const setValue = (nextValue) => {
|
|
1283
|
+
if (!isControlled) {
|
|
1284
|
+
setInternalValue(nextValue);
|
|
1285
|
+
}
|
|
1286
|
+
onChange?.(nextValue);
|
|
1287
|
+
};
|
|
1288
|
+
return [value, setValue];
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1076
1291
|
// src/MultiSelectDropdown.tsx
|
|
1077
1292
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
1078
|
-
var
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
filterUnSelectAllText: "Unselect filtered",
|
|
1086
|
-
enableFilterSelectAll: true,
|
|
1087
|
-
enableSearchFilter: false,
|
|
1088
|
-
searchBy: [],
|
|
1089
|
-
maxHeight: 300,
|
|
1090
|
-
badgeShowLimit: Number.MAX_SAFE_INTEGER,
|
|
1091
|
-
classes: "",
|
|
1092
|
-
limitSelection: 0,
|
|
1093
|
-
disabled: false,
|
|
1094
|
-
searchPlaceholderText: "Search",
|
|
1095
|
-
groupBy: "",
|
|
1096
|
-
showCheckbox: true,
|
|
1097
|
-
noDataLabel: "No Data Available",
|
|
1098
|
-
searchAutofocus: true,
|
|
1099
|
-
lazyLoading: false,
|
|
1100
|
-
labelKey: "itemName",
|
|
1101
|
-
primaryKey: "id",
|
|
1102
|
-
position: "bottom",
|
|
1103
|
-
autoPosition: true,
|
|
1104
|
-
loading: false,
|
|
1105
|
-
selectGroup: false,
|
|
1106
|
-
addNewItemOnFilter: false,
|
|
1107
|
-
addNewButtonText: "Add",
|
|
1108
|
-
escapeToClose: true,
|
|
1109
|
-
clearAll: true,
|
|
1110
|
-
closeDropDownOnSelection: false,
|
|
1111
|
-
tagToBody: false,
|
|
1112
|
-
appendToBody: false,
|
|
1113
|
-
theme: "",
|
|
1114
|
-
skin: "classic",
|
|
1115
|
-
ariaLabel: "Multiselect dropdown",
|
|
1116
|
-
listboxAriaLabel: "Dropdown options",
|
|
1117
|
-
searchAriaLabel: "Search options",
|
|
1118
|
-
clearSearchAriaLabel: "Clear search",
|
|
1119
|
-
clearAllAriaLabel: "Clear selected options",
|
|
1120
|
-
removeItemAriaLabel: "Remove selected option",
|
|
1121
|
-
openDropdownAriaLabel: "Open dropdown",
|
|
1122
|
-
closeDropdownAriaLabel: "Close dropdown",
|
|
1123
|
-
loadingText: "Loading options"
|
|
1124
|
-
};
|
|
1125
|
-
var useClientLayoutEffect = typeof window === "undefined" ? import_react.useEffect : import_react.useLayoutEffect;
|
|
1293
|
+
var useClientLayoutEffect = typeof window === "undefined" ? import_react2.useEffect : import_react2.useLayoutEffect;
|
|
1294
|
+
function isSpaceKey(key) {
|
|
1295
|
+
return key === " " || key === "Spacebar";
|
|
1296
|
+
}
|
|
1297
|
+
function renderSlot(Slot, props, fallback) {
|
|
1298
|
+
return Slot ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: Slot(props) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: fallback });
|
|
1299
|
+
}
|
|
1126
1300
|
function StacklineIcon({ name, className = "rmsd-icon" }) {
|
|
1127
1301
|
if (name === "remove") {
|
|
1128
1302
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className, viewBox: "0 0 47.971 47.971", focusable: "false", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M28.228,23.986L47.092,5.122c1.172-1.171,1.172-3.071,0-4.242c-1.172-1.172-3.07-1.172-4.242,0L23.986,19.744L5.121,0.88c-1.172-1.172-3.07-1.172-4.242,0c-1.172,1.171-1.172,3.071,0,4.242l18.865,18.864L0.879,42.85c-1.172,1.171-1.172,3.071,0,4.242C1.465,47.677,2.233,47.97,3,47.97s1.535-0.293,2.121-0.879l18.865-18.864L42.85,47.091c0.586,0.586,1.354,0.879,2.121,0.879s1.535-0.293,2.121-0.879c1.172-1.171,1.172-3.071,0-4.242L28.228,23.986z" }) });
|
|
@@ -1135,131 +1309,6 @@ function StacklineIcon({ name, className = "rmsd-icon" }) {
|
|
|
1135
1309
|
}
|
|
1136
1310
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className, viewBox: "0 0 612 612", focusable: "false", "aria-hidden": "true", children: name === "angle-up" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M604.501,440.509L325.398,134.956c-5.331-5.357-12.423-7.627-19.386-7.27c-6.989-0.357-14.056,1.913-19.387,7.27L7.499,440.509c-9.999,10.024-9.999,26.298,0,36.323s26.223,10.024,36.222,0l262.293-287.164L568.28,476.832c9.999,10.024,26.222,10.024,36.221,0C614.5,466.809,614.5,450.534,604.501,440.509z" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M604.501,134.782c-9.999-10.05-26.222-10.05-36.221,0L306.014,422.558L43.721,134.782c-9.999-10.05-26.223-10.05-36.222,0s-9.999,26.35,0,36.399l279.103,306.241c5.331,5.357,12.422,7.652,19.386,7.296c6.988,0.356,14.055-1.939,19.386-7.296l279.128-306.268C614.5,161.106,614.5,144.832,604.501,134.782z" }) });
|
|
1137
1311
|
}
|
|
1138
|
-
function isPrimitiveItem(item) {
|
|
1139
|
-
return typeof item === "string" || typeof item === "number" || typeof item === "boolean";
|
|
1140
|
-
}
|
|
1141
|
-
function getLabel(item, settings) {
|
|
1142
|
-
if (isPrimitiveItem(item)) {
|
|
1143
|
-
return String(item);
|
|
1144
|
-
}
|
|
1145
|
-
const keys = [settings.labelKey, "itemName", "name", "label", "title", "value"].filter(Boolean);
|
|
1146
|
-
for (const key of keys) {
|
|
1147
|
-
if (key && item[key] != null) {
|
|
1148
|
-
return String(item[key]);
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
return JSON.stringify(item);
|
|
1152
|
-
}
|
|
1153
|
-
function getPrimaryValue(item, settings) {
|
|
1154
|
-
if (isPrimitiveItem(item)) {
|
|
1155
|
-
return String(item);
|
|
1156
|
-
}
|
|
1157
|
-
const keys = [settings.primaryKey, "id", "value", "key"].filter(Boolean);
|
|
1158
|
-
for (const key of keys) {
|
|
1159
|
-
if (key && item[key] != null) {
|
|
1160
|
-
return String(item[key]);
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
return getLabel(item, settings);
|
|
1164
|
-
}
|
|
1165
|
-
function itemMatchesQuery(item, query, settings) {
|
|
1166
|
-
if (!query.trim()) {
|
|
1167
|
-
return true;
|
|
1168
|
-
}
|
|
1169
|
-
const needle = query.trim().toLowerCase();
|
|
1170
|
-
const haystack = /* @__PURE__ */ new Set();
|
|
1171
|
-
haystack.add(getLabel(item, settings).toLowerCase());
|
|
1172
|
-
if (!isPrimitiveItem(item)) {
|
|
1173
|
-
const searchKeys = settings.searchBy.length ? settings.searchBy : [settings.labelKey];
|
|
1174
|
-
for (const key of searchKeys) {
|
|
1175
|
-
if (key && item[key] != null) {
|
|
1176
|
-
haystack.add(String(item[key]).toLowerCase());
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
for (const value of haystack) {
|
|
1181
|
-
if (value.includes(needle)) {
|
|
1182
|
-
return true;
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
|
-
return false;
|
|
1186
|
-
}
|
|
1187
|
-
function getGroupName(item, settings) {
|
|
1188
|
-
if (!settings.groupBy) {
|
|
1189
|
-
return "";
|
|
1190
|
-
}
|
|
1191
|
-
if (typeof settings.groupBy === "function") {
|
|
1192
|
-
return settings.groupBy(item);
|
|
1193
|
-
}
|
|
1194
|
-
if (!isPrimitiveItem(item)) {
|
|
1195
|
-
const groupKey = settings.groupBy;
|
|
1196
|
-
const objectItem = item;
|
|
1197
|
-
if (groupKey in objectItem) {
|
|
1198
|
-
return String(objectItem[groupKey] ?? "");
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
return "";
|
|
1202
|
-
}
|
|
1203
|
-
function mergeUniqueItems(base, extra, settings) {
|
|
1204
|
-
const bucket = /* @__PURE__ */ new Map();
|
|
1205
|
-
for (const item of [...base, ...extra]) {
|
|
1206
|
-
bucket.set(getPrimaryValue(item, settings), item);
|
|
1207
|
-
}
|
|
1208
|
-
return Array.from(bucket.values());
|
|
1209
|
-
}
|
|
1210
|
-
function createItemFromQuery(query, settings, sample) {
|
|
1211
|
-
if (sample && !isPrimitiveItem(sample)) {
|
|
1212
|
-
return {
|
|
1213
|
-
[settings.primaryKey]: query.toLowerCase().replace(/\s+/g, "-"),
|
|
1214
|
-
[settings.labelKey]: query
|
|
1215
|
-
};
|
|
1216
|
-
}
|
|
1217
|
-
return query;
|
|
1218
|
-
}
|
|
1219
|
-
function isDisabledItem(item) {
|
|
1220
|
-
return !isPrimitiveItem(item) && Boolean(item.disabled);
|
|
1221
|
-
}
|
|
1222
|
-
function buildGroups(items, settings) {
|
|
1223
|
-
if (!settings.groupBy) {
|
|
1224
|
-
return [];
|
|
1225
|
-
}
|
|
1226
|
-
const map = /* @__PURE__ */ new Map();
|
|
1227
|
-
for (const item of items) {
|
|
1228
|
-
const groupName = getGroupName(item, settings) || "Ungrouped";
|
|
1229
|
-
const current = map.get(groupName) || [];
|
|
1230
|
-
current.push(item);
|
|
1231
|
-
map.set(groupName, current);
|
|
1232
|
-
}
|
|
1233
|
-
return Array.from(map.entries()).map(([name, groupedItems]) => ({
|
|
1234
|
-
name,
|
|
1235
|
-
items: groupedItems
|
|
1236
|
-
}));
|
|
1237
|
-
}
|
|
1238
|
-
function useControllableSelection(controlledValue, defaultValue, onChange) {
|
|
1239
|
-
const [internalValue, setInternalValue] = (0, import_react.useState)(defaultValue ?? []);
|
|
1240
|
-
const isControlled = controlledValue !== void 0;
|
|
1241
|
-
const value = isControlled ? controlledValue : internalValue;
|
|
1242
|
-
const setValue = (nextValue) => {
|
|
1243
|
-
if (!isControlled) {
|
|
1244
|
-
setInternalValue(nextValue);
|
|
1245
|
-
}
|
|
1246
|
-
onChange?.(nextValue);
|
|
1247
|
-
};
|
|
1248
|
-
return [value, setValue];
|
|
1249
|
-
}
|
|
1250
|
-
function sanitizeId(value) {
|
|
1251
|
-
return value.replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 56) || "option";
|
|
1252
|
-
}
|
|
1253
|
-
function normalizeSkinName(value) {
|
|
1254
|
-
return sanitizeId(value.toLowerCase()) || "classic";
|
|
1255
|
-
}
|
|
1256
|
-
function getVisibleBadgeLimit(selectedCount, rawLimit) {
|
|
1257
|
-
if (!Number.isFinite(rawLimit)) {
|
|
1258
|
-
return selectedCount;
|
|
1259
|
-
}
|
|
1260
|
-
const limit = Math.max(0, Math.floor(rawLimit));
|
|
1261
|
-
return Math.min(selectedCount, limit);
|
|
1262
|
-
}
|
|
1263
1312
|
function InnerMultiSelectDropdown({
|
|
1264
1313
|
data,
|
|
1265
1314
|
settings: incomingSettings,
|
|
@@ -1284,41 +1333,43 @@ function InnerMultiSelectDropdown({
|
|
|
1284
1333
|
renderItem,
|
|
1285
1334
|
renderBadge,
|
|
1286
1335
|
renderSearch,
|
|
1287
|
-
renderEmptyState
|
|
1336
|
+
renderEmptyState,
|
|
1337
|
+
slots
|
|
1288
1338
|
}, ref) {
|
|
1289
1339
|
ensureDropdownStyles();
|
|
1290
|
-
const settings =
|
|
1340
|
+
const settings = (0, import_react2.useMemo)(() => resolveDropdownSettings(incomingSettings), [incomingSettings]);
|
|
1291
1341
|
const [selectedItems, setSelectedItems] = useControllableSelection(
|
|
1292
1342
|
controlledSelectedItems,
|
|
1293
1343
|
defaultSelectedItems,
|
|
1294
1344
|
onChange
|
|
1295
1345
|
);
|
|
1296
|
-
const [isOpen, setIsOpen] = (0,
|
|
1297
|
-
const [filter, setFilter] = (0,
|
|
1298
|
-
const [addedItems, setAddedItems] = (0,
|
|
1299
|
-
const [activeDescendantId, setActiveDescendantId] = (0,
|
|
1300
|
-
const [bodyMenuStyle, setBodyMenuStyle] = (0,
|
|
1301
|
-
const [bodyListMaxHeight, setBodyListMaxHeight] = (0,
|
|
1302
|
-
const [effectivePosition, setEffectivePosition] = (0,
|
|
1346
|
+
const [isOpen, setIsOpen] = (0, import_react2.useState)(false);
|
|
1347
|
+
const [filter, setFilter] = (0, import_react2.useState)("");
|
|
1348
|
+
const [addedItems, setAddedItems] = (0, import_react2.useState)([]);
|
|
1349
|
+
const [activeDescendantId, setActiveDescendantId] = (0, import_react2.useState)(null);
|
|
1350
|
+
const [bodyMenuStyle, setBodyMenuStyle] = (0, import_react2.useState)();
|
|
1351
|
+
const [bodyListMaxHeight, setBodyListMaxHeight] = (0, import_react2.useState)();
|
|
1352
|
+
const [effectivePosition, setEffectivePosition] = (0, import_react2.useState)(
|
|
1303
1353
|
settings.position === "top" ? "top" : "bottom"
|
|
1304
1354
|
);
|
|
1305
|
-
const rootRef = (0,
|
|
1306
|
-
const triggerRef = (0,
|
|
1307
|
-
const menuRef = (0,
|
|
1308
|
-
const searchRef = (0,
|
|
1309
|
-
const listRef = (0,
|
|
1310
|
-
const lastScrollHeightRef = (0,
|
|
1311
|
-
const pendingFocusRef = (0,
|
|
1312
|
-
const
|
|
1313
|
-
const
|
|
1314
|
-
|
|
1315
|
-
[
|
|
1355
|
+
const rootRef = (0, import_react2.useRef)(null);
|
|
1356
|
+
const triggerRef = (0, import_react2.useRef)(null);
|
|
1357
|
+
const menuRef = (0, import_react2.useRef)(null);
|
|
1358
|
+
const searchRef = (0, import_react2.useRef)(null);
|
|
1359
|
+
const listRef = (0, import_react2.useRef)(null);
|
|
1360
|
+
const lastScrollHeightRef = (0, import_react2.useRef)(0);
|
|
1361
|
+
const pendingFocusRef = (0, import_react2.useRef)(null);
|
|
1362
|
+
const addRequestIdRef = (0, import_react2.useRef)(0);
|
|
1363
|
+
const instanceIdRef = (0, import_react2.useRef)(`rmsd-${Math.random().toString(36).slice(2)}`);
|
|
1364
|
+
const allItems = (0, import_react2.useMemo)(
|
|
1365
|
+
() => mergeUniqueItems(data, [...selectedItems, ...addedItems], settings),
|
|
1366
|
+
[addedItems, data, selectedItems, settings]
|
|
1316
1367
|
);
|
|
1317
|
-
const filteredItems = (0,
|
|
1368
|
+
const filteredItems = (0, import_react2.useMemo)(
|
|
1318
1369
|
() => allItems.filter((item) => itemMatchesQuery(item, filter, settings)),
|
|
1319
1370
|
[allItems, filter, settings]
|
|
1320
1371
|
);
|
|
1321
|
-
const groupedItems = (0,
|
|
1372
|
+
const groupedItems = (0, import_react2.useMemo)(() => buildGroups(filteredItems, settings), [filteredItems, settings]);
|
|
1322
1373
|
const listboxId = `${instanceIdRef.current}-listbox`;
|
|
1323
1374
|
const getOptionId = (item, index, prefix) => `${instanceIdRef.current}-${prefix}-${index}-${sanitizeId(getPrimaryValue(item, settings))}`;
|
|
1324
1375
|
const isSelected = (item) => selectedItems.some(
|
|
@@ -1343,6 +1394,68 @@ function InnerMultiSelectDropdown({
|
|
|
1343
1394
|
};
|
|
1344
1395
|
const focusFirstOption = () => focusOptionByIndex(0);
|
|
1345
1396
|
const focusLastOption = () => focusOptionByIndex(getOptionElements().length - 1);
|
|
1397
|
+
const focusOptionById = (optionId) => {
|
|
1398
|
+
const option = document.getElementById(optionId);
|
|
1399
|
+
if (!option || !listRef.current?.contains(option) || option.getAttribute("aria-disabled") === "true") {
|
|
1400
|
+
return false;
|
|
1401
|
+
}
|
|
1402
|
+
option.focus();
|
|
1403
|
+
setActiveDescendantId(option.id || null);
|
|
1404
|
+
option.scrollIntoView({ block: "nearest" });
|
|
1405
|
+
return true;
|
|
1406
|
+
};
|
|
1407
|
+
const focusOptionAfterPointerSelection = (optionId, fallbackIndex) => {
|
|
1408
|
+
window.setTimeout(() => {
|
|
1409
|
+
const focusAfterRender = () => {
|
|
1410
|
+
if (focusOptionById(optionId)) {
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1413
|
+
focusOptionByIndex(fallbackIndex);
|
|
1414
|
+
};
|
|
1415
|
+
if (typeof window.requestAnimationFrame === "function") {
|
|
1416
|
+
window.requestAnimationFrame(focusAfterRender);
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
focusAfterRender();
|
|
1420
|
+
}, 0);
|
|
1421
|
+
};
|
|
1422
|
+
const focusOptionAfterKeyboardSelection = (optionId, fallbackIndex, moveToNextOption) => {
|
|
1423
|
+
window.setTimeout(() => {
|
|
1424
|
+
const focusAfterRender = () => {
|
|
1425
|
+
if (moveToNextOption) {
|
|
1426
|
+
const options = getOptionElements();
|
|
1427
|
+
const currentIndex = options.findIndex((option) => option.id === optionId);
|
|
1428
|
+
const nextOption = currentIndex >= 0 ? options[currentIndex + 1] : void 0;
|
|
1429
|
+
if (nextOption) {
|
|
1430
|
+
nextOption.focus();
|
|
1431
|
+
setActiveDescendantId(nextOption.id || null);
|
|
1432
|
+
nextOption.scrollIntoView({ block: "nearest" });
|
|
1433
|
+
return;
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
if (!focusOptionById(optionId)) {
|
|
1437
|
+
focusOptionByIndex(fallbackIndex);
|
|
1438
|
+
}
|
|
1439
|
+
};
|
|
1440
|
+
if (typeof window.requestAnimationFrame === "function") {
|
|
1441
|
+
window.requestAnimationFrame(focusAfterRender);
|
|
1442
|
+
return;
|
|
1443
|
+
}
|
|
1444
|
+
focusAfterRender();
|
|
1445
|
+
}, 0);
|
|
1446
|
+
};
|
|
1447
|
+
const focusAfterSelectionChange = (target = "search") => {
|
|
1448
|
+
if (target === "none") {
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
window.setTimeout(() => {
|
|
1452
|
+
if (target === "search" && isOpen && settings.enableSearchFilter) {
|
|
1453
|
+
searchRef.current?.focus();
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
triggerRef.current?.focus();
|
|
1457
|
+
}, 0);
|
|
1458
|
+
};
|
|
1346
1459
|
const updateSelection = (nextItems) => {
|
|
1347
1460
|
setSelectedItems(nextItems);
|
|
1348
1461
|
};
|
|
@@ -1376,6 +1489,7 @@ function InnerMultiSelectDropdown({
|
|
|
1376
1489
|
const previousItems = selectedItems;
|
|
1377
1490
|
updateSelection([]);
|
|
1378
1491
|
onDeSelectAll?.(previousItems);
|
|
1492
|
+
focusAfterSelectionChange();
|
|
1379
1493
|
};
|
|
1380
1494
|
const toggleDropdown = () => {
|
|
1381
1495
|
if (isOpen) {
|
|
@@ -1384,25 +1498,39 @@ function InnerMultiSelectDropdown({
|
|
|
1384
1498
|
openDropdown("search");
|
|
1385
1499
|
}
|
|
1386
1500
|
};
|
|
1387
|
-
const removeItem = (item) => {
|
|
1501
|
+
const removeItem = (item, focusTarget = "search") => {
|
|
1388
1502
|
const nextItems = selectedItems.filter(
|
|
1389
1503
|
(selectedItem) => getPrimaryValue(selectedItem, settings) !== getPrimaryValue(item, settings)
|
|
1390
1504
|
);
|
|
1391
1505
|
updateSelection(nextItems);
|
|
1392
1506
|
onDeSelect?.(item);
|
|
1507
|
+
focusAfterSelectionChange(focusTarget);
|
|
1393
1508
|
};
|
|
1394
|
-
const
|
|
1509
|
+
const removeLastSelectedItem = () => {
|
|
1510
|
+
const lastItem = selectedItems[selectedItems.length - 1];
|
|
1511
|
+
if (!lastItem) {
|
|
1512
|
+
return;
|
|
1513
|
+
}
|
|
1514
|
+
removeItem(lastItem);
|
|
1515
|
+
};
|
|
1516
|
+
const selectItem = (item, focusTarget = "search") => {
|
|
1395
1517
|
if (settings.disabled || isDisabledItem(item)) {
|
|
1396
1518
|
return;
|
|
1397
1519
|
}
|
|
1398
1520
|
if (isSelected(item)) {
|
|
1399
|
-
|
|
1521
|
+
if (settings.singleSelection) {
|
|
1522
|
+
closeDropdown(true);
|
|
1523
|
+
focusAfterSelectionChange("trigger");
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
removeItem(item, focusTarget);
|
|
1400
1527
|
return;
|
|
1401
1528
|
}
|
|
1402
1529
|
if (settings.singleSelection) {
|
|
1403
1530
|
updateSelection([item]);
|
|
1404
1531
|
onSelect?.(item);
|
|
1405
1532
|
closeDropdown(true);
|
|
1533
|
+
focusAfterSelectionChange("trigger");
|
|
1406
1534
|
return;
|
|
1407
1535
|
}
|
|
1408
1536
|
if (settings.limitSelection && selectedItems.length >= settings.limitSelection) {
|
|
@@ -1413,7 +1541,10 @@ function InnerMultiSelectDropdown({
|
|
|
1413
1541
|
onSelect?.(item);
|
|
1414
1542
|
if (settings.closeDropDownOnSelection) {
|
|
1415
1543
|
closeDropdown(true);
|
|
1544
|
+
focusAfterSelectionChange("trigger");
|
|
1545
|
+
return;
|
|
1416
1546
|
}
|
|
1547
|
+
focusAfterSelectionChange(focusTarget);
|
|
1417
1548
|
};
|
|
1418
1549
|
const selectAllItems = (items, filteredSelection = false) => {
|
|
1419
1550
|
if (settings.singleSelection) {
|
|
@@ -1429,6 +1560,7 @@ function InnerMultiSelectDropdown({
|
|
|
1429
1560
|
} else {
|
|
1430
1561
|
onSelectAll?.(nextItems);
|
|
1431
1562
|
}
|
|
1563
|
+
focusAfterSelectionChange();
|
|
1432
1564
|
};
|
|
1433
1565
|
const deSelectAllItems = (items, filteredSelection = false) => {
|
|
1434
1566
|
const ids = new Set(items.map((item) => getPrimaryValue(item, settings)));
|
|
@@ -1439,13 +1571,19 @@ function InnerMultiSelectDropdown({
|
|
|
1439
1571
|
} else {
|
|
1440
1572
|
onDeSelectAll?.(items);
|
|
1441
1573
|
}
|
|
1574
|
+
focusAfterSelectionChange();
|
|
1442
1575
|
};
|
|
1443
1576
|
const handleAddFilterNewItem = async () => {
|
|
1444
1577
|
const query = filter.trim();
|
|
1445
1578
|
if (!query) {
|
|
1446
1579
|
return;
|
|
1447
1580
|
}
|
|
1581
|
+
const requestId = addRequestIdRef.current + 1;
|
|
1582
|
+
addRequestIdRef.current = requestId;
|
|
1448
1583
|
const result = await onAddFilterNewItem?.(query);
|
|
1584
|
+
if (requestId !== addRequestIdRef.current) {
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1449
1587
|
const nextItem = result === void 0 ? createItemFromQuery(query, settings, data[0]) : result;
|
|
1450
1588
|
setAddedItems((currentItems) => mergeUniqueItems(currentItems, [nextItem], settings));
|
|
1451
1589
|
if (settings.singleSelection) {
|
|
@@ -1454,6 +1592,7 @@ function InnerMultiSelectDropdown({
|
|
|
1454
1592
|
updateSelection(mergeUniqueItems(selectedItems, [nextItem], settings));
|
|
1455
1593
|
}
|
|
1456
1594
|
setFilter("");
|
|
1595
|
+
focusAfterSelectionChange();
|
|
1457
1596
|
};
|
|
1458
1597
|
const toggleGroup = (groupName, items) => {
|
|
1459
1598
|
const groupItems = items.filter((item) => !isDisabledItem(item));
|
|
@@ -1461,10 +1600,12 @@ function InnerMultiSelectDropdown({
|
|
|
1461
1600
|
if (allSelected) {
|
|
1462
1601
|
deSelectAllItems(groupItems, false);
|
|
1463
1602
|
onGroupDeSelect?.(groupName, groupItems);
|
|
1603
|
+
focusAfterSelectionChange();
|
|
1464
1604
|
return;
|
|
1465
1605
|
}
|
|
1466
1606
|
selectAllItems(groupItems, false);
|
|
1467
1607
|
onGroupSelect?.(groupName, groupItems);
|
|
1608
|
+
focusAfterSelectionChange();
|
|
1468
1609
|
};
|
|
1469
1610
|
const handleListScroll = () => {
|
|
1470
1611
|
if (!listRef.current || !onScrollToEnd) {
|
|
@@ -1493,7 +1634,7 @@ function InnerMultiSelectDropdown({
|
|
|
1493
1634
|
}
|
|
1494
1635
|
return "bottom";
|
|
1495
1636
|
};
|
|
1496
|
-
(0,
|
|
1637
|
+
(0, import_react2.useEffect)(() => {
|
|
1497
1638
|
if (!isOpen) {
|
|
1498
1639
|
return;
|
|
1499
1640
|
}
|
|
@@ -1504,7 +1645,7 @@ function InnerMultiSelectDropdown({
|
|
|
1504
1645
|
}
|
|
1505
1646
|
};
|
|
1506
1647
|
const handleKeyDown = (event) => {
|
|
1507
|
-
if (event.key === "Escape" && settings.
|
|
1648
|
+
if (event.key === "Escape" && settings.keyboard.escape) {
|
|
1508
1649
|
closeDropdown(true);
|
|
1509
1650
|
}
|
|
1510
1651
|
};
|
|
@@ -1516,7 +1657,7 @@ function InnerMultiSelectDropdown({
|
|
|
1516
1657
|
document.removeEventListener("touchstart", handlePointerDown);
|
|
1517
1658
|
document.removeEventListener("keydown", handleKeyDown);
|
|
1518
1659
|
};
|
|
1519
|
-
}, [isOpen, settings.
|
|
1660
|
+
}, [isOpen, settings.keyboard.escape]);
|
|
1520
1661
|
const updateBodyMenuPosition = () => {
|
|
1521
1662
|
if (!shouldAppendToBody || !triggerRef.current || typeof window === "undefined") {
|
|
1522
1663
|
return;
|
|
@@ -1576,7 +1717,7 @@ function InnerMultiSelectDropdown({
|
|
|
1576
1717
|
selectedItems.length,
|
|
1577
1718
|
filter
|
|
1578
1719
|
]);
|
|
1579
|
-
(0,
|
|
1720
|
+
(0, import_react2.useEffect)(() => {
|
|
1580
1721
|
if (!isOpen || !shouldAppendToBody || typeof window === "undefined") {
|
|
1581
1722
|
return;
|
|
1582
1723
|
}
|
|
@@ -1602,7 +1743,7 @@ function InnerMultiSelectDropdown({
|
|
|
1602
1743
|
filteredItems.length,
|
|
1603
1744
|
selectedItems.length
|
|
1604
1745
|
]);
|
|
1605
|
-
(0,
|
|
1746
|
+
(0, import_react2.useEffect)(() => {
|
|
1606
1747
|
if (!isOpen) {
|
|
1607
1748
|
return;
|
|
1608
1749
|
}
|
|
@@ -1622,10 +1763,10 @@ function InnerMultiSelectDropdown({
|
|
|
1622
1763
|
}
|
|
1623
1764
|
}, 0);
|
|
1624
1765
|
}, [isOpen, filteredItems.length, settings.enableSearchFilter, settings.searchAutofocus]);
|
|
1625
|
-
(0,
|
|
1766
|
+
(0, import_react2.useEffect)(() => {
|
|
1626
1767
|
lastScrollHeightRef.current = 0;
|
|
1627
1768
|
}, [filteredItems.length]);
|
|
1628
|
-
(0,
|
|
1769
|
+
(0, import_react2.useImperativeHandle)(
|
|
1629
1770
|
ref,
|
|
1630
1771
|
() => ({
|
|
1631
1772
|
openDropdown: () => openDropdown("search"),
|
|
@@ -1675,20 +1816,38 @@ function InnerMultiSelectDropdown({
|
|
|
1675
1816
|
return `${settings.ariaLabel}: ${selectedItems.map((item) => getLabel(item, settings)).join(", ")}`;
|
|
1676
1817
|
};
|
|
1677
1818
|
const stopInlineKey = (event) => {
|
|
1678
|
-
if (event.key
|
|
1819
|
+
if (isSpaceKey(event.key) && !settings.keyboard.space) {
|
|
1820
|
+
event.preventDefault();
|
|
1821
|
+
event.stopPropagation();
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
1824
|
+
if (event.key === "Enter" || isSpaceKey(event.key)) {
|
|
1825
|
+
event.stopPropagation();
|
|
1826
|
+
}
|
|
1827
|
+
};
|
|
1828
|
+
const handleBadgeRemoveKeyDown = (event, item) => {
|
|
1829
|
+
if (settings.keyboard.deleteRemovesFocusedBadge && (event.key === "Backspace" || event.key === "Delete")) {
|
|
1830
|
+
event.preventDefault();
|
|
1679
1831
|
event.stopPropagation();
|
|
1832
|
+
removeItem(item);
|
|
1833
|
+
return;
|
|
1680
1834
|
}
|
|
1835
|
+
stopInlineKey(event);
|
|
1681
1836
|
};
|
|
1682
1837
|
const handleTriggerKeyDown = (event) => {
|
|
1683
1838
|
if (settings.disabled) {
|
|
1684
1839
|
return;
|
|
1685
1840
|
}
|
|
1686
|
-
if (event.key
|
|
1841
|
+
if (isSpaceKey(event.key) && !settings.keyboard.space) {
|
|
1842
|
+
event.preventDefault();
|
|
1843
|
+
return;
|
|
1844
|
+
}
|
|
1845
|
+
if (event.key === "Enter" || isSpaceKey(event.key)) {
|
|
1687
1846
|
event.preventDefault();
|
|
1688
1847
|
toggleDropdown();
|
|
1689
1848
|
return;
|
|
1690
1849
|
}
|
|
1691
|
-
if (event.key === "ArrowDown") {
|
|
1850
|
+
if (settings.keyboard.arrows && event.key === "ArrowDown") {
|
|
1692
1851
|
event.preventDefault();
|
|
1693
1852
|
if (!isOpen) {
|
|
1694
1853
|
openDropdown("first");
|
|
@@ -1697,7 +1856,7 @@ function InnerMultiSelectDropdown({
|
|
|
1697
1856
|
}
|
|
1698
1857
|
return;
|
|
1699
1858
|
}
|
|
1700
|
-
if (event.key === "ArrowUp") {
|
|
1859
|
+
if (settings.keyboard.arrows && event.key === "ArrowUp") {
|
|
1701
1860
|
event.preventDefault();
|
|
1702
1861
|
if (!isOpen) {
|
|
1703
1862
|
openDropdown("last");
|
|
@@ -1706,48 +1865,84 @@ function InnerMultiSelectDropdown({
|
|
|
1706
1865
|
}
|
|
1707
1866
|
return;
|
|
1708
1867
|
}
|
|
1709
|
-
if (event.key === "Escape" && isOpen) {
|
|
1868
|
+
if (settings.keyboard.escape && event.key === "Escape" && isOpen) {
|
|
1710
1869
|
event.preventDefault();
|
|
1711
1870
|
closeDropdown(true);
|
|
1712
1871
|
}
|
|
1713
1872
|
};
|
|
1714
1873
|
const handleArrowButtonKeyDown = (event) => {
|
|
1715
|
-
if (event.key
|
|
1874
|
+
if (isSpaceKey(event.key) && !settings.keyboard.space) {
|
|
1875
|
+
event.preventDefault();
|
|
1876
|
+
event.stopPropagation();
|
|
1877
|
+
return;
|
|
1878
|
+
}
|
|
1879
|
+
if (event.key === "Enter" || isSpaceKey(event.key)) {
|
|
1716
1880
|
event.preventDefault();
|
|
1717
1881
|
event.stopPropagation();
|
|
1718
1882
|
toggleDropdown();
|
|
1719
1883
|
return;
|
|
1720
1884
|
}
|
|
1721
|
-
if (event.key === "ArrowDown") {
|
|
1885
|
+
if (settings.keyboard.arrows && event.key === "ArrowDown") {
|
|
1722
1886
|
event.preventDefault();
|
|
1723
1887
|
event.stopPropagation();
|
|
1724
|
-
|
|
1888
|
+
if (isOpen) {
|
|
1889
|
+
focusFirstOption();
|
|
1890
|
+
} else {
|
|
1891
|
+
openDropdown("first");
|
|
1892
|
+
}
|
|
1725
1893
|
return;
|
|
1726
1894
|
}
|
|
1727
|
-
if (event.key === "ArrowUp") {
|
|
1895
|
+
if (settings.keyboard.arrows && event.key === "ArrowUp") {
|
|
1728
1896
|
event.preventDefault();
|
|
1729
1897
|
event.stopPropagation();
|
|
1730
|
-
|
|
1898
|
+
if (isOpen) {
|
|
1899
|
+
focusLastOption();
|
|
1900
|
+
} else {
|
|
1901
|
+
openDropdown("last");
|
|
1902
|
+
}
|
|
1731
1903
|
}
|
|
1732
1904
|
};
|
|
1733
1905
|
const handleSearchKeyDown = (event) => {
|
|
1734
|
-
if (event.key
|
|
1906
|
+
if (isSpaceKey(event.key) && !settings.keyboard.space) {
|
|
1907
|
+
event.preventDefault();
|
|
1908
|
+
return;
|
|
1909
|
+
}
|
|
1910
|
+
if (settings.keyboard.backspaceRemovesLastWhenSearchEmpty && event.key === "Backspace" && !filter && selectedItems.length > 0 && !settings.singleSelection) {
|
|
1911
|
+
event.preventDefault();
|
|
1912
|
+
removeLastSelectedItem();
|
|
1913
|
+
return;
|
|
1914
|
+
}
|
|
1915
|
+
if (settings.keyboard.arrows && event.key === "ArrowDown") {
|
|
1735
1916
|
event.preventDefault();
|
|
1736
1917
|
focusFirstOption();
|
|
1737
1918
|
return;
|
|
1738
1919
|
}
|
|
1739
|
-
if (event.key === "Escape"
|
|
1920
|
+
if (settings.keyboard.escape && event.key === "Escape") {
|
|
1740
1921
|
event.preventDefault();
|
|
1741
1922
|
closeDropdown(true);
|
|
1742
1923
|
}
|
|
1743
1924
|
};
|
|
1744
1925
|
const handleOptionKeyDown = (event, item, optionIndex) => {
|
|
1745
|
-
if (event.key
|
|
1926
|
+
if (isSpaceKey(event.key) && !settings.keyboard.space) {
|
|
1927
|
+
event.preventDefault();
|
|
1928
|
+
return;
|
|
1929
|
+
}
|
|
1930
|
+
if ((event.key === "Enter" || isSpaceKey(event.key)) && event.repeat) {
|
|
1746
1931
|
event.preventDefault();
|
|
1747
|
-
selectItem(item);
|
|
1748
1932
|
return;
|
|
1749
1933
|
}
|
|
1750
|
-
if (event.key === "
|
|
1934
|
+
if (event.key === "Enter" || isSpaceKey(event.key)) {
|
|
1935
|
+
event.preventDefault();
|
|
1936
|
+
const willClose = settings.singleSelection || !isSelected(item) && settings.closeDropDownOnSelection;
|
|
1937
|
+
const currentOptionId = event.currentTarget.id;
|
|
1938
|
+
const moveToNextOption = isSpaceKey(event.key) && settings.keyboard.spaceOptionAction === "toggle-and-next";
|
|
1939
|
+
selectItem(item, willClose ? "trigger" : "none");
|
|
1940
|
+
if (!willClose) {
|
|
1941
|
+
focusOptionAfterKeyboardSelection(currentOptionId, optionIndex, moveToNextOption);
|
|
1942
|
+
}
|
|
1943
|
+
return;
|
|
1944
|
+
}
|
|
1945
|
+
if (settings.keyboard.arrows && event.key === "ArrowDown") {
|
|
1751
1946
|
event.preventDefault();
|
|
1752
1947
|
const nextIndex = optionIndex + 1;
|
|
1753
1948
|
const options = getOptionElements();
|
|
@@ -1758,7 +1953,7 @@ function InnerMultiSelectDropdown({
|
|
|
1758
1953
|
}
|
|
1759
1954
|
return;
|
|
1760
1955
|
}
|
|
1761
|
-
if (event.key === "ArrowUp") {
|
|
1956
|
+
if (settings.keyboard.arrows && event.key === "ArrowUp") {
|
|
1762
1957
|
event.preventDefault();
|
|
1763
1958
|
if (optionIndex > 0) {
|
|
1764
1959
|
focusOptionByIndex(optionIndex - 1);
|
|
@@ -1769,21 +1964,54 @@ function InnerMultiSelectDropdown({
|
|
|
1769
1964
|
}
|
|
1770
1965
|
return;
|
|
1771
1966
|
}
|
|
1772
|
-
if (event.key === "Home") {
|
|
1967
|
+
if (settings.keyboard.arrows && event.key === "Home") {
|
|
1773
1968
|
event.preventDefault();
|
|
1774
1969
|
focusFirstOption();
|
|
1775
1970
|
return;
|
|
1776
1971
|
}
|
|
1777
|
-
if (event.key === "End") {
|
|
1972
|
+
if (settings.keyboard.arrows && event.key === "End") {
|
|
1778
1973
|
event.preventDefault();
|
|
1779
1974
|
focusLastOption();
|
|
1780
1975
|
return;
|
|
1781
1976
|
}
|
|
1782
|
-
if (event.key === "Escape"
|
|
1977
|
+
if (settings.keyboard.escape && event.key === "Escape") {
|
|
1783
1978
|
event.preventDefault();
|
|
1784
1979
|
closeDropdown(true);
|
|
1785
1980
|
}
|
|
1786
1981
|
};
|
|
1982
|
+
const slotState = {
|
|
1983
|
+
settings,
|
|
1984
|
+
isOpen,
|
|
1985
|
+
filter,
|
|
1986
|
+
selectedItems,
|
|
1987
|
+
visibleBadges,
|
|
1988
|
+
hiddenBadgeCount,
|
|
1989
|
+
filteredItems,
|
|
1990
|
+
selectableItems,
|
|
1991
|
+
allFilteredSelected,
|
|
1992
|
+
hasFilteredResults,
|
|
1993
|
+
loading: Boolean(loading ?? settings.loading),
|
|
1994
|
+
listboxId,
|
|
1995
|
+
activeDescendantId: activeDescendantId || void 0,
|
|
1996
|
+
label: selectedItems.length ? selectedItems.map((item) => getLabel(item, settings)).join(", ") : settings.text
|
|
1997
|
+
};
|
|
1998
|
+
const slotActions = {
|
|
1999
|
+
openDropdown: () => openDropdown("search"),
|
|
2000
|
+
closeDropdown: () => closeDropdown(),
|
|
2001
|
+
toggleDropdown,
|
|
2002
|
+
clearSelection,
|
|
2003
|
+
selectItem: (item) => selectItem(item),
|
|
2004
|
+
removeItem: (item) => removeItem(item),
|
|
2005
|
+
selectAll: (items = selectableItems) => selectAllItems(items),
|
|
2006
|
+
deSelectAll: (items = selectedItems) => deSelectAllItems(items),
|
|
2007
|
+
toggleGroup,
|
|
2008
|
+
addFilterNewItem: handleAddFilterNewItem,
|
|
2009
|
+
setFilter
|
|
2010
|
+
};
|
|
2011
|
+
const slotBase = {
|
|
2012
|
+
state: slotState,
|
|
2013
|
+
actions: slotActions
|
|
2014
|
+
};
|
|
1787
2015
|
const renderItemNode = (item) => {
|
|
1788
2016
|
const context = {
|
|
1789
2017
|
item,
|
|
@@ -1799,49 +2027,122 @@ function InnerMultiSelectDropdown({
|
|
|
1799
2027
|
!isPrimitiveItem(item) && item.caption ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rmsd-option-hint", children: String(item.caption) }) : null
|
|
1800
2028
|
] });
|
|
1801
2029
|
};
|
|
1802
|
-
const
|
|
2030
|
+
const renderBadgeLabel = (item) => {
|
|
2031
|
+
const label = getLabel(item, settings);
|
|
1803
2032
|
const context = {
|
|
1804
2033
|
item,
|
|
1805
|
-
label
|
|
2034
|
+
label,
|
|
1806
2035
|
selected: true,
|
|
1807
2036
|
disabled: settings.disabled || isDisabledItem(item),
|
|
1808
2037
|
query: filter,
|
|
1809
2038
|
toggle: () => selectItem(item),
|
|
1810
2039
|
remove: () => removeItem(item)
|
|
1811
2040
|
};
|
|
1812
|
-
|
|
2041
|
+
const badgeContent = renderBadge ? renderBadge(item, context) : context.label;
|
|
2042
|
+
const badgeLabelProps = { className: "rmsd-badge-label" };
|
|
2043
|
+
return renderSlot(
|
|
2044
|
+
slots?.BadgeLabel,
|
|
2045
|
+
{ ...slotBase, props: badgeLabelProps, item, label, children: badgeContent },
|
|
2046
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...badgeLabelProps, children: badgeContent })
|
|
2047
|
+
);
|
|
2048
|
+
};
|
|
2049
|
+
const renderCheckbox = (checked, context) => {
|
|
2050
|
+
if (!settings.showCheckbox) {
|
|
2051
|
+
return null;
|
|
2052
|
+
}
|
|
2053
|
+
const checkboxProps = {
|
|
2054
|
+
className: "rmsd-checkbox",
|
|
2055
|
+
"data-checked": checked,
|
|
2056
|
+
"aria-hidden": true
|
|
2057
|
+
};
|
|
2058
|
+
return renderSlot(
|
|
2059
|
+
slots?.Checkbox,
|
|
2060
|
+
{ ...slotBase, props: checkboxProps, checked, context },
|
|
2061
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...checkboxProps })
|
|
2062
|
+
);
|
|
2063
|
+
};
|
|
2064
|
+
const renderBadgeRemoveButton = (item) => {
|
|
2065
|
+
const label = getLabel(item, settings);
|
|
2066
|
+
const removeProps = {
|
|
2067
|
+
type: "button",
|
|
2068
|
+
className: "rmsd-badge-remove",
|
|
2069
|
+
"aria-label": getRemoveItemAriaLabel(item),
|
|
2070
|
+
onKeyDown: (event) => handleBadgeRemoveKeyDown(event, item),
|
|
2071
|
+
onClick: (event) => {
|
|
2072
|
+
event.stopPropagation();
|
|
2073
|
+
removeItem(item);
|
|
2074
|
+
}
|
|
2075
|
+
};
|
|
2076
|
+
const icon = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StacklineIcon, { name: "remove" });
|
|
2077
|
+
return renderSlot(
|
|
2078
|
+
slots?.BadgeRemove,
|
|
2079
|
+
{ ...slotBase, props: removeProps, item, label, icon },
|
|
2080
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { ...removeProps, children: icon })
|
|
2081
|
+
);
|
|
2082
|
+
};
|
|
2083
|
+
const renderBadgeNode = (item) => {
|
|
2084
|
+
const label = getLabel(item, settings);
|
|
2085
|
+
const badgeProps = { className: "rmsd-badge" };
|
|
2086
|
+
const removeButton = !settings.disabled ? renderBadgeRemoveButton(item) : null;
|
|
2087
|
+
const children = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
2088
|
+
renderBadgeLabel(item),
|
|
2089
|
+
removeButton
|
|
2090
|
+
] });
|
|
2091
|
+
return renderSlot(
|
|
2092
|
+
slots?.Badge,
|
|
2093
|
+
{ ...slotBase, props: badgeProps, item, label, children, removeButton },
|
|
2094
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...badgeProps, children })
|
|
2095
|
+
);
|
|
1813
2096
|
};
|
|
1814
|
-
let
|
|
2097
|
+
let enabledOptionCursor = -1;
|
|
1815
2098
|
const renderOption = (item, prefix, localIndex) => {
|
|
1816
2099
|
const selected = isSelected(item);
|
|
1817
2100
|
const disabled = settings.disabled || isDisabledItem(item) || limitReached && !selected;
|
|
1818
|
-
|
|
1819
|
-
const optionIndex = optionCursor;
|
|
2101
|
+
const optionIndex = disabled ? -1 : enabledOptionCursor += 1;
|
|
1820
2102
|
const optionId = getOptionId(item, localIndex, prefix);
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
2103
|
+
const optionKey = `${prefix}-${getPrimaryValue(item, settings)}-${localIndex}`;
|
|
2104
|
+
const optionSlot = {
|
|
2105
|
+
item,
|
|
2106
|
+
id: optionId,
|
|
2107
|
+
key: optionKey,
|
|
2108
|
+
label: getLabel(item, settings),
|
|
2109
|
+
selected,
|
|
2110
|
+
disabled,
|
|
2111
|
+
index: optionIndex,
|
|
2112
|
+
groupName: prefix.startsWith("group-") ? prefix : void 0
|
|
2113
|
+
};
|
|
2114
|
+
const optionProps = {
|
|
2115
|
+
id: optionId,
|
|
2116
|
+
className: `rmsd-option${selected ? " rmsd-selected" : ""}${disabled ? " rmsd-disabled" : ""}`,
|
|
2117
|
+
role: "option",
|
|
2118
|
+
"aria-selected": selected,
|
|
2119
|
+
"aria-checked": selected,
|
|
2120
|
+
"aria-disabled": disabled,
|
|
2121
|
+
tabIndex: disabled || !settings.keyboard.tab ? -1 : 0,
|
|
2122
|
+
"data-rmsd-option": "true",
|
|
2123
|
+
onFocus: () => setActiveDescendantId(optionId),
|
|
2124
|
+
onClick: () => {
|
|
2125
|
+
if (disabled) {
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
2128
|
+
const willClose = settings.singleSelection || !isSelected(item) && settings.closeDropDownOnSelection;
|
|
2129
|
+
selectItem(item, willClose ? "trigger" : "none");
|
|
2130
|
+
if (!willClose) {
|
|
2131
|
+
focusOptionAfterPointerSelection(optionId, optionIndex);
|
|
2132
|
+
}
|
|
1842
2133
|
},
|
|
1843
|
-
|
|
1844
|
-
|
|
2134
|
+
onKeyDown: (event) => handleOptionKeyDown(event, item, optionIndex)
|
|
2135
|
+
};
|
|
2136
|
+
const checkbox = renderCheckbox(selected, "option");
|
|
2137
|
+
const children = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
2138
|
+
checkbox,
|
|
2139
|
+
renderItemNode(item)
|
|
2140
|
+
] });
|
|
2141
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.Fragment, { children: renderSlot(
|
|
2142
|
+
slots?.Option,
|
|
2143
|
+
{ ...slotBase, props: optionProps, option: optionSlot, checkbox, children },
|
|
2144
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ...optionProps, children })
|
|
2145
|
+
) }, optionKey);
|
|
1845
2146
|
};
|
|
1846
2147
|
const handleTriggerClick = (event) => {
|
|
1847
2148
|
if (event.target.closest("button")) {
|
|
@@ -1849,174 +2150,1233 @@ function InnerMultiSelectDropdown({
|
|
|
1849
2150
|
}
|
|
1850
2151
|
toggleDropdown();
|
|
1851
2152
|
};
|
|
1852
|
-
const
|
|
1853
|
-
|
|
1854
|
-
{
|
|
2153
|
+
const renderSelectAllButton = () => {
|
|
2154
|
+
const label = allFilteredSelected ? filter.trim() ? settings.filterUnSelectAllText : settings.unSelectAllText : filter.trim() ? settings.filterSelectAllText : settings.selectAllText;
|
|
2155
|
+
const selectAllProps = {
|
|
2156
|
+
type: "button",
|
|
2157
|
+
className: "rmsd-inline-button rmsd-select-all-button",
|
|
2158
|
+
onClick: () => allFilteredSelected ? deSelectAllItems(selectableItems, Boolean(filter.trim())) : selectAllItems(selectableItems, Boolean(filter.trim())),
|
|
2159
|
+
onKeyDown: stopInlineKey,
|
|
2160
|
+
disabled: settings.disabled || selectableItems.length === 0
|
|
2161
|
+
};
|
|
2162
|
+
const checkbox = renderCheckbox(allFilteredSelected, "selectAll");
|
|
2163
|
+
return renderSlot(
|
|
2164
|
+
slots?.SelectAll,
|
|
2165
|
+
{ ...slotBase, props: selectAllProps, checked: allFilteredSelected, label, checkbox },
|
|
2166
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { ...selectAllProps, children: [
|
|
2167
|
+
checkbox,
|
|
2168
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: label })
|
|
2169
|
+
] })
|
|
2170
|
+
);
|
|
2171
|
+
};
|
|
2172
|
+
const renderAddNewItemButton = () => {
|
|
2173
|
+
const query = filter.trim();
|
|
2174
|
+
if (!query) {
|
|
2175
|
+
return null;
|
|
2176
|
+
}
|
|
2177
|
+
const label = `${settings.addNewButtonText} "${query}"`;
|
|
2178
|
+
const addItemProps = {
|
|
2179
|
+
type: "button",
|
|
2180
|
+
className: "rmsd-inline-button rmsd-add-button",
|
|
2181
|
+
onKeyDown: stopInlineKey,
|
|
2182
|
+
onClick: handleAddFilterNewItem
|
|
2183
|
+
};
|
|
2184
|
+
return renderSlot(
|
|
2185
|
+
slots?.AddNewItem,
|
|
2186
|
+
{ ...slotBase, props: addItemProps, query, label },
|
|
2187
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { ...addItemProps, children: label })
|
|
2188
|
+
);
|
|
2189
|
+
};
|
|
2190
|
+
const renderSearchNode = () => {
|
|
2191
|
+
if (!settings.enableSearchFilter) {
|
|
2192
|
+
return null;
|
|
2193
|
+
}
|
|
2194
|
+
const searchShellProps = { className: "rmsd-search-shell" };
|
|
2195
|
+
const searchInputProps = {
|
|
2196
|
+
ref: searchRef,
|
|
2197
|
+
className: "rmsd-search-input",
|
|
2198
|
+
value: filter,
|
|
2199
|
+
onChange: (event) => setFilter(event.target.value),
|
|
2200
|
+
onKeyDown: handleSearchKeyDown,
|
|
2201
|
+
placeholder: settings.searchPlaceholderText,
|
|
2202
|
+
"aria-label": settings.searchAriaLabel
|
|
2203
|
+
};
|
|
2204
|
+
const searchClearProps = {
|
|
2205
|
+
type: "button",
|
|
2206
|
+
className: "rmsd-search-clear",
|
|
2207
|
+
"aria-label": settings.clearSearchAriaLabel,
|
|
2208
|
+
onKeyDown: stopInlineKey,
|
|
2209
|
+
onClick: () => setFilter("")
|
|
2210
|
+
};
|
|
2211
|
+
const icon = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StacklineIcon, { name: "search", className: "rmsd-search-icon" });
|
|
2212
|
+
const clearIcon = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StacklineIcon, { name: "clear" });
|
|
2213
|
+
const fallback = renderSearch ? renderSearch({ query: filter, setQuery: setFilter, closeDropdown: () => closeDropdown() }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ...searchShellProps, children: [
|
|
2214
|
+
icon,
|
|
2215
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { ...searchInputProps }),
|
|
2216
|
+
filter ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { ...searchClearProps, children: clearIcon }) : null
|
|
2217
|
+
] });
|
|
2218
|
+
return renderSlot(
|
|
2219
|
+
slots?.Search,
|
|
2220
|
+
{
|
|
2221
|
+
...slotBase,
|
|
2222
|
+
props: searchShellProps,
|
|
2223
|
+
inputProps: searchInputProps,
|
|
2224
|
+
clearButtonProps: searchClearProps,
|
|
2225
|
+
query: filter,
|
|
2226
|
+
icon,
|
|
2227
|
+
clearIcon
|
|
2228
|
+
},
|
|
2229
|
+
fallback
|
|
2230
|
+
);
|
|
2231
|
+
};
|
|
2232
|
+
const renderGroupAction = (group) => {
|
|
2233
|
+
if (!settings.selectGroup || settings.singleSelection) {
|
|
2234
|
+
return null;
|
|
2235
|
+
}
|
|
2236
|
+
const label = group.selected ? "Unselect" : "Select";
|
|
2237
|
+
const groupActionProps = {
|
|
2238
|
+
type: "button",
|
|
2239
|
+
className: "rmsd-group-action",
|
|
2240
|
+
onKeyDown: stopInlineKey,
|
|
2241
|
+
onClick: () => toggleGroup(group.name, group.items)
|
|
2242
|
+
};
|
|
2243
|
+
return renderSlot(
|
|
2244
|
+
slots?.GroupAction,
|
|
2245
|
+
{ ...slotBase, props: groupActionProps, group, label },
|
|
2246
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { ...groupActionProps, children: label })
|
|
2247
|
+
);
|
|
2248
|
+
};
|
|
2249
|
+
const renderGroupNode = (group, groupIndex) => {
|
|
2250
|
+
const enabledItems = group.items.filter((item) => !isDisabledItem(item));
|
|
2251
|
+
const groupSlot = {
|
|
2252
|
+
name: group.name,
|
|
2253
|
+
items: group.items,
|
|
2254
|
+
enabledItems,
|
|
2255
|
+
selected: enabledItems.length > 0 && enabledItems.every((item) => isSelected(item)),
|
|
2256
|
+
disabled: enabledItems.length === 0,
|
|
2257
|
+
index: groupIndex
|
|
2258
|
+
};
|
|
2259
|
+
const action = renderGroupAction(groupSlot);
|
|
2260
|
+
const groupHeaderProps = { className: "rmsd-group-header" };
|
|
2261
|
+
const header = renderSlot(
|
|
2262
|
+
slots?.GroupHeader,
|
|
2263
|
+
{ ...slotBase, props: groupHeaderProps, group: groupSlot, action },
|
|
2264
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ...groupHeaderProps, children: [
|
|
2265
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
2266
|
+
group.name,
|
|
2267
|
+
" \xB7 ",
|
|
2268
|
+
group.items.length
|
|
2269
|
+
] }),
|
|
2270
|
+
action
|
|
2271
|
+
] })
|
|
2272
|
+
);
|
|
2273
|
+
const groupProps = {
|
|
2274
|
+
className: "rmsd-group",
|
|
2275
|
+
role: "group",
|
|
2276
|
+
"aria-label": group.name
|
|
2277
|
+
};
|
|
2278
|
+
const children = group.items.map((item, index) => renderOption(item, `group-${groupIndex}`, index));
|
|
2279
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.Fragment, { children: renderSlot(
|
|
2280
|
+
slots?.Group,
|
|
2281
|
+
{ ...slotBase, props: groupProps, group: groupSlot, header, children },
|
|
2282
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ...groupProps, children: [
|
|
2283
|
+
header,
|
|
2284
|
+
children
|
|
2285
|
+
] })
|
|
2286
|
+
) }, group.name);
|
|
2287
|
+
};
|
|
2288
|
+
const renderOptionListChildren = () => {
|
|
2289
|
+
if (loading ?? settings.loading) {
|
|
2290
|
+
const loadingProps = { className: "rmsd-state", role: "status" };
|
|
2291
|
+
return renderSlot(
|
|
2292
|
+
slots?.LoadingState,
|
|
2293
|
+
{ ...slotBase, props: loadingProps, text: settings.loadingText },
|
|
2294
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ...loadingProps, children: settings.loadingText })
|
|
2295
|
+
);
|
|
2296
|
+
}
|
|
2297
|
+
if (groupedItems.length > 0) {
|
|
2298
|
+
return groupedItems.map((group, groupIndex) => renderGroupNode(group, groupIndex));
|
|
2299
|
+
}
|
|
2300
|
+
if (hasFilteredResults) {
|
|
2301
|
+
return filteredItems.map((item, index) => renderOption(item, "item", index));
|
|
2302
|
+
}
|
|
2303
|
+
const emptyProps = { className: "rmsd-state" };
|
|
2304
|
+
return renderSlot(
|
|
2305
|
+
slots?.EmptyState,
|
|
2306
|
+
{ ...slotBase, props: emptyProps, query: filter, text: settings.noDataLabel },
|
|
2307
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ...emptyProps, children: renderEmptyState ? renderEmptyState(filter) : settings.noDataLabel })
|
|
2308
|
+
);
|
|
2309
|
+
};
|
|
2310
|
+
const renderOptionList = () => {
|
|
2311
|
+
const listProps = {
|
|
2312
|
+
className: "rmsd-list",
|
|
2313
|
+
ref: listRef,
|
|
2314
|
+
style: {
|
|
2315
|
+
maxHeight: shouldAppendToBody ? bodyListMaxHeight ?? settings.maxHeight : settings.maxHeight
|
|
2316
|
+
},
|
|
2317
|
+
onScroll: settings.lazyLoading ? handleListScroll : void 0,
|
|
2318
|
+
id: listboxId,
|
|
2319
|
+
role: "listbox",
|
|
2320
|
+
"aria-label": settings.listboxAriaLabel,
|
|
2321
|
+
"aria-multiselectable": !settings.singleSelection
|
|
2322
|
+
};
|
|
2323
|
+
const children = renderOptionListChildren();
|
|
2324
|
+
return renderSlot(
|
|
2325
|
+
slots?.OptionList,
|
|
2326
|
+
{ ...slotBase, props: listProps, children },
|
|
2327
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ...listProps, children })
|
|
2328
|
+
);
|
|
2329
|
+
};
|
|
2330
|
+
const renderBulkActions = () => {
|
|
2331
|
+
if (!hasBulkActions) {
|
|
2332
|
+
return null;
|
|
2333
|
+
}
|
|
2334
|
+
const bulkActionsProps = { className: "rmsd-bulk-actions" };
|
|
2335
|
+
const children = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
2336
|
+
settings.enableCheckAll && !settings.singleSelection ? renderSelectAllButton() : null,
|
|
2337
|
+
settings.addNewItemOnFilter && filter.trim() ? renderAddNewItemButton() : null
|
|
2338
|
+
] });
|
|
2339
|
+
return renderSlot(
|
|
2340
|
+
slots?.BulkActions,
|
|
2341
|
+
{ ...slotBase, props: bulkActionsProps, children },
|
|
2342
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ...bulkActionsProps, children })
|
|
2343
|
+
);
|
|
2344
|
+
};
|
|
2345
|
+
const renderToolbar = () => {
|
|
2346
|
+
const toolbarProps = { className: "rmsd-toolbar" };
|
|
2347
|
+
const children = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
2348
|
+
renderBulkActions(),
|
|
2349
|
+
renderSearchNode()
|
|
2350
|
+
] });
|
|
2351
|
+
return renderSlot(
|
|
2352
|
+
slots?.Toolbar,
|
|
2353
|
+
{ ...slotBase, props: toolbarProps, children },
|
|
2354
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ...toolbarProps, children })
|
|
2355
|
+
);
|
|
2356
|
+
};
|
|
2357
|
+
const renderMenuFooter = () => {
|
|
2358
|
+
if (!slots?.MenuFooter) {
|
|
2359
|
+
return null;
|
|
2360
|
+
}
|
|
2361
|
+
return renderSlot(slots.MenuFooter, { ...slotBase, props: { className: "rmsd-menu-footer" } }, null);
|
|
2362
|
+
};
|
|
2363
|
+
const renderMenu = () => {
|
|
2364
|
+
if (!isOpen) {
|
|
2365
|
+
return null;
|
|
2366
|
+
}
|
|
2367
|
+
const menuProps = {
|
|
1855
2368
|
ref: menuRef,
|
|
1856
2369
|
className: `rmsd-menu rmsd-${effectivePosition} skin-${skinName} theme-${skinName}${skinFallbackClass ? ` ${skinFallbackClass}` : ""}${shouldAppendToBody ? " rmsd-body-overlay" : ""}`,
|
|
1857
2370
|
style: shouldAppendToBody ? bodyMenuStyle : void 0,
|
|
1858
2371
|
onMouseDown: (event) => event.stopPropagation(),
|
|
1859
|
-
onTouchStart: (event) => event.stopPropagation()
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
onClick: () => allFilteredSelected ? deSelectAllItems(selectableItems, Boolean(filter.trim())) : selectAllItems(selectableItems, Boolean(filter.trim())),
|
|
1869
|
-
disabled: settings.disabled || selectableItems.length === 0,
|
|
1870
|
-
children: [
|
|
1871
|
-
settings.showCheckbox ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rmsd-checkbox", "data-checked": allFilteredSelected, "aria-hidden": "true" }) : null,
|
|
1872
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: allFilteredSelected ? filter.trim() ? settings.filterUnSelectAllText : settings.unSelectAllText : filter.trim() ? settings.filterSelectAllText : settings.selectAllText })
|
|
1873
|
-
]
|
|
1874
|
-
}
|
|
1875
|
-
) : null,
|
|
1876
|
-
settings.addNewItemOnFilter && filter.trim() ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rmsd-inline-button rmsd-add-button", onClick: handleAddFilterNewItem, children: [
|
|
1877
|
-
settings.addNewButtonText,
|
|
1878
|
-
' "',
|
|
1879
|
-
filter.trim(),
|
|
1880
|
-
'"'
|
|
1881
|
-
] }) : null
|
|
1882
|
-
] }) : null,
|
|
1883
|
-
settings.enableSearchFilter ? renderSearch ? renderSearch({ query: filter, setQuery: setFilter, closeDropdown: () => closeDropdown() }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rmsd-search-shell", children: [
|
|
1884
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StacklineIcon, { name: "search", className: "rmsd-search-icon" }),
|
|
1885
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1886
|
-
"input",
|
|
1887
|
-
{
|
|
1888
|
-
ref: searchRef,
|
|
1889
|
-
className: "rmsd-search-input",
|
|
1890
|
-
value: filter,
|
|
1891
|
-
onChange: (event) => setFilter(event.target.value),
|
|
1892
|
-
onKeyDown: handleSearchKeyDown,
|
|
1893
|
-
placeholder: settings.searchPlaceholderText,
|
|
1894
|
-
"aria-label": settings.searchAriaLabel
|
|
1895
|
-
}
|
|
1896
|
-
),
|
|
1897
|
-
filter ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1898
|
-
"button",
|
|
1899
|
-
{
|
|
1900
|
-
type: "button",
|
|
1901
|
-
className: "rmsd-search-clear",
|
|
1902
|
-
"aria-label": settings.clearSearchAriaLabel,
|
|
1903
|
-
onKeyDown: stopInlineKey,
|
|
1904
|
-
onClick: () => setFilter(""),
|
|
1905
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StacklineIcon, { name: "clear" })
|
|
1906
|
-
}
|
|
1907
|
-
) : null
|
|
1908
|
-
] }) : null
|
|
1909
|
-
] }),
|
|
1910
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1911
|
-
"div",
|
|
1912
|
-
{
|
|
1913
|
-
className: "rmsd-list",
|
|
1914
|
-
ref: listRef,
|
|
1915
|
-
style: { maxHeight: shouldAppendToBody ? bodyListMaxHeight ?? settings.maxHeight : settings.maxHeight },
|
|
1916
|
-
onScroll: settings.lazyLoading ? handleListScroll : void 0,
|
|
1917
|
-
id: listboxId,
|
|
1918
|
-
role: "listbox",
|
|
1919
|
-
"aria-label": settings.listboxAriaLabel,
|
|
1920
|
-
"aria-multiselectable": !settings.singleSelection,
|
|
1921
|
-
children: loading ?? settings.loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rmsd-state", role: "status", children: settings.loadingText }) : groupedItems.length > 0 ? groupedItems.map((group, groupIndex) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rmsd-group", role: "group", "aria-label": group.name, children: [
|
|
1922
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rmsd-group-header", children: [
|
|
1923
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
1924
|
-
group.name,
|
|
1925
|
-
" \xB7 ",
|
|
1926
|
-
group.items.length
|
|
1927
|
-
] }),
|
|
1928
|
-
settings.selectGroup && !settings.singleSelection ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rmsd-group-action", onClick: () => toggleGroup(group.name, group.items), children: group.items.filter((item) => !isDisabledItem(item)).every((item) => isSelected(item)) ? "Unselect" : "Select" }) : null
|
|
1929
|
-
] }),
|
|
1930
|
-
group.items.map((item, index) => renderOption(item, `group-${groupIndex}`, index))
|
|
1931
|
-
] }, group.name)) : hasFilteredResults ? filteredItems.map((item, index) => renderOption(item, "item", index)) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rmsd-state", children: renderEmptyState ? renderEmptyState(filter) : settings.noDataLabel })
|
|
1932
|
-
}
|
|
1933
|
-
)
|
|
1934
|
-
]
|
|
1935
|
-
}
|
|
1936
|
-
) : null;
|
|
1937
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: rootClassName, style, ref: rootRef, "data-open": isOpen, children: [
|
|
1938
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1939
|
-
"div",
|
|
2372
|
+
onTouchStart: (event) => event.stopPropagation()
|
|
2373
|
+
};
|
|
2374
|
+
const children = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
2375
|
+
renderToolbar(),
|
|
2376
|
+
renderOptionList(),
|
|
2377
|
+
renderMenuFooter()
|
|
2378
|
+
] });
|
|
2379
|
+
return renderSlot(
|
|
2380
|
+
slots?.Menu,
|
|
1940
2381
|
{
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
}
|
|
2008
|
-
)
|
|
2009
|
-
] })
|
|
2010
|
-
]
|
|
2382
|
+
...slotBase,
|
|
2383
|
+
props: menuProps,
|
|
2384
|
+
children,
|
|
2385
|
+
position: effectivePosition,
|
|
2386
|
+
appendToBody: shouldAppendToBody
|
|
2387
|
+
},
|
|
2388
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ...menuProps, children })
|
|
2389
|
+
);
|
|
2390
|
+
};
|
|
2391
|
+
const renderValue = () => {
|
|
2392
|
+
const valueProps = { className: "rmsd-value" };
|
|
2393
|
+
let children;
|
|
2394
|
+
if (selectedItems.length === 0) {
|
|
2395
|
+
const placeholderProps = { className: "rmsd-placeholder" };
|
|
2396
|
+
children = renderSlot(
|
|
2397
|
+
slots?.Placeholder,
|
|
2398
|
+
{ ...slotBase, props: placeholderProps, text: settings.text },
|
|
2399
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...placeholderProps, children: settings.text })
|
|
2400
|
+
);
|
|
2401
|
+
} else if (settings.singleSelection) {
|
|
2402
|
+
const item = selectedItems[0];
|
|
2403
|
+
const singleValueProps = { className: "rmsd-single-value" };
|
|
2404
|
+
children = renderSlot(
|
|
2405
|
+
slots?.SingleValue,
|
|
2406
|
+
{ ...slotBase, props: singleValueProps, item, label: getLabel(item, settings) },
|
|
2407
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...singleValueProps, children: getLabel(item, settings) })
|
|
2408
|
+
);
|
|
2409
|
+
} else {
|
|
2410
|
+
const badgeListProps = { className: "rmsd-badge-list" };
|
|
2411
|
+
const badgeListChildren = visibleBadges.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.Fragment, { children: renderBadgeNode(item) }, getPrimaryValue(item, settings)));
|
|
2412
|
+
children = renderSlot(
|
|
2413
|
+
slots?.BadgeList,
|
|
2414
|
+
{ ...slotBase, props: badgeListProps, items: visibleBadges, children: badgeListChildren },
|
|
2415
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ...badgeListProps, children: badgeListChildren })
|
|
2416
|
+
);
|
|
2417
|
+
}
|
|
2418
|
+
return renderSlot(
|
|
2419
|
+
slots?.Value,
|
|
2420
|
+
{ ...slotBase, props: valueProps, children },
|
|
2421
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ...valueProps, children })
|
|
2422
|
+
);
|
|
2423
|
+
};
|
|
2424
|
+
const renderActions = () => {
|
|
2425
|
+
const actionsProps = { className: "rmsd-actions" };
|
|
2426
|
+
const overflowProps = { className: "rmsd-overflow" };
|
|
2427
|
+
const clearAllProps = {
|
|
2428
|
+
type: "button",
|
|
2429
|
+
className: "rmsd-clear",
|
|
2430
|
+
"aria-label": settings.clearAllAriaLabel,
|
|
2431
|
+
onKeyDown: stopInlineKey,
|
|
2432
|
+
onClick: (event) => {
|
|
2433
|
+
event.stopPropagation();
|
|
2434
|
+
clearSelection();
|
|
2435
|
+
}
|
|
2436
|
+
};
|
|
2437
|
+
const arrowProps = {
|
|
2438
|
+
type: "button",
|
|
2439
|
+
className: "rmsd-arrow-button",
|
|
2440
|
+
disabled: settings.disabled,
|
|
2441
|
+
"aria-label": isOpen ? settings.closeDropdownAriaLabel : settings.openDropdownAriaLabel,
|
|
2442
|
+
"aria-expanded": isOpen,
|
|
2443
|
+
"aria-controls": listboxId,
|
|
2444
|
+
onKeyDown: handleArrowButtonKeyDown,
|
|
2445
|
+
onClick: (event) => {
|
|
2446
|
+
event.stopPropagation();
|
|
2447
|
+
toggleDropdown();
|
|
2011
2448
|
}
|
|
2012
|
-
|
|
2449
|
+
};
|
|
2450
|
+
const clearIcon = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StacklineIcon, { name: "remove" });
|
|
2451
|
+
const arrowIcon = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rmsd-arrow", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StacklineIcon, { name: isOpen ? "angle-up" : "angle-down" }) });
|
|
2452
|
+
const children = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
2453
|
+
hiddenBadgeCount > 0 ? renderSlot(
|
|
2454
|
+
slots?.OverflowCounter,
|
|
2455
|
+
{ ...slotBase, props: overflowProps, count: hiddenBadgeCount },
|
|
2456
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { ...overflowProps, children: [
|
|
2457
|
+
"+",
|
|
2458
|
+
hiddenBadgeCount
|
|
2459
|
+
] })
|
|
2460
|
+
) : null,
|
|
2461
|
+
settings.clearAll && selectedItems.length > 0 && !settings.disabled ? renderSlot(
|
|
2462
|
+
slots?.ClearAll,
|
|
2463
|
+
{ ...slotBase, props: clearAllProps, icon: clearIcon },
|
|
2464
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { ...clearAllProps, children: clearIcon })
|
|
2465
|
+
) : null,
|
|
2466
|
+
renderSlot(
|
|
2467
|
+
slots?.Arrow,
|
|
2468
|
+
{ ...slotBase, props: arrowProps, icon: arrowIcon, direction: isOpen ? "up" : "down" },
|
|
2469
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { ...arrowProps, children: arrowIcon })
|
|
2470
|
+
)
|
|
2471
|
+
] });
|
|
2472
|
+
return renderSlot(
|
|
2473
|
+
slots?.Actions,
|
|
2474
|
+
{ ...slotBase, props: actionsProps, children },
|
|
2475
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ...actionsProps, children })
|
|
2476
|
+
);
|
|
2477
|
+
};
|
|
2478
|
+
const renderTrigger = () => {
|
|
2479
|
+
const triggerProps = {
|
|
2480
|
+
ref: triggerRef,
|
|
2481
|
+
className: `rmsd-trigger${settings.disabled ? " rmsd-disabled" : ""}`,
|
|
2482
|
+
onClick: handleTriggerClick,
|
|
2483
|
+
onKeyDown: handleTriggerKeyDown,
|
|
2484
|
+
tabIndex: settings.disabled ? -1 : 0,
|
|
2485
|
+
role: "combobox",
|
|
2486
|
+
"aria-expanded": isOpen,
|
|
2487
|
+
"aria-haspopup": "listbox",
|
|
2488
|
+
"aria-controls": listboxId,
|
|
2489
|
+
"aria-disabled": settings.disabled,
|
|
2490
|
+
"aria-activedescendant": activeDescendantId || void 0,
|
|
2491
|
+
"aria-label": getTriggerAriaLabel()
|
|
2492
|
+
};
|
|
2493
|
+
const children = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
2494
|
+
renderValue(),
|
|
2495
|
+
renderActions()
|
|
2496
|
+
] });
|
|
2497
|
+
return renderSlot(
|
|
2498
|
+
slots?.Trigger,
|
|
2499
|
+
{ ...slotBase, props: triggerProps, children },
|
|
2500
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ...triggerProps, children })
|
|
2501
|
+
);
|
|
2502
|
+
};
|
|
2503
|
+
const menu = renderMenu();
|
|
2504
|
+
const rootChildren = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
2505
|
+
renderTrigger(),
|
|
2013
2506
|
shouldAppendToBody && menu && typeof document !== "undefined" ? (0, import_react_dom.createPortal)(menu, document.body) : menu
|
|
2014
2507
|
] });
|
|
2508
|
+
const rootProps = {
|
|
2509
|
+
className: rootClassName,
|
|
2510
|
+
style,
|
|
2511
|
+
ref: rootRef,
|
|
2512
|
+
"data-open": isOpen
|
|
2513
|
+
};
|
|
2514
|
+
return renderSlot(
|
|
2515
|
+
slots?.Root,
|
|
2516
|
+
{ ...slotBase, props: rootProps, children: rootChildren },
|
|
2517
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ...rootProps, children: rootChildren })
|
|
2518
|
+
);
|
|
2015
2519
|
}
|
|
2016
|
-
var ReactMultiSelectDropdown = (0,
|
|
2520
|
+
var ReactMultiSelectDropdown = (0, import_react2.forwardRef)(InnerMultiSelectDropdown);
|
|
2017
2521
|
var MultiSelectDropdown = ReactMultiSelectDropdown;
|
|
2522
|
+
|
|
2523
|
+
// src/useMultiSelectDropdown.ts
|
|
2524
|
+
var import_react4 = require("react");
|
|
2525
|
+
|
|
2526
|
+
// src/reactUtils.ts
|
|
2527
|
+
function assignRef(ref, value) {
|
|
2528
|
+
if (typeof ref === "function") {
|
|
2529
|
+
ref(value);
|
|
2530
|
+
return;
|
|
2531
|
+
}
|
|
2532
|
+
if (ref && "current" in ref) {
|
|
2533
|
+
ref.current = value;
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
function eventWasPrevented(event) {
|
|
2537
|
+
return Boolean(event.defaultPrevented);
|
|
2538
|
+
}
|
|
2539
|
+
function callAll(ownHandler, userHandler) {
|
|
2540
|
+
return (event) => {
|
|
2541
|
+
userHandler?.(event);
|
|
2542
|
+
if (!eventWasPrevented(event)) {
|
|
2543
|
+
ownHandler?.(event);
|
|
2544
|
+
}
|
|
2545
|
+
};
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
// src/useMultiSelectState.ts
|
|
2549
|
+
var import_react3 = require("react");
|
|
2550
|
+
function useMultiSelectState({
|
|
2551
|
+
data,
|
|
2552
|
+
settings: incomingSettings,
|
|
2553
|
+
selectedItems: controlledSelectedItems,
|
|
2554
|
+
defaultSelectedItems,
|
|
2555
|
+
onChange,
|
|
2556
|
+
onSelect,
|
|
2557
|
+
onDeSelect,
|
|
2558
|
+
onSelectAll,
|
|
2559
|
+
onDeSelectAll,
|
|
2560
|
+
onFilterSelectAll,
|
|
2561
|
+
onFilterDeSelectAll,
|
|
2562
|
+
onAddFilterNewItem,
|
|
2563
|
+
onGroupSelect,
|
|
2564
|
+
onGroupDeSelect,
|
|
2565
|
+
onSelectionShouldClose
|
|
2566
|
+
}) {
|
|
2567
|
+
const [filter, setFilter] = (0, import_react3.useState)("");
|
|
2568
|
+
const [addedItems, setAddedItems] = (0, import_react3.useState)([]);
|
|
2569
|
+
const addRequestIdRef = (0, import_react3.useRef)(0);
|
|
2570
|
+
const settings = (0, import_react3.useMemo)(() => resolveDropdownSettings(incomingSettings), [incomingSettings]);
|
|
2571
|
+
const [selectedItems, setSelectedItems] = useControllableSelection(
|
|
2572
|
+
controlledSelectedItems,
|
|
2573
|
+
defaultSelectedItems,
|
|
2574
|
+
onChange
|
|
2575
|
+
);
|
|
2576
|
+
const allItems = (0, import_react3.useMemo)(
|
|
2577
|
+
() => mergeUniqueItems(data, [...selectedItems, ...addedItems], settings),
|
|
2578
|
+
[addedItems, data, selectedItems, settings]
|
|
2579
|
+
);
|
|
2580
|
+
const filteredItems = (0, import_react3.useMemo)(
|
|
2581
|
+
() => allItems.filter((item) => itemMatchesQuery(item, filter, settings)),
|
|
2582
|
+
[allItems, filter, settings]
|
|
2583
|
+
);
|
|
2584
|
+
const isSelected = (item) => selectedItems.some(
|
|
2585
|
+
(selectedItem) => getPrimaryValue(selectedItem, settings) === getPrimaryValue(item, settings)
|
|
2586
|
+
);
|
|
2587
|
+
const isDisabled = (item) => settings.disabled || isDisabledItem(item);
|
|
2588
|
+
const selectableItems = filteredItems.filter((item) => !isDisabled(item));
|
|
2589
|
+
const allFilteredSelected = selectableItems.length > 0 && selectableItems.every((item) => isSelected(item));
|
|
2590
|
+
const visibleBadgeLimit = getVisibleBadgeLimit(selectedItems.length, settings.badgeShowLimit);
|
|
2591
|
+
const visibleBadges = settings.singleSelection ? selectedItems : selectedItems.slice(0, visibleBadgeLimit);
|
|
2592
|
+
const hiddenBadgeCount = settings.singleSelection ? 0 : Math.max(selectedItems.length - visibleBadges.length, 0);
|
|
2593
|
+
const updateSelection = (nextItems) => {
|
|
2594
|
+
setSelectedItems(nextItems);
|
|
2595
|
+
};
|
|
2596
|
+
const removeItem = (item) => {
|
|
2597
|
+
if (settings.disabled) {
|
|
2598
|
+
return;
|
|
2599
|
+
}
|
|
2600
|
+
const nextItems = selectedItems.filter(
|
|
2601
|
+
(selectedItem) => getPrimaryValue(selectedItem, settings) !== getPrimaryValue(item, settings)
|
|
2602
|
+
);
|
|
2603
|
+
updateSelection(nextItems);
|
|
2604
|
+
onDeSelect?.(item);
|
|
2605
|
+
};
|
|
2606
|
+
const removeLastSelectedItem = () => {
|
|
2607
|
+
const lastItem = selectedItems[selectedItems.length - 1];
|
|
2608
|
+
if (!lastItem || settings.disabled) {
|
|
2609
|
+
return;
|
|
2610
|
+
}
|
|
2611
|
+
removeItem(lastItem);
|
|
2612
|
+
};
|
|
2613
|
+
const selectItem = (item) => {
|
|
2614
|
+
if (settings.disabled || isDisabledItem(item)) {
|
|
2615
|
+
return;
|
|
2616
|
+
}
|
|
2617
|
+
if (isSelected(item)) {
|
|
2618
|
+
if (settings.singleSelection) {
|
|
2619
|
+
onSelectionShouldClose?.();
|
|
2620
|
+
return;
|
|
2621
|
+
}
|
|
2622
|
+
removeItem(item);
|
|
2623
|
+
return;
|
|
2624
|
+
}
|
|
2625
|
+
if (settings.singleSelection) {
|
|
2626
|
+
updateSelection([item]);
|
|
2627
|
+
onSelect?.(item);
|
|
2628
|
+
onSelectionShouldClose?.();
|
|
2629
|
+
return;
|
|
2630
|
+
}
|
|
2631
|
+
if (settings.limitSelection && selectedItems.length >= settings.limitSelection) {
|
|
2632
|
+
return;
|
|
2633
|
+
}
|
|
2634
|
+
const nextItems = [...selectedItems, item];
|
|
2635
|
+
updateSelection(nextItems);
|
|
2636
|
+
onSelect?.(item);
|
|
2637
|
+
if (settings.closeDropDownOnSelection) {
|
|
2638
|
+
onSelectionShouldClose?.();
|
|
2639
|
+
}
|
|
2640
|
+
};
|
|
2641
|
+
const selectAll = (items = selectableItems, filteredSelection = false) => {
|
|
2642
|
+
if (settings.disabled || settings.singleSelection) {
|
|
2643
|
+
return;
|
|
2644
|
+
}
|
|
2645
|
+
const selectedIds = new Set(selectedItems.map((item) => getPrimaryValue(item, settings)));
|
|
2646
|
+
const remainingCapacity = settings.limitSelection ? Math.max(settings.limitSelection - selectedItems.length, 0) : Number.MAX_SAFE_INTEGER;
|
|
2647
|
+
const nextItemsToAdd = items.filter((item) => !selectedIds.has(getPrimaryValue(item, settings))).filter((item) => !isDisabledItem(item)).slice(0, remainingCapacity);
|
|
2648
|
+
const nextItems = [...selectedItems, ...nextItemsToAdd];
|
|
2649
|
+
updateSelection(nextItems);
|
|
2650
|
+
if (filteredSelection) {
|
|
2651
|
+
onFilterSelectAll?.(nextItemsToAdd);
|
|
2652
|
+
} else {
|
|
2653
|
+
onSelectAll?.(nextItems);
|
|
2654
|
+
}
|
|
2655
|
+
};
|
|
2656
|
+
const deSelectAll = (items = selectedItems, filteredSelection = false) => {
|
|
2657
|
+
if (settings.disabled) {
|
|
2658
|
+
return;
|
|
2659
|
+
}
|
|
2660
|
+
const itemIds = new Set(items.map((item) => getPrimaryValue(item, settings)));
|
|
2661
|
+
const nextItems = selectedItems.filter((item) => !itemIds.has(getPrimaryValue(item, settings)));
|
|
2662
|
+
updateSelection(nextItems);
|
|
2663
|
+
if (filteredSelection) {
|
|
2664
|
+
onFilterDeSelectAll?.(items);
|
|
2665
|
+
} else {
|
|
2666
|
+
onDeSelectAll?.(items);
|
|
2667
|
+
}
|
|
2668
|
+
};
|
|
2669
|
+
const clearSelection = () => deSelectAll(selectedItems);
|
|
2670
|
+
const toggleGroup = (groupName, items) => {
|
|
2671
|
+
if (settings.disabled) {
|
|
2672
|
+
return;
|
|
2673
|
+
}
|
|
2674
|
+
const enabledItems = items.filter((item) => !isDisabledItem(item));
|
|
2675
|
+
const allSelected = enabledItems.length > 0 && enabledItems.every((item) => isSelected(item));
|
|
2676
|
+
if (allSelected) {
|
|
2677
|
+
deSelectAll(enabledItems);
|
|
2678
|
+
onGroupDeSelect?.(groupName, enabledItems);
|
|
2679
|
+
return;
|
|
2680
|
+
}
|
|
2681
|
+
selectAll(enabledItems);
|
|
2682
|
+
onGroupSelect?.(groupName, enabledItems);
|
|
2683
|
+
};
|
|
2684
|
+
const addFilterNewItem = async () => {
|
|
2685
|
+
if (settings.disabled) {
|
|
2686
|
+
return;
|
|
2687
|
+
}
|
|
2688
|
+
const query = filter.trim();
|
|
2689
|
+
if (!query) {
|
|
2690
|
+
return;
|
|
2691
|
+
}
|
|
2692
|
+
const requestId = addRequestIdRef.current + 1;
|
|
2693
|
+
addRequestIdRef.current = requestId;
|
|
2694
|
+
const result = await onAddFilterNewItem?.(query);
|
|
2695
|
+
if (requestId !== addRequestIdRef.current) {
|
|
2696
|
+
return;
|
|
2697
|
+
}
|
|
2698
|
+
const nextItem = result === void 0 ? createItemFromQuery(query, settings, data[0]) : result;
|
|
2699
|
+
setAddedItems((currentItems) => mergeUniqueItems(currentItems, [nextItem], settings));
|
|
2700
|
+
if (settings.singleSelection) {
|
|
2701
|
+
updateSelection([nextItem]);
|
|
2702
|
+
} else {
|
|
2703
|
+
updateSelection(mergeUniqueItems(selectedItems, [nextItem], settings));
|
|
2704
|
+
}
|
|
2705
|
+
setFilter("");
|
|
2706
|
+
};
|
|
2707
|
+
return {
|
|
2708
|
+
settings,
|
|
2709
|
+
filter,
|
|
2710
|
+
setFilter,
|
|
2711
|
+
selectedItems,
|
|
2712
|
+
allItems,
|
|
2713
|
+
filteredItems,
|
|
2714
|
+
selectableItems,
|
|
2715
|
+
visibleBadges,
|
|
2716
|
+
hiddenBadgeCount,
|
|
2717
|
+
allFilteredSelected,
|
|
2718
|
+
isSelected,
|
|
2719
|
+
isDisabled,
|
|
2720
|
+
getItemLabel: (item) => getLabel(item, settings),
|
|
2721
|
+
getItemKey: (item) => getPrimaryValue(item, settings),
|
|
2722
|
+
selectItem,
|
|
2723
|
+
removeItem,
|
|
2724
|
+
removeLastSelectedItem,
|
|
2725
|
+
selectAll,
|
|
2726
|
+
deSelectAll,
|
|
2727
|
+
clearSelection,
|
|
2728
|
+
toggleGroup,
|
|
2729
|
+
addFilterNewItem
|
|
2730
|
+
};
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2733
|
+
// src/useMultiSelectDropdown.ts
|
|
2734
|
+
function isSpaceKey2(key) {
|
|
2735
|
+
return key === " " || key === "Spacebar";
|
|
2736
|
+
}
|
|
2737
|
+
function useMultiSelectDropdown({
|
|
2738
|
+
id,
|
|
2739
|
+
data,
|
|
2740
|
+
settings: incomingSettings,
|
|
2741
|
+
selectedItems: controlledSelectedItems,
|
|
2742
|
+
defaultSelectedItems,
|
|
2743
|
+
onChange,
|
|
2744
|
+
onSelect,
|
|
2745
|
+
onDeSelect,
|
|
2746
|
+
onSelectAll,
|
|
2747
|
+
onDeSelectAll,
|
|
2748
|
+
onOpen,
|
|
2749
|
+
onClose,
|
|
2750
|
+
onScrollToEnd,
|
|
2751
|
+
onFilterSelectAll,
|
|
2752
|
+
onFilterDeSelectAll,
|
|
2753
|
+
onAddFilterNewItem,
|
|
2754
|
+
onGroupSelect,
|
|
2755
|
+
onGroupDeSelect
|
|
2756
|
+
}) {
|
|
2757
|
+
const reactId = (0, import_react4.useId)();
|
|
2758
|
+
const instanceId = sanitizeId(id || `stackline-multiselect-${reactId}`);
|
|
2759
|
+
const rootRef = (0, import_react4.useRef)(null);
|
|
2760
|
+
const triggerRef = (0, import_react4.useRef)(null);
|
|
2761
|
+
const searchRef = (0, import_react4.useRef)(null);
|
|
2762
|
+
const listboxRef = (0, import_react4.useRef)(null);
|
|
2763
|
+
const optionRefs = (0, import_react4.useRef)(/* @__PURE__ */ new Map());
|
|
2764
|
+
const lastScrollHeightRef = (0, import_react4.useRef)(0);
|
|
2765
|
+
const closeDropdownRef = (0, import_react4.useRef)(() => void 0);
|
|
2766
|
+
const pendingFocusRef = (0, import_react4.useRef)("search");
|
|
2767
|
+
const [isOpen, setIsOpen] = (0, import_react4.useState)(false);
|
|
2768
|
+
const [activeOptionIndex, setActiveOptionIndex] = (0, import_react4.useState)(-1);
|
|
2769
|
+
const listboxId = `${instanceId}-listbox`;
|
|
2770
|
+
const state = useMultiSelectState({
|
|
2771
|
+
data,
|
|
2772
|
+
settings: incomingSettings,
|
|
2773
|
+
selectedItems: controlledSelectedItems,
|
|
2774
|
+
defaultSelectedItems,
|
|
2775
|
+
onChange,
|
|
2776
|
+
onSelect,
|
|
2777
|
+
onDeSelect,
|
|
2778
|
+
onSelectAll,
|
|
2779
|
+
onDeSelectAll,
|
|
2780
|
+
onFilterSelectAll,
|
|
2781
|
+
onFilterDeSelectAll,
|
|
2782
|
+
onAddFilterNewItem,
|
|
2783
|
+
onGroupSelect,
|
|
2784
|
+
onGroupDeSelect,
|
|
2785
|
+
onSelectionShouldClose: () => closeDropdownRef.current()
|
|
2786
|
+
});
|
|
2787
|
+
const {
|
|
2788
|
+
settings,
|
|
2789
|
+
filter,
|
|
2790
|
+
setFilter,
|
|
2791
|
+
selectedItems,
|
|
2792
|
+
filteredItems,
|
|
2793
|
+
selectableItems,
|
|
2794
|
+
visibleBadges,
|
|
2795
|
+
hiddenBadgeCount,
|
|
2796
|
+
allFilteredSelected,
|
|
2797
|
+
isSelected,
|
|
2798
|
+
isDisabled
|
|
2799
|
+
} = state;
|
|
2800
|
+
const limitReached = Boolean(settings.limitSelection) && selectedItems.length >= settings.limitSelection;
|
|
2801
|
+
const options = (0, import_react4.useMemo)(() => {
|
|
2802
|
+
let optionIndex = -1;
|
|
2803
|
+
return filteredItems.map((item) => {
|
|
2804
|
+
optionIndex += 1;
|
|
2805
|
+
const groupName = getGroupName(item, settings) || void 0;
|
|
2806
|
+
const selected = selectedItems.some(
|
|
2807
|
+
(selectedItem) => getPrimaryValue(selectedItem, settings) === getPrimaryValue(item, settings)
|
|
2808
|
+
);
|
|
2809
|
+
const disabled = settings.disabled || isDisabledItem(item) || limitReached && !selected;
|
|
2810
|
+
const key = getPrimaryValue(item, settings);
|
|
2811
|
+
return {
|
|
2812
|
+
item,
|
|
2813
|
+
key,
|
|
2814
|
+
id: `${instanceId}-option-${optionIndex}-${sanitizeId(key)}`,
|
|
2815
|
+
label: getLabel(item, settings),
|
|
2816
|
+
selected,
|
|
2817
|
+
disabled,
|
|
2818
|
+
index: optionIndex,
|
|
2819
|
+
groupName
|
|
2820
|
+
};
|
|
2821
|
+
});
|
|
2822
|
+
}, [filteredItems, instanceId, limitReached, selectedItems, settings]);
|
|
2823
|
+
const selectableOptions = options.filter((option) => !option.disabled);
|
|
2824
|
+
const selectedOptions = options.filter((option) => option.selected);
|
|
2825
|
+
const groups = (0, import_react4.useMemo)(() => {
|
|
2826
|
+
if (!settings.groupBy) {
|
|
2827
|
+
return [];
|
|
2828
|
+
}
|
|
2829
|
+
const bucket = /* @__PURE__ */ new Map();
|
|
2830
|
+
for (const option of options) {
|
|
2831
|
+
const groupName = option.groupName || "Ungrouped";
|
|
2832
|
+
const current = bucket.get(groupName) || [];
|
|
2833
|
+
current.push(option);
|
|
2834
|
+
bucket.set(groupName, current);
|
|
2835
|
+
}
|
|
2836
|
+
return Array.from(bucket.entries()).map(([name, groupOptions]) => {
|
|
2837
|
+
const enabledOptions = groupOptions.filter((option) => !option.disabled);
|
|
2838
|
+
return {
|
|
2839
|
+
name,
|
|
2840
|
+
items: groupOptions,
|
|
2841
|
+
selected: enabledOptions.length > 0 && enabledOptions.every((option) => option.selected),
|
|
2842
|
+
disabled: enabledOptions.length === 0
|
|
2843
|
+
};
|
|
2844
|
+
});
|
|
2845
|
+
}, [options, settings.groupBy]);
|
|
2846
|
+
const activeOption = options.find((option) => option.index === activeOptionIndex && !option.disabled);
|
|
2847
|
+
const activeDescendantId = activeOption?.id;
|
|
2848
|
+
const hasFilteredResults = options.length > 0;
|
|
2849
|
+
const label = selectedItems.length ? selectedItems.map((item) => getLabel(item, settings)).join(", ") : settings.text;
|
|
2850
|
+
const focusOption = (index) => {
|
|
2851
|
+
if (!options.length) {
|
|
2852
|
+
setActiveOptionIndex(-1);
|
|
2853
|
+
return;
|
|
2854
|
+
}
|
|
2855
|
+
const enabledOptions = options.filter((option2) => !option2.disabled);
|
|
2856
|
+
if (!enabledOptions.length) {
|
|
2857
|
+
setActiveOptionIndex(-1);
|
|
2858
|
+
return;
|
|
2859
|
+
}
|
|
2860
|
+
const boundedIndex = Math.max(0, Math.min(index, enabledOptions.length - 1));
|
|
2861
|
+
const option = enabledOptions[boundedIndex];
|
|
2862
|
+
setActiveOptionIndex(option.index);
|
|
2863
|
+
window.setTimeout(() => {
|
|
2864
|
+
optionRefs.current.get(option.id)?.focus();
|
|
2865
|
+
optionRefs.current.get(option.id)?.scrollIntoView({ block: "nearest" });
|
|
2866
|
+
}, 0);
|
|
2867
|
+
};
|
|
2868
|
+
const focusOptionById = (optionId, fallbackIndex, moveToNextOption = false) => {
|
|
2869
|
+
setActiveOptionIndex(fallbackIndex);
|
|
2870
|
+
window.setTimeout(() => {
|
|
2871
|
+
const focusAfterRender = () => {
|
|
2872
|
+
if (moveToNextOption) {
|
|
2873
|
+
const optionNodes = Array.from(
|
|
2874
|
+
listboxRef.current?.querySelectorAll(
|
|
2875
|
+
'[data-headless-option="true"]:not([aria-disabled="true"])'
|
|
2876
|
+
) ?? []
|
|
2877
|
+
);
|
|
2878
|
+
const currentIndex = optionNodes.findIndex((optionNode2) => optionNode2.id === optionId);
|
|
2879
|
+
const nextOptionNode = currentIndex >= 0 ? optionNodes[currentIndex + 1] : void 0;
|
|
2880
|
+
if (nextOptionNode) {
|
|
2881
|
+
const nextOption = options.find((option) => option.id === nextOptionNode.id);
|
|
2882
|
+
nextOptionNode.focus();
|
|
2883
|
+
nextOptionNode.scrollIntoView({ block: "nearest" });
|
|
2884
|
+
setActiveOptionIndex(nextOption?.index ?? fallbackIndex + 1);
|
|
2885
|
+
return;
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
const optionNode = optionRefs.current.get(optionId);
|
|
2889
|
+
if (optionNode) {
|
|
2890
|
+
optionNode.focus();
|
|
2891
|
+
optionNode.scrollIntoView({ block: "nearest" });
|
|
2892
|
+
return;
|
|
2893
|
+
}
|
|
2894
|
+
focusOption(fallbackIndex);
|
|
2895
|
+
};
|
|
2896
|
+
if (typeof window.requestAnimationFrame === "function") {
|
|
2897
|
+
window.requestAnimationFrame(focusAfterRender);
|
|
2898
|
+
return;
|
|
2899
|
+
}
|
|
2900
|
+
focusAfterRender();
|
|
2901
|
+
}, 0);
|
|
2902
|
+
};
|
|
2903
|
+
const openDropdown = (focusTarget = "search") => {
|
|
2904
|
+
if (settings.disabled) {
|
|
2905
|
+
return;
|
|
2906
|
+
}
|
|
2907
|
+
pendingFocusRef.current = focusTarget;
|
|
2908
|
+
setIsOpen((current) => {
|
|
2909
|
+
if (!current) {
|
|
2910
|
+
onOpen?.();
|
|
2911
|
+
}
|
|
2912
|
+
return true;
|
|
2913
|
+
});
|
|
2914
|
+
};
|
|
2915
|
+
const closeDropdown = () => {
|
|
2916
|
+
setIsOpen((current) => {
|
|
2917
|
+
if (current) {
|
|
2918
|
+
onClose?.();
|
|
2919
|
+
}
|
|
2920
|
+
return false;
|
|
2921
|
+
});
|
|
2922
|
+
setActiveOptionIndex(-1);
|
|
2923
|
+
};
|
|
2924
|
+
closeDropdownRef.current = closeDropdown;
|
|
2925
|
+
const toggleDropdown = () => {
|
|
2926
|
+
if (isOpen) {
|
|
2927
|
+
closeDropdown();
|
|
2928
|
+
return;
|
|
2929
|
+
}
|
|
2930
|
+
openDropdown();
|
|
2931
|
+
};
|
|
2932
|
+
const focusAfterSelectionChange = (target = "search") => {
|
|
2933
|
+
if (target === "none") {
|
|
2934
|
+
return;
|
|
2935
|
+
}
|
|
2936
|
+
window.setTimeout(() => {
|
|
2937
|
+
if (target === "search" && isOpen && settings.enableSearchFilter) {
|
|
2938
|
+
searchRef.current?.focus();
|
|
2939
|
+
return;
|
|
2940
|
+
}
|
|
2941
|
+
triggerRef.current?.focus();
|
|
2942
|
+
}, 0);
|
|
2943
|
+
};
|
|
2944
|
+
const selectItem = (item, focusTarget = "search") => {
|
|
2945
|
+
const wasSelected = state.isSelected(item);
|
|
2946
|
+
const willClose = settings.singleSelection || !wasSelected && settings.closeDropDownOnSelection;
|
|
2947
|
+
state.selectItem(item);
|
|
2948
|
+
focusAfterSelectionChange(willClose ? "trigger" : focusTarget);
|
|
2949
|
+
};
|
|
2950
|
+
const removeItem = (item, focusTarget = "search") => {
|
|
2951
|
+
state.removeItem(item);
|
|
2952
|
+
focusAfterSelectionChange(focusTarget);
|
|
2953
|
+
};
|
|
2954
|
+
const removeLastSelectedItem = () => {
|
|
2955
|
+
state.removeLastSelectedItem();
|
|
2956
|
+
focusAfterSelectionChange();
|
|
2957
|
+
};
|
|
2958
|
+
const selectAll = (items = selectableItems, filteredSelection = false) => {
|
|
2959
|
+
state.selectAll(items, filteredSelection);
|
|
2960
|
+
focusAfterSelectionChange();
|
|
2961
|
+
};
|
|
2962
|
+
const deSelectAll = (items = selectedItems, filteredSelection = false) => {
|
|
2963
|
+
state.deSelectAll(items, filteredSelection);
|
|
2964
|
+
focusAfterSelectionChange();
|
|
2965
|
+
};
|
|
2966
|
+
const clearSelection = () => {
|
|
2967
|
+
state.clearSelection();
|
|
2968
|
+
focusAfterSelectionChange();
|
|
2969
|
+
};
|
|
2970
|
+
const toggleGroup = (groupName, items) => {
|
|
2971
|
+
state.toggleGroup(groupName, items);
|
|
2972
|
+
focusAfterSelectionChange();
|
|
2973
|
+
};
|
|
2974
|
+
const addFilterNewItem = async () => {
|
|
2975
|
+
await state.addFilterNewItem();
|
|
2976
|
+
focusAfterSelectionChange();
|
|
2977
|
+
};
|
|
2978
|
+
const handleListScroll = () => {
|
|
2979
|
+
if (!listboxRef.current || !onScrollToEnd) {
|
|
2980
|
+
return;
|
|
2981
|
+
}
|
|
2982
|
+
const { scrollHeight, scrollTop, clientHeight } = listboxRef.current;
|
|
2983
|
+
if (scrollHeight === lastScrollHeightRef.current && scrollTop + clientHeight < scrollHeight - 12) {
|
|
2984
|
+
return;
|
|
2985
|
+
}
|
|
2986
|
+
if (scrollTop + clientHeight >= scrollHeight - 12) {
|
|
2987
|
+
lastScrollHeightRef.current = scrollHeight;
|
|
2988
|
+
onScrollToEnd({ scrollTop, scrollHeight, clientHeight });
|
|
2989
|
+
}
|
|
2990
|
+
};
|
|
2991
|
+
const handleTriggerKeyDown = (event) => {
|
|
2992
|
+
if (settings.disabled) {
|
|
2993
|
+
return;
|
|
2994
|
+
}
|
|
2995
|
+
if (isSpaceKey2(event.key) && !settings.keyboard.space) {
|
|
2996
|
+
event.preventDefault();
|
|
2997
|
+
return;
|
|
2998
|
+
}
|
|
2999
|
+
if (event.key === "Enter" || isSpaceKey2(event.key)) {
|
|
3000
|
+
event.preventDefault();
|
|
3001
|
+
toggleDropdown();
|
|
3002
|
+
return;
|
|
3003
|
+
}
|
|
3004
|
+
if (settings.keyboard.arrows && event.key === "ArrowDown") {
|
|
3005
|
+
event.preventDefault();
|
|
3006
|
+
if (isOpen) {
|
|
3007
|
+
focusOption(0);
|
|
3008
|
+
} else {
|
|
3009
|
+
openDropdown("first");
|
|
3010
|
+
}
|
|
3011
|
+
return;
|
|
3012
|
+
}
|
|
3013
|
+
if (settings.keyboard.arrows && event.key === "ArrowUp") {
|
|
3014
|
+
event.preventDefault();
|
|
3015
|
+
if (isOpen) {
|
|
3016
|
+
focusOption(selectableOptions.length - 1);
|
|
3017
|
+
} else {
|
|
3018
|
+
openDropdown("last");
|
|
3019
|
+
}
|
|
3020
|
+
return;
|
|
3021
|
+
}
|
|
3022
|
+
if (settings.keyboard.escape && event.key === "Escape" && isOpen) {
|
|
3023
|
+
event.preventDefault();
|
|
3024
|
+
closeDropdown();
|
|
3025
|
+
}
|
|
3026
|
+
};
|
|
3027
|
+
const handleSearchKeyDown = (event) => {
|
|
3028
|
+
if (isSpaceKey2(event.key) && !settings.keyboard.space) {
|
|
3029
|
+
event.preventDefault();
|
|
3030
|
+
return;
|
|
3031
|
+
}
|
|
3032
|
+
if (settings.keyboard.backspaceRemovesLastWhenSearchEmpty && event.key === "Backspace" && !filter && selectedItems.length > 0 && !settings.singleSelection) {
|
|
3033
|
+
event.preventDefault();
|
|
3034
|
+
removeLastSelectedItem();
|
|
3035
|
+
return;
|
|
3036
|
+
}
|
|
3037
|
+
if (settings.keyboard.arrows && event.key === "ArrowDown") {
|
|
3038
|
+
event.preventDefault();
|
|
3039
|
+
focusOption(0);
|
|
3040
|
+
return;
|
|
3041
|
+
}
|
|
3042
|
+
if (settings.keyboard.escape && event.key === "Escape") {
|
|
3043
|
+
event.preventDefault();
|
|
3044
|
+
closeDropdown();
|
|
3045
|
+
triggerRef.current?.focus();
|
|
3046
|
+
}
|
|
3047
|
+
};
|
|
3048
|
+
const handleOptionKeyDown = (event, option) => {
|
|
3049
|
+
if (isSpaceKey2(event.key) && !settings.keyboard.space) {
|
|
3050
|
+
event.preventDefault();
|
|
3051
|
+
return;
|
|
3052
|
+
}
|
|
3053
|
+
if ((event.key === "Enter" || isSpaceKey2(event.key)) && event.repeat) {
|
|
3054
|
+
event.preventDefault();
|
|
3055
|
+
return;
|
|
3056
|
+
}
|
|
3057
|
+
if (event.key === "Enter" || isSpaceKey2(event.key)) {
|
|
3058
|
+
event.preventDefault();
|
|
3059
|
+
const enabledOptions2 = options.filter((currentOption) => !currentOption.disabled);
|
|
3060
|
+
const currentEnabledIndex2 = enabledOptions2.findIndex((currentOption) => currentOption.id === option.id);
|
|
3061
|
+
const willClose = settings.singleSelection || !state.isSelected(option.item) && settings.closeDropDownOnSelection;
|
|
3062
|
+
const moveToNextOption = isSpaceKey2(event.key) && settings.keyboard.spaceOptionAction === "toggle-and-next";
|
|
3063
|
+
selectItem(option.item, willClose ? "trigger" : "none");
|
|
3064
|
+
if (!willClose) {
|
|
3065
|
+
focusOptionById(option.id, Math.max(0, currentEnabledIndex2), moveToNextOption);
|
|
3066
|
+
}
|
|
3067
|
+
return;
|
|
3068
|
+
}
|
|
3069
|
+
const enabledOptions = options.filter((currentOption) => !currentOption.disabled);
|
|
3070
|
+
const currentEnabledIndex = enabledOptions.findIndex((currentOption) => currentOption.id === option.id);
|
|
3071
|
+
if (settings.keyboard.arrows && event.key === "ArrowDown") {
|
|
3072
|
+
event.preventDefault();
|
|
3073
|
+
if (currentEnabledIndex < enabledOptions.length - 1) {
|
|
3074
|
+
focusOption(currentEnabledIndex + 1);
|
|
3075
|
+
} else if (settings.lazyLoading) {
|
|
3076
|
+
handleListScroll();
|
|
3077
|
+
}
|
|
3078
|
+
return;
|
|
3079
|
+
}
|
|
3080
|
+
if (settings.keyboard.arrows && event.key === "ArrowUp") {
|
|
3081
|
+
event.preventDefault();
|
|
3082
|
+
if (currentEnabledIndex > 0) {
|
|
3083
|
+
focusOption(currentEnabledIndex - 1);
|
|
3084
|
+
} else if (settings.enableSearchFilter) {
|
|
3085
|
+
searchRef.current?.focus();
|
|
3086
|
+
} else {
|
|
3087
|
+
triggerRef.current?.focus();
|
|
3088
|
+
}
|
|
3089
|
+
return;
|
|
3090
|
+
}
|
|
3091
|
+
if (settings.keyboard.arrows && event.key === "Home") {
|
|
3092
|
+
event.preventDefault();
|
|
3093
|
+
focusOption(0);
|
|
3094
|
+
return;
|
|
3095
|
+
}
|
|
3096
|
+
if (settings.keyboard.arrows && event.key === "End") {
|
|
3097
|
+
event.preventDefault();
|
|
3098
|
+
focusOption(enabledOptions.length - 1);
|
|
3099
|
+
return;
|
|
3100
|
+
}
|
|
3101
|
+
if (settings.keyboard.escape && event.key === "Escape") {
|
|
3102
|
+
event.preventDefault();
|
|
3103
|
+
closeDropdown();
|
|
3104
|
+
triggerRef.current?.focus();
|
|
3105
|
+
}
|
|
3106
|
+
};
|
|
3107
|
+
const handleInlineButtonKeyDown = (event) => {
|
|
3108
|
+
if (isSpaceKey2(event.key) && !settings.keyboard.space) {
|
|
3109
|
+
event.preventDefault();
|
|
3110
|
+
event.stopPropagation();
|
|
3111
|
+
return;
|
|
3112
|
+
}
|
|
3113
|
+
if (event.key === "Enter" || isSpaceKey2(event.key)) {
|
|
3114
|
+
event.stopPropagation();
|
|
3115
|
+
}
|
|
3116
|
+
};
|
|
3117
|
+
const handleRemoveButtonKeyDown = (event, item) => {
|
|
3118
|
+
if (settings.keyboard.deleteRemovesFocusedBadge && (event.key === "Backspace" || event.key === "Delete")) {
|
|
3119
|
+
event.preventDefault();
|
|
3120
|
+
event.stopPropagation();
|
|
3121
|
+
removeItem(item);
|
|
3122
|
+
return;
|
|
3123
|
+
}
|
|
3124
|
+
handleInlineButtonKeyDown(event);
|
|
3125
|
+
};
|
|
3126
|
+
const getOptionFromItem = (optionOrItem) => {
|
|
3127
|
+
if (optionOrItem && typeof optionOrItem === "object" && "item" in optionOrItem && "id" in optionOrItem && "index" in optionOrItem) {
|
|
3128
|
+
return optionOrItem;
|
|
3129
|
+
}
|
|
3130
|
+
const item = optionOrItem;
|
|
3131
|
+
const key = getPrimaryValue(item, settings);
|
|
3132
|
+
const existingOption = options.find((option) => option.key === key);
|
|
3133
|
+
if (existingOption) {
|
|
3134
|
+
return existingOption;
|
|
3135
|
+
}
|
|
3136
|
+
const selected = isSelected(item);
|
|
3137
|
+
return {
|
|
3138
|
+
item,
|
|
3139
|
+
key,
|
|
3140
|
+
id: `${instanceId}-option-manual-${sanitizeId(key)}`,
|
|
3141
|
+
label: getLabel(item, settings),
|
|
3142
|
+
selected,
|
|
3143
|
+
disabled: settings.disabled || isDisabledItem(item) || limitReached && !selected,
|
|
3144
|
+
index: -1,
|
|
3145
|
+
groupName: getGroupName(item, settings) || void 0
|
|
3146
|
+
};
|
|
3147
|
+
};
|
|
3148
|
+
(0, import_react4.useEffect)(() => {
|
|
3149
|
+
if (!isOpen) {
|
|
3150
|
+
return;
|
|
3151
|
+
}
|
|
3152
|
+
const handlePointerDown = (event) => {
|
|
3153
|
+
const target = event.target;
|
|
3154
|
+
if (!rootRef.current?.contains(target) && !listboxRef.current?.contains(target)) {
|
|
3155
|
+
closeDropdown();
|
|
3156
|
+
}
|
|
3157
|
+
};
|
|
3158
|
+
const handleKeyDown = (event) => {
|
|
3159
|
+
if (event.key === "Escape" && settings.keyboard.escape) {
|
|
3160
|
+
closeDropdown();
|
|
3161
|
+
}
|
|
3162
|
+
};
|
|
3163
|
+
document.addEventListener("mousedown", handlePointerDown);
|
|
3164
|
+
document.addEventListener("touchstart", handlePointerDown);
|
|
3165
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
3166
|
+
return () => {
|
|
3167
|
+
document.removeEventListener("mousedown", handlePointerDown);
|
|
3168
|
+
document.removeEventListener("touchstart", handlePointerDown);
|
|
3169
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
3170
|
+
};
|
|
3171
|
+
}, [isOpen, settings.keyboard.escape]);
|
|
3172
|
+
(0, import_react4.useEffect)(() => {
|
|
3173
|
+
lastScrollHeightRef.current = 0;
|
|
3174
|
+
}, [options.length]);
|
|
3175
|
+
(0, import_react4.useEffect)(() => {
|
|
3176
|
+
if (!isOpen) {
|
|
3177
|
+
return;
|
|
3178
|
+
}
|
|
3179
|
+
const pendingFocus = pendingFocusRef.current;
|
|
3180
|
+
pendingFocusRef.current = "search";
|
|
3181
|
+
window.setTimeout(() => {
|
|
3182
|
+
if (pendingFocus === "first") {
|
|
3183
|
+
focusOption(0);
|
|
3184
|
+
return;
|
|
3185
|
+
}
|
|
3186
|
+
if (pendingFocus === "last") {
|
|
3187
|
+
focusOption(selectableOptions.length - 1);
|
|
3188
|
+
return;
|
|
3189
|
+
}
|
|
3190
|
+
if (settings.enableSearchFilter && settings.searchAutofocus) {
|
|
3191
|
+
searchRef.current?.focus();
|
|
3192
|
+
}
|
|
3193
|
+
}, 0);
|
|
3194
|
+
}, [
|
|
3195
|
+
isOpen,
|
|
3196
|
+
options.length,
|
|
3197
|
+
selectableOptions.length,
|
|
3198
|
+
settings.enableSearchFilter,
|
|
3199
|
+
settings.searchAutofocus
|
|
3200
|
+
]);
|
|
3201
|
+
return {
|
|
3202
|
+
settings,
|
|
3203
|
+
isOpen,
|
|
3204
|
+
filter,
|
|
3205
|
+
setFilter,
|
|
3206
|
+
selectedItems,
|
|
3207
|
+
selectedOptions,
|
|
3208
|
+
options,
|
|
3209
|
+
groups,
|
|
3210
|
+
visibleOptions: options,
|
|
3211
|
+
visibleBadges,
|
|
3212
|
+
hiddenBadgeCount,
|
|
3213
|
+
allFilteredSelected,
|
|
3214
|
+
hasFilteredResults,
|
|
3215
|
+
activeDescendantId,
|
|
3216
|
+
listboxId,
|
|
3217
|
+
label,
|
|
3218
|
+
openDropdown,
|
|
3219
|
+
closeDropdown,
|
|
3220
|
+
toggleDropdown,
|
|
3221
|
+
clearSelection,
|
|
3222
|
+
selectItem,
|
|
3223
|
+
removeItem,
|
|
3224
|
+
selectAll,
|
|
3225
|
+
deSelectAll,
|
|
3226
|
+
toggleGroup,
|
|
3227
|
+
addFilterNewItem,
|
|
3228
|
+
isSelected,
|
|
3229
|
+
isDisabled,
|
|
3230
|
+
getItemLabel: (item) => getLabel(item, settings),
|
|
3231
|
+
getItemKey: (item) => getPrimaryValue(item, settings),
|
|
3232
|
+
getRootProps: (props = {}) => ({
|
|
3233
|
+
...props,
|
|
3234
|
+
ref: (node) => {
|
|
3235
|
+
rootRef.current = node;
|
|
3236
|
+
assignRef(props.ref, node);
|
|
3237
|
+
}
|
|
3238
|
+
}),
|
|
3239
|
+
getTriggerProps: (props = {}) => ({
|
|
3240
|
+
type: "button",
|
|
3241
|
+
...props,
|
|
3242
|
+
ref: (node) => {
|
|
3243
|
+
triggerRef.current = node;
|
|
3244
|
+
assignRef(props.ref, node);
|
|
3245
|
+
},
|
|
3246
|
+
disabled: settings.disabled || props.disabled,
|
|
3247
|
+
role: "combobox",
|
|
3248
|
+
"aria-expanded": isOpen,
|
|
3249
|
+
"aria-haspopup": "listbox",
|
|
3250
|
+
"aria-controls": listboxId,
|
|
3251
|
+
"aria-disabled": settings.disabled || void 0,
|
|
3252
|
+
"aria-activedescendant": activeDescendantId,
|
|
3253
|
+
"aria-label": selectedItems.length ? `${settings.ariaLabel}: ${selectedItems.map((item) => getLabel(item, settings)).join(", ")}` : settings.ariaLabel,
|
|
3254
|
+
onClick: callAll(() => toggleDropdown(), props.onClick),
|
|
3255
|
+
onKeyDown: callAll((event) => {
|
|
3256
|
+
handleTriggerKeyDown(event);
|
|
3257
|
+
}, props.onKeyDown)
|
|
3258
|
+
}),
|
|
3259
|
+
getListboxProps: (props = {}) => ({
|
|
3260
|
+
...props,
|
|
3261
|
+
ref: (node) => {
|
|
3262
|
+
listboxRef.current = node;
|
|
3263
|
+
assignRef(props.ref, node);
|
|
3264
|
+
},
|
|
3265
|
+
id: props.id || listboxId,
|
|
3266
|
+
role: "listbox",
|
|
3267
|
+
"aria-label": props["aria-label"] || settings.listboxAriaLabel,
|
|
3268
|
+
"aria-multiselectable": !settings.singleSelection,
|
|
3269
|
+
onScroll: callAll(() => {
|
|
3270
|
+
if (settings.lazyLoading) {
|
|
3271
|
+
handleListScroll();
|
|
3272
|
+
}
|
|
3273
|
+
}, props.onScroll)
|
|
3274
|
+
}),
|
|
3275
|
+
getOptionProps: (optionOrItem, props = {}) => {
|
|
3276
|
+
const option = getOptionFromItem(optionOrItem);
|
|
3277
|
+
return {
|
|
3278
|
+
...props,
|
|
3279
|
+
ref: (node) => {
|
|
3280
|
+
if (node) {
|
|
3281
|
+
optionRefs.current.set(option.id, node);
|
|
3282
|
+
} else {
|
|
3283
|
+
optionRefs.current.delete(option.id);
|
|
3284
|
+
}
|
|
3285
|
+
assignRef(props.ref, node);
|
|
3286
|
+
},
|
|
3287
|
+
id: props.id || option.id,
|
|
3288
|
+
role: "option",
|
|
3289
|
+
tabIndex: option.disabled || !settings.keyboard.tab ? -1 : props.tabIndex ?? 0,
|
|
3290
|
+
"aria-selected": option.selected,
|
|
3291
|
+
"aria-checked": option.selected,
|
|
3292
|
+
"aria-disabled": option.disabled || void 0,
|
|
3293
|
+
"data-headless-option": "true",
|
|
3294
|
+
onClick: callAll((event) => {
|
|
3295
|
+
if (option.disabled) {
|
|
3296
|
+
return;
|
|
3297
|
+
}
|
|
3298
|
+
const willClose = settings.singleSelection || !state.isSelected(option.item) && settings.closeDropDownOnSelection;
|
|
3299
|
+
selectItem(option.item, willClose ? "trigger" : "none");
|
|
3300
|
+
if (!willClose) {
|
|
3301
|
+
setActiveOptionIndex(option.index);
|
|
3302
|
+
focusOptionById(option.id, option.index);
|
|
3303
|
+
}
|
|
3304
|
+
}, props.onClick),
|
|
3305
|
+
onFocus: callAll(() => setActiveOptionIndex(option.index), props.onFocus),
|
|
3306
|
+
onKeyDown: callAll((event) => {
|
|
3307
|
+
handleOptionKeyDown(event, option);
|
|
3308
|
+
}, props.onKeyDown)
|
|
3309
|
+
};
|
|
3310
|
+
},
|
|
3311
|
+
getSearchInputProps: (props = {}) => ({
|
|
3312
|
+
type: "search",
|
|
3313
|
+
...props,
|
|
3314
|
+
ref: (node) => {
|
|
3315
|
+
searchRef.current = node;
|
|
3316
|
+
assignRef(props.ref, node);
|
|
3317
|
+
},
|
|
3318
|
+
value: props.value ?? filter,
|
|
3319
|
+
placeholder: props.placeholder ?? settings.searchPlaceholderText,
|
|
3320
|
+
"aria-label": props["aria-label"] ?? settings.searchAriaLabel,
|
|
3321
|
+
onChange: callAll((event) => {
|
|
3322
|
+
setFilter(event.currentTarget.value);
|
|
3323
|
+
}, props.onChange),
|
|
3324
|
+
onKeyDown: callAll((event) => {
|
|
3325
|
+
handleSearchKeyDown(event);
|
|
3326
|
+
}, props.onKeyDown)
|
|
3327
|
+
}),
|
|
3328
|
+
getClearAllButtonProps: (props = {}) => ({
|
|
3329
|
+
type: "button",
|
|
3330
|
+
...props,
|
|
3331
|
+
disabled: settings.disabled || selectedItems.length === 0 || props.disabled,
|
|
3332
|
+
"aria-label": props["aria-label"] ?? settings.clearAllAriaLabel,
|
|
3333
|
+
onKeyDown: callAll((event) => {
|
|
3334
|
+
handleInlineButtonKeyDown(event);
|
|
3335
|
+
}, props.onKeyDown),
|
|
3336
|
+
onClick: callAll(() => clearSelection(), props.onClick)
|
|
3337
|
+
}),
|
|
3338
|
+
getRemoveButtonProps: (item, props = {}) => ({
|
|
3339
|
+
type: "button",
|
|
3340
|
+
...props,
|
|
3341
|
+
disabled: settings.disabled || props.disabled,
|
|
3342
|
+
"aria-label": props["aria-label"] ?? (typeof settings.removeItemAriaLabel === "function" ? settings.removeItemAriaLabel(item) : `${settings.removeItemAriaLabel}: ${getLabel(item, settings)}`),
|
|
3343
|
+
onKeyDown: callAll((event) => {
|
|
3344
|
+
handleRemoveButtonKeyDown(event, item);
|
|
3345
|
+
}, props.onKeyDown),
|
|
3346
|
+
onClick: callAll(() => removeItem(item), props.onClick)
|
|
3347
|
+
})
|
|
3348
|
+
};
|
|
3349
|
+
}
|
|
3350
|
+
|
|
3351
|
+
// src/createMultiSelectDropdown.ts
|
|
3352
|
+
function createMultiSelectDropdown() {
|
|
3353
|
+
const TypedDropdown = MultiSelectDropdown;
|
|
3354
|
+
function useDropdown(props) {
|
|
3355
|
+
return useMultiSelectDropdown(props);
|
|
3356
|
+
}
|
|
3357
|
+
function useSelectionState(props) {
|
|
3358
|
+
return useMultiSelectState(props);
|
|
3359
|
+
}
|
|
3360
|
+
function defineSettings(settings) {
|
|
3361
|
+
return settings;
|
|
3362
|
+
}
|
|
3363
|
+
function defineSlots(slots) {
|
|
3364
|
+
return slots;
|
|
3365
|
+
}
|
|
3366
|
+
return {
|
|
3367
|
+
Dropdown: TypedDropdown,
|
|
3368
|
+
MultiSelectDropdown: TypedDropdown,
|
|
3369
|
+
useDropdown,
|
|
3370
|
+
useSelectionState,
|
|
3371
|
+
defineSettings,
|
|
3372
|
+
defineSlots
|
|
3373
|
+
};
|
|
3374
|
+
}
|
|
2018
3375
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2019
3376
|
0 && (module.exports = {
|
|
2020
3377
|
MultiSelectDropdown,
|
|
2021
|
-
ReactMultiSelectDropdown
|
|
3378
|
+
ReactMultiSelectDropdown,
|
|
3379
|
+
createMultiSelectDropdown,
|
|
3380
|
+
useMultiSelectDropdown,
|
|
3381
|
+
useMultiSelectState
|
|
2022
3382
|
});
|