@umodeapporg/ui 0.12.0 → 0.13.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.
Files changed (35) hide show
  1. package/dist/components/Form/ControlledButton.d.ts +12 -0
  2. package/dist/components/Form/ControlledButton.js +10 -5
  3. package/dist/components/Form/ControlledButton.js.map +1 -1
  4. package/dist/components/Form/ControlledDialog.d.ts +9 -2
  5. package/dist/components/Form/ControlledDialog.js +14 -8
  6. package/dist/components/Form/ControlledDialog.js.map +1 -1
  7. package/dist/components/Form/ControlledDialogPrimitives.d.ts +23 -0
  8. package/dist/components/Form/ControlledDialogPrimitives.js +114 -5
  9. package/dist/components/Form/ControlledDialogPrimitives.js.map +1 -1
  10. package/dist/components/Form/ControlledSelect.d.ts +11 -2
  11. package/dist/components/Form/ControlledSelect.js +273 -48
  12. package/dist/components/Form/ControlledSelect.js.map +1 -1
  13. package/dist/components/Form/ControlledSwitch.d.ts +9 -2
  14. package/dist/components/Form/ControlledSwitch.js +88 -69
  15. package/dist/components/Form/ControlledSwitch.js.map +1 -1
  16. package/dist/components/Navigation/LinkButton.d.ts +26 -0
  17. package/dist/components/Navigation/LinkButton.js +82 -0
  18. package/dist/components/Navigation/LinkButton.js.map +1 -0
  19. package/dist/components/UI/ControlledMenu.d.ts +6 -0
  20. package/dist/components/UI/ControlledMenu.js +3 -4
  21. package/dist/components/UI/ControlledMenu.js.map +1 -1
  22. package/dist/components/UI/DataTable/DataTable.js +1 -0
  23. package/dist/components/UI/DataTable/DataTable.js.map +1 -1
  24. package/dist/components/UI/DataTable/DataTableBulkEditRowsPicker.js +2 -1
  25. package/dist/components/UI/DataTable/DataTableBulkEditRowsPicker.js.map +1 -1
  26. package/dist/components/UI/DataTable/DataTableFilters.js +1 -0
  27. package/dist/components/UI/DataTable/DataTableFilters.js.map +1 -1
  28. package/dist/components/UI/DataTable/DataTableToolbar.js +2 -1
  29. package/dist/components/UI/DataTable/DataTableToolbar.js.map +1 -1
  30. package/dist/components/UI/DataTable/applyDataTableFilters.js +1 -0
  31. package/dist/components/UI/DataTable/applyDataTableFilters.js.map +1 -1
  32. package/dist/index.d.ts +2 -1
  33. package/dist/index.js +50 -47
  34. package/dist/index.js.map +1 -1
  35. package/package.json +9 -2
