@underverse-ui/underverse 2.0.30 → 2.0.31

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "@underverse-ui/underverse",
3
- "version": "2.0.30",
3
+ "version": "2.0.31",
4
4
  "sourceEntry": "src/index.ts",
5
5
  "totalExports": 263,
6
6
  "exports": [
package/dist/index.cjs CHANGED
@@ -15838,6 +15838,7 @@ var Combobox = ({
15838
15838
  virtualized = false,
15839
15839
  estimatedItemHeight = 44,
15840
15840
  overscan = 8,
15841
+ enableSearch: enableSearchProp,
15841
15842
  searchMode = "auto",
15842
15843
  onSearchChange,
15843
15844
  searchDebounceMs = 0,
@@ -15866,28 +15867,28 @@ var Combobox = ({
15866
15867
  const autoId = (0, import_react18.useId)();
15867
15868
  const resolvedId = id ? String(id) : `combobox-${autoId}`;
15868
15869
  const labelId = label ? `${resolvedId}-label` : void 0;
15869
- const enableSearch = options.length > 10 || searchMode === "manual" || minSearchLength > 0 || !!onSearchChange;
15870
+ const isSearchEnabled = enableSearchProp ?? (options.length > 10 || searchMode === "manual" || minSearchLength > 0 || !!onSearchChange);
15870
15871
  const trimmedQuery = query.trim();
15871
15872
  const queryMeetsMinimum = trimmedQuery.length >= minSearchLength;
15872
- const shouldPromptForSearch = minSearchLength > 0 && !queryMeetsMinimum && (searchMode === "manual" || showSearchPromptWhenEmptyQuery);
15873
+ const shouldPromptForSearch = isSearchEnabled && minSearchLength > 0 && !queryMeetsMinimum && (searchMode === "manual" || showSearchPromptWhenEmptyQuery);
15873
15874
  const filteredOptions = React31.useMemo(
15874
15875
  () => {
15875
15876
  if (shouldPromptForSearch) return [];
15876
- if (!enableSearch || searchMode === "manual") return options;
15877
+ if (!isSearchEnabled || searchMode === "manual") return options;
15877
15878
  const normalizedQuery = trimmedQuery.toLowerCase();
15878
15879
  if (!normalizedQuery) return options;
15879
15880
  return options.filter((o) => getOptionLabel(o).toLowerCase().includes(normalizedQuery));
15880
15881
  },
15881
- [enableSearch, options, searchMode, shouldPromptForSearch, trimmedQuery]
15882
+ [isSearchEnabled, options, searchMode, shouldPromptForSearch, trimmedQuery]
15882
15883
  );
15883
15884
  const renderLimitedOptions = React31.useMemo(
15884
15885
  () => {
15885
- if (trimmedQuery || maxInitialOptions === void 0 || maxInitialOptions < 1) {
15886
+ if (!isSearchEnabled || trimmedQuery || maxInitialOptions === void 0 || maxInitialOptions < 1) {
15886
15887
  return filteredOptions;
15887
15888
  }
15888
15889
  return filteredOptions.slice(0, maxInitialOptions);
15889
15890
  },
15890
- [filteredOptions, maxInitialOptions, trimmedQuery]
15891
+ [filteredOptions, isSearchEnabled, maxInitialOptions, trimmedQuery]
15891
15892
  );
15892
15893
  const canVirtualize = virtualized && !groupBy;
15893
15894
  const optionVirtualizer = (0, import_react_virtual.useVirtualizer)({
@@ -15948,7 +15949,7 @@ var Combobox = ({
15948
15949
  setQuery("");
15949
15950
  setActiveIndex(null);
15950
15951
  scrollVirtualListToStart();
15951
- } else if (enableSearch) {
15952
+ } else if (isSearchEnabled) {
15952
15953
  const triggerWindow = triggerRef.current?.ownerDocument.defaultView;
15953
15954
  if (!triggerWindow) return void 0;
15954
15955
  const timeoutId = triggerWindow.setTimeout(() => {
@@ -15957,14 +15958,14 @@ var Combobox = ({
15957
15958
  return () => triggerWindow.clearTimeout(timeoutId);
15958
15959
  }
15959
15960
  return void 0;
15960
- }, [enableSearch, open, scrollVirtualListToStart]);
15961
+ }, [isSearchEnabled, open, scrollVirtualListToStart]);
15961
15962
  React31.useEffect(() => {
15962
- if (!onSearchChange) return void 0;
15963
+ if (!isSearchEnabled || !onSearchChange) return void 0;
15963
15964
  const triggerWindow = triggerRef.current?.ownerDocument.defaultView;
15964
15965
  if (!triggerWindow) return void 0;
15965
15966
  const timeoutId = triggerWindow.setTimeout(() => onSearchChange(query), searchDebounceMs);
15966
15967
  return () => triggerWindow.clearTimeout(timeoutId);
15967
- }, [onSearchChange, query, searchDebounceMs]);
15968
+ }, [isSearchEnabled, onSearchChange, query, searchDebounceMs]);
15968
15969
  React31.useEffect(() => {
15969
15970
  if (process.env.NODE_ENV !== "production" && options.length > 300 && !virtualized && searchMode !== "manual" && maxInitialOptions === void 0) {
15970
15971
  console.warn(
@@ -16100,7 +16101,7 @@ var Combobox = ({
16100
16101
  style: usePortal ? { maxHeight: "var(--underverse-popover-available-height)" } : void 0,
16101
16102
  className: cn("w-full overflow-hidden", usePortal && "flex min-h-0 flex-col", getPanelBorderRadiusClass(resolvedBorderMode)),
16102
16103
  children: [
16103
- enableSearch && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: cn("relative shrink-0 border-b border-border/30", size === "xs" ? "p-1.5" : size === "sm" ? "p-2" : size === "lg" ? "p-3" : size === "xl" ? "p-3.5" : "p-2.5"), children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "relative", children: [
16104
+ isSearchEnabled && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: cn("relative shrink-0 border-b border-border/30", size === "xs" ? "p-1.5" : size === "sm" ? "p-2" : size === "lg" ? "p-3" : size === "xl" ? "p-3.5" : "p-2.5"), children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "relative", children: [
16104
16105
  /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
16105
16106
  import_lucide_react15.Search,
16106
16107
  {
@@ -16256,11 +16257,24 @@ var Combobox = ({
16256
16257
  "aria-invalid": !!effectiveError,
16257
16258
  id: resolvedId,
16258
16259
  "aria-labelledby": labelId,
16260
+ "aria-activedescendant": activeIndex !== null ? `${resolvedId}-item-${activeIndex}` : void 0,
16259
16261
  onKeyDown: (e) => {
16260
16262
  if (disabled) return;
16261
- if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
16263
+ if (!open && (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ")) {
16262
16264
  e.preventDefault();
16263
16265
  setOpen(true);
16266
+ if (!isSearchEnabled && e.key === "ArrowDown") moveActiveIndex(1);
16267
+ } else if (open && !isSearchEnabled && e.key === "ArrowDown") {
16268
+ e.preventDefault();
16269
+ moveActiveIndex(1);
16270
+ } else if (open && !isSearchEnabled && e.key === "ArrowUp") {
16271
+ e.preventDefault();
16272
+ moveActiveIndex(-1);
16273
+ } else if (open && !isSearchEnabled && e.key === "Enter") {
16274
+ e.preventDefault();
16275
+ if (activeIndex !== null && renderLimitedOptions[activeIndex] && !getOptionDisabled(renderLimitedOptions[activeIndex])) {
16276
+ handleSelect(renderLimitedOptions[activeIndex]);
16277
+ }
16264
16278
  } else if (e.key === "Escape") {
16265
16279
  e.preventDefault();
16266
16280
  setOpen(false);
@@ -26250,6 +26264,7 @@ var MultiCombobox = ({
26250
26264
  virtualized = false,
26251
26265
  estimatedItemHeight = 44,
26252
26266
  overscan = 8,
26267
+ enableSearch: enableSearchProp,
26253
26268
  searchMode = "auto",
26254
26269
  onSearchChange,
26255
26270
  searchDebounceMs = 0,
@@ -26282,25 +26297,25 @@ var MultiCombobox = ({
26282
26297
  ),
26283
26298
  [options]
26284
26299
  );
26285
- const enableSearch = normalizedOptions.length > 10 || searchMode === "manual" || minSearchLength > 0 || !!onSearchChange;
26300
+ const isSearchEnabled = enableSearchProp ?? (normalizedOptions.length > 10 || searchMode === "manual" || minSearchLength > 0 || !!onSearchChange);
26286
26301
  const trimmedQuery = query.trim();
26287
26302
  const queryMeetsMinimum = trimmedQuery.length >= minSearchLength;
26288
- const shouldPromptForSearch = minSearchLength > 0 && !queryMeetsMinimum && (searchMode === "manual" || showSearchPromptWhenEmptyQuery);
26303
+ const shouldPromptForSearch = isSearchEnabled && minSearchLength > 0 && !queryMeetsMinimum && (searchMode === "manual" || showSearchPromptWhenEmptyQuery);
26289
26304
  const filtered = React47.useMemo(() => {
26290
26305
  if (shouldPromptForSearch) return [];
26291
- if (!enableSearch || searchMode === "manual") return normalizedOptions;
26306
+ if (!isSearchEnabled || searchMode === "manual") return normalizedOptions;
26292
26307
  const normalizedQuery = trimmedQuery.toLowerCase();
26293
26308
  if (!normalizedQuery) return normalizedOptions;
26294
26309
  return normalizedOptions.filter(
26295
26310
  (opt) => opt.label.toLowerCase().includes(normalizedQuery) || opt.description?.toLowerCase().includes(normalizedQuery)
26296
26311
  );
26297
- }, [enableSearch, normalizedOptions, searchMode, shouldPromptForSearch, trimmedQuery]);
26312
+ }, [isSearchEnabled, normalizedOptions, searchMode, shouldPromptForSearch, trimmedQuery]);
26298
26313
  const renderLimitedOptions = React47.useMemo(() => {
26299
- if (trimmedQuery || maxInitialOptions === void 0 || maxInitialOptions < 1) {
26314
+ if (!isSearchEnabled || trimmedQuery || maxInitialOptions === void 0 || maxInitialOptions < 1) {
26300
26315
  return filtered;
26301
26316
  }
26302
26317
  return filtered.slice(0, maxInitialOptions);
26303
- }, [filtered, maxInitialOptions, trimmedQuery]);
26318
+ }, [filtered, isSearchEnabled, maxInitialOptions, trimmedQuery]);
26304
26319
  const canVirtualize = virtualized && !groupBy;
26305
26320
  const optionVirtualizer = (0, import_react_virtual2.useVirtualizer)({
26306
26321
  count: canVirtualize ? renderLimitedOptions.length : 0,
@@ -26344,24 +26359,32 @@ var MultiCombobox = ({
26344
26359
  onChange(value.filter((v) => v !== val));
26345
26360
  };
26346
26361
  const handleKeyDown2 = (e) => {
26347
- if (!open) setOpen(true);
26348
26362
  if (e.key === "ArrowDown") {
26349
26363
  e.preventDefault();
26364
+ if (!open) setOpen(true);
26350
26365
  if (renderLimitedOptions.length === 0) return;
26351
26366
  const next = activeIndex === null ? 0 : (activeIndex + 1) % renderLimitedOptions.length;
26352
26367
  setActiveIndex(next);
26353
26368
  scrollVirtualListToIndex(next);
26354
26369
  } else if (e.key === "ArrowUp") {
26355
26370
  e.preventDefault();
26371
+ if (!open) setOpen(true);
26356
26372
  if (renderLimitedOptions.length === 0) return;
26357
26373
  const next = activeIndex === null ? renderLimitedOptions.length - 1 : (activeIndex - 1 + renderLimitedOptions.length) % renderLimitedOptions.length;
26358
26374
  setActiveIndex(next);
26359
26375
  scrollVirtualListToIndex(next);
26360
26376
  } else if (e.key === "Enter") {
26361
26377
  e.preventDefault();
26378
+ if (!open) {
26379
+ setOpen(true);
26380
+ return;
26381
+ }
26362
26382
  if (activeIndex !== null && renderLimitedOptions[activeIndex]) {
26363
26383
  toggleSelect(renderLimitedOptions[activeIndex].value);
26364
26384
  }
26385
+ } else if (e.key === " " && !open) {
26386
+ e.preventDefault();
26387
+ setOpen(true);
26365
26388
  } else if (e.key === "Escape") {
26366
26389
  e.preventDefault();
26367
26390
  setOpen(false);
@@ -26377,7 +26400,7 @@ var MultiCombobox = ({
26377
26400
  }
26378
26401
  }, [disabled, required, value.length]);
26379
26402
  React47.useEffect(() => {
26380
- if (open && enableSearch) {
26403
+ if (open && isSearchEnabled) {
26381
26404
  setTimeout(() => {
26382
26405
  inputRef.current?.focus();
26383
26406
  }, 100);
@@ -26386,12 +26409,12 @@ var MultiCombobox = ({
26386
26409
  setActiveIndex(null);
26387
26410
  scrollVirtualListToStart();
26388
26411
  }
26389
- }, [enableSearch, open, scrollVirtualListToStart]);
26412
+ }, [isSearchEnabled, open, scrollVirtualListToStart]);
26390
26413
  React47.useEffect(() => {
26391
- if (!onSearchChange) return void 0;
26414
+ if (!isSearchEnabled || !onSearchChange) return void 0;
26392
26415
  const timeoutId = window.setTimeout(() => onSearchChange(query), searchDebounceMs);
26393
26416
  return () => window.clearTimeout(timeoutId);
26394
- }, [onSearchChange, query, searchDebounceMs]);
26417
+ }, [isSearchEnabled, onSearchChange, query, searchDebounceMs]);
26395
26418
  React47.useEffect(() => {
26396
26419
  if (process.env.NODE_ENV !== "production" && normalizedOptions.length > 300 && !virtualized && searchMode !== "manual" && maxInitialOptions === void 0) {
26397
26420
  console.warn(
@@ -26466,6 +26489,10 @@ var MultiCombobox = ({
26466
26489
  return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
26467
26490
  "li",
26468
26491
  {
26492
+ id: `${resolvedId}-option-${index}`,
26493
+ role: "option",
26494
+ "aria-selected": isSelected,
26495
+ "aria-disabled": isDisabled,
26469
26496
  ref: (node) => {
26470
26497
  measureRef?.(node);
26471
26498
  listRef.current[index] = node;
@@ -26477,7 +26504,7 @@ var MultiCombobox = ({
26477
26504
  e.preventDefault();
26478
26505
  e.stopPropagation();
26479
26506
  if (!isDisabled) toggleSelect(item.value);
26480
- inputRef.current?.focus();
26507
+ (isSearchEnabled ? inputRef.current : triggerRef.current)?.focus();
26481
26508
  },
26482
26509
  children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: cn("dropdown-item", isDisabled && "opacity-50 cursor-not-allowed pointer-events-none"), children: renderOption(item, isSelected) })
26483
26510
  },
@@ -26487,6 +26514,10 @@ var MultiCombobox = ({
26487
26514
  return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
26488
26515
  "li",
26489
26516
  {
26517
+ id: `${resolvedId}-option-${index}`,
26518
+ role: "option",
26519
+ "aria-selected": isSelected,
26520
+ "aria-disabled": isDisabled,
26490
26521
  ref: (node) => {
26491
26522
  measureRef?.(node);
26492
26523
  listRef.current[index] = node;
@@ -26498,7 +26529,7 @@ var MultiCombobox = ({
26498
26529
  e.preventDefault();
26499
26530
  e.stopPropagation();
26500
26531
  if (!isDisabled) toggleSelect(item.value);
26501
- inputRef.current?.focus();
26532
+ (isSearchEnabled ? inputRef.current : triggerRef.current)?.focus();
26502
26533
  },
26503
26534
  children: /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(
26504
26535
  "div",
@@ -26554,7 +26585,7 @@ var MultiCombobox = ({
26554
26585
  }
26555
26586
  )
26556
26587
  ] }),
26557
- enableSearch && /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "relative border-b border-border/40", children: [
26588
+ isSearchEnabled && /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "relative border-b border-border/40", children: [
26558
26589
  /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_lucide_react27.Search, { className: cn("absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", sizeStyles8[size].icon) }),
26559
26590
  /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
26560
26591
  "input",
@@ -26666,6 +26697,8 @@ var MultiCombobox = ({
26666
26697
  "aria-controls": listboxId,
26667
26698
  "aria-required": required,
26668
26699
  "aria-invalid": !!effectiveError,
26700
+ "aria-activedescendant": activeIndex !== null ? `${resolvedId}-option-${activeIndex}` : void 0,
26701
+ onKeyDown: !isSearchEnabled ? handleKeyDown2 : void 0,
26669
26702
  className: cn(
26670
26703
  "group flex w-full items-center gap-2 transition-all duration-200",
26671
26704
  formControlFixedClass,
@@ -26735,7 +26768,13 @@ var MultiCombobox = ({
26735
26768
  e.stopPropagation();
26736
26769
  handleClearAll();
26737
26770
  },
26738
- onKeyDown: (e) => (e.key === "Enter" || e.key === " ") && handleClearAll(),
26771
+ onKeyDown: (e) => {
26772
+ if (e.key === "Enter" || e.key === " ") {
26773
+ e.preventDefault();
26774
+ e.stopPropagation();
26775
+ handleClearAll();
26776
+ }
26777
+ },
26739
26778
  className: "opacity-0 group-hover:opacity-100 transition-all duration-200 p-1 rounded-lg hover:bg-destructive/10 text-muted-foreground hover:text-destructive",
26740
26779
  children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_lucide_react27.X, { className: "h-3.5 w-3.5" })
26741
26780
  }
@@ -28381,7 +28420,7 @@ function CategoryTreeSelect(props) {
28381
28420
  const trimmedQuery = (0, import_react26.useMemo)(() => query.trim(), [query]);
28382
28421
  const normalizedQuery = (0, import_react26.useMemo)(() => trimmedQuery.toLowerCase(), [trimmedQuery]);
28383
28422
  const queryMeetsMinimum = trimmedQuery.length >= minSearchLength;
28384
- const shouldPromptForSearch = minSearchLength > 0 && !queryMeetsMinimum && (searchMode === "manual" || showSearchPromptWhenEmptyQuery);
28423
+ const shouldPromptForSearch = isSearchEnabled && minSearchLength > 0 && !queryMeetsMinimum && (searchMode === "manual" || showSearchPromptWhenEmptyQuery);
28385
28424
  const isSearchMode = isSearchEnabled && normalizedQuery.length > 0;
28386
28425
  const shouldAutoExpandSearchResults = searchMode === "auto" && isSearchMode;
28387
28426
  const effectiveExpandedNodes = (0, import_react26.useMemo)(() => getExpandedNodesState(expandedIds, expandedNodes), [expandedIds, expandedNodes]);
@@ -28425,12 +28464,12 @@ function CategoryTreeSelect(props) {
28425
28464
  return visible;
28426
28465
  }, [byId, categories, childrenMap, isSearchMode, normalizedQuery, searchMode, shouldPromptForSearch]);
28427
28466
  (0, import_react26.useEffect)(() => {
28428
- if (!onSearchChange) return;
28467
+ if (!isSearchEnabled || !onSearchChange) return;
28429
28468
  const timeoutId = window.setTimeout(() => {
28430
28469
  onSearchChange(trimmedQuery);
28431
28470
  }, Math.max(0, searchDebounceMs));
28432
28471
  return () => window.clearTimeout(timeoutId);
28433
- }, [onSearchChange, searchDebounceMs, trimmedQuery]);
28472
+ }, [isSearchEnabled, onSearchChange, searchDebounceMs, trimmedQuery]);
28434
28473
  (0, import_react26.useEffect)(() => {
28435
28474
  if (!isOpen) return;
28436
28475
  if (!isSearchEnabled) return;
@@ -28513,7 +28552,7 @@ function CategoryTreeSelect(props) {
28513
28552
  const flattenedRows = (0, import_react26.useMemo)(() => {
28514
28553
  if (shouldPromptForSearch) return [];
28515
28554
  const rows = flattenVisibleCategories(effectiveParentCategories, effectiveChildrenMap, effectiveExpandedNodes, shouldAutoExpandSearchResults);
28516
- if (trimmedQuery || maxInitialOptions === void 0 || maxInitialOptions < 1) {
28555
+ if (!isSearchEnabled || trimmedQuery || maxInitialOptions === void 0 || maxInitialOptions < 1) {
28517
28556
  return rows;
28518
28557
  }
28519
28558
  const limitedRows = rows.slice(0, maxInitialOptions);
@@ -28541,6 +28580,7 @@ function CategoryTreeSelect(props) {
28541
28580
  effectiveExpandedNodes,
28542
28581
  effectiveParentCategories,
28543
28582
  expandToId,
28583
+ isSearchEnabled,
28544
28584
  maxInitialOptions,
28545
28585
  selectedIds,
28546
28586
  shouldAutoExpandSearchResults,