@dbcdk/react-components 0.0.168 → 0.0.170

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.
@@ -115,6 +115,8 @@ function Typeahead({
115
115
  onClear,
116
116
  emptyMessage = "Ingen resultater",
117
117
  filterOptions,
118
+ maxVisibleOptions,
119
+ allowCustomValue = false,
118
120
  inputProps,
119
121
  inputSize,
120
122
  width,
@@ -128,7 +130,7 @@ function Typeahead({
128
130
  fitContent = false,
129
131
  enableHotkey = false
130
132
  }) {
131
- var _a, _b;
133
+ var _a;
132
134
  const rootRef = React.useRef(null);
133
135
  const triggerWrapperRef = React.useRef(null);
134
136
  const inputRef = React.useRef(null);
@@ -156,10 +158,14 @@ function Typeahead({
156
158
  if (mode !== "multi" || !Array.isArray(selectedValue)) return [];
157
159
  return options.filter((option) => selectedValue.includes(option.value));
158
160
  }, [options, selectedValue, mode]);
161
+ const selectedSingleText = React.useMemo(() => {
162
+ if (mode !== "single") return "";
163
+ if (selectedOption) return selectedOption.label;
164
+ if (allowCustomValue && selectedValue != null) return String(selectedValue);
165
+ return "";
166
+ }, [mode, selectedOption, allowCustomValue, selectedValue]);
159
167
  const [open, setOpen] = React.useState(false);
160
- const [inputValue, setInputValue] = React.useState(
161
- mode === "multi" ? "" : (_a = selectedOption == null ? void 0 : selectedOption.label) != null ? _a : ""
162
- );
168
+ const [inputValue, setInputValue] = React.useState(mode === "multi" ? "" : selectedSingleText);
163
169
  const [query, setQuery] = React.useState("");
164
170
  const [activeIndex, setActiveIndex] = React.useState(-1);
165
171
  const [hideSelectedHighlight, setHideSelectedHighlight] = React.useState(false);
@@ -195,9 +201,12 @@ function Typeahead({
195
201
  );
196
202
  }
197
203
  }
204
+ if (typeof maxVisibleOptions === "number" && Number.isFinite(maxVisibleOptions)) {
205
+ filteredOptions = filteredOptions.slice(0, Math.max(0, maxVisibleOptions));
206
+ }
198
207
  const commitSelection = React__namespace.useCallback(
199
208
  (option) => {
200
- var _a2, _b2;
209
+ var _a2, _b;
201
210
  locallyClearedRef.current = false;
202
211
  setHideSelectedHighlight(false);
203
212
  if (mode === "multi") {
@@ -218,7 +227,7 @@ function Typeahead({
218
227
  return;
219
228
  }
220
229
  onChange((_a2 = option == null ? void 0 : option.value) != null ? _a2 : null);
221
- setInputValue((_b2 = option == null ? void 0 : option.label) != null ? _b2 : "");
230
+ setInputValue((_b = option == null ? void 0 : option.label) != null ? _b : "");
222
231
  setQuery("");
223
232
  setOpen(false);
224
233
  setActiveIndex(-1);
@@ -251,13 +260,13 @@ function Typeahead({
251
260
  ] });
252
261
  })() : void 0;
253
262
  const usesCountAdornment = mode === "multi" && multiValueDisplayMode === "count" && selectedOptions.length > 0;
254
- const resolvedInputSize = (_b = inputSize != null ? inputSize : inputProps == null ? void 0 : inputProps.inputSize) != null ? _b : "md";
263
+ const resolvedInputSize = (_a = inputSize != null ? inputSize : inputProps == null ? void 0 : inputProps.inputSize) != null ? _a : "md";
255
264
  const shouldFitContent = fitContent && mode === "single";
256
265
  const [fittedWidthPx, setFittedWidthPx] = React.useState(null);
257
266
  const visibleSingleValueText = React.useMemo(() => {
258
267
  if (!shouldFitContent) return "";
259
- return inputValue || (selectedOption == null ? void 0 : selectedOption.label) || placeholder || "";
260
- }, [shouldFitContent, inputValue, selectedOption, placeholder]);
268
+ return inputValue || selectedSingleText || placeholder || "";
269
+ }, [shouldFitContent, inputValue, selectedSingleText, placeholder]);
261
270
  React.useLayoutEffect(() => {
262
271
  if (!shouldFitContent) return;
263
272
  const input = inputRef.current;
@@ -311,15 +320,18 @@ function Typeahead({
311
320
  return () => window.removeEventListener("keydown", handleKeyDown2);
312
321
  }, [enableHotkey]);
313
322
  React.useEffect(() => {
314
- var _a2;
323
+ if (selectedOption) {
324
+ locallyClearedRef.current = false;
325
+ setHideSelectedHighlight(false);
326
+ }
315
327
  if (justClearedRef.current || locallyClearedRef.current) return;
316
328
  if (mode === "multi") {
317
329
  setInputValue("");
318
330
  setQuery("");
319
331
  } else {
320
- setInputValue((_a2 = selectedOption == null ? void 0 : selectedOption.label) != null ? _a2 : "");
332
+ setInputValue(selectedSingleText);
321
333
  }
322
- }, [selectedOption, mode]);
334
+ }, [selectedOption, selectedSingleText, mode]);
323
335
  const getSelectedIndex = React__namespace.useCallback(
324
336
  (items) => {
325
337
  if (items.length === 0) return -1;
@@ -343,10 +355,10 @@ function Typeahead({
343
355
  });
344
356
  }, [filteredOptions, getSelectedIndex]);
345
357
  React.useEffect(() => {
346
- var _a2, _b2;
358
+ var _a2, _b;
347
359
  if (!open || activeIndex < 0) return;
348
360
  const activeEl = (_a2 = document.getElementById(listboxId)) == null ? void 0 : _a2.querySelector(`#${CSS.escape(`${listboxId}-option-${activeIndex}`)}`);
349
- (_b2 = activeEl == null ? void 0 : activeEl.scrollIntoView) == null ? void 0 : _b2.call(activeEl, { block: "nearest" });
361
+ (_b = activeEl == null ? void 0 : activeEl.scrollIntoView) == null ? void 0 : _b.call(activeEl, { block: "nearest" });
350
362
  }, [open, activeIndex, filteredOptions, listboxId]);
351
363
  const getFocusableElements = React__namespace.useCallback(() => {
352
364
  const selector = [
@@ -373,13 +385,13 @@ function Typeahead({
373
385
  return focusables;
374
386
  }, []);
375
387
  const isFocusWithinTypeahead = React__namespace.useCallback((target) => {
376
- var _a2, _b2;
388
+ var _a2, _b;
377
389
  if (!(target instanceof Node)) return false;
378
- return Boolean(((_a2 = rootRef.current) == null ? void 0 : _a2.contains(target)) || ((_b2 = popoverContentRef.current) == null ? void 0 : _b2.contains(target)));
390
+ return Boolean(((_a2 = rootRef.current) == null ? void 0 : _a2.contains(target)) || ((_b = popoverContentRef.current) == null ? void 0 : _b.contains(target)));
379
391
  }, []);
380
392
  const handleTabKeyDown = React__namespace.useCallback(
381
393
  (event) => {
382
- var _a2, _b2, _c;
394
+ var _a2, _b, _c;
383
395
  if (event.key !== "Tab" || !open) return;
384
396
  const focusables = getFocusableElements();
385
397
  if (focusables.length === 0) return;
@@ -389,7 +401,7 @@ function Typeahead({
389
401
  if (mode === "multi") {
390
402
  if (activeIndexInScope === -1) {
391
403
  event.preventDefault();
392
- (_b2 = focusables[0]) == null ? void 0 : _b2.focus();
404
+ (_b = focusables[0]) == null ? void 0 : _b.focus();
393
405
  return;
394
406
  }
395
407
  event.preventDefault();
@@ -410,6 +422,7 @@ function Typeahead({
410
422
  setQuery(nextValue);
411
423
  if (!open) setOpen(true);
412
424
  if (mode === "multi") return;
425
+ if (allowCustomValue) return;
413
426
  const exactMatch = options.find((option) => option.label === nextValue);
414
427
  if (!exactMatch && selectedValue !== null) {
415
428
  onChange(null);
@@ -442,6 +455,22 @@ function Typeahead({
442
455
  commitSelection(exactLabelMatch);
443
456
  return;
444
457
  }
458
+ if (allowCustomValue) {
459
+ const customValue = inputValue.trim();
460
+ if (customValue) {
461
+ onChange(customValue);
462
+ setInputValue(customValue);
463
+ } else {
464
+ if (selectedValue !== null) {
465
+ onChange(null);
466
+ }
467
+ setInputValue("");
468
+ }
469
+ setQuery("");
470
+ setOpen(false);
471
+ setActiveIndex(-1);
472
+ return;
473
+ }
445
474
  if (selectedOption) {
446
475
  setInputValue(selectedOption.label);
447
476
  } else {
@@ -478,7 +507,6 @@ function Typeahead({
478
507
  }, [mode, selectedOption, prepareSingleSearchInput, openWithAllOptions, openWithCurrentFilter]);
479
508
  const handleTriggerMouseDown = React__namespace.useCallback(
480
509
  (e) => {
481
- var _a2;
482
510
  inputPropsOnMouseDown == null ? void 0 : inputPropsOnMouseDown(e);
483
511
  if (e.defaultPrevented) return;
484
512
  if (justClearedRef.current) {
@@ -492,9 +520,7 @@ function Typeahead({
492
520
  setOpen(false);
493
521
  setActiveIndex(-1);
494
522
  setQuery("");
495
- setInputValue(
496
- mode === "single" && !locallyClearedRef.current ? (_a2 = selectedOption == null ? void 0 : selectedOption.label) != null ? _a2 : "" : ""
497
- );
523
+ setInputValue(mode === "single" && !locallyClearedRef.current ? selectedSingleText : "");
498
524
  return;
499
525
  }
500
526
  if (isAlreadyFocused && mode === "single" && selectedOption && !locallyClearedRef.current) {
@@ -511,6 +537,7 @@ function Typeahead({
511
537
  open,
512
538
  mode,
513
539
  selectedOption,
540
+ selectedSingleText,
514
541
  prepareSingleSearchInput,
515
542
  getSelectedIndex,
516
543
  options,
@@ -519,7 +546,7 @@ function Typeahead({
519
546
  );
520
547
  const handleChevronMouseDown = React__namespace.useCallback(
521
548
  (e) => {
522
- var _a2, _b2;
549
+ var _a2;
523
550
  e.preventDefault();
524
551
  e.stopPropagation();
525
552
  if (justClearedRef.current) return;
@@ -528,14 +555,12 @@ function Typeahead({
528
555
  setOpen(false);
529
556
  setActiveIndex(-1);
530
557
  setQuery("");
531
- setInputValue(
532
- mode === "single" && !locallyClearedRef.current ? (_b2 = selectedOption == null ? void 0 : selectedOption.label) != null ? _b2 : "" : ""
533
- );
558
+ setInputValue(mode === "single" && !locallyClearedRef.current ? selectedSingleText : "");
534
559
  return;
535
560
  }
536
561
  handleOpen();
537
562
  },
538
- [open, mode, selectedOption, handleOpen]
563
+ [open, mode, selectedSingleText, handleOpen]
539
564
  );
540
565
  const handleKeyDown = (e) => {
541
566
  switch (e.key) {
@@ -586,9 +611,7 @@ function Typeahead({
586
611
  setOpen(false);
587
612
  setActiveIndex(-1);
588
613
  setQuery("");
589
- setInputValue(
590
- mode === "single" && selectedOption && !locallyClearedRef.current ? selectedOption.label : ""
591
- );
614
+ setInputValue(mode === "single" && !locallyClearedRef.current ? selectedSingleText : "");
592
615
  return;
593
616
  }
594
617
  };
@@ -599,7 +622,9 @@ function Typeahead({
599
622
  style: {
600
623
  display: "flex",
601
624
  flexDirection: "column",
625
+ justifyContent: mode === "multi" && multiSelectedValuesDisplayMode === "below-input" && selectedOptions.length > 0 ? "flex-start" : "center",
602
626
  gap: mode === "multi" && multiSelectedValuesDisplayMode === "below-input" && selectedOptions.length > 0 ? 8 : 0,
627
+ height: "100%",
603
628
  width: width != null ? width : fullWidth ? "100%" : shouldFitContent ? measuredRootWidth : void 0,
604
629
  flex: width ? "0 0 auto" : fullWidth || shouldFitContent ? "1 1 auto" : void 0,
605
630
  minWidth: width ? void 0 : shouldFitContent ? measuredRootWidth : 0,
@@ -637,11 +662,18 @@ function Typeahead({
637
662
  returnFocus: false,
638
663
  overlayRef: popoverContentRef,
639
664
  trigger: (openPopover, icon) => {
640
- var _a2, _b2, _c, _d;
665
+ var _a2, _b, _c, _d;
641
666
  return /* @__PURE__ */ jsxRuntime.jsx(
642
667
  "div",
643
668
  {
644
669
  ref: triggerWrapperRef,
670
+ style: {
671
+ display: "flex",
672
+ alignItems: "stretch",
673
+ minWidth: 0,
674
+ width: "100%",
675
+ height: "100%"
676
+ },
645
677
  onMouseDown: (e) => {
646
678
  const target = e.target;
647
679
  if (justClearedRef.current) {
@@ -692,7 +724,7 @@ function Typeahead({
692
724
  inputSize: resolvedInputSize,
693
725
  width: width != null ? width : shouldFitContent ? void 0 : inputProps == null ? void 0 : inputProps.width,
694
726
  autoComplete: (_a2 = autoComplete != null ? autoComplete : inputProps == null ? void 0 : inputProps.autoComplete) != null ? _a2 : "off",
695
- autoCorrect: (_b2 = autoCorrect != null ? autoCorrect : inputProps == null ? void 0 : inputProps.autoCorrect) != null ? _b2 : "off",
727
+ autoCorrect: (_b = autoCorrect != null ? autoCorrect : inputProps == null ? void 0 : inputProps.autoCorrect) != null ? _b : "off",
696
728
  autoCapitalize: (_c = autoCapitalize != null ? autoCapitalize : inputProps == null ? void 0 : inputProps.autoCapitalize) != null ? _c : "none",
697
729
  spellCheck: (_d = spellCheck != null ? spellCheck : inputProps == null ? void 0 : inputProps.spellCheck) != null ? _d : false,
698
730
  disabled,
@@ -703,9 +735,9 @@ function Typeahead({
703
735
  usesCountAdornment ? styles__default.default.inputWithoutStartPadding : ""
704
736
  ].filter(Boolean).join(" "),
705
737
  onClear: (event) => {
706
- var _a3, _b3;
738
+ var _a3, _b2;
707
739
  (_a3 = event == null ? void 0 : event.preventDefault) == null ? void 0 : _a3.call(event);
708
- (_b3 = event == null ? void 0 : event.stopPropagation) == null ? void 0 : _b3.call(event);
740
+ (_b2 = event == null ? void 0 : event.stopPropagation) == null ? void 0 : _b2.call(event);
709
741
  justClearedRef.current = true;
710
742
  locallyClearedRef.current = true;
711
743
  setHideSelectedHighlight(true);
@@ -22,6 +22,12 @@ interface TypeaheadProps<T> {
22
22
  onClear?: () => void;
23
23
  emptyMessage?: string;
24
24
  filterOptions?: (options: TypeaheadOption<T>[], query: string) => TypeaheadOption<T>[];
25
+ maxVisibleOptions?: number;
26
+ /**
27
+ * Allows a single-select typeahead to commit a freeform value that does not
28
+ * match one of the provided options. Intended primarily for string values.
29
+ */
30
+ allowCustomValue?: boolean;
25
31
  inputProps?: Omit<InputProps, 'onChange' | 'value'>;
26
32
  inputSize?: InputProps['inputSize'];
27
33
  /** Explicit width; takes precedence over `fullWidth` and `fitContent` when set. */
@@ -36,5 +42,5 @@ interface TypeaheadProps<T> {
36
42
  fitContent?: boolean;
37
43
  enableHotkey?: boolean;
38
44
  }
39
- export declare function Typeahead<T extends string | number>({ options, mode, multiValueDisplayMode, multiSelectedValuesDisplayMode, multiSelectedValueChipContent, selectedValue, onChange, placeholder, variant, disabled, fullWidth, onClear, emptyMessage, filterOptions, inputProps, inputSize, width, minWidth, popoverWidth, autoComplete, autoCorrect, autoCapitalize, spellCheck, popoverAnchorRef, fitContent, enableHotkey, }: TypeaheadProps<T>): React.ReactElement;
45
+ export declare function Typeahead<T extends string | number>({ options, mode, multiValueDisplayMode, multiSelectedValuesDisplayMode, multiSelectedValueChipContent, selectedValue, onChange, placeholder, variant, disabled, fullWidth, onClear, emptyMessage, filterOptions, maxVisibleOptions, allowCustomValue, inputProps, inputSize, width, minWidth, popoverWidth, autoComplete, autoCorrect, autoCapitalize, spellCheck, popoverAnchorRef, fitContent, enableHotkey, }: TypeaheadProps<T>): React.ReactElement;
40
46
  export {};
@@ -91,6 +91,8 @@ function Typeahead({
91
91
  onClear,
92
92
  emptyMessage = "Ingen resultater",
93
93
  filterOptions,
94
+ maxVisibleOptions,
95
+ allowCustomValue = false,
94
96
  inputProps,
95
97
  inputSize,
96
98
  width,
@@ -104,7 +106,7 @@ function Typeahead({
104
106
  fitContent = false,
105
107
  enableHotkey = false
106
108
  }) {
107
- var _a, _b;
109
+ var _a;
108
110
  const rootRef = useRef(null);
109
111
  const triggerWrapperRef = useRef(null);
110
112
  const inputRef = useRef(null);
@@ -132,10 +134,14 @@ function Typeahead({
132
134
  if (mode !== "multi" || !Array.isArray(selectedValue)) return [];
133
135
  return options.filter((option) => selectedValue.includes(option.value));
134
136
  }, [options, selectedValue, mode]);
137
+ const selectedSingleText = useMemo(() => {
138
+ if (mode !== "single") return "";
139
+ if (selectedOption) return selectedOption.label;
140
+ if (allowCustomValue && selectedValue != null) return String(selectedValue);
141
+ return "";
142
+ }, [mode, selectedOption, allowCustomValue, selectedValue]);
135
143
  const [open, setOpen] = useState(false);
136
- const [inputValue, setInputValue] = useState(
137
- mode === "multi" ? "" : (_a = selectedOption == null ? void 0 : selectedOption.label) != null ? _a : ""
138
- );
144
+ const [inputValue, setInputValue] = useState(mode === "multi" ? "" : selectedSingleText);
139
145
  const [query, setQuery] = useState("");
140
146
  const [activeIndex, setActiveIndex] = useState(-1);
141
147
  const [hideSelectedHighlight, setHideSelectedHighlight] = useState(false);
@@ -171,9 +177,12 @@ function Typeahead({
171
177
  );
172
178
  }
173
179
  }
180
+ if (typeof maxVisibleOptions === "number" && Number.isFinite(maxVisibleOptions)) {
181
+ filteredOptions = filteredOptions.slice(0, Math.max(0, maxVisibleOptions));
182
+ }
174
183
  const commitSelection = React.useCallback(
175
184
  (option) => {
176
- var _a2, _b2;
185
+ var _a2, _b;
177
186
  locallyClearedRef.current = false;
178
187
  setHideSelectedHighlight(false);
179
188
  if (mode === "multi") {
@@ -194,7 +203,7 @@ function Typeahead({
194
203
  return;
195
204
  }
196
205
  onChange((_a2 = option == null ? void 0 : option.value) != null ? _a2 : null);
197
- setInputValue((_b2 = option == null ? void 0 : option.label) != null ? _b2 : "");
206
+ setInputValue((_b = option == null ? void 0 : option.label) != null ? _b : "");
198
207
  setQuery("");
199
208
  setOpen(false);
200
209
  setActiveIndex(-1);
@@ -227,13 +236,13 @@ function Typeahead({
227
236
  ] });
228
237
  })() : void 0;
229
238
  const usesCountAdornment = mode === "multi" && multiValueDisplayMode === "count" && selectedOptions.length > 0;
230
- const resolvedInputSize = (_b = inputSize != null ? inputSize : inputProps == null ? void 0 : inputProps.inputSize) != null ? _b : "md";
239
+ const resolvedInputSize = (_a = inputSize != null ? inputSize : inputProps == null ? void 0 : inputProps.inputSize) != null ? _a : "md";
231
240
  const shouldFitContent = fitContent && mode === "single";
232
241
  const [fittedWidthPx, setFittedWidthPx] = useState(null);
233
242
  const visibleSingleValueText = useMemo(() => {
234
243
  if (!shouldFitContent) return "";
235
- return inputValue || (selectedOption == null ? void 0 : selectedOption.label) || placeholder || "";
236
- }, [shouldFitContent, inputValue, selectedOption, placeholder]);
244
+ return inputValue || selectedSingleText || placeholder || "";
245
+ }, [shouldFitContent, inputValue, selectedSingleText, placeholder]);
237
246
  useLayoutEffect(() => {
238
247
  if (!shouldFitContent) return;
239
248
  const input = inputRef.current;
@@ -287,15 +296,18 @@ function Typeahead({
287
296
  return () => window.removeEventListener("keydown", handleKeyDown2);
288
297
  }, [enableHotkey]);
289
298
  useEffect(() => {
290
- var _a2;
299
+ if (selectedOption) {
300
+ locallyClearedRef.current = false;
301
+ setHideSelectedHighlight(false);
302
+ }
291
303
  if (justClearedRef.current || locallyClearedRef.current) return;
292
304
  if (mode === "multi") {
293
305
  setInputValue("");
294
306
  setQuery("");
295
307
  } else {
296
- setInputValue((_a2 = selectedOption == null ? void 0 : selectedOption.label) != null ? _a2 : "");
308
+ setInputValue(selectedSingleText);
297
309
  }
298
- }, [selectedOption, mode]);
310
+ }, [selectedOption, selectedSingleText, mode]);
299
311
  const getSelectedIndex = React.useCallback(
300
312
  (items) => {
301
313
  if (items.length === 0) return -1;
@@ -319,10 +331,10 @@ function Typeahead({
319
331
  });
320
332
  }, [filteredOptions, getSelectedIndex]);
321
333
  useEffect(() => {
322
- var _a2, _b2;
334
+ var _a2, _b;
323
335
  if (!open || activeIndex < 0) return;
324
336
  const activeEl = (_a2 = document.getElementById(listboxId)) == null ? void 0 : _a2.querySelector(`#${CSS.escape(`${listboxId}-option-${activeIndex}`)}`);
325
- (_b2 = activeEl == null ? void 0 : activeEl.scrollIntoView) == null ? void 0 : _b2.call(activeEl, { block: "nearest" });
337
+ (_b = activeEl == null ? void 0 : activeEl.scrollIntoView) == null ? void 0 : _b.call(activeEl, { block: "nearest" });
326
338
  }, [open, activeIndex, filteredOptions, listboxId]);
327
339
  const getFocusableElements = React.useCallback(() => {
328
340
  const selector = [
@@ -349,13 +361,13 @@ function Typeahead({
349
361
  return focusables;
350
362
  }, []);
351
363
  const isFocusWithinTypeahead = React.useCallback((target) => {
352
- var _a2, _b2;
364
+ var _a2, _b;
353
365
  if (!(target instanceof Node)) return false;
354
- return Boolean(((_a2 = rootRef.current) == null ? void 0 : _a2.contains(target)) || ((_b2 = popoverContentRef.current) == null ? void 0 : _b2.contains(target)));
366
+ return Boolean(((_a2 = rootRef.current) == null ? void 0 : _a2.contains(target)) || ((_b = popoverContentRef.current) == null ? void 0 : _b.contains(target)));
355
367
  }, []);
356
368
  const handleTabKeyDown = React.useCallback(
357
369
  (event) => {
358
- var _a2, _b2, _c;
370
+ var _a2, _b, _c;
359
371
  if (event.key !== "Tab" || !open) return;
360
372
  const focusables = getFocusableElements();
361
373
  if (focusables.length === 0) return;
@@ -365,7 +377,7 @@ function Typeahead({
365
377
  if (mode === "multi") {
366
378
  if (activeIndexInScope === -1) {
367
379
  event.preventDefault();
368
- (_b2 = focusables[0]) == null ? void 0 : _b2.focus();
380
+ (_b = focusables[0]) == null ? void 0 : _b.focus();
369
381
  return;
370
382
  }
371
383
  event.preventDefault();
@@ -386,6 +398,7 @@ function Typeahead({
386
398
  setQuery(nextValue);
387
399
  if (!open) setOpen(true);
388
400
  if (mode === "multi") return;
401
+ if (allowCustomValue) return;
389
402
  const exactMatch = options.find((option) => option.label === nextValue);
390
403
  if (!exactMatch && selectedValue !== null) {
391
404
  onChange(null);
@@ -418,6 +431,22 @@ function Typeahead({
418
431
  commitSelection(exactLabelMatch);
419
432
  return;
420
433
  }
434
+ if (allowCustomValue) {
435
+ const customValue = inputValue.trim();
436
+ if (customValue) {
437
+ onChange(customValue);
438
+ setInputValue(customValue);
439
+ } else {
440
+ if (selectedValue !== null) {
441
+ onChange(null);
442
+ }
443
+ setInputValue("");
444
+ }
445
+ setQuery("");
446
+ setOpen(false);
447
+ setActiveIndex(-1);
448
+ return;
449
+ }
421
450
  if (selectedOption) {
422
451
  setInputValue(selectedOption.label);
423
452
  } else {
@@ -454,7 +483,6 @@ function Typeahead({
454
483
  }, [mode, selectedOption, prepareSingleSearchInput, openWithAllOptions, openWithCurrentFilter]);
455
484
  const handleTriggerMouseDown = React.useCallback(
456
485
  (e) => {
457
- var _a2;
458
486
  inputPropsOnMouseDown == null ? void 0 : inputPropsOnMouseDown(e);
459
487
  if (e.defaultPrevented) return;
460
488
  if (justClearedRef.current) {
@@ -468,9 +496,7 @@ function Typeahead({
468
496
  setOpen(false);
469
497
  setActiveIndex(-1);
470
498
  setQuery("");
471
- setInputValue(
472
- mode === "single" && !locallyClearedRef.current ? (_a2 = selectedOption == null ? void 0 : selectedOption.label) != null ? _a2 : "" : ""
473
- );
499
+ setInputValue(mode === "single" && !locallyClearedRef.current ? selectedSingleText : "");
474
500
  return;
475
501
  }
476
502
  if (isAlreadyFocused && mode === "single" && selectedOption && !locallyClearedRef.current) {
@@ -487,6 +513,7 @@ function Typeahead({
487
513
  open,
488
514
  mode,
489
515
  selectedOption,
516
+ selectedSingleText,
490
517
  prepareSingleSearchInput,
491
518
  getSelectedIndex,
492
519
  options,
@@ -495,7 +522,7 @@ function Typeahead({
495
522
  );
496
523
  const handleChevronMouseDown = React.useCallback(
497
524
  (e) => {
498
- var _a2, _b2;
525
+ var _a2;
499
526
  e.preventDefault();
500
527
  e.stopPropagation();
501
528
  if (justClearedRef.current) return;
@@ -504,14 +531,12 @@ function Typeahead({
504
531
  setOpen(false);
505
532
  setActiveIndex(-1);
506
533
  setQuery("");
507
- setInputValue(
508
- mode === "single" && !locallyClearedRef.current ? (_b2 = selectedOption == null ? void 0 : selectedOption.label) != null ? _b2 : "" : ""
509
- );
534
+ setInputValue(mode === "single" && !locallyClearedRef.current ? selectedSingleText : "");
510
535
  return;
511
536
  }
512
537
  handleOpen();
513
538
  },
514
- [open, mode, selectedOption, handleOpen]
539
+ [open, mode, selectedSingleText, handleOpen]
515
540
  );
516
541
  const handleKeyDown = (e) => {
517
542
  switch (e.key) {
@@ -562,9 +587,7 @@ function Typeahead({
562
587
  setOpen(false);
563
588
  setActiveIndex(-1);
564
589
  setQuery("");
565
- setInputValue(
566
- mode === "single" && selectedOption && !locallyClearedRef.current ? selectedOption.label : ""
567
- );
590
+ setInputValue(mode === "single" && !locallyClearedRef.current ? selectedSingleText : "");
568
591
  return;
569
592
  }
570
593
  };
@@ -575,7 +598,9 @@ function Typeahead({
575
598
  style: {
576
599
  display: "flex",
577
600
  flexDirection: "column",
601
+ justifyContent: mode === "multi" && multiSelectedValuesDisplayMode === "below-input" && selectedOptions.length > 0 ? "flex-start" : "center",
578
602
  gap: mode === "multi" && multiSelectedValuesDisplayMode === "below-input" && selectedOptions.length > 0 ? 8 : 0,
603
+ height: "100%",
579
604
  width: width != null ? width : fullWidth ? "100%" : shouldFitContent ? measuredRootWidth : void 0,
580
605
  flex: width ? "0 0 auto" : fullWidth || shouldFitContent ? "1 1 auto" : void 0,
581
606
  minWidth: width ? void 0 : shouldFitContent ? measuredRootWidth : 0,
@@ -613,11 +638,18 @@ function Typeahead({
613
638
  returnFocus: false,
614
639
  overlayRef: popoverContentRef,
615
640
  trigger: (openPopover, icon) => {
616
- var _a2, _b2, _c, _d;
641
+ var _a2, _b, _c, _d;
617
642
  return /* @__PURE__ */ jsx(
618
643
  "div",
619
644
  {
620
645
  ref: triggerWrapperRef,
646
+ style: {
647
+ display: "flex",
648
+ alignItems: "stretch",
649
+ minWidth: 0,
650
+ width: "100%",
651
+ height: "100%"
652
+ },
621
653
  onMouseDown: (e) => {
622
654
  const target = e.target;
623
655
  if (justClearedRef.current) {
@@ -668,7 +700,7 @@ function Typeahead({
668
700
  inputSize: resolvedInputSize,
669
701
  width: width != null ? width : shouldFitContent ? void 0 : inputProps == null ? void 0 : inputProps.width,
670
702
  autoComplete: (_a2 = autoComplete != null ? autoComplete : inputProps == null ? void 0 : inputProps.autoComplete) != null ? _a2 : "off",
671
- autoCorrect: (_b2 = autoCorrect != null ? autoCorrect : inputProps == null ? void 0 : inputProps.autoCorrect) != null ? _b2 : "off",
703
+ autoCorrect: (_b = autoCorrect != null ? autoCorrect : inputProps == null ? void 0 : inputProps.autoCorrect) != null ? _b : "off",
672
704
  autoCapitalize: (_c = autoCapitalize != null ? autoCapitalize : inputProps == null ? void 0 : inputProps.autoCapitalize) != null ? _c : "none",
673
705
  spellCheck: (_d = spellCheck != null ? spellCheck : inputProps == null ? void 0 : inputProps.spellCheck) != null ? _d : false,
674
706
  disabled,
@@ -679,9 +711,9 @@ function Typeahead({
679
711
  usesCountAdornment ? styles.inputWithoutStartPadding : ""
680
712
  ].filter(Boolean).join(" "),
681
713
  onClear: (event) => {
682
- var _a3, _b3;
714
+ var _a3, _b2;
683
715
  (_a3 = event == null ? void 0 : event.preventDefault) == null ? void 0 : _a3.call(event);
684
- (_b3 = event == null ? void 0 : event.stopPropagation) == null ? void 0 : _b3.call(event);
716
+ (_b2 = event == null ? void 0 : event.stopPropagation) == null ? void 0 : _b2.call(event);
685
717
  justClearedRef.current = true;
686
718
  locallyClearedRef.current = true;
687
719
  setHideSelectedHighlight(true);
@@ -172,7 +172,7 @@
172
172
  gap: var(--spacing-xs);
173
173
  color: var(--color-fg-muted);
174
174
  font-family: var(--font-family);
175
- font-size: var(--font-size-md);
175
+ font-size: var(--font-size-sm);
176
176
  text-decoration: none;
177
177
  border-radius: var(--border-radius-default);
178
178
  padding-block: var(--spacing-xs);
@@ -162,18 +162,27 @@ function Modal({
162
162
  ] }),
163
163
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.body, children: body }),
164
164
  shouldRenderFooter && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.footer, children: [
165
- resolvedSecondaryAction && /* @__PURE__ */ jsxRuntime.jsxs(Button.Button, { type: "button", onClick: resolvedSecondaryAction.onClick, disabled: isLoading, children: [
166
- resolvedSecondaryAction.icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.icon, children: resolvedSecondaryAction.icon }),
167
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: resolvedSecondaryAction.label })
168
- ] }),
165
+ resolvedSecondaryAction && /* @__PURE__ */ jsxRuntime.jsxs(
166
+ Button.Button,
167
+ {
168
+ type: "button",
169
+ onClick: resolvedSecondaryAction.onClick,
170
+ disabled: resolvedSecondaryAction.disabled || resolvedSecondaryAction.loading || isLoading,
171
+ loading: resolvedSecondaryAction.loading,
172
+ children: [
173
+ resolvedSecondaryAction.icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.icon, children: resolvedSecondaryAction.icon }),
174
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: resolvedSecondaryAction.label })
175
+ ]
176
+ }
177
+ ),
169
178
  primaryAction && /* @__PURE__ */ jsxRuntime.jsxs(
170
179
  Button.Button,
171
180
  {
172
181
  type: "button",
173
182
  variant: primaryAction.severity || "primary",
174
183
  onClick: primaryAction.onClick,
175
- disabled: primaryAction.disabled || isLoading,
176
- loading: isLoading,
184
+ disabled: primaryAction.disabled || primaryAction.loading || isLoading,
185
+ loading: primaryAction.loading || isLoading,
177
186
  children: [
178
187
  primaryAction.icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.icon, children: primaryAction.icon }),
179
188
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: primaryAction.label })
@@ -7,6 +7,7 @@ export type ModalActionConfig = {
7
7
  onClick?: () => void;
8
8
  icon?: ReactNode;
9
9
  disabled?: boolean;
10
+ loading?: boolean;
10
11
  };
11
12
  export type ModalProps = {
12
13
  isLoading?: boolean;
@@ -156,18 +156,27 @@ function Modal({
156
156
  ] }),
157
157
  /* @__PURE__ */ jsx("div", { className: styles.body, children: body }),
158
158
  shouldRenderFooter && /* @__PURE__ */ jsxs("div", { className: styles.footer, children: [
159
- resolvedSecondaryAction && /* @__PURE__ */ jsxs(Button, { type: "button", onClick: resolvedSecondaryAction.onClick, disabled: isLoading, children: [
160
- resolvedSecondaryAction.icon && /* @__PURE__ */ jsx("span", { className: styles.icon, children: resolvedSecondaryAction.icon }),
161
- /* @__PURE__ */ jsx("span", { children: resolvedSecondaryAction.label })
162
- ] }),
159
+ resolvedSecondaryAction && /* @__PURE__ */ jsxs(
160
+ Button,
161
+ {
162
+ type: "button",
163
+ onClick: resolvedSecondaryAction.onClick,
164
+ disabled: resolvedSecondaryAction.disabled || resolvedSecondaryAction.loading || isLoading,
165
+ loading: resolvedSecondaryAction.loading,
166
+ children: [
167
+ resolvedSecondaryAction.icon && /* @__PURE__ */ jsx("span", { className: styles.icon, children: resolvedSecondaryAction.icon }),
168
+ /* @__PURE__ */ jsx("span", { children: resolvedSecondaryAction.label })
169
+ ]
170
+ }
171
+ ),
163
172
  primaryAction && /* @__PURE__ */ jsxs(
164
173
  Button,
165
174
  {
166
175
  type: "button",
167
176
  variant: primaryAction.severity || "primary",
168
177
  onClick: primaryAction.onClick,
169
- disabled: primaryAction.disabled || isLoading,
170
- loading: isLoading,
178
+ disabled: primaryAction.disabled || primaryAction.loading || isLoading,
179
+ loading: primaryAction.loading || isLoading,
171
180
  children: [
172
181
  primaryAction.icon && /* @__PURE__ */ jsx("span", { className: styles.icon, children: primaryAction.icon }),
173
182
  /* @__PURE__ */ jsx("span", { children: primaryAction.label })