@@ -8,29 +8,13 @@ import { ControlledMenuItem, ControlledMenuRoot, ControlledMenuHandler, Controll
8
8
  import ControlledTooltip from "../UI/ControlledTooltip.js";
9
9
  import { capitalizeAllLetters, formatFilter } from "../../helpers/stringHelpers.js";
10
10
  import { uiLogger } from "../../helpers/uiLogger.js";
11
- import "./ControlledButton.js";
11
+ import UnifiedSearchBar from "../Search/UnifiedSearchBar.js";
12
12
  import OutlinedChip from "../UI/Chips/OutlinedChip.js";
13
+ import LoadingSpinner from "../UI/LoadingSpinner.js";
13
14
  import { MaterialSymbol } from "../UI/MaterialSymbol/index.js";
14
- import "date-fns";
15
- import "date-fns/locale";
16
- import "react-day-picker";
17
- import "tailwind-merge";
18
- import "../UI/ControlledPopover.js";
19
- import ControlledInput from "./ControlledInput.js";
20
- import "react-day-picker/style.css";
21
- import "../DateTime/DateRangePicker.js";
22
- import ControlledCheckbox from "./ControlledCheckbox.js";
23
15
  import NoDataFound from "../UI/NoDataFound/index.js";
24
- import "../Search/filters/FiltersButton.js";
25
- import "../../helpers/utilityHelpers.js";
26
- import "./ControlledDialog.js";
27
- import "motion/react";
28
- import "react-dom";
29
- import "../../motion/motionPresets.js";
30
- import "../../motion/motionTokens.js";
31
- import "../Navigation/UiNavigationContext.js";
32
- import UnifiedSearchBar from "../Search/UnifiedSearchBar.js";
33
- import LoadingSpinner from "../UI/LoadingSpinner.js";
16
+ import ControlledCheckbox from "./ControlledCheckbox.js";
17
+ import ControlledInput from "./ControlledInput.js";
34
18
  /* empty css */
35
19
  const ITEM_HEIGHT = 40;
36
20
  const MAX_LIST_HEIGHT = 300;
@@ -38,6 +22,9 @@ const OVERSCAN_COUNT = 10;
38
22
  const SEARCH_DEBOUNCE_MS = 300;
39
23
  const MAX_IN_FIELD_CHIPS = 2;
40
24
  const SEARCH_INPUT_MARKER_CLASS = "controlled-select-search-input";
25
+ const TYPEAHEAD_RESET_MS = 500;
26
+ const FOCUS_RETRY_FRAMES = 6;
27
+ const isPrintableKey = (event) => event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey;
41
28
  const SelectSearchSlot = ({ children }) => /* @__PURE__ */ jsx("div", { className: "px-1 py-0.5", onClick: (event) => event.stopPropagation(), children });
42
29
  function ControlledSelect({
43
30
  label,
@@ -67,7 +54,14 @@ function ControlledSelect({
67
54
  ariaRequired,
68
55
  inputClassName,
69
56
  matchTriggerWidth = false,
70
- compact = false
57
+ compact = false,
58
+ id,
59
+ name,
60
+ required,
61
+ onBlur,
62
+ onFocus,
63
+ autoFocus,
64
+ ...triggerAttributes
71
65
  }) {
72
66
  const { t } = useTranslation("ui");
73
67
  const options = useMemo(() => Array.isArray(optionsProp) ? optionsProp : [], [optionsProp]);
@@ -102,6 +96,12 @@ function ControlledSelect({
102
96
  const [isOpen, setIsOpen] = useState(false);
103
97
  const menuListRef = useRef(null);
104
98
  const triggerRef = useRef(null);
99
+ const triggerInputRef = useRef(null);
100
+ const virtualListRef = useRef(null);
101
+ const [activeIndex, setActiveIndex] = useState(-1);
102
+ const [focusRequest, setFocusRequest] = useState(0);
103
+ const pendingFocusIndexRef = useRef(null);
104
+ const typeaheadRef = useRef({ buffer: "", timer: 0 });
105
105
  const [currentSearchText, setCurrentSearchText] = useState("");
106
106
  const scrollableContainerId = useId();
107
107
  const scrollableContainerRef = useRef(null);
@@ -210,8 +210,216 @@ function ControlledSelect({
210
210
  return formatLabel(foundOption?.label || selected || label);
211
211
  }
212
212
  }, [multiple, selected, options, label, formatLabel]);
213
+ const getOptionId = useCallback(
214
+ (index) => `${scrollableContainerId}-option-${index}`,
215
+ [scrollableContainerId]
216
+ );
217
+ const isOptionEnabled = useCallback(
218
+ (index) => {
219
+ const option = filteredOptions[index];
220
+ return Boolean(option) && !option.disabled;
221
+ },
222
+ [filteredOptions]
223
+ );
224
+ const findEnabledIndex = useCallback(
225
+ (start, step) => {
226
+ for (let index = start; index >= 0 && index < filteredOptions.length; index += step) {
227
+ if (isOptionEnabled(index)) return index;
228
+ }
229
+ return -1;
230
+ },
231
+ [filteredOptions.length, isOptionEnabled]
232
+ );
233
+ const getSelectedIndex = useCallback(() => {
234
+ const firstSelected = multiple ? selected[0] : selected;
235
+ const index = filteredOptions.findIndex((option) => option.value === firstSelected);
236
+ return index >= 0 && isOptionEnabled(index) ? index : -1;
237
+ }, [multiple, selected, filteredOptions, isOptionEnabled]);
238
+ const focusOptionAt = useCallback((index) => {
239
+ if (index < 0) return;
240
+ setActiveIndex(index);
241
+ pendingFocusIndexRef.current = index;
242
+ setFocusRequest((request) => request + 1);
243
+ }, []);
244
+ const focusSearchInput = useCallback(() => {
245
+ const searchInput = menuListRef.current?.querySelector(`.${SEARCH_INPUT_MARKER_CLASS}`);
246
+ if (!searchInput) return false;
247
+ searchInput.focus();
248
+ return true;
249
+ }, []);
250
+ const findTypeaheadMatch = useCallback(
251
+ (key, fromIndex) => {
252
+ const state = typeaheadRef.current;
253
+ window.clearTimeout(state.timer);
254
+ state.buffer += key.toLowerCase();
255
+ state.timer = window.setTimeout(() => {
256
+ state.buffer = "";
257
+ }, TYPEAHEAD_RESET_MS);
258
+ const isCycling = state.buffer.split("").every((char) => char === state.buffer[0]);
259
+ const term = isCycling ? state.buffer[0] : state.buffer;
260
+ const start = isCycling ? fromIndex + 1 : Math.max(fromIndex, 0);
261
+ const total = filteredOptions.length;
262
+ for (let offset = 0; offset < total; offset += 1) {
263
+ const index = (start + offset) % total;
264
+ const label2 = formatLabel(filteredOptions[index]?.label).toLowerCase();
265
+ if (isOptionEnabled(index) && label2.startsWith(term)) return index;
266
+ }
267
+ return -1;
268
+ },
269
+ [filteredOptions, formatLabel, isOptionEnabled]
270
+ );
271
+ const openAndFocus = useCallback(
272
+ (target) => {
273
+ if (disabled) return;
274
+ if (!isOpen) {
275
+ setIsOpen(true);
276
+ onOpen?.();
277
+ }
278
+ if (searchable && target === "selected") {
279
+ pendingFocusIndexRef.current = -1;
280
+ setFocusRequest((request) => request + 1);
281
+ return;
282
+ }
283
+ let index;
284
+ if (typeof target === "number") index = target;
285
+ else if (target === "first") index = findEnabledIndex(0, 1);
286
+ else if (target === "last") index = findEnabledIndex(filteredOptions.length - 1, -1);
287
+ else index = getSelectedIndex() >= 0 ? getSelectedIndex() : findEnabledIndex(0, 1);
288
+ focusOptionAt(index);
289
+ },
290
+ [disabled, isOpen, onOpen, searchable, findEnabledIndex, filteredOptions.length, getSelectedIndex, focusOptionAt]
291
+ );
292
+ useEffect(() => {
293
+ const index = pendingFocusIndexRef.current;
294
+ if (!isOpen || index === null) return void 0;
295
+ if (index >= 0) virtualListRef.current?.scrollToItem(index);
296
+ let attempts = 0;
297
+ let frame = 0;
298
+ const tryFocus = () => {
299
+ if (index < 0) {
300
+ if (focusSearchInput()) {
301
+ pendingFocusIndexRef.current = null;
302
+ return;
303
+ }
304
+ } else {
305
+ const node = document.getElementById(getOptionId(index));
306
+ if (node) {
307
+ pendingFocusIndexRef.current = null;
308
+ node.focus({ preventScroll: Boolean(virtualListRef.current) });
309
+ if (!virtualListRef.current) node.scrollIntoView?.({ block: "nearest" });
310
+ return;
311
+ }
312
+ }
313
+ attempts += 1;
314
+ if (attempts < FOCUS_RETRY_FRAMES) frame = window.requestAnimationFrame(tryFocus);
315
+ };
316
+ tryFocus();
317
+ return () => window.cancelAnimationFrame(frame);
318
+ }, [isOpen, focusRequest]);
319
+ const wasOpenRef = useRef(false);
320
+ useEffect(() => {
321
+ if (isOpen) {
322
+ wasOpenRef.current = true;
323
+ return;
324
+ }
325
+ if (!wasOpenRef.current) return;
326
+ wasOpenRef.current = false;
327
+ setActiveIndex(-1);
328
+ pendingFocusIndexRef.current = null;
329
+ const active = document.activeElement;
330
+ const focusWasInList = Boolean(active && menuListRef.current?.contains(active));
331
+ if (focusWasInList || active === document.body || active === null) {
332
+ triggerInputRef.current?.focus({ preventScroll: true });
333
+ }
334
+ }, [isOpen]);
335
+ useEffect(() => {
336
+ setActiveIndex(-1);
337
+ }, [filteredOptions]);
338
+ useEffect(() => {
339
+ if (!isOpen || activeIndex < 0) return;
340
+ const active = document.activeElement;
341
+ if (active === null || active === document.body) focusOptionAt(activeIndex);
342
+ }, [selected]);
343
+ useEffect(() => () => window.clearTimeout(typeaheadRef.current.timer), []);
344
+ const listboxLabel = triggerAttributes["aria-label"] ?? ariaLabel ?? label ?? (isOpen ? triggerInputRef.current?.labels?.[0]?.textContent?.trim() || void 0 : void 0);
345
+ const handleTriggerKeyDown = (event) => {
346
+ if (disabled) return;
347
+ switch (event.key) {
348
+ case "Enter":
349
+ case " ":
350
+ case "ArrowDown":
351
+ event.preventDefault();
352
+ openAndFocus(isOpen && event.key === "ArrowDown" && activeIndex >= 0 ? activeIndex : "selected");
353
+ return;
354
+ case "ArrowUp":
355
+ event.preventDefault();
356
+ openAndFocus(getSelectedIndex() >= 0 ? getSelectedIndex() : "last");
357
+ return;
358
+ case "Home":
359
+ event.preventDefault();
360
+ openAndFocus("first");
361
+ return;
362
+ case "End":
363
+ event.preventDefault();
364
+ openAndFocus("last");
365
+ return;
366
+ case "Escape":
367
+ if (!isOpen) return;
368
+ event.preventDefault();
369
+ setIsOpen(false);
370
+ return;
371
+ case "Tab":
372
+ if (isOpen) setIsOpen(false);
373
+ return;
374
+ default:
375
+ if (!isPrintableKey(event) || searchable) return;
376
+ event.preventDefault();
377
+ openAndFocus(findTypeaheadMatch(event.key, getSelectedIndex()));
378
+ }
379
+ };
380
+ const handleListKeyDown = (event) => {
381
+ const target = event.target;
382
+ const isOnSearch = target.classList.contains(SEARCH_INPUT_MARKER_CLASS);
383
+ const optionNode = target.closest('[role="option"]');
384
+ const currentIndex = optionNode ? Number(optionNode.dataset.optionIndex ?? -1) : activeIndex;
385
+ switch (event.key) {
386
+ case "ArrowDown": {
387
+ event.preventDefault();
388
+ const next = isOnSearch ? findEnabledIndex(0, 1) : findEnabledIndex(currentIndex + 1, 1);
389
+ focusOptionAt(next);
390
+ return;
391
+ }
392
+ case "ArrowUp": {
393
+ if (isOnSearch) return;
394
+ event.preventDefault();
395
+ const previous = findEnabledIndex(currentIndex - 1, -1);
396
+ if (previous >= 0) focusOptionAt(previous);
397
+ else if (searchable) focusSearchInput();
398
+ return;
399
+ }
400
+ case "Home":
401
+ case "End":
402
+ if (isOnSearch) return;
403
+ event.preventDefault();
404
+ focusOptionAt(event.key === "Home" ? findEnabledIndex(0, 1) : findEnabledIndex(filteredOptions.length - 1, -1));
405
+ return;
406
+ case "Escape":
407
+ event.preventDefault();
408
+ setIsOpen(false);
409
+ return;
410
+ case "Tab":
411
+ event.preventDefault();
412
+ setIsOpen(false);
413
+ triggerInputRef.current?.focus({ preventScroll: true });
414
+ return;
415
+ default:
416
+ if (isOnSearch || !isPrintableKey(event) || event.key === " ") return;
417
+ event.preventDefault();
418
+ focusOptionAt(findTypeaheadMatch(event.key, currentIndex));
419
+ }
420
+ };
213
421
  const renderOptionItem = useCallback(
214
- (option, style) => {
422
+ (option, index, style) => {
215
423
  const isSelected = multiple ? Array.isArray(selected) && selected.includes(option.value) : option.value === selected;
216
424
  const isDisabledWithReason = Boolean(option.disabled && option.tooltip);
217
425
  const menuItem = /* @__PURE__ */ jsxs(
@@ -219,20 +427,32 @@ function ControlledSelect({
219
427
  {
220
428
  disabled: option.disabled,
221
429
  onClick: (event) => handleSelect(option.value, event),
222
- className: classNames("relative flex items-center p-2 pl-3 text-foreground", {
223
- // The hover variant has to live inside the selected branch: the menu item's own
224
- // default hover would otherwise wipe the selected tint.
225
- "bg-brand-soft font-medium text-brand-emphasis hover:!bg-brand-soft focus:!bg-brand-soft": isSelected,
226
- "hover:!bg-muted": !isSelected
227
- }),
430
+ role: "option",
431
+ id: getOptionId(index),
432
+ tabIndex: -1,
433
+ "aria-selected": isSelected,
434
+ "aria-setsize": filteredOptions.length,
435
+ "aria-posinset": index + 1,
436
+ "data-option-index": index,
437
+ className: classNames(
438
+ "relative flex items-center p-2 pl-3 text-foreground",
439
+ "focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
440
+ {
441
+ // The hover variant has to live inside the selected branch: the menu item's own
442
+ // default hover would otherwise wipe the selected tint.
443
+ "bg-brand-soft font-medium text-brand-emphasis hover:!bg-brand-soft focus:!bg-brand-soft": isSelected,
444
+ "hover:!bg-muted": !isSelected
445
+ }
446
+ ),
228
447
  style: {
229
448
  ...isDisabledWithReason ? {} : style,
230
449
  height: ITEM_HEIGHT - 1
231
450
  },
232
451
  children: [
233
452
  isSelected && /* @__PURE__ */ jsx("span", { className: "absolute inset-y-1 left-0 w-0.5 rounded-full bg-brand" }),
234
- multiple && /* @__PURE__ */ jsx(ControlledCheckbox, { checked: isSelected, onChange: () => {
235
- } }),
453
+ multiple && // The option itself carries aria-selected; the checkbox is only its picture.
454
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "pointer-events-none", children: /* @__PURE__ */ jsx(ControlledCheckbox, { checked: isSelected, onChange: () => {
455
+ } }) }),
236
456
  option.tooltip && !isDisabledWithReason && /* @__PURE__ */ jsx(ControlledTooltip, { content: option.tooltip, className: "w-full max-w-72", children: /* @__PURE__ */ jsx(MaterialSymbol, { icon: "info", size: 15, color: "rgb(var(--color-foreground-subtle))", className: "mr-1" }) }),
237
457
  option.swatch && /* @__PURE__ */ jsx(
238
458
  "span",
@@ -252,13 +472,13 @@ function ControlledSelect({
252
472
  );
253
473
  return isDisabledWithReason ? /* @__PURE__ */ jsx(ControlledTooltip, { content: option.tooltip, className: "z-[9999] max-w-72", children: /* @__PURE__ */ jsx("div", { style, children: menuItem }) }, option.value) : menuItem;
254
474
  },
255
- [multiple, selected, handleSelect, formatLabel]
475
+ [multiple, selected, handleSelect, formatLabel, getOptionId, filteredOptions.length]
256
476
  );
257
477
  const ItemRenderer = useCallback(
258
478
  ({ index, style }) => {
259
479
  const option = filteredOptions[index];
260
480
  if (!option) return null;
261
- return renderOptionItem(option, style);
481
+ return renderOptionItem(option, index, style);
262
482
  },
263
483
  [filteredOptions, renderOptionItem]
264
484
  );
@@ -376,7 +596,7 @@ function ControlledSelect({
376
596
  },
377
597
  dismiss: { itemPress: !multiple },
378
598
  children: [
379
- /* @__PURE__ */ jsx(ControlledMenuHandler, { children: /* @__PURE__ */ jsxs(
599
+ /* @__PURE__ */ jsx(ControlledMenuHandler, { skipTriggerAria: true, children: /* @__PURE__ */ jsxs(
380
600
  "div",
381
601
  {
382
602
  className: classNames("flex", {
@@ -430,21 +650,18 @@ function ControlledSelect({
430
650
  /* @__PURE__ */ jsx(
431
651
  ControlledInput,
432
652
  {
653
+ ...triggerAttributes,
654
+ id,
655
+ inputRef: triggerInputRef,
656
+ onBlur,
657
+ onFocus,
658
+ autoFocus,
433
659
  readOnly: true,
434
660
  role: "combobox",
435
661
  "aria-haspopup": "listbox",
436
662
  "aria-expanded": isOpen,
437
- "aria-controls": scrollableContainerId,
438
- onKeyDown: (event) => {
439
- if (disabled) return;
440
- if (event.key === "Enter" || event.key === " " || event.key === "ArrowDown") {
441
- event.preventDefault();
442
- setIsOpen(true);
443
- onOpen?.();
444
- } else if (event.key === "Escape" && isOpen) {
445
- setIsOpen(false);
446
- }
447
- },
663
+ "aria-controls": isOpen ? scrollableContainerId : void 0,
664
+ onKeyDown: handleTriggerKeyDown,
448
665
  className: classNames(
449
666
  "controlled-select-input flex w-full min-w-0 justify-between truncate text-left",
450
667
  !compact && "tablet:min-w-52",
@@ -458,8 +675,8 @@ function ControlledSelect({
458
675
  autoComplete: "off",
459
676
  rightSlot: /* @__PURE__ */ jsx(MaterialSymbol, { className: cursorClassName, icon: "keyboard_arrow_down", size: 15 }),
460
677
  label: selectedHasValue && !persistentLabel ? void 0 : label,
461
- "aria-label": ariaLabel ?? label,
462
- "aria-required": ariaRequired ? "true" : void 0,
678
+ "aria-label": triggerAttributes["aria-label"] ?? ariaLabel ?? label,
679
+ "aria-required": ariaRequired || required ? true : triggerAttributes["aria-required"],
463
680
  value: showInFieldChips ? " " : selectedHasValue || hasEmptyValueOption ? getDisplayLabel() : "",
464
681
  disabled
465
682
  }
@@ -473,6 +690,7 @@ function ControlledSelect({
473
690
  }
474
691
  ) })
475
692
  ] }),
693
+ name && (multiple ? selected.map((value) => /* @__PURE__ */ jsx("input", { type: "hidden", name, value, disabled }, value)) : selected !== "" && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: selected, disabled })),
476
694
  (selectedItemsPosition === "bottom" || selectedItemsPosition === "right") && selectedItemsChips
477
695
  ]
478
696
  }
@@ -481,6 +699,8 @@ function ControlledSelect({
481
699
  ControlledMenuList,
482
700
  {
483
701
  ref: menuListRef,
702
+ role: "presentation",
703
+ onKeyDown: handleListKeyDown,
484
704
  className: classNames("zIndexMax w-80 max-w-[calc(100vw-1.5rem)] overflow-hidden", listClassName),
485
705
  children: [
486
706
  searchable && /* @__PURE__ */ jsx(SelectSearchSlot, { children: /* @__PURE__ */ jsx(
@@ -507,6 +727,10 @@ function ControlledSelect({
507
727
  "overflow-hidden": !infiniteScroll
508
728
  }),
509
729
  id: scrollableContainerId,
730
+ role: "listbox",
731
+ "aria-multiselectable": multiple || void 0,
732
+ "aria-labelledby": triggerAttributes["aria-labelledby"],
733
+ "aria-label": triggerAttributes["aria-labelledby"] ? void 0 : listboxLabel,
510
734
  children: infiniteScroll ? /* @__PURE__ */ jsx(
511
735
  InfiniteScroll,
512
736
  {
@@ -516,11 +740,12 @@ function ControlledSelect({
516
740
  loader: /* @__PURE__ */ jsx("div", { className: "flex size-full items-center justify-center py-1", children: /* @__PURE__ */ jsx(LoadingSpinner, { size: 4, label: t("controlledSelect.loadingOptions") }) }),
517
741
  scrollableTarget: scrollableContainerId,
518
742
  className: "!overflow-hidden",
519
- children: filteredOptions.length > 0 ? filteredOptions.map((option) => renderOptionItem(option)) : loading ? /* @__PURE__ */ jsx("div", { className: "flex w-full items-center justify-center py-4", children: /* @__PURE__ */ jsx(LoadingSpinner, { size: 4, label: t("controlledSelect.loadingOptions") }) }) : /* @__PURE__ */ jsx(ControlledMenuItem, { disabled: true, children: /* @__PURE__ */ jsx(NoDataFound, { compact: true, message: t("controlledSelect.noDataToShow") }) })
743
+ children: filteredOptions.length > 0 ? filteredOptions.map((option, index) => renderOptionItem(option, index)) : loading ? /* @__PURE__ */ jsx("div", { className: "flex w-full items-center justify-center py-4", children: /* @__PURE__ */ jsx(LoadingSpinner, { size: 4, label: t("controlledSelect.loadingOptions") }) }) : /* @__PURE__ */ jsx(ControlledMenuItem, { disabled: true, role: "presentation", children: /* @__PURE__ */ jsx(NoDataFound, { compact: true, message: t("controlledSelect.noDataToShow") }) })
520
744
  }
521
745
  ) : filteredOptions.length > 0 ? /* @__PURE__ */ jsx(
522
746
  FixedSizeList,
523
747
  {
748
+ ref: virtualListRef,
524
749
  className: "virtualized-select-list",
525
750
  height: listHeight,
526
751
  width: "100%",
@@ -529,7 +754,7 @@ function ControlledSelect({
529
754
  overscanCount: OVERSCAN_COUNT,
530
755
  children: ItemRenderer
531
756
  }
532
- ) : loading ? /* @__PURE__ */ jsx("div", { className: "flex w-full items-center justify-center py-4", children: /* @__PURE__ */ jsx(LoadingSpinner, { size: 4, label: t("controlledSelect.loadingOptions") }) }) : /* @__PURE__ */ jsx(ControlledMenuItem, { disabled: true, children: /* @__PURE__ */ jsx(NoDataFound, { compact: true, message: t("controlledSelect.noDataToShow") }) })
757
+ ) : loading ? /* @__PURE__ */ jsx("div", { className: "flex w-full items-center justify-center py-4", children: /* @__PURE__ */ jsx(LoadingSpinner, { size: 4, label: t("controlledSelect.loadingOptions") }) }) : /* @__PURE__ */ jsx(ControlledMenuItem, { disabled: true, role: "presentation", children: /* @__PURE__ */ jsx(NoDataFound, { compact: true, message: t("controlledSelect.noDataToShow") }) })
533
758
  }
534
759
  )
535
760
  ]