@entur/dropdown 7.3.2-beta.8 → 7.3.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.
@@ -0,0 +1,1465 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const utils = require("@entur/utils");
4
+ const jsxRuntime = require("react/jsx-runtime");
5
+ const React = require("react");
6
+ const downshift = require("downshift");
7
+ const classNames = require("classnames");
8
+ const reactDom = require("@floating-ui/react-dom");
9
+ const form = require("@entur/form");
10
+ const tokens = require("@entur/tokens");
11
+ const a11y = require("@entur/a11y");
12
+ const button = require("@entur/button");
13
+ const chip = require("@entur/chip");
14
+ const icons = require("@entur/icons");
15
+ const loader = require("@entur/loader");
16
+ const tooltip = require("@entur/tooltip");
17
+ const DropdownList = ({
18
+ ariaLabelChosenSingular = "valgt",
19
+ ariaLabelSelectedItem = ", valgt element, trykk for å fjerne",
20
+ getItemProps,
21
+ getMenuProps,
22
+ isOpen,
23
+ highlightedIndex,
24
+ listItems,
25
+ floatingStyles,
26
+ setListRef,
27
+ loading = false,
28
+ loadingText = "Laster inn …",
29
+ noMatchesText = "Ingen treff for søket",
30
+ selectAllCheckboxState,
31
+ selectAllItem,
32
+ selectedItems,
33
+ readOnly = false,
34
+ ...rest
35
+ }) => {
36
+ const isMultiselect = selectAllItem !== void 0;
37
+ const isNoMatches = !loading && (listItems.length === 0 || listItems?.length === 1 && listItems?.[0]?.value === selectAllItem?.value);
38
+ const isItemSelected = (item) => selectedItems.some(
39
+ (selectedItem) => selectedItem?.value === item?.value && selectedItem?.label === item?.label
40
+ );
41
+ const ariaValuesSelectAll = () => {
42
+ switch (selectAllCheckboxState?.()) {
43
+ case "indeterminate": {
44
+ return {
45
+ label: `${selectAllItem?.label}, delvis valgt`,
46
+ selected: false
47
+ };
48
+ }
49
+ case true: {
50
+ return {
51
+ label: `${selectAllItem?.label}, ${ariaLabelChosenSingular}`,
52
+ selected: true
53
+ };
54
+ }
55
+ default: {
56
+ return { label: `${selectAllItem?.label}`, selected: false };
57
+ }
58
+ }
59
+ };
60
+ const selectAllListItemContent = () => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
61
+ /* @__PURE__ */ jsxRuntime.jsx(
62
+ form.Checkbox,
63
+ {
64
+ "aria-hidden": "true",
65
+ checked: selectAllCheckboxState?.(),
66
+ className: "eds-dropdown__list__item__checkbox",
67
+ tabIndex: -1,
68
+ onChange: () => void 0
69
+ }
70
+ ),
71
+ /* @__PURE__ */ jsxRuntime.jsx(
72
+ "span",
73
+ {
74
+ className: "eds-dropdown__list__item__text",
75
+ "aria-label": ariaValuesSelectAll().label,
76
+ children: selectAllItem?.label
77
+ }
78
+ )
79
+ ] });
80
+ const isReactComponent = (icon) => {
81
+ return typeof icon === "function" || typeof icon === "object" && icon !== null && "$$typeof" in icon && typeof icon.$$typeof === "symbol";
82
+ };
83
+ const listItemContent = (item) => {
84
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
85
+ isMultiselect && /* @__PURE__ */ jsxRuntime.jsx(
86
+ form.Checkbox,
87
+ {
88
+ "aria-hidden": "true",
89
+ checked: isItemSelected(item),
90
+ className: "eds-dropdown__list__item__checkbox",
91
+ tabIndex: -1,
92
+ onChange: () => void 0
93
+ }
94
+ ),
95
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "eds-dropdown__list__item__text", children: [
96
+ item.label,
97
+ /* @__PURE__ */ jsxRuntime.jsx(a11y.VisuallyHidden, { children: isItemSelected(item) ? ariaLabelSelectedItem : "" })
98
+ ] }),
99
+ Array.isArray(item.icons) ? item.icons.filter(isReactComponent).map((Icon, index) => {
100
+ const key = `${Icon.displayName ?? Icon.name ?? Icon.name}-${index}`;
101
+ return /* @__PURE__ */ jsxRuntime.jsx(
102
+ Icon,
103
+ {
104
+ inline: true,
105
+ className: "eds-dropdown__list__item__icon"
106
+ },
107
+ key
108
+ );
109
+ }) : null
110
+ ] });
111
+ };
112
+ return (
113
+ // use popover from @entur/tooltip when that package upgrades to floating-ui
114
+ /* @__PURE__ */ jsxRuntime.jsx(
115
+ "ul",
116
+ {
117
+ ...getMenuProps({
118
+ "aria-multiselectable": isMultiselect,
119
+ ref: setListRef,
120
+ className: "eds-dropdown__list",
121
+ style: {
122
+ ...floatingStyles,
123
+ display: isOpen && !readOnly ? void 0 : "none",
124
+ ...rest.style
125
+ }
126
+ }),
127
+ children: (() => {
128
+ if (!isOpen || readOnly) {
129
+ return null;
130
+ }
131
+ if (loading) {
132
+ return /* @__PURE__ */ jsxRuntime.jsx(
133
+ "li",
134
+ {
135
+ className: "eds-dropdown__list__item",
136
+ children: loadingText
137
+ },
138
+ "dropdown-list-loading"
139
+ );
140
+ }
141
+ if (isNoMatches) {
142
+ return /* @__PURE__ */ jsxRuntime.jsx(
143
+ "li",
144
+ {
145
+ className: "eds-dropdown__list__item",
146
+ children: noMatchesText
147
+ },
148
+ "dropdown-list-no-match"
149
+ );
150
+ }
151
+ return listItems.map((item, index) => {
152
+ const key = item.itemKey ?? `${item.label ?? ""}-${item.value ?? ""}-${(item.icons ?? []).map((icon) => icon?.displayName ?? icon?.name ?? "unknown").join("-")}`;
153
+ const itemIsSelectAll = item.value === selectAllItem?.value;
154
+ if (itemIsSelectAll && listItems.length <= 2) return null;
155
+ return /* @__PURE__ */ jsxRuntime.jsx(
156
+ "li",
157
+ {
158
+ className: classNames("eds-dropdown__list__item", {
159
+ "eds-dropdown__list__item--select-all": itemIsSelectAll,
160
+ "eds-dropdown__list__item--highlighted": highlightedIndex === index,
161
+ "eds-dropdown__list__item--selected": !isMultiselect && isItemSelected(item)
162
+ }),
163
+ ...getItemProps({
164
+ // @ts-expect-error Since getItemProps expects the same item type
165
+ // here as items, it throws error when selectAllItem is a string.
166
+ // This does, however, not cause any functional issues.
167
+ item,
168
+ index,
169
+ "aria-selected": itemIsSelectAll ? ariaValuesSelectAll().selected : isItemSelected(item)
170
+ }),
171
+ children: itemIsSelectAll ? selectAllListItemContent() : listItemContent(
172
+ item
173
+ )
174
+ },
175
+ key
176
+ );
177
+ });
178
+ })()
179
+ }
180
+ )
181
+ );
182
+ };
183
+ const SelectedItemTag = ({
184
+ ariaLabelRemoveSelected,
185
+ ariaLabelChosen = "valgt",
186
+ disabled,
187
+ getSelectedItemProps,
188
+ index,
189
+ readOnly,
190
+ removeSelectedItem,
191
+ selectedItem
192
+ }) => {
193
+ const { tabIndex: _, ...selectedItemProps } = getSelectedItemProps?.({
194
+ selectedItem,
195
+ index
196
+ }) ?? {};
197
+ return /* @__PURE__ */ React.createElement(
198
+ chip.TagChip,
199
+ {
200
+ size: "small",
201
+ className: classNames("eds-dropdown__selected-item-tag", {
202
+ "eds-dropdown__selected-item-tag--readonly": readOnly,
203
+ "eds-dropdown__selected-item-tag--disabled": disabled
204
+ }),
205
+ ...selectedItemProps,
206
+ onClose: (e) => {
207
+ e.stopPropagation();
208
+ removeSelectedItem(selectedItem);
209
+ },
210
+ onClick: (e) => e.stopPropagation(),
211
+ closeButtonAriaLabel: `${selectedItem.label} ${ariaLabelChosen}, ${ariaLabelRemoveSelected} `,
212
+ key: selectedItem.value,
213
+ "aria-live": "polite"
214
+ },
215
+ /* @__PURE__ */ jsxRuntime.jsx(
216
+ "span",
217
+ {
218
+ "aria-hidden": "true",
219
+ className: "eds-dropdown__selected-item-tag__text",
220
+ children: selectedItem.label
221
+ }
222
+ )
223
+ );
224
+ };
225
+ const DropdownFieldAppendix = React.forwardRef(
226
+ ({
227
+ ariaLabelCloseList,
228
+ ariaLabelOpenList,
229
+ clearable = false,
230
+ labelClearSelected,
231
+ focusable = false,
232
+ disabled,
233
+ isOpen,
234
+ loading = false,
235
+ loadingText,
236
+ onClear,
237
+ itemIsSelected,
238
+ ...rest
239
+ }, ref) => {
240
+ function getToggleAriaLabel() {
241
+ if (loading) return loadingText;
242
+ if (isOpen) return ariaLabelCloseList;
243
+ return ariaLabelOpenList;
244
+ }
245
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: !disabled && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "eds-dropdown__appendix", children: [
246
+ clearable && itemIsSelected && /* @__PURE__ */ jsxRuntime.jsx(
247
+ ClearableButton,
248
+ {
249
+ onClear,
250
+ focusable: true,
251
+ labelClearSelectedItems: labelClearSelected
252
+ }
253
+ ),
254
+ /* @__PURE__ */ jsxRuntime.jsx(
255
+ button.IconButton,
256
+ {
257
+ className: classNames("eds-dropdown__appendix__toggle-button", {
258
+ "eds-dropdown__appendix__toggle-button--open": isOpen
259
+ }),
260
+ ref,
261
+ "aria-label": getToggleAriaLabel(),
262
+ ...rest,
263
+ type: "button",
264
+ tabIndex: focusable ? 0 : -1,
265
+ children: !loading ? /* @__PURE__ */ jsxRuntime.jsx(icons.DownArrowIcon, { "aria-hidden": "true" }) : /* @__PURE__ */ jsxRuntime.jsx(loader.LoadingDots, { "aria-hidden": "true" })
266
+ }
267
+ )
268
+ ] }) });
269
+ }
270
+ );
271
+ const ClearableButton = ({
272
+ onClear,
273
+ labelClearSelectedItems = "Fjern valgte",
274
+ focusable = false
275
+ }) => {
276
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
277
+ /* @__PURE__ */ jsxRuntime.jsx(
278
+ tooltip.Tooltip,
279
+ {
280
+ "aria-hidden": "true",
281
+ placement: "top",
282
+ content: labelClearSelectedItems,
283
+ className: "eds-dropdown__appendix__clear-button__tooltip",
284
+ children: /* @__PURE__ */ jsxRuntime.jsx(
285
+ button.IconButton,
286
+ {
287
+ className: "eds-dropdown__appendix__clear-button",
288
+ type: "button",
289
+ tabIndex: focusable ? 0 : -1,
290
+ onClick: (e) => {
291
+ e.stopPropagation();
292
+ onClear();
293
+ },
294
+ onKeyDown: (e) => {
295
+ if (e.key === "Enter" || e.key === " ") {
296
+ e.preventDefault();
297
+ e.stopPropagation();
298
+ onClear();
299
+ }
300
+ },
301
+ "aria-label": labelClearSelectedItems,
302
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.CloseSmallIcon, { "aria-hidden": "true" })
303
+ }
304
+ )
305
+ }
306
+ ),
307
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "eds-dropdown__appendix__divider" })
308
+ ] });
309
+ };
310
+ const useNormalizedItems = (items) => React.useMemo(
311
+ () => items.map((item) => {
312
+ if (typeof item == "string") {
313
+ return {
314
+ value: item,
315
+ label: item
316
+ };
317
+ }
318
+ if (item?.value === void 0) {
319
+ return {
320
+ ...item,
321
+ value: item.label
322
+ };
323
+ }
324
+ return { ...item, value: item.value };
325
+ }),
326
+ [items]
327
+ );
328
+ const useResolvedItems = (itemsOrItemsResolver, debounceTimeout = 250) => {
329
+ const itemsIsAFunction = typeof itemsOrItemsResolver === "function";
330
+ const [items, setItems] = React.useState(
331
+ itemsIsAFunction ? [] : itemsOrItemsResolver
332
+ );
333
+ const [loading, setLoading] = React.useState(false);
334
+ const abortControllerRef = React.useRef(
335
+ new AbortController()
336
+ );
337
+ const itemsResolver = React.useMemo(() => {
338
+ if (itemsIsAFunction)
339
+ return itemsOrItemsResolver;
340
+ return () => Promise.resolve(itemsOrItemsResolver);
341
+ }, [itemsOrItemsResolver, itemsIsAFunction]);
342
+ const updateItems = async (inputValue) => {
343
+ if (abortControllerRef?.current) abortControllerRef?.current?.abort();
344
+ const abortController = new AbortController();
345
+ abortControllerRef.current = abortController;
346
+ setLoading(true);
347
+ try {
348
+ const resolvedItems = await itemsResolver(
349
+ inputValue ?? "",
350
+ abortControllerRef
351
+ );
352
+ if (abortControllerRef?.current?.signal?.aborted) {
353
+ console.warn(
354
+ "Avbryt den asynkrone funksjonen din med signalet fra AbortController-en for å for å unngå minnelekkasje.",
355
+ 'Funksjonen bør kaste en DOMException med navnet "AbortError" når den avbrytes.',
356
+ "",
357
+ "\n\nSe eksempel her: https://linje.entur.no/komponenter/skjemaelementer/dropdown#s%C3%B8kbar-dropdown-med-valg-fra-nettverkskall-bassert-p%C3%A5-tekstinput",
358
+ "\nLes mer om AbortController her: https://developer.mozilla.org/en-US/docs/Web/API/AbortController"
359
+ );
360
+ return;
361
+ }
362
+ setLoading(false);
363
+ setItems(resolvedItems);
364
+ } catch (error2) {
365
+ if (error2 && typeof error2 === "object" && "name" in error2 && error2.name === "AbortError") {
366
+ return;
367
+ }
368
+ console.warn(
369
+ "The following error was received but not handled inside Entur Designsystems useResolvedItems hook:"
370
+ );
371
+ throw error2;
372
+ }
373
+ };
374
+ const debouncedFetchItems = utils.useDebounce(updateItems, debounceTimeout);
375
+ const normalizedItems = useNormalizedItems(items);
376
+ React.useEffect(() => {
377
+ return () => abortControllerRef?.current?.abort("Component unmounted");
378
+ }, []);
379
+ React.useEffect(() => {
380
+ if (itemsIsAFunction) {
381
+ debouncedFetchItems("");
382
+ }
383
+ }, [itemsIsAFunction, itemsResolver]);
384
+ return {
385
+ items: normalizedItems,
386
+ loading: itemsIsAFunction ? loading : false,
387
+ fetchItems: debouncedFetchItems
388
+ };
389
+ };
390
+ const EMPTY_INPUT = "";
391
+ function lowerCaseFilterTest(item, input) {
392
+ if (!input) {
393
+ return true;
394
+ }
395
+ const sanitizeEscapeCharacters = input.replace(
396
+ /[-/\\^$*+?.()|[\]{}]/g,
397
+ "\\$&"
398
+ );
399
+ const inputRegex = new RegExp(sanitizeEscapeCharacters, "i");
400
+ return inputRegex.test(item.label);
401
+ }
402
+ function noFilter(item, input) {
403
+ return true;
404
+ }
405
+ const itemToString = (item) => item ? item.label : "";
406
+ const itemToKey = (item) => item?.label + item?.value;
407
+ const isFunctionWithQueryArgument = (object) => typeof object === "function" && object.length > 0;
408
+ const clamp = (val, min = 1, max = 10) => Math.min(Math.max(val, min), max);
409
+ const useMultiselectUtils = ({
410
+ listItems,
411
+ selectedItems,
412
+ selectAll
413
+ }) => {
414
+ const hasSelectedItems = selectedItems.length > 0;
415
+ const listItemsWithoutSelectAll = listItems.filter(
416
+ (item) => item.value !== selectAll.value
417
+ );
418
+ const unselectedItemsInListItems = listItemsWithoutSelectAll.filter(
419
+ (listItem) => !selectedItems.some(
420
+ (selectedItem) => selectedItem.value === listItem.value
421
+ )
422
+ );
423
+ const allListItemsAreSelected = !listItemsWithoutSelectAll.some(
424
+ (listItem) => !selectedItems.some(
425
+ (selectedItem) => selectedItem.value === listItem.value
426
+ )
427
+ );
428
+ const someListItemsAreSelected = listItemsWithoutSelectAll.some(
429
+ (listItem) => selectedItems.some((selectedItem) => selectedItem.value === listItem.value)
430
+ );
431
+ const addClickedItemToSelectedItems = (clickedItem, onChange) => onChange([...selectedItems, clickedItem]);
432
+ const clickedItemIsInSelectedItems = (clickedItem) => selectedItems.some(
433
+ (selectedItem) => selectedItem.value === clickedItem.value
434
+ );
435
+ const clickedItemIsSelectAll = (clickedItem) => clickedItem.value === selectAll.value;
436
+ const handleListItemClicked = ({
437
+ clickedItem,
438
+ onChange
439
+ }) => {
440
+ if (clickedItemIsSelectAll(clickedItem)) {
441
+ if (allListItemsAreSelected) {
442
+ return unselectAllListItems(onChange);
443
+ }
444
+ return selectAllUnselectedItemsInListItems(onChange);
445
+ }
446
+ if (clickedItemIsInSelectedItems(clickedItem)) {
447
+ return removeClickedItemFromSelectedItems(clickedItem, onChange);
448
+ }
449
+ addClickedItemToSelectedItems(clickedItem, onChange);
450
+ };
451
+ const removeClickedItemFromSelectedItems = (clickedItem, onChange) => onChange(
452
+ selectedItems.filter(
453
+ (selectedItem) => selectedItem.value !== clickedItem.value
454
+ )
455
+ );
456
+ const selectAllCheckboxState = () => {
457
+ if (allListItemsAreSelected) return true;
458
+ if (someListItemsAreSelected) return "indeterminate";
459
+ return false;
460
+ };
461
+ const selectAllUnselectedItemsInListItems = (onChange) => {
462
+ onChange([...selectedItems, ...unselectedItemsInListItems]);
463
+ };
464
+ const unselectAllListItems = (onChange) => {
465
+ const selectedItemsWithoutItemsInListItems = selectedItems.filter(
466
+ (selectedItem) => !listItemsWithoutSelectAll.some(
467
+ (listItem) => listItem.value === selectedItem.value
468
+ )
469
+ );
470
+ onChange(selectedItemsWithoutItemsInListItems);
471
+ };
472
+ return {
473
+ addClickedItemToSelectedItems,
474
+ allListItemsAreSelected,
475
+ clickedItemIsInSelectedItems,
476
+ clickedItemIsSelectAll,
477
+ handleListItemClicked,
478
+ hasSelectedItems,
479
+ listItemsWithoutSelectAll,
480
+ removeClickedItemFromSelectedItems,
481
+ selectAllCheckboxState,
482
+ selectAllUnselectedItemsInListItems,
483
+ someListItemsAreSelected,
484
+ unselectAllListItems
485
+ };
486
+ };
487
+ function getA11yStatusMessage(options) {
488
+ const { isOpen, selectAllItemIncluded = false, resultCount } = options;
489
+ if (!isOpen) {
490
+ return "";
491
+ }
492
+ const resultCountWithoutSelectAll = selectAllItemIncluded ? resultCount - 1 : resultCount;
493
+ if (resultCountWithoutSelectAll === 0) {
494
+ return "Ingen resultater";
495
+ }
496
+ return `${resultCountWithoutSelectAll} resultat${resultCountWithoutSelectAll === 1 ? "" : "er"} tilgjengelig, naviger med pil opp eller ned, velg elementer med Enter.`;
497
+ }
498
+ const SearchableDropdown = React.forwardRef(
499
+ ({
500
+ ariaLabelChosenSingular,
501
+ ariaLabelCloseList = "Lukk liste med valg",
502
+ ariaLabelOpenList = "Åpne liste med valg",
503
+ ariaLabelSelectedItem,
504
+ className,
505
+ clearable = true,
506
+ debounceTimeout,
507
+ disabled = false,
508
+ disableLabelAnimation = false,
509
+ feedback,
510
+ items: initialItems,
511
+ itemFilter = isFunctionWithQueryArgument(initialItems) ? noFilter : lowerCaseFilterTest,
512
+ label,
513
+ labelClearSelectedItem = "fjern valgt",
514
+ labelTooltip,
515
+ listStyle,
516
+ loading,
517
+ loadingText = "Laster resultater …",
518
+ noMatchesText = "Ingen tilgjengelige valg …",
519
+ onChange = () => void 0,
520
+ placeholder,
521
+ prepend,
522
+ readOnly = false,
523
+ selectedItem: value,
524
+ selectOnBlur = false,
525
+ selectOnTab = false,
526
+ style,
527
+ variant = "info",
528
+ ...rest
529
+ }, ref) => {
530
+ const [showSelectedItem, setShowSelectedItem] = React.useState(value !== null);
531
+ const [lastHighlightedIndex, setLastHighlightedIndex] = React.useState(0);
532
+ const inputRef = React.useRef(null);
533
+ const {
534
+ items: normalizedItems,
535
+ loading: resolvedItemsLoading,
536
+ fetchItems
537
+ } = useResolvedItems(initialItems, debounceTimeout);
538
+ const [listItems, setListItems] = React.useState(normalizedItems);
539
+ const filterListItems = ({ inputValue: inputValue2 }) => setListItems(
540
+ normalizedItems.filter((item) => itemFilter(item, inputValue2))
541
+ );
542
+ const updateListItems = ({ inputValue: inputValue2 }) => {
543
+ const shouldRefetchItems = isFunctionWithQueryArgument(initialItems);
544
+ if (shouldRefetchItems) fetchItems(inputValue2 ?? EMPTY_INPUT);
545
+ filterListItems({ inputValue: inputValue2 ?? EMPTY_INPUT });
546
+ };
547
+ const resetInputState = ({
548
+ changes
549
+ }) => {
550
+ updateListItems({ inputValue: EMPTY_INPUT });
551
+ return {
552
+ ...changes,
553
+ inputValue: EMPTY_INPUT
554
+ };
555
+ };
556
+ const inputHasFocus = typeof document !== "undefined" ? inputRef?.current === document?.activeElement : false;
557
+ React.useEffect(() => {
558
+ filterListItems({ inputValue });
559
+ }, [normalizedItems]);
560
+ React.useEffect(() => {
561
+ if (selectedItem !== null && !inputHasFocus) {
562
+ setShowSelectedItem(true);
563
+ updateListItems({ inputValue: EMPTY_INPUT });
564
+ setInputValue(EMPTY_INPUT);
565
+ }
566
+ }, []);
567
+ const stateReducer = React.useCallback(
568
+ (state, {
569
+ type,
570
+ changes
571
+ }) => {
572
+ if (changes.highlightedIndex !== void 0 && changes?.highlightedIndex >= 0) {
573
+ setLastHighlightedIndex(changes?.highlightedIndex);
574
+ }
575
+ switch (type) {
576
+ // empty input to show selected item and reset dropdown list on item selection
577
+ case downshift.useCombobox.stateChangeTypes.ItemClick:
578
+ case downshift.useCombobox.stateChangeTypes.InputKeyDownEnter:
579
+ case downshift.useCombobox.stateChangeTypes.InputBlur:
580
+ return resetInputState({ changes });
581
+ case downshift.useCombobox.stateChangeTypes.ControlledPropUpdatedSelectedItem:
582
+ if (changes.selectedItem !== null && !inputHasFocus)
583
+ setShowSelectedItem(true);
584
+ return resetInputState({ changes });
585
+ // remove leading whitespace, select element with spacebar on empty input
586
+ case downshift.useCombobox.stateChangeTypes.InputChange: {
587
+ const leadingWhitespaceTest = /^\s+/g;
588
+ const isSpacePressedOnEmptyInput = changes.inputValue === " ";
589
+ if (!isSpacePressedOnEmptyInput) setLastHighlightedIndex(0);
590
+ if (changes.inputValue?.match(leadingWhitespaceTest)) {
591
+ const sanitizedInputValue = changes.inputValue.replace(
592
+ leadingWhitespaceTest,
593
+ EMPTY_INPUT
594
+ );
595
+ if (isSpacePressedOnEmptyInput) {
596
+ if (!state.isOpen)
597
+ return {
598
+ ...changes,
599
+ inputValue: sanitizedInputValue,
600
+ isOpen: true
601
+ };
602
+ if (changes.highlightedIndex !== void 0) {
603
+ return {
604
+ ...changes,
605
+ inputValue: sanitizedInputValue,
606
+ selectedItem: listItems[changes.highlightedIndex]
607
+ };
608
+ }
609
+ }
610
+ }
611
+ return { ...changes, highlightedIndex: 0 };
612
+ }
613
+ default:
614
+ return changes;
615
+ }
616
+ },
617
+ [fetchItems, filterListItems, inputHasFocus, resetInputState]
618
+ );
619
+ const {
620
+ isOpen,
621
+ getToggleButtonProps,
622
+ getLabelProps,
623
+ getMenuProps,
624
+ getInputProps,
625
+ highlightedIndex,
626
+ getItemProps,
627
+ selectedItem,
628
+ inputValue,
629
+ setInputValue,
630
+ selectItem,
631
+ reset
632
+ } = downshift.useCombobox({
633
+ defaultHighlightedIndex: lastHighlightedIndex,
634
+ items: listItems,
635
+ itemToString,
636
+ selectedItem: value,
637
+ stateReducer,
638
+ onInputValueChange(changes) {
639
+ updateListItems({ inputValue: changes.inputValue });
640
+ },
641
+ onSelectedItemChange({ selectedItem: newSelectedItem }) {
642
+ onChange(newSelectedItem);
643
+ },
644
+ // Accessibility
645
+ getA11yStatusMessage: (options) => getA11yStatusMessage({ ...options, resultCount: listItems.length })
646
+ });
647
+ const { refs, floatingStyles, update } = reactDom.useFloating({
648
+ open: isOpen,
649
+ placement: "bottom-start",
650
+ middleware: [
651
+ reactDom.offset(tokens.space.extraSmall2),
652
+ reactDom.shift({ padding: tokens.space.extraSmall }),
653
+ reactDom.size({
654
+ apply({ rects, elements, availableHeight }) {
655
+ Object.assign(elements.floating.style, {
656
+ width: `${rects.reference.width}px`,
657
+ // Floating will flip when smaller than 10*16 px
658
+ // and never exceed 20*16 px.
659
+ maxHeight: `${clamp(10 * 16, availableHeight, 20 * 16)}px`
660
+ });
661
+ }
662
+ }),
663
+ reactDom.flip({ fallbackStrategy: "initialPlacement" })
664
+ ]
665
+ });
666
+ React.useEffect(() => {
667
+ if (isOpen && refs.reference.current && refs.floating.current) {
668
+ return reactDom.autoUpdate(
669
+ refs.reference.current,
670
+ refs.floating.current,
671
+ update
672
+ );
673
+ }
674
+ }, [isOpen, refs.reference, refs.floating, update]);
675
+ const handleOnClear = () => {
676
+ inputRef.current?.focus();
677
+ reset();
678
+ };
679
+ return /* @__PURE__ */ jsxRuntime.jsxs(
680
+ form.BaseFormControl,
681
+ {
682
+ className: classNames(
683
+ "eds-dropdown",
684
+ "eds-dropdown--searchable",
685
+ className,
686
+ { "eds-dropdown--has-tooltip": labelTooltip !== void 0 }
687
+ ),
688
+ disabled,
689
+ disableLabelAnimation,
690
+ feedback,
691
+ isFilled: selectedItem !== null || inputValue !== EMPTY_INPUT,
692
+ label,
693
+ labelId: getLabelProps().id,
694
+ labelProps: getLabelProps(),
695
+ labelTooltip,
696
+ onClick: (e) => {
697
+ if (e.target === e.currentTarget) {
698
+ getInputProps()?.onClick?.(e);
699
+ }
700
+ },
701
+ prepend,
702
+ readOnly,
703
+ ref: refs.setReference,
704
+ style,
705
+ tabIndex: disabled || readOnly ? -1 : void 0,
706
+ variant,
707
+ after: /* @__PURE__ */ jsxRuntime.jsx(
708
+ DropdownList,
709
+ {
710
+ ariaLabelChosenSingular,
711
+ ariaLabelSelectedItem,
712
+ floatingStyles,
713
+ getItemProps,
714
+ getMenuProps,
715
+ highlightedIndex,
716
+ isOpen,
717
+ listItems,
718
+ style: listStyle,
719
+ setListRef: refs.setFloating,
720
+ loading: loading ?? resolvedItemsLoading,
721
+ loadingText,
722
+ noMatchesText,
723
+ selectedItems: selectedItem !== null ? [selectedItem] : [],
724
+ readOnly
725
+ }
726
+ ),
727
+ ...rest,
728
+ append: void 0,
729
+ children: [
730
+ /* @__PURE__ */ jsxRuntime.jsx(
731
+ "span",
732
+ {
733
+ className: classNames("eds-dropdown--searchable__selected-item", {
734
+ "eds-dropdown--searchable__selected-item--hidden": !showSelectedItem
735
+ }),
736
+ onClick: (event) => {
737
+ if (!disabled && !readOnly) {
738
+ inputRef.current?.focus();
739
+ getInputProps()?.onClick?.(event);
740
+ }
741
+ },
742
+ tabIndex: readOnly ? 0 : -1,
743
+ children: showSelectedItem ? selectedItem?.label : ""
744
+ }
745
+ ),
746
+ /* @__PURE__ */ jsxRuntime.jsx(
747
+ "input",
748
+ {
749
+ className: classNames("eds-dropdown__input eds-form-control", {
750
+ "eds-dropdown__input--hidden": showSelectedItem
751
+ }),
752
+ ...getInputProps({
753
+ onKeyDown(e) {
754
+ if (isOpen && e.key === "Tab") {
755
+ const highlitedItem = listItems[highlightedIndex];
756
+ if ((selectOnTab || selectOnBlur) && highlitedItem && highlitedItem !== selectedItem) {
757
+ selectItem(highlitedItem);
758
+ }
759
+ }
760
+ },
761
+ onBlur() {
762
+ if (selectedItem !== null) setShowSelectedItem(true);
763
+ },
764
+ onFocus() {
765
+ if (!readOnly) {
766
+ setShowSelectedItem(false);
767
+ }
768
+ },
769
+ disabled,
770
+ readOnly,
771
+ placeholder: selectedItem?.label ?? placeholder,
772
+ tabIndex: disabled || readOnly ? -1 : void 0,
773
+ ref: utils.mergeRefs(inputRef, ref)
774
+ })
775
+ }
776
+ ),
777
+ /* @__PURE__ */ jsxRuntime.jsx(
778
+ DropdownFieldAppendix,
779
+ {
780
+ ...getToggleButtonProps({
781
+ "aria-busy": !(loading ?? resolvedItemsLoading) ? void 0 : "true"
782
+ }),
783
+ ariaLabelCloseList,
784
+ ariaLabelOpenList,
785
+ clearable,
786
+ disabled: disabled || readOnly,
787
+ onClear: handleOnClear,
788
+ focusable: false,
789
+ labelClearSelected: labelClearSelectedItem,
790
+ isOpen,
791
+ itemIsSelected: selectedItem !== null,
792
+ loadingText,
793
+ loading: loading ?? resolvedItemsLoading
794
+ }
795
+ )
796
+ ]
797
+ }
798
+ );
799
+ }
800
+ );
801
+ const MultiSelect = React.forwardRef(
802
+ ({
803
+ className,
804
+ clearable = true,
805
+ clearInputOnSelect = false,
806
+ debounceTimeout,
807
+ disabled = false,
808
+ disableLabelAnimation,
809
+ feedback,
810
+ hideSelectAll = false,
811
+ items: initialItems,
812
+ itemFilter = isFunctionWithQueryArgument(initialItems) ? noFilter : lowerCaseFilterTest,
813
+ label,
814
+ labelAllItemsSelected = "Alle valgt",
815
+ labelClearAllItems = "Fjern valgte",
816
+ labelSelectAll = "Velg alle",
817
+ labelTooltip,
818
+ listStyle,
819
+ loading,
820
+ loadingText = "Laster resultater …",
821
+ maxChips = 10,
822
+ noMatchesText,
823
+ onChange = () => void 0,
824
+ placeholder,
825
+ readOnly = false,
826
+ selectedItems = [],
827
+ selectOnBlur = false,
828
+ selectOnTab = false,
829
+ style,
830
+ variant = "information",
831
+ ariaLabelChosenSingular,
832
+ ariaLabelChosenPlural = "valgte",
833
+ ariaLabelCloseList = "Lukk liste med valg",
834
+ ariaLabelJumpToInput = `${selectedItems.length} valgte elementer, trykk for å hoppe til tekstfeltet`,
835
+ ariaLabelOpenList = "Åpne liste med valg",
836
+ ariaLabelRemoveSelected = "trykk for å fjerne valg",
837
+ ariaLabelSelectedItem,
838
+ ...rest
839
+ }, ref) => {
840
+ const [lastHighlightedIndex, setLastHighlightedIndex] = React.useState(0);
841
+ const inputRef = React.useRef(null);
842
+ React.useEffect(() => {
843
+ if (rest.selectedItem !== void 0)
844
+ console.warn(
845
+ "Incorrect 'selectedItem' prop found, did you mean to use 'selectedItems?"
846
+ );
847
+ }, [rest.selectedItem]);
848
+ const {
849
+ items: normalizedItems,
850
+ loading: resolvedItemsLoading,
851
+ fetchItems
852
+ } = useResolvedItems(initialItems, debounceTimeout);
853
+ const isAllNonAsyncItemsSelected = typeof initialItems !== "function" && selectedItems.length === normalizedItems.length;
854
+ const selectAll = {
855
+ value: utils.useRandomId("select-all"),
856
+ label: labelSelectAll
857
+ };
858
+ const summarySelectedItems = React.useMemo(
859
+ () => ({
860
+ value: EMPTY_INPUT,
861
+ label: isAllNonAsyncItemsSelected ? labelAllItemsSelected : selectedItems.length + " " + ariaLabelChosenPlural
862
+ }),
863
+ [
864
+ isAllNonAsyncItemsSelected,
865
+ selectedItems,
866
+ labelAllItemsSelected,
867
+ ariaLabelChosenPlural
868
+ ]
869
+ );
870
+ const [listItems, setListItems] = React.useState([
871
+ ...!hideSelectAll ? [selectAll] : [],
872
+ ...normalizedItems
873
+ ]);
874
+ const filterListItems = ({ inputValue: inputValue2 }) => setListItems([
875
+ ...!hideSelectAll ? [selectAll] : [],
876
+ ...normalizedItems.filter((item) => itemFilter(item, inputValue2))
877
+ ]);
878
+ const updateListItems = ({ inputValue: inputValue2 }) => {
879
+ const shouldRefetchItems = isFunctionWithQueryArgument(initialItems);
880
+ if (shouldRefetchItems) fetchItems(inputValue2 ?? EMPTY_INPUT);
881
+ filterListItems({ inputValue: inputValue2 ?? EMPTY_INPUT });
882
+ };
883
+ React.useEffect(() => {
884
+ filterListItems({ inputValue });
885
+ }, [normalizedItems]);
886
+ const {
887
+ hasSelectedItems,
888
+ handleListItemClicked,
889
+ selectAllCheckboxState,
890
+ clickedItemIsInSelectedItems,
891
+ clickedItemIsSelectAll
892
+ } = useMultiselectUtils({
893
+ listItems,
894
+ selectAll,
895
+ selectedItems
896
+ });
897
+ const {
898
+ getSelectedItemProps,
899
+ getDropdownProps,
900
+ reset,
901
+ removeSelectedItem,
902
+ setSelectedItems
903
+ } = downshift.useMultipleSelection({
904
+ selectedItems,
905
+ // @ts-expect-error prop missing from library types
906
+ itemToString,
907
+ itemToKey,
908
+ onSelectedItemsChange({ selectedItems: newSelectedItems }) {
909
+ onChange(newSelectedItems);
910
+ }
911
+ });
912
+ const stateReducer = React.useCallback(
913
+ (state, {
914
+ changes,
915
+ type
916
+ }) => {
917
+ if (changes.highlightedIndex !== void 0 && changes?.highlightedIndex >= 0) {
918
+ setLastHighlightedIndex(changes?.highlightedIndex);
919
+ }
920
+ switch (type) {
921
+ // reset input value when leaving input field
922
+ case downshift.useCombobox.stateChangeTypes.InputBlur:
923
+ return {
924
+ ...changes,
925
+ inputValue: EMPTY_INPUT
926
+ };
927
+ // keep menu open and edit input value on item selection
928
+ case downshift.useCombobox.stateChangeTypes.InputKeyDownEnter:
929
+ case downshift.useCombobox.stateChangeTypes.ItemClick: {
930
+ return {
931
+ ...changes,
932
+ isOpen: true,
933
+ inputValue: clearInputOnSelect ? EMPTY_INPUT : inputRef?.current?.value ?? EMPTY_INPUT
934
+ };
935
+ }
936
+ // edit input value when selected items is updated outside component
937
+ case downshift.useCombobox.stateChangeTypes.ControlledPropUpdatedSelectedItem: {
938
+ return {
939
+ ...changes,
940
+ inputValue: inputRef?.current?.value ?? EMPTY_INPUT
941
+ };
942
+ }
943
+ // remove leading whitespace, select item with spacebar if input is empty and filter list items
944
+ case downshift.useCombobox.stateChangeTypes.InputChange: {
945
+ const leadingWhitespaceTest = /^\s+/g;
946
+ const isSpacePressedOnEmptyInput = changes.inputValue === " ";
947
+ if (changes.inputValue?.match(leadingWhitespaceTest)) {
948
+ const sanitizedInputValue = changes.inputValue.replace(
949
+ leadingWhitespaceTest,
950
+ EMPTY_INPUT
951
+ );
952
+ if (isSpacePressedOnEmptyInput) {
953
+ if (!state.isOpen)
954
+ return {
955
+ ...changes,
956
+ inputValue: sanitizedInputValue,
957
+ isOpen: true
958
+ };
959
+ if (changes.highlightedIndex !== void 0) {
960
+ return {
961
+ ...changes,
962
+ inputValue: sanitizedInputValue,
963
+ selectedItem: listItems[changes.highlightedIndex]
964
+ };
965
+ }
966
+ }
967
+ }
968
+ return changes;
969
+ }
970
+ default:
971
+ return changes;
972
+ }
973
+ },
974
+ [hideSelectAll, normalizedItems, filterListItems, initialItems]
975
+ );
976
+ const {
977
+ getInputProps,
978
+ getItemProps,
979
+ getLabelProps,
980
+ getMenuProps,
981
+ getToggleButtonProps,
982
+ highlightedIndex,
983
+ setHighlightedIndex,
984
+ inputValue,
985
+ isOpen,
986
+ setInputValue
987
+ } = downshift.useCombobox({
988
+ defaultHighlightedIndex: lastHighlightedIndex,
989
+ // after selection, highlight previously selected item.
990
+ items: listItems,
991
+ itemToString,
992
+ selectedItem: null,
993
+ stateReducer,
994
+ onInputValueChange(changes) {
995
+ updateListItems({ inputValue: changes.inputValue });
996
+ setHighlightedIndex(hideSelectAll ? 0 : 1);
997
+ setLastHighlightedIndex(hideSelectAll ? 0 : 1);
998
+ },
999
+ onSelectedItemChange({ selectedItem: clickedItem }) {
1000
+ if (!clickedItem) return;
1001
+ handleListItemClicked({
1002
+ clickedItem,
1003
+ onChange: setSelectedItems
1004
+ });
1005
+ },
1006
+ // Accessibility
1007
+ getA11yStatusMessage: (options) => getA11yStatusMessage({
1008
+ ...options,
1009
+ selectAllItemIncluded: !hideSelectAll,
1010
+ resultCount: listItems.length
1011
+ }),
1012
+ ...rest
1013
+ });
1014
+ const { refs, floatingStyles, update } = reactDom.useFloating({
1015
+ open: isOpen,
1016
+ placement: "bottom-start",
1017
+ middleware: [
1018
+ reactDom.offset(tokens.space.extraSmall2),
1019
+ reactDom.shift({ padding: tokens.space.extraSmall }),
1020
+ reactDom.size({
1021
+ apply({ rects, elements, availableHeight }) {
1022
+ Object.assign(elements.floating.style, {
1023
+ width: `${rects.reference.width}px`,
1024
+ // Floating will flip when smaller than 10*16 px
1025
+ // and never exceed 20*16 px.
1026
+ maxHeight: `${clamp(10 * 16, availableHeight, 20 * 16)}px`
1027
+ });
1028
+ }
1029
+ }),
1030
+ reactDom.flip({ fallbackStrategy: "initialPlacement" })
1031
+ ]
1032
+ });
1033
+ React.useEffect(() => {
1034
+ if (isOpen && refs.reference.current && refs.floating.current) {
1035
+ return reactDom.autoUpdate(
1036
+ refs.reference.current,
1037
+ refs.floating.current,
1038
+ update
1039
+ );
1040
+ }
1041
+ }, [isOpen, refs.reference, refs.floating, update]);
1042
+ const handleOnClear = () => {
1043
+ inputRef.current?.focus();
1044
+ reset();
1045
+ };
1046
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1047
+ form.BaseFormControl,
1048
+ {
1049
+ className: classNames(
1050
+ "eds-dropdown",
1051
+ "eds-dropdown--multiselect",
1052
+ className,
1053
+ { "eds-dropdown--has-tooltip": labelTooltip !== void 0 }
1054
+ ),
1055
+ disabled,
1056
+ disableLabelAnimation,
1057
+ feedback,
1058
+ isFilled: hasSelectedItems || inputValue !== EMPTY_INPUT,
1059
+ label,
1060
+ labelId: getLabelProps().id,
1061
+ labelProps: getLabelProps(),
1062
+ labelTooltip,
1063
+ onBlur: () => setInputValue(""),
1064
+ onClick: (e) => {
1065
+ if (e.target === e.currentTarget) {
1066
+ getInputProps()?.onClick?.(e);
1067
+ }
1068
+ },
1069
+ readOnly,
1070
+ ref: refs.setReference,
1071
+ style,
1072
+ variant,
1073
+ after: /* @__PURE__ */ jsxRuntime.jsx(
1074
+ DropdownList,
1075
+ {
1076
+ ariaLabelChosenSingular,
1077
+ ariaLabelSelectedItem,
1078
+ floatingStyles,
1079
+ getItemProps,
1080
+ getMenuProps,
1081
+ highlightedIndex,
1082
+ isOpen,
1083
+ listItems,
1084
+ style: listStyle,
1085
+ setListRef: refs.setFloating,
1086
+ loading: loading ?? resolvedItemsLoading,
1087
+ loadingText,
1088
+ noMatchesText,
1089
+ selectAllCheckboxState,
1090
+ selectAllItem: selectAll,
1091
+ selectedItems,
1092
+ readOnly
1093
+ }
1094
+ ),
1095
+ ...rest,
1096
+ children: [
1097
+ /* @__PURE__ */ jsxRuntime.jsxs(
1098
+ "div",
1099
+ {
1100
+ className: classNames(
1101
+ "eds-dropdown--multiselect__selected-items-and-input",
1102
+ {
1103
+ "eds-dropdown--multiselect__selected-items-and-input--filled": hasSelectedItems
1104
+ }
1105
+ ),
1106
+ children: [
1107
+ selectedItems.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx(a11y.VisuallyHidden, { onClick: inputRef.current?.focus, children: ariaLabelJumpToInput }) : null,
1108
+ selectedItems.length <= maxChips ? selectedItems.map((selectedItem, index) => /* @__PURE__ */ jsxRuntime.jsx(
1109
+ SelectedItemTag,
1110
+ {
1111
+ ariaLabelChosen: ariaLabelChosenSingular,
1112
+ ariaLabelRemoveSelected,
1113
+ disabled,
1114
+ getSelectedItemProps,
1115
+ index,
1116
+ readOnly,
1117
+ removeSelectedItem: () => {
1118
+ removeSelectedItem(selectedItem);
1119
+ inputRef?.current?.focus();
1120
+ },
1121
+ selectedItem
1122
+ },
1123
+ selectedItem?.label + (typeof selectedItem?.value === "string" ? selectedItem.value : "")
1124
+ )) : /* @__PURE__ */ jsxRuntime.jsx(
1125
+ SelectedItemTag,
1126
+ {
1127
+ ariaLabelRemoveSelected: labelClearAllItems,
1128
+ ariaLabelChosen: "",
1129
+ disabled,
1130
+ readOnly,
1131
+ removeSelectedItem: handleOnClear,
1132
+ selectedItem: summarySelectedItems
1133
+ }
1134
+ ),
1135
+ /* @__PURE__ */ jsxRuntime.jsx(
1136
+ "input",
1137
+ {
1138
+ ...getInputProps({
1139
+ onKeyDown: (e) => {
1140
+ if (selectOnTab && isOpen && e.key === "Tab") {
1141
+ const highlitedItem = listItems[highlightedIndex];
1142
+ if (!highlitedItem) return;
1143
+ const shouldSkipTabSelection = clickedItemIsSelectAll(highlitedItem) || !clickedItemIsSelectAll(highlitedItem) && clickedItemIsInSelectedItems(highlitedItem);
1144
+ if (shouldSkipTabSelection) return;
1145
+ handleListItemClicked({
1146
+ clickedItem: highlitedItem,
1147
+ onChange: setSelectedItems
1148
+ });
1149
+ }
1150
+ },
1151
+ ...getDropdownProps({
1152
+ preventKeyAction: isOpen,
1153
+ value: inputValue ?? EMPTY_INPUT,
1154
+ ref: utils.mergeRefs(inputRef, ref)
1155
+ }),
1156
+ className: "eds-dropdown__input eds-form-control",
1157
+ disabled: readOnly || disabled,
1158
+ placeholder,
1159
+ tabIndex: disabled || readOnly ? -1 : void 0
1160
+ })
1161
+ }
1162
+ )
1163
+ ]
1164
+ }
1165
+ ),
1166
+ /* @__PURE__ */ jsxRuntime.jsx(
1167
+ DropdownFieldAppendix,
1168
+ {
1169
+ ...getToggleButtonProps({
1170
+ "aria-busy": !(loading ?? resolvedItemsLoading) ? void 0 : "true"
1171
+ }),
1172
+ ariaLabelCloseList,
1173
+ ariaLabelOpenList,
1174
+ clearable,
1175
+ disabled: disabled || readOnly,
1176
+ onClear: handleOnClear,
1177
+ focusable: false,
1178
+ labelClearSelected: labelClearAllItems,
1179
+ isOpen,
1180
+ itemIsSelected: selectedItems.length > 0,
1181
+ loadingText,
1182
+ loading: loading ?? resolvedItemsLoading
1183
+ }
1184
+ )
1185
+ ]
1186
+ }
1187
+ );
1188
+ }
1189
+ );
1190
+ const Dropdown = React.forwardRef(
1191
+ ({
1192
+ ariaLabelChosenSingular,
1193
+ ariaLabelCloseList = "Lukk liste med valg",
1194
+ ariaLabelOpenList = "Åpne liste med valg",
1195
+ ariaLabelSelectedItem,
1196
+ className,
1197
+ clearable = false,
1198
+ disabled = false,
1199
+ disableLabelAnimation,
1200
+ feedback,
1201
+ items: initialItems,
1202
+ label,
1203
+ labelClearSelectedItem = "fjern valgt",
1204
+ labelTooltip,
1205
+ listStyle,
1206
+ loading,
1207
+ loadingText = "Laster resultater …",
1208
+ noMatchesText = "Ingen tilgjengelige valg …",
1209
+ onChange,
1210
+ placeholder,
1211
+ prepend,
1212
+ readOnly = false,
1213
+ selectedItem,
1214
+ selectOnBlur = false,
1215
+ selectOnTab = false,
1216
+ style,
1217
+ variant = "information",
1218
+ ...rest
1219
+ }, ref) => {
1220
+ const { items: normalizedItems, loading: resolvedItemsLoading } = useResolvedItems(initialItems);
1221
+ const isFilled = selectedItem !== null || placeholder !== void 0;
1222
+ const {
1223
+ isOpen,
1224
+ getItemProps,
1225
+ getLabelProps,
1226
+ getMenuProps,
1227
+ getToggleButtonProps,
1228
+ highlightedIndex,
1229
+ selectItem,
1230
+ reset
1231
+ } = downshift.useSelect({
1232
+ items: normalizedItems,
1233
+ defaultHighlightedIndex: selectedItem ? void 0 : 0,
1234
+ selectedItem,
1235
+ stateReducer(_, { changes, type }) {
1236
+ const toggleButtonIsFocused = typeof document !== "undefined" && document.activeElement === refs.reference.current;
1237
+ switch (type) {
1238
+ case downshift.useSelect.stateChangeTypes.ToggleButtonKeyDownArrowDown:
1239
+ case downshift.useSelect.stateChangeTypes.ToggleButtonKeyDownArrowUp:
1240
+ if (!toggleButtonIsFocused) return { ...changes, isOpen: false };
1241
+ }
1242
+ return changes;
1243
+ },
1244
+ onStateChange({ type, selectedItem: newSelectedItem }) {
1245
+ switch (type) {
1246
+ case downshift.useSelect.stateChangeTypes.ToggleButtonBlur:
1247
+ if (!selectOnBlur) return;
1248
+ }
1249
+ if (newSelectedItem === void 0) return;
1250
+ onChange?.(newSelectedItem ?? null);
1251
+ },
1252
+ itemToString
1253
+ });
1254
+ const { refs, floatingStyles, update } = reactDom.useFloating({
1255
+ open: isOpen,
1256
+ placement: "bottom-start",
1257
+ middleware: [
1258
+ reactDom.offset(tokens.space.extraSmall2),
1259
+ reactDom.shift({ padding: tokens.space.extraSmall }),
1260
+ reactDom.size({
1261
+ apply({ rects, elements, availableHeight }) {
1262
+ Object.assign(elements.floating.style, {
1263
+ width: `${rects.reference.width}px`,
1264
+ // Floating will flip when smaller than 10*16 px
1265
+ // and never exceed 20*16 px.
1266
+ maxHeight: `${clamp(10 * 16, availableHeight, 20 * 16)}px`
1267
+ });
1268
+ }
1269
+ }),
1270
+ reactDom.flip({ fallbackStrategy: "initialPlacement" })
1271
+ ]
1272
+ });
1273
+ React.useEffect(() => {
1274
+ if (isOpen && refs.reference.current && refs.floating.current) {
1275
+ return reactDom.autoUpdate(
1276
+ refs.reference.current,
1277
+ refs.floating.current,
1278
+ update
1279
+ );
1280
+ }
1281
+ }, [isOpen, refs.reference, refs.floating, update]);
1282
+ const handleOnClear = () => {
1283
+ reset();
1284
+ refs.reference.current?.focus();
1285
+ };
1286
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1287
+ form.BaseFormControl,
1288
+ {
1289
+ className: classNames("eds-dropdown", className, {
1290
+ "eds-dropdown--has-tooltip": labelTooltip !== void 0
1291
+ }),
1292
+ disableLabelAnimation,
1293
+ feedback,
1294
+ isFilled,
1295
+ labelProps: getLabelProps(),
1296
+ labelTooltip,
1297
+ prepend,
1298
+ style,
1299
+ variant,
1300
+ ...getToggleButtonProps({
1301
+ ref: utils.mergeRefs(ref, refs.setReference),
1302
+ "aria-disabled": disabled,
1303
+ "aria-label": disabled ? "Disabled dropdown" : "",
1304
+ disabled,
1305
+ readOnly,
1306
+ label,
1307
+ labelId: getLabelProps()?.id,
1308
+ children: void 0,
1309
+ tabIndex: disabled || readOnly ? -1 : 0,
1310
+ onKeyDown(e) {
1311
+ if (isOpen && e.key === "Tab") {
1312
+ const highlitedItem = normalizedItems[highlightedIndex];
1313
+ if ((selectOnTab || selectOnBlur) && highlitedItem && highlitedItem !== selectedItem) {
1314
+ selectItem(highlitedItem);
1315
+ }
1316
+ }
1317
+ }
1318
+ }),
1319
+ after: /* @__PURE__ */ jsxRuntime.jsx(
1320
+ DropdownList,
1321
+ {
1322
+ ariaLabelChosenSingular,
1323
+ ariaLabelSelectedItem,
1324
+ floatingStyles,
1325
+ getItemProps,
1326
+ getMenuProps,
1327
+ highlightedIndex,
1328
+ isOpen,
1329
+ listItems: normalizedItems,
1330
+ noMatchesText,
1331
+ style: listStyle,
1332
+ setListRef: refs.setFloating,
1333
+ loading: loading ?? resolvedItemsLoading,
1334
+ loadingText,
1335
+ selectedItems: selectedItem !== null ? [selectedItem] : [],
1336
+ readOnly
1337
+ }
1338
+ ),
1339
+ ...rest,
1340
+ append: void 0,
1341
+ children: [
1342
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "eds-dropdown__selected-item", children: selectedItem?.label ?? /* @__PURE__ */ jsxRuntime.jsx(
1343
+ "div",
1344
+ {
1345
+ className: classNames(
1346
+ "eds-dropdown__selected-item__placeholder",
1347
+ {
1348
+ "eds-dropdown__selected-item__placeholder--readonly": readOnly
1349
+ }
1350
+ ),
1351
+ children: placeholder
1352
+ }
1353
+ ) }),
1354
+ /* @__PURE__ */ jsxRuntime.jsx(
1355
+ DropdownFieldAppendix,
1356
+ {
1357
+ "aria-busy": !(loading ?? resolvedItemsLoading) ? void 0 : "true",
1358
+ "aria-expanded": isOpen,
1359
+ clearable,
1360
+ onClear: handleOnClear,
1361
+ disabled: disabled || readOnly,
1362
+ focusable: false,
1363
+ labelClearSelected: labelClearSelectedItem,
1364
+ isOpen,
1365
+ itemIsSelected: selectedItem !== null,
1366
+ ariaLabelCloseList,
1367
+ ariaLabelOpenList,
1368
+ loading: false,
1369
+ loadingText: void 0
1370
+ }
1371
+ )
1372
+ ]
1373
+ }
1374
+ );
1375
+ }
1376
+ );
1377
+ const error = "error";
1378
+ const NativeDropdown = React.forwardRef(
1379
+ ({
1380
+ className,
1381
+ disabled = false,
1382
+ disableLabelAnimation,
1383
+ feedback,
1384
+ items,
1385
+ label,
1386
+ loadingText,
1387
+ onChange,
1388
+ prepend,
1389
+ readOnly = false,
1390
+ selectedItem,
1391
+ style,
1392
+ value,
1393
+ variant,
1394
+ ...rest
1395
+ }, ref) => {
1396
+ const { items: normalizedItems, loading } = useResolvedItems(items);
1397
+ const nativeDropdownId = utils.useRandomId("eds-dropdown-native");
1398
+ return /* @__PURE__ */ jsxRuntime.jsx(
1399
+ form.BaseFormControl,
1400
+ {
1401
+ disabled,
1402
+ readOnly,
1403
+ prepend,
1404
+ append: /* @__PURE__ */ jsxRuntime.jsx(
1405
+ FieldAppend,
1406
+ {
1407
+ hidden: disabled || readOnly,
1408
+ loading,
1409
+ loadingText
1410
+ }
1411
+ ),
1412
+ className,
1413
+ style,
1414
+ label,
1415
+ labelId: nativeDropdownId,
1416
+ variant,
1417
+ feedback,
1418
+ disableLabelAnimation,
1419
+ isFilled: true,
1420
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1421
+ "select",
1422
+ {
1423
+ "aria-invalid": variant === "negative" || variant === error,
1424
+ "aria-labelledby": nativeDropdownId,
1425
+ "aria-busy": loading,
1426
+ className: "eds-form-control eds-dropdown--native",
1427
+ disabled: disabled || readOnly,
1428
+ onChange: (event) => {
1429
+ onChange?.({
1430
+ value: event.target.value,
1431
+ selectedItem: normalizedItems.find(
1432
+ (item) => item.value === event.target.value
1433
+ ) ?? null,
1434
+ target: event.target
1435
+ });
1436
+ },
1437
+ value: value ?? selectedItem?.value ?? void 0,
1438
+ ref,
1439
+ ...rest,
1440
+ children: normalizedItems.map((item) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: item.value, children: item.label }, item.value))
1441
+ }
1442
+ )
1443
+ }
1444
+ );
1445
+ }
1446
+ );
1447
+ const FieldAppend = ({
1448
+ loading,
1449
+ loadingText,
1450
+ hidden
1451
+ }) => {
1452
+ if (loading) {
1453
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "eds-dropdown-native__loading-dots", children: /* @__PURE__ */ jsxRuntime.jsx(loader.LoadingDots, { "aria-label": loadingText }) });
1454
+ }
1455
+ if (hidden) {
1456
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
1457
+ }
1458
+ return /* @__PURE__ */ jsxRuntime.jsx(icons.DownArrowIcon, { inline: true });
1459
+ };
1460
+ utils.warnAboutMissingStyles("dropdown", "form", "a11y", "chip");
1461
+ exports.Dropdown = Dropdown;
1462
+ exports.MultiSelect = MultiSelect;
1463
+ exports.NativeDropdown = NativeDropdown;
1464
+ exports.SearchableDropdown = SearchableDropdown;
1465
+ //# sourceMappingURL=dropdown.cjs.js.map