@entur/dropdown 7.3.11 → 8.0.0
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/dist/Dropdown.d.ts +1 -7
- package/dist/MultiSelect.d.ts +4 -4
- package/dist/SearchableDropdown.d.ts +4 -4
- package/dist/components/DropdownList.d.ts +4 -5
- package/dist/components/FieldComponents.d.ts +1 -1
- package/dist/dropdown.cjs.js +339 -345
- package/dist/dropdown.cjs.js.map +1 -1
- package/dist/dropdown.esm.js +340 -346
- package/dist/dropdown.esm.js.map +1 -1
- package/dist/styles.css +4 -1
- package/dist/utils.d.ts +7 -1
- package/package.json +4 -4
package/dist/dropdown.esm.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useDebounce, mergeRefs, useRandomId, warnAboutMissingStyles } from "@entur/utils";
|
|
2
2
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
3
|
-
import React, { forwardRef, createElement, useState, useRef, useEffect, useCallback } from "react";
|
|
3
|
+
import React, { forwardRef, createElement, useMemo, useState, useRef, useEffect, useCallback, useLayoutEffect } from "react";
|
|
4
4
|
import { useCombobox, useMultipleSelection, useSelect } from "downshift";
|
|
5
5
|
import classNames from "classnames";
|
|
6
6
|
import { useFloating, offset, shift, size, flip, autoUpdate } from "@floating-ui/react-dom";
|
|
@@ -14,21 +14,21 @@ import { LoadingDots } from "@entur/loader";
|
|
|
14
14
|
import { Tooltip } from "@entur/tooltip";
|
|
15
15
|
const DropdownList = ({
|
|
16
16
|
ariaLabelChosenSingular = "valgt",
|
|
17
|
-
ariaLabelSelectedItem = ", valgt element
|
|
17
|
+
ariaLabelSelectedItem = ", valgt element",
|
|
18
18
|
getItemProps,
|
|
19
|
-
getMenuProps,
|
|
20
19
|
isOpen,
|
|
21
20
|
highlightedIndex,
|
|
22
21
|
listItems,
|
|
23
22
|
floatingStyles,
|
|
24
|
-
|
|
23
|
+
innerRef,
|
|
25
24
|
loading = false,
|
|
26
25
|
loadingText = "Laster inn …",
|
|
27
26
|
noMatchesText = "Ingen treff for søket",
|
|
27
|
+
readOnly = false,
|
|
28
28
|
selectAllCheckboxState,
|
|
29
29
|
selectAllItem,
|
|
30
30
|
selectedItems,
|
|
31
|
-
|
|
31
|
+
style,
|
|
32
32
|
...rest
|
|
33
33
|
}) => {
|
|
34
34
|
const isMultiselect = selectAllItem !== void 0;
|
|
@@ -41,7 +41,7 @@ const DropdownList = ({
|
|
|
41
41
|
return true;
|
|
42
42
|
});
|
|
43
43
|
const ariaValuesSelectAll = () => {
|
|
44
|
-
switch (selectAllCheckboxState
|
|
44
|
+
switch (selectAllCheckboxState) {
|
|
45
45
|
case "indeterminate": {
|
|
46
46
|
return {
|
|
47
47
|
label: `${selectAllItem?.label}, delvis valgt`,
|
|
@@ -64,7 +64,7 @@ const DropdownList = ({
|
|
|
64
64
|
Checkbox,
|
|
65
65
|
{
|
|
66
66
|
"aria-hidden": "true",
|
|
67
|
-
checked: selectAllCheckboxState
|
|
67
|
+
checked: selectAllCheckboxState,
|
|
68
68
|
className: "eds-dropdown__list__item__checkbox",
|
|
69
69
|
tabIndex: -1,
|
|
70
70
|
onChange: () => void 0
|
|
@@ -111,75 +111,68 @@ const DropdownList = ({
|
|
|
111
111
|
}) : null
|
|
112
112
|
] });
|
|
113
113
|
};
|
|
114
|
-
return (
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
"
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
{
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
},
|
|
177
|
-
key
|
|
178
|
-
);
|
|
179
|
-
});
|
|
180
|
-
})()
|
|
181
|
-
}
|
|
182
|
-
)
|
|
114
|
+
return /* @__PURE__ */ jsx(
|
|
115
|
+
"ul",
|
|
116
|
+
{
|
|
117
|
+
className: "eds-dropdown__list",
|
|
118
|
+
ref: innerRef,
|
|
119
|
+
style: {
|
|
120
|
+
display: isOpen && !readOnly ? void 0 : "none",
|
|
121
|
+
...floatingStyles,
|
|
122
|
+
...style
|
|
123
|
+
},
|
|
124
|
+
...rest,
|
|
125
|
+
children: (() => {
|
|
126
|
+
if (!isOpen || readOnly) return null;
|
|
127
|
+
if (loading) {
|
|
128
|
+
return /* @__PURE__ */ jsx(
|
|
129
|
+
"li",
|
|
130
|
+
{
|
|
131
|
+
className: "eds-dropdown__list__item",
|
|
132
|
+
children: loadingText
|
|
133
|
+
},
|
|
134
|
+
"dropdown-list-loading"
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
if (isNoMatches) {
|
|
138
|
+
return /* @__PURE__ */ jsx(
|
|
139
|
+
"li",
|
|
140
|
+
{
|
|
141
|
+
className: "eds-dropdown__list__item",
|
|
142
|
+
children: noMatchesText
|
|
143
|
+
},
|
|
144
|
+
"dropdown-list-no-match"
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
return listItems.map((item, index) => {
|
|
148
|
+
const key = item.itemKey ?? `${item.label ?? ""}-${item.value ?? ""}-${(item.icons ?? []).map((icon) => icon?.displayName ?? icon?.name ?? "unknown").join("-")}`;
|
|
149
|
+
const itemIsSelectAll = item.value === selectAllItem?.value;
|
|
150
|
+
if (itemIsSelectAll && listItems.length <= 2) return null;
|
|
151
|
+
return /* @__PURE__ */ jsx(
|
|
152
|
+
"li",
|
|
153
|
+
{
|
|
154
|
+
className: classNames("eds-dropdown__list__item", {
|
|
155
|
+
"eds-dropdown__list__item--select-all": itemIsSelectAll,
|
|
156
|
+
"eds-dropdown__list__item--highlighted": highlightedIndex === index,
|
|
157
|
+
"eds-dropdown__list__item--selected": !isMultiselect && isItemSelected(item)
|
|
158
|
+
}),
|
|
159
|
+
...getItemProps({
|
|
160
|
+
// @ts-expect-error Since getItemProps expects the same item type
|
|
161
|
+
// here as items, it throws error when selectAllItem is a string.
|
|
162
|
+
// This does, however, not cause any functional issues.
|
|
163
|
+
item,
|
|
164
|
+
index,
|
|
165
|
+
"aria-selected": itemIsSelectAll ? ariaValuesSelectAll().selected : isItemSelected(item)
|
|
166
|
+
}),
|
|
167
|
+
children: itemIsSelectAll ? selectAllListItemContent() : listItemContent(
|
|
168
|
+
item
|
|
169
|
+
)
|
|
170
|
+
},
|
|
171
|
+
key
|
|
172
|
+
);
|
|
173
|
+
});
|
|
174
|
+
})()
|
|
175
|
+
}
|
|
183
176
|
);
|
|
184
177
|
};
|
|
185
178
|
const SelectedItemTag = ({
|
|
@@ -192,6 +185,7 @@ const SelectedItemTag = ({
|
|
|
192
185
|
removeSelectedItem,
|
|
193
186
|
selectedItem
|
|
194
187
|
}) => {
|
|
188
|
+
if (!selectedItem) return null;
|
|
195
189
|
const { tabIndex: _, ...selectedItemProps } = getSelectedItemProps?.({
|
|
196
190
|
selectedItem,
|
|
197
191
|
index
|
|
@@ -329,25 +323,25 @@ const useNormalizedItems = (items) => React.useMemo(
|
|
|
329
323
|
);
|
|
330
324
|
const useResolvedItems = (itemsOrItemsResolver, debounceTimeout = 250) => {
|
|
331
325
|
const itemsIsAFunction = typeof itemsOrItemsResolver === "function";
|
|
332
|
-
const [
|
|
333
|
-
itemsIsAFunction ? [] : itemsOrItemsResolver
|
|
334
|
-
);
|
|
326
|
+
const [resolvedItems, setResolvedItems] = React.useState([]);
|
|
335
327
|
const [loading, setLoading] = React.useState(false);
|
|
336
328
|
const abortControllerRef = React.useRef(
|
|
337
329
|
new AbortController()
|
|
338
330
|
);
|
|
339
331
|
const itemsResolver = React.useMemo(() => {
|
|
340
|
-
if (itemsIsAFunction)
|
|
332
|
+
if (itemsIsAFunction) {
|
|
341
333
|
return itemsOrItemsResolver;
|
|
342
|
-
|
|
334
|
+
}
|
|
335
|
+
return null;
|
|
343
336
|
}, [itemsOrItemsResolver, itemsIsAFunction]);
|
|
344
337
|
const updateItems = async (inputValue) => {
|
|
338
|
+
if (!itemsResolver) return;
|
|
345
339
|
if (abortControllerRef?.current) abortControllerRef?.current?.abort();
|
|
346
340
|
const abortController = new AbortController();
|
|
347
341
|
abortControllerRef.current = abortController;
|
|
348
342
|
setLoading(true);
|
|
349
343
|
try {
|
|
350
|
-
const
|
|
344
|
+
const fetchedItems = await itemsResolver(
|
|
351
345
|
inputValue ?? "",
|
|
352
346
|
abortControllerRef
|
|
353
347
|
);
|
|
@@ -361,8 +355,7 @@ const useResolvedItems = (itemsOrItemsResolver, debounceTimeout = 250) => {
|
|
|
361
355
|
);
|
|
362
356
|
return;
|
|
363
357
|
}
|
|
364
|
-
|
|
365
|
-
setItems(resolvedItems);
|
|
358
|
+
setResolvedItems(fetchedItems);
|
|
366
359
|
} catch (error2) {
|
|
367
360
|
if (error2 && typeof error2 === "object" && "name" in error2 && error2.name === "AbortError") {
|
|
368
361
|
return;
|
|
@@ -371,9 +364,12 @@ const useResolvedItems = (itemsOrItemsResolver, debounceTimeout = 250) => {
|
|
|
371
364
|
"The following error was received but not handled inside Entur Designsystems useResolvedItems hook:"
|
|
372
365
|
);
|
|
373
366
|
throw error2;
|
|
367
|
+
} finally {
|
|
368
|
+
setLoading(false);
|
|
374
369
|
}
|
|
375
370
|
};
|
|
376
371
|
const debouncedFetchItems = useDebounce(updateItems, debounceTimeout);
|
|
372
|
+
const items = itemsIsAFunction ? resolvedItems : itemsOrItemsResolver;
|
|
377
373
|
const normalizedItems = useNormalizedItems(items);
|
|
378
374
|
React.useEffect(() => {
|
|
379
375
|
return () => abortControllerRef?.current?.abort("Component unmounted");
|
|
@@ -408,6 +404,10 @@ const itemToString = (item) => item ? item.label : "";
|
|
|
408
404
|
const itemToKey = (item) => item?.label + item?.value;
|
|
409
405
|
const isFunctionWithQueryArgument = (object) => typeof object === "function" && object.length > 0;
|
|
410
406
|
const clamp = (val, min = 1, max = 10) => Math.min(Math.max(val, min), max);
|
|
407
|
+
const resetInputState = (changes) => ({
|
|
408
|
+
...changes,
|
|
409
|
+
inputValue: EMPTY_INPUT
|
|
410
|
+
});
|
|
411
411
|
const useMultiselectUtils = ({
|
|
412
412
|
listItems,
|
|
413
413
|
selectedItems,
|
|
@@ -455,11 +455,11 @@ const useMultiselectUtils = ({
|
|
|
455
455
|
(selectedItem) => selectedItem.value !== clickedItem.value
|
|
456
456
|
)
|
|
457
457
|
);
|
|
458
|
-
const selectAllCheckboxState = () => {
|
|
458
|
+
const selectAllCheckboxState = useMemo(() => {
|
|
459
459
|
if (allListItemsAreSelected) return true;
|
|
460
460
|
if (someListItemsAreSelected) return "indeterminate";
|
|
461
461
|
return false;
|
|
462
|
-
};
|
|
462
|
+
}, [allListItemsAreSelected, someListItemsAreSelected]);
|
|
463
463
|
const selectAllUnselectedItemsInListItems = (onChange) => {
|
|
464
464
|
onChange([...selectedItems, ...unselectedItemsInListItems]);
|
|
465
465
|
};
|
|
@@ -523,7 +523,6 @@ const SearchableDropdown = React.forwardRef(
|
|
|
523
523
|
prepend,
|
|
524
524
|
readOnly = false,
|
|
525
525
|
selectedItem: value,
|
|
526
|
-
selectOnBlur = false,
|
|
527
526
|
selectOnTab = false,
|
|
528
527
|
style,
|
|
529
528
|
variant = "info",
|
|
@@ -550,77 +549,65 @@ const SearchableDropdown = React.forwardRef(
|
|
|
550
549
|
if (shouldRefetchItems) fetchItems(inputValue2 ?? EMPTY_INPUT);
|
|
551
550
|
filterListItems({ inputValue: inputValue2 ?? EMPTY_INPUT });
|
|
552
551
|
};
|
|
553
|
-
const resetInputState = ({
|
|
554
|
-
changes
|
|
555
|
-
}) => {
|
|
556
|
-
updateListItems({ inputValue: EMPTY_INPUT });
|
|
557
|
-
return {
|
|
558
|
-
...changes,
|
|
559
|
-
inputValue: EMPTY_INPUT
|
|
560
|
-
};
|
|
561
|
-
};
|
|
562
552
|
const inputHasFocus = typeof document !== "undefined" ? inputRef?.current === document?.activeElement : false;
|
|
563
553
|
useEffect(() => {
|
|
564
554
|
filterListItems({ inputValue });
|
|
565
555
|
}, [normalizedItems]);
|
|
566
|
-
useEffect(() => {
|
|
567
|
-
if (selectedItem !== null && !inputHasFocus) {
|
|
568
|
-
setShowSelectedItem(true);
|
|
569
|
-
updateListItems({ inputValue: EMPTY_INPUT });
|
|
570
|
-
setInputValue(EMPTY_INPUT);
|
|
571
|
-
}
|
|
572
|
-
}, []);
|
|
573
556
|
const stateReducer = useCallback(
|
|
574
557
|
(state, {
|
|
575
558
|
type,
|
|
576
559
|
changes
|
|
577
560
|
}) => {
|
|
578
|
-
if (changes.highlightedIndex !== void 0 && changes?.highlightedIndex >= 0) {
|
|
579
|
-
setLastHighlightedIndex(changes?.highlightedIndex);
|
|
580
|
-
}
|
|
581
561
|
switch (type) {
|
|
582
562
|
// empty input to show selected item and reset dropdown list on item selection
|
|
583
563
|
case useCombobox.stateChangeTypes.ItemClick:
|
|
584
|
-
case useCombobox.stateChangeTypes.InputKeyDownEnter:
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
case useCombobox.stateChangeTypes.
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
564
|
+
case useCombobox.stateChangeTypes.InputKeyDownEnter: {
|
|
565
|
+
return resetInputState(changes);
|
|
566
|
+
}
|
|
567
|
+
case useCombobox.stateChangeTypes.InputBlur: {
|
|
568
|
+
return resetInputState({
|
|
569
|
+
...changes,
|
|
570
|
+
selectedItem: state.selectedItem
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
case useCombobox.stateChangeTypes.InputKeyDownEscape: {
|
|
574
|
+
return {
|
|
575
|
+
...changes,
|
|
576
|
+
selectedItem: clearable && !state.isOpen ? null : state.selectedItem
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
case useCombobox.stateChangeTypes.ControlledPropUpdatedSelectedItem: {
|
|
580
|
+
return { ...changes, inputValue: state.inputValue };
|
|
581
|
+
}
|
|
591
582
|
// remove leading whitespace, select element with spacebar on empty input
|
|
592
583
|
case useCombobox.stateChangeTypes.InputChange: {
|
|
593
|
-
const leadingWhitespaceTest = /^\s+/g;
|
|
594
584
|
const isSpacePressedOnEmptyInput = changes.inputValue === " ";
|
|
595
|
-
if (!isSpacePressedOnEmptyInput)
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
return { ...changes, highlightedIndex: 0 };
|
|
585
|
+
if (!isSpacePressedOnEmptyInput)
|
|
586
|
+
return { ...changes, highlightedIndex: 0 };
|
|
587
|
+
const sanitizedInputValue = (changes.inputValue ?? "").replace(
|
|
588
|
+
/^\s+/,
|
|
589
|
+
EMPTY_INPUT
|
|
590
|
+
);
|
|
591
|
+
if (!state.isOpen)
|
|
592
|
+
return {
|
|
593
|
+
...changes,
|
|
594
|
+
inputValue: sanitizedInputValue,
|
|
595
|
+
isOpen: true
|
|
596
|
+
};
|
|
597
|
+
const i = changes.highlightedIndex ?? -1;
|
|
598
|
+
if (i >= 0 && i < listItems.length)
|
|
599
|
+
return {
|
|
600
|
+
...changes,
|
|
601
|
+
inputValue: sanitizedInputValue,
|
|
602
|
+
selectedItem: listItems[i]
|
|
603
|
+
};
|
|
604
|
+
return { ...changes, inputValue: sanitizedInputValue };
|
|
618
605
|
}
|
|
619
606
|
default:
|
|
620
607
|
return changes;
|
|
621
608
|
}
|
|
622
609
|
},
|
|
623
|
-
[
|
|
610
|
+
[listItems, EMPTY_INPUT, clearable]
|
|
624
611
|
);
|
|
625
612
|
const {
|
|
626
613
|
isOpen,
|
|
@@ -647,9 +634,23 @@ const SearchableDropdown = React.forwardRef(
|
|
|
647
634
|
onSelectedItemChange({ selectedItem: newSelectedItem }) {
|
|
648
635
|
onChange(newSelectedItem);
|
|
649
636
|
},
|
|
637
|
+
onHighlightedIndexChange: ({ highlightedIndex: highlightedIndex2 }) => {
|
|
638
|
+
if (highlightedIndex2 >= 0) setLastHighlightedIndex(highlightedIndex2);
|
|
639
|
+
},
|
|
650
640
|
// Accessibility
|
|
651
641
|
getA11yStatusMessage: (options) => getA11yStatusMessage({ ...options, resultCount: listItems.length })
|
|
652
642
|
});
|
|
643
|
+
useEffect(() => {
|
|
644
|
+
if (value !== null && !inputHasFocus) {
|
|
645
|
+
setShowSelectedItem(true);
|
|
646
|
+
updateListItems({ inputValue: EMPTY_INPUT });
|
|
647
|
+
setInputValue(EMPTY_INPUT);
|
|
648
|
+
}
|
|
649
|
+
}, [value]);
|
|
650
|
+
const handleOnClear = () => {
|
|
651
|
+
inputRef.current?.focus();
|
|
652
|
+
reset();
|
|
653
|
+
};
|
|
653
654
|
const { refs, floatingStyles, update } = useFloating({
|
|
654
655
|
open: isOpen,
|
|
655
656
|
placement: "bottom-start",
|
|
@@ -657,19 +658,17 @@ const SearchableDropdown = React.forwardRef(
|
|
|
657
658
|
offset(space.extraSmall2),
|
|
658
659
|
shift({ padding: space.extraSmall }),
|
|
659
660
|
size({
|
|
660
|
-
apply({
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
maxHeight: `${clamp(10 * 16, availableHeight, 20 * 16)}px`
|
|
666
|
-
});
|
|
661
|
+
apply({ elements, availableHeight }) {
|
|
662
|
+
elements.floating.style.setProperty(
|
|
663
|
+
"--list-max-height",
|
|
664
|
+
`${clamp(10 * 16, availableHeight, 20 * 16)}px`
|
|
665
|
+
);
|
|
667
666
|
}
|
|
668
667
|
}),
|
|
669
668
|
flip({ fallbackStrategy: "initialPlacement" })
|
|
670
669
|
]
|
|
671
670
|
});
|
|
672
|
-
|
|
671
|
+
useLayoutEffect(() => {
|
|
673
672
|
if (isOpen && refs.reference.current && refs.floating.current) {
|
|
674
673
|
return autoUpdate(
|
|
675
674
|
refs.reference.current,
|
|
@@ -678,10 +677,39 @@ const SearchableDropdown = React.forwardRef(
|
|
|
678
677
|
);
|
|
679
678
|
}
|
|
680
679
|
}, [isOpen, refs.reference, refs.floating, update]);
|
|
681
|
-
const
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
};
|
|
680
|
+
const labelProps = getLabelProps();
|
|
681
|
+
const toggleButtonProps = getToggleButtonProps({
|
|
682
|
+
"aria-busy": !(loading ?? resolvedItemsLoading) ? void 0 : "true"
|
|
683
|
+
});
|
|
684
|
+
const menuProps = getMenuProps({
|
|
685
|
+
refKey: "innerRef",
|
|
686
|
+
ref: refs.setFloating,
|
|
687
|
+
style: listStyle
|
|
688
|
+
});
|
|
689
|
+
const inputProps = getInputProps({
|
|
690
|
+
onKeyDown(e) {
|
|
691
|
+
if (isOpen && e.key === "Tab") {
|
|
692
|
+
const highlitedItem = listItems[highlightedIndex];
|
|
693
|
+
if (selectOnTab && highlitedItem) {
|
|
694
|
+
selectItem(highlitedItem);
|
|
695
|
+
setShowSelectedItem(true);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
},
|
|
699
|
+
onBlur(e) {
|
|
700
|
+
if (selectedItem !== null) setShowSelectedItem(true);
|
|
701
|
+
onBlur?.(e);
|
|
702
|
+
},
|
|
703
|
+
onFocus(e) {
|
|
704
|
+
if (!readOnly) setShowSelectedItem(false);
|
|
705
|
+
onFocus?.(e);
|
|
706
|
+
},
|
|
707
|
+
disabled,
|
|
708
|
+
readOnly,
|
|
709
|
+
placeholder: selectedItem?.label ?? placeholder,
|
|
710
|
+
tabIndex: disabled || readOnly ? -1 : void 0,
|
|
711
|
+
ref: mergeRefs(inputRef, ref)
|
|
712
|
+
});
|
|
685
713
|
return /* @__PURE__ */ jsxs(
|
|
686
714
|
BaseFormControl,
|
|
687
715
|
{
|
|
@@ -696,17 +724,14 @@ const SearchableDropdown = React.forwardRef(
|
|
|
696
724
|
feedback,
|
|
697
725
|
isFilled: selectedItem !== null || inputValue !== EMPTY_INPUT,
|
|
698
726
|
label,
|
|
699
|
-
labelId:
|
|
700
|
-
labelProps
|
|
727
|
+
labelId: labelProps.id,
|
|
728
|
+
labelProps,
|
|
701
729
|
labelTooltip,
|
|
702
730
|
onClick: (e) => {
|
|
703
|
-
if (e.target === e.currentTarget)
|
|
704
|
-
getInputProps()?.onClick?.(e);
|
|
705
|
-
}
|
|
731
|
+
if (e.target === e.currentTarget) inputProps?.onClick?.(e);
|
|
706
732
|
onClick?.(e);
|
|
707
733
|
},
|
|
708
734
|
onKeyDown,
|
|
709
|
-
onFocus,
|
|
710
735
|
prepend,
|
|
711
736
|
readOnly,
|
|
712
737
|
ref: refs.setReference,
|
|
@@ -720,17 +745,15 @@ const SearchableDropdown = React.forwardRef(
|
|
|
720
745
|
ariaLabelSelectedItem,
|
|
721
746
|
floatingStyles,
|
|
722
747
|
getItemProps,
|
|
723
|
-
getMenuProps,
|
|
724
748
|
highlightedIndex,
|
|
725
749
|
isOpen,
|
|
726
750
|
listItems,
|
|
727
|
-
style: listStyle,
|
|
728
|
-
setListRef: refs.setFloating,
|
|
729
751
|
loading: loading ?? resolvedItemsLoading,
|
|
730
752
|
loadingText,
|
|
731
753
|
noMatchesText,
|
|
732
754
|
selectedItems: selectedItem !== null ? [selectedItem] : [],
|
|
733
|
-
readOnly
|
|
755
|
+
readOnly,
|
|
756
|
+
...menuProps
|
|
734
757
|
}
|
|
735
758
|
),
|
|
736
759
|
...rest,
|
|
@@ -745,7 +768,7 @@ const SearchableDropdown = React.forwardRef(
|
|
|
745
768
|
onClick: (event) => {
|
|
746
769
|
if (!disabled && !readOnly) {
|
|
747
770
|
inputRef.current?.focus();
|
|
748
|
-
|
|
771
|
+
inputProps?.onClick?.(event);
|
|
749
772
|
}
|
|
750
773
|
},
|
|
751
774
|
tabIndex: readOnly ? 0 : -1,
|
|
@@ -758,38 +781,13 @@ const SearchableDropdown = React.forwardRef(
|
|
|
758
781
|
className: classNames("eds-dropdown__input eds-form-control", {
|
|
759
782
|
"eds-dropdown__input--hidden": showSelectedItem
|
|
760
783
|
}),
|
|
761
|
-
...
|
|
762
|
-
onKeyDown(e) {
|
|
763
|
-
if (isOpen && e.key === "Tab") {
|
|
764
|
-
const highlitedItem = listItems[highlightedIndex];
|
|
765
|
-
if ((selectOnTab || selectOnBlur) && highlitedItem && highlitedItem !== selectedItem) {
|
|
766
|
-
selectItem(highlitedItem);
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
},
|
|
770
|
-
onBlur(e) {
|
|
771
|
-
if (selectedItem !== null) setShowSelectedItem(true);
|
|
772
|
-
onBlur?.(e);
|
|
773
|
-
},
|
|
774
|
-
onFocus() {
|
|
775
|
-
if (!readOnly) {
|
|
776
|
-
setShowSelectedItem(false);
|
|
777
|
-
}
|
|
778
|
-
},
|
|
779
|
-
disabled,
|
|
780
|
-
readOnly,
|
|
781
|
-
placeholder: selectedItem?.label ?? placeholder,
|
|
782
|
-
tabIndex: disabled || readOnly ? -1 : void 0,
|
|
783
|
-
ref: mergeRefs(inputRef, ref)
|
|
784
|
-
})
|
|
784
|
+
...inputProps
|
|
785
785
|
}
|
|
786
786
|
),
|
|
787
787
|
/* @__PURE__ */ jsx(
|
|
788
788
|
DropdownFieldAppendix,
|
|
789
789
|
{
|
|
790
|
-
...
|
|
791
|
-
"aria-busy": !(loading ?? resolvedItemsLoading) ? void 0 : "true"
|
|
792
|
-
}),
|
|
790
|
+
...toggleButtonProps,
|
|
793
791
|
ariaLabelCloseList,
|
|
794
792
|
ariaLabelOpenList,
|
|
795
793
|
clearable,
|
|
@@ -834,7 +832,6 @@ const MultiSelect = React.forwardRef(
|
|
|
834
832
|
placeholder,
|
|
835
833
|
readOnly = false,
|
|
836
834
|
selectedItems = [],
|
|
837
|
-
selectOnBlur = false,
|
|
838
835
|
selectOnTab = false,
|
|
839
836
|
style,
|
|
840
837
|
variant = "information",
|
|
@@ -865,10 +862,14 @@ const MultiSelect = React.forwardRef(
|
|
|
865
862
|
fetchItems
|
|
866
863
|
} = useResolvedItems(initialItems, debounceTimeout);
|
|
867
864
|
const isAllNonAsyncItemsSelected = typeof initialItems !== "function" && selectedItems.length === normalizedItems.length;
|
|
868
|
-
const
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
865
|
+
const selectAllUniqueId = useRandomId("select-all");
|
|
866
|
+
const selectAll = React.useMemo(
|
|
867
|
+
() => ({
|
|
868
|
+
value: selectAllUniqueId,
|
|
869
|
+
label: labelSelectAll
|
|
870
|
+
}),
|
|
871
|
+
[labelSelectAll]
|
|
872
|
+
);
|
|
872
873
|
const summarySelectedItems = React.useMemo(
|
|
873
874
|
() => ({
|
|
874
875
|
value: EMPTY_INPUT,
|
|
@@ -885,15 +886,21 @@ const MultiSelect = React.forwardRef(
|
|
|
885
886
|
...!hideSelectAll ? [selectAll] : [],
|
|
886
887
|
...normalizedItems
|
|
887
888
|
]);
|
|
888
|
-
const filterListItems = (
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
889
|
+
const filterListItems = React.useCallback(
|
|
890
|
+
({ inputValue: inputValue2 }) => setListItems([
|
|
891
|
+
...!hideSelectAll ? [selectAll] : [],
|
|
892
|
+
...normalizedItems.filter((item) => itemFilter(item, inputValue2))
|
|
893
|
+
]),
|
|
894
|
+
[hideSelectAll, selectAll, normalizedItems, itemFilter]
|
|
895
|
+
);
|
|
896
|
+
const updateListItems = React.useCallback(
|
|
897
|
+
({ inputValue: inputValue2 }) => {
|
|
898
|
+
const shouldRefetchItems = isFunctionWithQueryArgument(initialItems);
|
|
899
|
+
if (shouldRefetchItems) fetchItems(inputValue2 ?? EMPTY_INPUT);
|
|
900
|
+
filterListItems({ inputValue: inputValue2 ?? EMPTY_INPUT });
|
|
901
|
+
},
|
|
902
|
+
[filterListItems, initialItems, fetchItems]
|
|
903
|
+
);
|
|
897
904
|
React.useEffect(() => {
|
|
898
905
|
filterListItems({ inputValue });
|
|
899
906
|
}, [normalizedItems]);
|
|
@@ -925,67 +932,57 @@ const MultiSelect = React.forwardRef(
|
|
|
925
932
|
});
|
|
926
933
|
const stateReducer = React.useCallback(
|
|
927
934
|
(state, {
|
|
928
|
-
|
|
929
|
-
|
|
935
|
+
type,
|
|
936
|
+
changes
|
|
930
937
|
}) => {
|
|
931
|
-
if (changes.highlightedIndex !== void 0 && changes?.highlightedIndex >= 0) {
|
|
932
|
-
setLastHighlightedIndex(changes?.highlightedIndex);
|
|
933
|
-
}
|
|
934
938
|
switch (type) {
|
|
935
|
-
// reset input value when leaving input field
|
|
936
|
-
case useCombobox.stateChangeTypes.InputBlur:
|
|
937
|
-
return {
|
|
938
|
-
...changes,
|
|
939
|
-
inputValue: EMPTY_INPUT
|
|
940
|
-
};
|
|
941
939
|
// keep menu open and edit input value on item selection
|
|
942
940
|
case useCombobox.stateChangeTypes.InputKeyDownEnter:
|
|
943
941
|
case useCombobox.stateChangeTypes.ItemClick: {
|
|
944
942
|
return {
|
|
945
943
|
...changes,
|
|
946
944
|
isOpen: true,
|
|
947
|
-
inputValue: clearInputOnSelect ? EMPTY_INPUT :
|
|
945
|
+
inputValue: clearInputOnSelect ? EMPTY_INPUT : state.inputValue
|
|
948
946
|
};
|
|
949
947
|
}
|
|
948
|
+
// reset input value when leaving input field
|
|
949
|
+
case useCombobox.stateChangeTypes.InputBlur: {
|
|
950
|
+
const { selectedItem: _, ...otherChanges } = changes;
|
|
951
|
+
return resetInputState(otherChanges);
|
|
952
|
+
}
|
|
950
953
|
// edit input value when selected items is updated outside component
|
|
951
954
|
case useCombobox.stateChangeTypes.ControlledPropUpdatedSelectedItem: {
|
|
952
|
-
return {
|
|
953
|
-
...changes,
|
|
954
|
-
inputValue: inputRef?.current?.value ?? EMPTY_INPUT
|
|
955
|
-
};
|
|
955
|
+
return { ...changes, inputValue: state.inputValue };
|
|
956
956
|
}
|
|
957
957
|
// remove leading whitespace, select item with spacebar if input is empty and filter list items
|
|
958
958
|
case useCombobox.stateChangeTypes.InputChange: {
|
|
959
|
-
const leadingWhitespaceTest = /^\s+/g;
|
|
960
959
|
const isSpacePressedOnEmptyInput = changes.inputValue === " ";
|
|
961
|
-
if (
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
}
|
|
982
|
-
return changes;
|
|
960
|
+
if (!isSpacePressedOnEmptyInput)
|
|
961
|
+
return { ...changes, highlightedIndex: hideSelectAll ? 0 : 1 };
|
|
962
|
+
const sanitizedInputValue = (changes.inputValue ?? "").replace(
|
|
963
|
+
/^\s+/,
|
|
964
|
+
EMPTY_INPUT
|
|
965
|
+
);
|
|
966
|
+
if (!state.isOpen)
|
|
967
|
+
return {
|
|
968
|
+
...changes,
|
|
969
|
+
inputValue: sanitizedInputValue,
|
|
970
|
+
isOpen: true
|
|
971
|
+
};
|
|
972
|
+
const i = changes.highlightedIndex ?? -1;
|
|
973
|
+
if (i >= 0 && i < listItems.length)
|
|
974
|
+
return {
|
|
975
|
+
...changes,
|
|
976
|
+
inputValue: sanitizedInputValue,
|
|
977
|
+
selectedItem: listItems[i]
|
|
978
|
+
};
|
|
979
|
+
return { ...changes, inputValue: sanitizedInputValue };
|
|
983
980
|
}
|
|
984
981
|
default:
|
|
985
982
|
return changes;
|
|
986
983
|
}
|
|
987
984
|
},
|
|
988
|
-
[hideSelectAll,
|
|
985
|
+
[hideSelectAll, listItems, clearInputOnSelect]
|
|
989
986
|
);
|
|
990
987
|
const {
|
|
991
988
|
getInputProps,
|
|
@@ -994,10 +991,8 @@ const MultiSelect = React.forwardRef(
|
|
|
994
991
|
getMenuProps,
|
|
995
992
|
getToggleButtonProps,
|
|
996
993
|
highlightedIndex,
|
|
997
|
-
setHighlightedIndex,
|
|
998
994
|
inputValue,
|
|
999
|
-
isOpen
|
|
1000
|
-
setInputValue
|
|
995
|
+
isOpen
|
|
1001
996
|
} = useCombobox({
|
|
1002
997
|
defaultHighlightedIndex: lastHighlightedIndex,
|
|
1003
998
|
// after selection, highlight previously selected item.
|
|
@@ -1007,8 +1002,6 @@ const MultiSelect = React.forwardRef(
|
|
|
1007
1002
|
stateReducer,
|
|
1008
1003
|
onInputValueChange(changes) {
|
|
1009
1004
|
updateListItems({ inputValue: changes.inputValue });
|
|
1010
|
-
setHighlightedIndex(hideSelectAll ? 0 : 1);
|
|
1011
|
-
setLastHighlightedIndex(hideSelectAll ? 0 : 1);
|
|
1012
1005
|
},
|
|
1013
1006
|
onSelectedItemChange({ selectedItem: clickedItem }) {
|
|
1014
1007
|
if (!clickedItem) return;
|
|
@@ -1017,6 +1010,9 @@ const MultiSelect = React.forwardRef(
|
|
|
1017
1010
|
onChange: setSelectedItems
|
|
1018
1011
|
});
|
|
1019
1012
|
},
|
|
1013
|
+
onHighlightedIndexChange: ({ highlightedIndex: highlightedIndex2 }) => {
|
|
1014
|
+
if (highlightedIndex2 >= 0) setLastHighlightedIndex(highlightedIndex2);
|
|
1015
|
+
},
|
|
1020
1016
|
// Accessibility
|
|
1021
1017
|
getA11yStatusMessage: (options) => getA11yStatusMessage({
|
|
1022
1018
|
...options,
|
|
@@ -1032,19 +1028,17 @@ const MultiSelect = React.forwardRef(
|
|
|
1032
1028
|
offset(space.extraSmall2),
|
|
1033
1029
|
shift({ padding: space.extraSmall }),
|
|
1034
1030
|
size({
|
|
1035
|
-
apply({
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
maxHeight: `${clamp(10 * 16, availableHeight, 20 * 16)}px`
|
|
1041
|
-
});
|
|
1031
|
+
apply({ elements, availableHeight }) {
|
|
1032
|
+
elements.floating.style.setProperty(
|
|
1033
|
+
"--list-max-height",
|
|
1034
|
+
`${clamp(10 * 16, availableHeight, 20 * 16)}px`
|
|
1035
|
+
);
|
|
1042
1036
|
}
|
|
1043
1037
|
}),
|
|
1044
1038
|
flip({ fallbackStrategy: "initialPlacement" })
|
|
1045
1039
|
]
|
|
1046
1040
|
});
|
|
1047
|
-
|
|
1041
|
+
useLayoutEffect(() => {
|
|
1048
1042
|
if (isOpen && refs.reference.current && refs.floating.current) {
|
|
1049
1043
|
return autoUpdate(
|
|
1050
1044
|
refs.reference.current,
|
|
@@ -1057,6 +1051,42 @@ const MultiSelect = React.forwardRef(
|
|
|
1057
1051
|
inputRef.current?.focus();
|
|
1058
1052
|
reset();
|
|
1059
1053
|
};
|
|
1054
|
+
const dropdownProps = getDropdownProps({
|
|
1055
|
+
preventKeyAction: isOpen,
|
|
1056
|
+
value: inputValue ?? EMPTY_INPUT,
|
|
1057
|
+
ref: mergeRefs(inputRef, ref)
|
|
1058
|
+
});
|
|
1059
|
+
const inputProps = getInputProps({
|
|
1060
|
+
onKeyDown: (e) => {
|
|
1061
|
+
if (selectOnTab && isOpen && e.key === "Tab") {
|
|
1062
|
+
const highlitedItem = listItems[highlightedIndex];
|
|
1063
|
+
if (!highlitedItem) return;
|
|
1064
|
+
const shouldSkipTabSelection = clickedItemIsSelectAll(highlitedItem) || !clickedItemIsSelectAll(highlitedItem) && clickedItemIsInSelectedItems(highlitedItem);
|
|
1065
|
+
if (shouldSkipTabSelection) return;
|
|
1066
|
+
handleListItemClicked({
|
|
1067
|
+
clickedItem: highlitedItem,
|
|
1068
|
+
onChange: setSelectedItems
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
},
|
|
1072
|
+
onBlur,
|
|
1073
|
+
onFocus,
|
|
1074
|
+
...dropdownProps,
|
|
1075
|
+
className: "eds-dropdown__input eds-form-control",
|
|
1076
|
+
disabled: readOnly || disabled,
|
|
1077
|
+
placeholder,
|
|
1078
|
+
tabIndex: disabled || readOnly ? -1 : void 0
|
|
1079
|
+
});
|
|
1080
|
+
const labelProps = getLabelProps();
|
|
1081
|
+
const menuProps = getMenuProps({
|
|
1082
|
+
"aria-multiselectable": true,
|
|
1083
|
+
refKey: "innerRef",
|
|
1084
|
+
ref: refs.setFloating,
|
|
1085
|
+
style: listStyle
|
|
1086
|
+
});
|
|
1087
|
+
const toggleButtonProps = getToggleButtonProps({
|
|
1088
|
+
"aria-busy": !(loading ?? resolvedItemsLoading) ? void 0 : "true"
|
|
1089
|
+
});
|
|
1060
1090
|
return /* @__PURE__ */ jsxs(
|
|
1061
1091
|
BaseFormControl,
|
|
1062
1092
|
{
|
|
@@ -1071,21 +1101,14 @@ const MultiSelect = React.forwardRef(
|
|
|
1071
1101
|
feedback,
|
|
1072
1102
|
isFilled: hasSelectedItems || inputValue !== EMPTY_INPUT,
|
|
1073
1103
|
label,
|
|
1074
|
-
labelId:
|
|
1075
|
-
labelProps
|
|
1104
|
+
labelId: labelProps.id,
|
|
1105
|
+
labelProps,
|
|
1076
1106
|
labelTooltip,
|
|
1077
|
-
onBlur: (e) => {
|
|
1078
|
-
setInputValue("");
|
|
1079
|
-
onBlur?.(e);
|
|
1080
|
-
},
|
|
1081
1107
|
onClick: (e) => {
|
|
1082
|
-
if (e.target === e.currentTarget)
|
|
1083
|
-
getInputProps()?.onClick?.(e);
|
|
1084
|
-
}
|
|
1108
|
+
if (e.target === e.currentTarget) inputProps?.onClick?.(e);
|
|
1085
1109
|
onClick?.(e);
|
|
1086
1110
|
},
|
|
1087
1111
|
onKeyDown,
|
|
1088
|
-
onFocus,
|
|
1089
1112
|
readOnly,
|
|
1090
1113
|
ref: refs.setReference,
|
|
1091
1114
|
style,
|
|
@@ -1097,19 +1120,17 @@ const MultiSelect = React.forwardRef(
|
|
|
1097
1120
|
ariaLabelSelectedItem,
|
|
1098
1121
|
floatingStyles,
|
|
1099
1122
|
getItemProps,
|
|
1100
|
-
getMenuProps,
|
|
1101
1123
|
highlightedIndex,
|
|
1102
1124
|
isOpen,
|
|
1103
1125
|
listItems,
|
|
1104
|
-
style: listStyle,
|
|
1105
|
-
setListRef: refs.setFloating,
|
|
1106
1126
|
loading: loading ?? resolvedItemsLoading,
|
|
1107
1127
|
loadingText,
|
|
1108
1128
|
noMatchesText,
|
|
1109
1129
|
selectAllCheckboxState,
|
|
1110
1130
|
selectAllItem: selectAll,
|
|
1111
1131
|
selectedItems,
|
|
1112
|
-
readOnly
|
|
1132
|
+
readOnly,
|
|
1133
|
+
...menuProps
|
|
1113
1134
|
}
|
|
1114
1135
|
),
|
|
1115
1136
|
...rest,
|
|
@@ -1124,7 +1145,7 @@ const MultiSelect = React.forwardRef(
|
|
|
1124
1145
|
}
|
|
1125
1146
|
),
|
|
1126
1147
|
children: [
|
|
1127
|
-
selectedItems.length > 1 ? /* @__PURE__ */ jsx(VisuallyHidden, { onClick: inputRef.current?.focus, children: ariaLabelJumpToInput }) : null,
|
|
1148
|
+
selectedItems.length > 1 ? /* @__PURE__ */ jsx(VisuallyHidden, { onClick: () => inputRef.current?.focus(), children: ariaLabelJumpToInput }) : null,
|
|
1128
1149
|
selectedItems.length <= maxChips ? selectedItems.map((selectedItem, index) => /* @__PURE__ */ jsx(
|
|
1129
1150
|
SelectedItemTag,
|
|
1130
1151
|
{
|
|
@@ -1152,43 +1173,14 @@ const MultiSelect = React.forwardRef(
|
|
|
1152
1173
|
selectedItem: summarySelectedItems
|
|
1153
1174
|
}
|
|
1154
1175
|
),
|
|
1155
|
-
/* @__PURE__ */ jsx(
|
|
1156
|
-
"input",
|
|
1157
|
-
{
|
|
1158
|
-
...getInputProps({
|
|
1159
|
-
onKeyDown: (e) => {
|
|
1160
|
-
if (selectOnTab && isOpen && e.key === "Tab") {
|
|
1161
|
-
const highlitedItem = listItems[highlightedIndex];
|
|
1162
|
-
if (!highlitedItem) return;
|
|
1163
|
-
const shouldSkipTabSelection = clickedItemIsSelectAll(highlitedItem) || !clickedItemIsSelectAll(highlitedItem) && clickedItemIsInSelectedItems(highlitedItem);
|
|
1164
|
-
if (shouldSkipTabSelection) return;
|
|
1165
|
-
handleListItemClicked({
|
|
1166
|
-
clickedItem: highlitedItem,
|
|
1167
|
-
onChange: setSelectedItems
|
|
1168
|
-
});
|
|
1169
|
-
}
|
|
1170
|
-
},
|
|
1171
|
-
...getDropdownProps({
|
|
1172
|
-
preventKeyAction: isOpen,
|
|
1173
|
-
value: inputValue ?? EMPTY_INPUT,
|
|
1174
|
-
ref: mergeRefs(inputRef, ref)
|
|
1175
|
-
}),
|
|
1176
|
-
className: "eds-dropdown__input eds-form-control",
|
|
1177
|
-
disabled: readOnly || disabled,
|
|
1178
|
-
placeholder,
|
|
1179
|
-
tabIndex: disabled || readOnly ? -1 : void 0
|
|
1180
|
-
})
|
|
1181
|
-
}
|
|
1182
|
-
)
|
|
1176
|
+
/* @__PURE__ */ jsx("input", { ...inputProps })
|
|
1183
1177
|
]
|
|
1184
1178
|
}
|
|
1185
1179
|
),
|
|
1186
1180
|
/* @__PURE__ */ jsx(
|
|
1187
1181
|
DropdownFieldAppendix,
|
|
1188
1182
|
{
|
|
1189
|
-
...
|
|
1190
|
-
"aria-busy": !(loading ?? resolvedItemsLoading) ? void 0 : "true"
|
|
1191
|
-
}),
|
|
1183
|
+
...toggleButtonProps,
|
|
1192
1184
|
ariaLabelCloseList,
|
|
1193
1185
|
ariaLabelOpenList,
|
|
1194
1186
|
clearable,
|
|
@@ -1214,7 +1206,7 @@ const Dropdown = React.forwardRef(
|
|
|
1214
1206
|
ariaLabelOpenList = "Åpne liste med valg",
|
|
1215
1207
|
ariaLabelSelectedItem,
|
|
1216
1208
|
className,
|
|
1217
|
-
clearable
|
|
1209
|
+
clearable,
|
|
1218
1210
|
disabled = false,
|
|
1219
1211
|
disableLabelAnimation,
|
|
1220
1212
|
feedback,
|
|
@@ -1231,7 +1223,6 @@ const Dropdown = React.forwardRef(
|
|
|
1231
1223
|
prepend,
|
|
1232
1224
|
readOnly = false,
|
|
1233
1225
|
selectedItem,
|
|
1234
|
-
selectOnBlur = false,
|
|
1235
1226
|
selectOnTab = false,
|
|
1236
1227
|
style,
|
|
1237
1228
|
variant = "information",
|
|
@@ -1252,22 +1243,20 @@ const Dropdown = React.forwardRef(
|
|
|
1252
1243
|
items: normalizedItems,
|
|
1253
1244
|
defaultHighlightedIndex: selectedItem ? void 0 : 0,
|
|
1254
1245
|
selectedItem,
|
|
1255
|
-
stateReducer(
|
|
1246
|
+
stateReducer(state, { changes, type }) {
|
|
1256
1247
|
const toggleButtonIsFocused = typeof document !== "undefined" && document.activeElement === refs.reference.current;
|
|
1257
1248
|
switch (type) {
|
|
1258
1249
|
case useSelect.stateChangeTypes.ToggleButtonKeyDownArrowDown:
|
|
1259
1250
|
case useSelect.stateChangeTypes.ToggleButtonKeyDownArrowUp:
|
|
1260
1251
|
if (!toggleButtonIsFocused) return { ...changes, isOpen: false };
|
|
1252
|
+
break;
|
|
1253
|
+
case useSelect.stateChangeTypes.ToggleButtonBlur:
|
|
1254
|
+
return { ...changes, selectedItem: state.selectedItem };
|
|
1261
1255
|
}
|
|
1262
1256
|
return changes;
|
|
1263
1257
|
},
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
case useSelect.stateChangeTypes.ToggleButtonBlur:
|
|
1267
|
-
if (!selectOnBlur) return;
|
|
1268
|
-
}
|
|
1269
|
-
if (newSelectedItem === void 0) return;
|
|
1270
|
-
onChange?.(newSelectedItem ?? null);
|
|
1258
|
+
onSelectedItemChange({ selectedItem: newSelectedItem }) {
|
|
1259
|
+
onChange?.(newSelectedItem);
|
|
1271
1260
|
},
|
|
1272
1261
|
itemToString
|
|
1273
1262
|
});
|
|
@@ -1278,19 +1267,17 @@ const Dropdown = React.forwardRef(
|
|
|
1278
1267
|
offset(space.extraSmall2),
|
|
1279
1268
|
shift({ padding: space.extraSmall }),
|
|
1280
1269
|
size({
|
|
1281
|
-
apply({
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
maxHeight: `${clamp(10 * 16, availableHeight, 20 * 16)}px`
|
|
1287
|
-
});
|
|
1270
|
+
apply({ elements, availableHeight }) {
|
|
1271
|
+
elements.floating.style.setProperty(
|
|
1272
|
+
"--list-max-height",
|
|
1273
|
+
`${clamp(10 * 16, availableHeight, 20 * 16)}px`
|
|
1274
|
+
);
|
|
1288
1275
|
}
|
|
1289
1276
|
}),
|
|
1290
1277
|
flip({ fallbackStrategy: "initialPlacement" })
|
|
1291
1278
|
]
|
|
1292
1279
|
});
|
|
1293
|
-
|
|
1280
|
+
useLayoutEffect(() => {
|
|
1294
1281
|
if (isOpen && refs.reference.current && refs.floating.current) {
|
|
1295
1282
|
return autoUpdate(
|
|
1296
1283
|
refs.reference.current,
|
|
@@ -1300,9 +1287,37 @@ const Dropdown = React.forwardRef(
|
|
|
1300
1287
|
}
|
|
1301
1288
|
}, [isOpen, refs.reference, refs.floating, update]);
|
|
1302
1289
|
const handleOnClear = () => {
|
|
1303
|
-
reset();
|
|
1304
1290
|
refs.reference.current?.focus();
|
|
1291
|
+
reset();
|
|
1305
1292
|
};
|
|
1293
|
+
const labelProps = getLabelProps({
|
|
1294
|
+
isFilled
|
|
1295
|
+
});
|
|
1296
|
+
const toggleButtonProps = getToggleButtonProps({
|
|
1297
|
+
ref: mergeRefs(ref, refs.setReference),
|
|
1298
|
+
"aria-disabled": disabled,
|
|
1299
|
+
"aria-label": disabled ? "Disabled dropdown" : "",
|
|
1300
|
+
disabled,
|
|
1301
|
+
readOnly,
|
|
1302
|
+
label,
|
|
1303
|
+
labelId: labelProps?.id,
|
|
1304
|
+
tabIndex: disabled || readOnly ? -1 : 0,
|
|
1305
|
+
onKeyDown(e) {
|
|
1306
|
+
if (isOpen && e.key === "Tab") {
|
|
1307
|
+
const highlitedItem = normalizedItems[highlightedIndex];
|
|
1308
|
+
if (selectOnTab && highlitedItem && highlitedItem !== selectedItem) {
|
|
1309
|
+
selectItem(highlitedItem);
|
|
1310
|
+
}
|
|
1311
|
+
} else if (!isOpen && e.key === "Escape" && clearable) {
|
|
1312
|
+
reset();
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
});
|
|
1316
|
+
const menuProps = getMenuProps({
|
|
1317
|
+
refKey: "innerRef",
|
|
1318
|
+
ref: refs.setFloating,
|
|
1319
|
+
style: listStyle
|
|
1320
|
+
});
|
|
1306
1321
|
return /* @__PURE__ */ jsxs(
|
|
1307
1322
|
BaseFormControl,
|
|
1308
1323
|
{
|
|
@@ -1311,31 +1326,12 @@ const Dropdown = React.forwardRef(
|
|
|
1311
1326
|
}),
|
|
1312
1327
|
disableLabelAnimation,
|
|
1313
1328
|
feedback,
|
|
1314
|
-
|
|
1315
|
-
labelProps: getLabelProps(),
|
|
1329
|
+
labelProps,
|
|
1316
1330
|
labelTooltip,
|
|
1317
1331
|
prepend,
|
|
1318
1332
|
style,
|
|
1319
1333
|
variant,
|
|
1320
|
-
...
|
|
1321
|
-
ref: mergeRefs(ref, refs.setReference),
|
|
1322
|
-
"aria-disabled": disabled,
|
|
1323
|
-
"aria-label": disabled ? "Disabled dropdown" : "",
|
|
1324
|
-
disabled,
|
|
1325
|
-
readOnly,
|
|
1326
|
-
label,
|
|
1327
|
-
labelId: getLabelProps()?.id,
|
|
1328
|
-
children: void 0,
|
|
1329
|
-
tabIndex: disabled || readOnly ? -1 : 0,
|
|
1330
|
-
onKeyDown(e) {
|
|
1331
|
-
if (isOpen && e.key === "Tab") {
|
|
1332
|
-
const highlitedItem = normalizedItems[highlightedIndex];
|
|
1333
|
-
if ((selectOnTab || selectOnBlur) && highlitedItem && highlitedItem !== selectedItem) {
|
|
1334
|
-
selectItem(highlitedItem);
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1338
|
-
}),
|
|
1334
|
+
...toggleButtonProps,
|
|
1339
1335
|
after: /* @__PURE__ */ jsx(
|
|
1340
1336
|
DropdownList,
|
|
1341
1337
|
{
|
|
@@ -1343,17 +1339,15 @@ const Dropdown = React.forwardRef(
|
|
|
1343
1339
|
ariaLabelSelectedItem,
|
|
1344
1340
|
floatingStyles,
|
|
1345
1341
|
getItemProps,
|
|
1346
|
-
getMenuProps,
|
|
1347
1342
|
highlightedIndex,
|
|
1348
1343
|
isOpen,
|
|
1349
1344
|
listItems: normalizedItems,
|
|
1350
|
-
noMatchesText,
|
|
1351
|
-
style: listStyle,
|
|
1352
|
-
setListRef: refs.setFloating,
|
|
1353
1345
|
loading: loading ?? resolvedItemsLoading,
|
|
1354
1346
|
loadingText,
|
|
1347
|
+
noMatchesText,
|
|
1355
1348
|
selectedItems: selectedItem !== null ? [selectedItem] : [],
|
|
1356
|
-
readOnly
|
|
1349
|
+
readOnly,
|
|
1350
|
+
...menuProps
|
|
1357
1351
|
}
|
|
1358
1352
|
),
|
|
1359
1353
|
...rest,
|