@underverse-ui/underverse 1.0.107 → 1.0.110
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api-reference.json +1 -1
- package/dist/index.cjs +343 -94
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -2
- package/dist/index.d.ts +42 -2
- package/dist/index.js +369 -120
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -7158,6 +7158,7 @@ import { ChevronLeft, ChevronRight as ChevronRight2, ChevronsLeft, ChevronsRight
|
|
|
7158
7158
|
// src/components/Combobox.tsx
|
|
7159
7159
|
import * as React24 from "react";
|
|
7160
7160
|
import { useId as useId6 } from "react";
|
|
7161
|
+
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
7161
7162
|
import { ChevronDown, Search as Search4, SearchX, Check as Check3, X as X9 } from "lucide-react";
|
|
7162
7163
|
import { Fragment as Fragment5, jsx as jsx29, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
7163
7164
|
var getOptionLabel = (option) => {
|
|
@@ -7178,6 +7179,20 @@ var getOptionDisabled = (option) => {
|
|
|
7178
7179
|
var findOptionByValue = (options, value) => {
|
|
7179
7180
|
return options.find((opt) => getOptionValue(opt) === value);
|
|
7180
7181
|
};
|
|
7182
|
+
var comboboxScrollClassName = [
|
|
7183
|
+
"scrollbar-thin",
|
|
7184
|
+
"[scrollbar-width:thin]",
|
|
7185
|
+
"[scrollbar-color:color-mix(in_oklch,var(--muted-foreground)_28%,transparent)_transparent]",
|
|
7186
|
+
"[&::-webkit-scrollbar]:w-2",
|
|
7187
|
+
"[&::-webkit-scrollbar-track]:bg-transparent",
|
|
7188
|
+
"[&::-webkit-scrollbar-thumb]:rounded-full",
|
|
7189
|
+
"[&::-webkit-scrollbar-thumb]:border-2",
|
|
7190
|
+
"[&::-webkit-scrollbar-thumb]:border-solid",
|
|
7191
|
+
"[&::-webkit-scrollbar-thumb]:border-transparent",
|
|
7192
|
+
"[&::-webkit-scrollbar-thumb]:bg-clip-padding",
|
|
7193
|
+
"[&::-webkit-scrollbar-thumb]:bg-muted-foreground/25",
|
|
7194
|
+
"[&::-webkit-scrollbar-thumb:hover]:bg-muted-foreground/45"
|
|
7195
|
+
].join(" ");
|
|
7181
7196
|
var Combobox = ({
|
|
7182
7197
|
id,
|
|
7183
7198
|
options,
|
|
@@ -7203,9 +7218,19 @@ var Combobox = ({
|
|
|
7203
7218
|
groupBy,
|
|
7204
7219
|
renderOption,
|
|
7205
7220
|
renderValue,
|
|
7221
|
+
selectedOption: selectedOptionProp,
|
|
7206
7222
|
error,
|
|
7207
7223
|
helperText,
|
|
7208
|
-
useOverlayScrollbar =
|
|
7224
|
+
useOverlayScrollbar = true,
|
|
7225
|
+
virtualized = false,
|
|
7226
|
+
estimatedItemHeight = 44,
|
|
7227
|
+
overscan = 8,
|
|
7228
|
+
searchMode = "auto",
|
|
7229
|
+
onSearchChange,
|
|
7230
|
+
searchDebounceMs = 0,
|
|
7231
|
+
minSearchLength = 0,
|
|
7232
|
+
maxInitialOptions,
|
|
7233
|
+
showSearchPromptWhenEmptyQuery = false
|
|
7209
7234
|
}) => {
|
|
7210
7235
|
const tv = useSmartTranslations("ValidationInput");
|
|
7211
7236
|
const [open, setOpen] = React24.useState(false);
|
|
@@ -7215,16 +7240,57 @@ var Combobox = ({
|
|
|
7215
7240
|
useShadCNAnimations();
|
|
7216
7241
|
const inputRef = React24.useRef(null);
|
|
7217
7242
|
const optionsViewportRef = React24.useRef(null);
|
|
7218
|
-
useOverlayScrollbarTarget(optionsViewportRef, { enabled: useOverlayScrollbar });
|
|
7243
|
+
useOverlayScrollbarTarget(optionsViewportRef, { enabled: useOverlayScrollbar && !virtualized });
|
|
7219
7244
|
const autoId = useId6();
|
|
7220
7245
|
const resolvedId = id ? String(id) : `combobox-${autoId}`;
|
|
7221
7246
|
const labelId = label ? `${resolvedId}-label` : void 0;
|
|
7222
|
-
const enableSearch = options.length > 10;
|
|
7247
|
+
const enableSearch = options.length > 10 || searchMode === "manual" || minSearchLength > 0 || !!onSearchChange;
|
|
7248
|
+
const trimmedQuery = query.trim();
|
|
7249
|
+
const queryMeetsMinimum = trimmedQuery.length >= minSearchLength;
|
|
7250
|
+
const shouldPromptForSearch = minSearchLength > 0 && !queryMeetsMinimum && (searchMode === "manual" || showSearchPromptWhenEmptyQuery);
|
|
7223
7251
|
const filteredOptions = React24.useMemo(
|
|
7224
|
-
() =>
|
|
7225
|
-
|
|
7252
|
+
() => {
|
|
7253
|
+
if (shouldPromptForSearch) return [];
|
|
7254
|
+
if (!enableSearch || searchMode === "manual") return options;
|
|
7255
|
+
const normalizedQuery = trimmedQuery.toLowerCase();
|
|
7256
|
+
if (!normalizedQuery) return options;
|
|
7257
|
+
return options.filter((o) => getOptionLabel(o).toLowerCase().includes(normalizedQuery));
|
|
7258
|
+
},
|
|
7259
|
+
[enableSearch, options, searchMode, shouldPromptForSearch, trimmedQuery]
|
|
7226
7260
|
);
|
|
7261
|
+
const renderLimitedOptions = React24.useMemo(
|
|
7262
|
+
() => {
|
|
7263
|
+
if (trimmedQuery || maxInitialOptions === void 0 || maxInitialOptions < 1) {
|
|
7264
|
+
return filteredOptions;
|
|
7265
|
+
}
|
|
7266
|
+
return filteredOptions.slice(0, maxInitialOptions);
|
|
7267
|
+
},
|
|
7268
|
+
[filteredOptions, maxInitialOptions, trimmedQuery]
|
|
7269
|
+
);
|
|
7270
|
+
const canVirtualize = virtualized && !groupBy;
|
|
7271
|
+
const optionVirtualizer = useVirtualizer({
|
|
7272
|
+
count: canVirtualize ? renderLimitedOptions.length : 0,
|
|
7273
|
+
getScrollElement: () => optionsViewportRef.current,
|
|
7274
|
+
estimateSize: () => estimatedItemHeight,
|
|
7275
|
+
initialRect: { width: 0, height: maxHeight },
|
|
7276
|
+
overscan,
|
|
7277
|
+
enabled: canVirtualize
|
|
7278
|
+
});
|
|
7279
|
+
const virtualItems = canVirtualize ? optionVirtualizer.getVirtualItems() : [];
|
|
7227
7280
|
const triggerRef = React24.useRef(null);
|
|
7281
|
+
const scrollVirtualListToIndex = React24.useCallback((index) => {
|
|
7282
|
+
if (!canVirtualize || renderLimitedOptions.length === 0) return;
|
|
7283
|
+
optionVirtualizer.scrollToIndex(index, { align: "auto" });
|
|
7284
|
+
}, [canVirtualize, optionVirtualizer, renderLimitedOptions.length]);
|
|
7285
|
+
const scrollVirtualListToStart = React24.useCallback(() => {
|
|
7286
|
+
scrollVirtualListToIndex(0);
|
|
7287
|
+
}, [scrollVirtualListToIndex]);
|
|
7288
|
+
const moveActiveIndex = React24.useCallback((direction) => {
|
|
7289
|
+
if (renderLimitedOptions.length === 0) return;
|
|
7290
|
+
const next = activeIndex === null ? direction === 1 ? 0 : renderLimitedOptions.length - 1 : (activeIndex + direction + renderLimitedOptions.length) % renderLimitedOptions.length;
|
|
7291
|
+
setActiveIndex(next);
|
|
7292
|
+
scrollVirtualListToIndex(next);
|
|
7293
|
+
}, [activeIndex, renderLimitedOptions.length, scrollVirtualListToIndex]);
|
|
7228
7294
|
const handleSelect = (option) => {
|
|
7229
7295
|
if (getOptionDisabled(option)) return;
|
|
7230
7296
|
const val = getOptionValue(option);
|
|
@@ -7237,6 +7303,9 @@ var Combobox = ({
|
|
|
7237
7303
|
};
|
|
7238
7304
|
const handleClear = (e) => {
|
|
7239
7305
|
e.stopPropagation();
|
|
7306
|
+
clearValue();
|
|
7307
|
+
};
|
|
7308
|
+
const clearValue = () => {
|
|
7240
7309
|
onChange(null);
|
|
7241
7310
|
setOpen(false);
|
|
7242
7311
|
};
|
|
@@ -7244,13 +7313,26 @@ var Combobox = ({
|
|
|
7244
7313
|
if (!open) {
|
|
7245
7314
|
setQuery("");
|
|
7246
7315
|
setActiveIndex(null);
|
|
7316
|
+
scrollVirtualListToStart();
|
|
7247
7317
|
} else if (enableSearch) {
|
|
7248
7318
|
setTimeout(() => {
|
|
7249
7319
|
inputRef.current?.focus();
|
|
7250
7320
|
}, 100);
|
|
7251
7321
|
}
|
|
7252
|
-
}, [open,
|
|
7253
|
-
|
|
7322
|
+
}, [enableSearch, open, scrollVirtualListToStart]);
|
|
7323
|
+
React24.useEffect(() => {
|
|
7324
|
+
if (!onSearchChange) return void 0;
|
|
7325
|
+
const timeoutId = window.setTimeout(() => onSearchChange(query), searchDebounceMs);
|
|
7326
|
+
return () => window.clearTimeout(timeoutId);
|
|
7327
|
+
}, [onSearchChange, query, searchDebounceMs]);
|
|
7328
|
+
React24.useEffect(() => {
|
|
7329
|
+
if (process.env.NODE_ENV !== "production" && options.length > 300 && !virtualized && searchMode !== "manual" && maxInitialOptions === void 0) {
|
|
7330
|
+
console.warn(
|
|
7331
|
+
'[Underverse UI] Combobox received more than 300 options without virtualization, manual search, or maxInitialOptions. Use virtualized, searchMode="manual", or maxInitialOptions to avoid rendering a large dropdown.'
|
|
7332
|
+
);
|
|
7333
|
+
}
|
|
7334
|
+
}, [maxInitialOptions, options.length, searchMode, virtualized]);
|
|
7335
|
+
const selectedOption = findOptionByValue(options, value) ?? (selectedOptionProp && getOptionValue(selectedOptionProp) === value ? selectedOptionProp : void 0);
|
|
7254
7336
|
const displayValue = selectedOption ? getOptionLabel(selectedOption) : "";
|
|
7255
7337
|
const selectedIcon = selectedOption ? getOptionIcon(selectedOption) : void 0;
|
|
7256
7338
|
const hasValue = value !== void 0 && value !== null && value !== "";
|
|
@@ -7263,13 +7345,13 @@ var Combobox = ({
|
|
|
7263
7345
|
const groupedOptions = React24.useMemo(() => {
|
|
7264
7346
|
if (!groupBy) return null;
|
|
7265
7347
|
const groups = {};
|
|
7266
|
-
|
|
7348
|
+
renderLimitedOptions.forEach((opt) => {
|
|
7267
7349
|
const group = groupBy(opt);
|
|
7268
7350
|
if (!groups[group]) groups[group] = [];
|
|
7269
7351
|
groups[group].push(opt);
|
|
7270
7352
|
});
|
|
7271
7353
|
return groups;
|
|
7272
|
-
}, [
|
|
7354
|
+
}, [renderLimitedOptions, groupBy]);
|
|
7273
7355
|
const itemSizeStyles = {
|
|
7274
7356
|
sm: "px-2.5 py-1.5 text-xs gap-2",
|
|
7275
7357
|
md: "px-3 py-2.5 text-sm gap-3",
|
|
@@ -7285,60 +7367,75 @@ var Combobox = ({
|
|
|
7285
7367
|
md: "h-4 w-4",
|
|
7286
7368
|
lg: "h-5 w-5"
|
|
7287
7369
|
};
|
|
7288
|
-
const renderOptionItem = (item, index) => {
|
|
7370
|
+
const renderOptionItem = (item, index, virtualItem) => {
|
|
7289
7371
|
const itemValue = getOptionValue(item);
|
|
7290
7372
|
const itemLabel = getOptionLabel(item);
|
|
7291
7373
|
const itemIcon = getOptionIcon(item);
|
|
7292
7374
|
const itemDescription = getOptionDescription(item);
|
|
7293
7375
|
const itemDisabled = getOptionDisabled(item);
|
|
7294
7376
|
const isSelected = itemValue === value;
|
|
7295
|
-
return /* @__PURE__ */ jsx29(
|
|
7296
|
-
"
|
|
7377
|
+
return /* @__PURE__ */ jsx29(
|
|
7378
|
+
"li",
|
|
7297
7379
|
{
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7302
|
-
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
},
|
|
7308
|
-
|
|
7309
|
-
"
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7380
|
+
ref: virtualItem ? optionVirtualizer.measureElement : void 0,
|
|
7381
|
+
"data-index": virtualItem?.index,
|
|
7382
|
+
className: "list-none",
|
|
7383
|
+
style: virtualItem ? {
|
|
7384
|
+
position: "absolute",
|
|
7385
|
+
top: 0,
|
|
7386
|
+
left: 0,
|
|
7387
|
+
width: "100%",
|
|
7388
|
+
transform: `translateY(${virtualItem.start}px)`
|
|
7389
|
+
} : void 0,
|
|
7390
|
+
children: /* @__PURE__ */ jsxs22(
|
|
7391
|
+
"button",
|
|
7392
|
+
{
|
|
7393
|
+
id: `${resolvedId}-item-${index}`,
|
|
7394
|
+
type: "button",
|
|
7395
|
+
role: "option",
|
|
7396
|
+
tabIndex: -1,
|
|
7397
|
+
disabled: itemDisabled,
|
|
7398
|
+
"aria-selected": isSelected,
|
|
7399
|
+
onClick: () => handleSelect(item),
|
|
7400
|
+
style: {
|
|
7401
|
+
animationDelay: open ? `${Math.min(index * 15, 150)}ms` : "0ms"
|
|
7402
|
+
},
|
|
7403
|
+
className: cn(
|
|
7404
|
+
"dropdown-item group flex w-full items-center rounded-full text-left",
|
|
7405
|
+
itemSizeStyles[size],
|
|
7406
|
+
"outline-none focus:outline-none focus-visible:outline-none",
|
|
7407
|
+
"transition-all duration-150",
|
|
7408
|
+
!itemDisabled && "cursor-pointer hover:bg-accent/70 hover:shadow-sm",
|
|
7409
|
+
!itemDisabled && "focus:bg-accent/80 focus:text-accent-foreground",
|
|
7410
|
+
index === activeIndex && !itemDisabled && "bg-accent/60",
|
|
7411
|
+
isSelected && "bg-primary/10 text-primary font-medium",
|
|
7412
|
+
itemDisabled && "opacity-50 cursor-not-allowed"
|
|
7413
|
+
),
|
|
7414
|
+
children: [
|
|
7415
|
+
itemIcon && /* @__PURE__ */ jsx29(
|
|
7416
|
+
"span",
|
|
7417
|
+
{
|
|
7418
|
+
className: cn("shrink-0 flex items-center justify-center", iconSizeStyles[size], isSelected ? "text-primary" : "text-muted-foreground"),
|
|
7419
|
+
children: itemIcon
|
|
7420
|
+
}
|
|
7421
|
+
),
|
|
7422
|
+
renderOption ? /* @__PURE__ */ jsx29("div", { className: "flex-1 min-w-0", children: renderOption(item, isSelected) }) : /* @__PURE__ */ jsxs22("div", { className: "flex-1 min-w-0", children: [
|
|
7423
|
+
/* @__PURE__ */ jsx29("span", { className: "block truncate", children: itemLabel }),
|
|
7424
|
+
itemDescription && /* @__PURE__ */ jsx29("span", { className: cn("block text-muted-foreground truncate mt-0.5", size === "sm" ? "text-[10px]" : "text-xs"), children: itemDescription })
|
|
7425
|
+
] }),
|
|
7426
|
+
isSelected && showSelectedIcon && /* @__PURE__ */ jsx29("span", { className: "shrink-0 ml-auto", children: /* @__PURE__ */ jsx29(Check3, { className: cn(checkIconSizeStyles[size], "text-primary") }) })
|
|
7427
|
+
]
|
|
7428
|
+
}
|
|
7429
|
+
)
|
|
7430
|
+
},
|
|
7431
|
+
`${itemValue}-${index}`
|
|
7432
|
+
);
|
|
7335
7433
|
};
|
|
7336
7434
|
const dropdownBody = /* @__PURE__ */ jsxs22(
|
|
7337
7435
|
"div",
|
|
7338
7436
|
{
|
|
7339
7437
|
"data-combobox-dropdown": true,
|
|
7340
7438
|
"data-state": open ? "open" : "closed",
|
|
7341
|
-
id: `${resolvedId}-listbox`,
|
|
7342
7439
|
className: "w-full rounded-2xl md:rounded-3xl overflow-hidden",
|
|
7343
7440
|
children: [
|
|
7344
7441
|
enableSearch && /* @__PURE__ */ jsx29("div", { className: cn("relative border-b border-border/30", size === "sm" ? "p-2" : size === "lg" ? "p-3" : "p-2.5"), children: /* @__PURE__ */ jsxs22("div", { className: "relative", children: [
|
|
@@ -7359,24 +7456,19 @@ var Combobox = ({
|
|
|
7359
7456
|
onChange: (e) => {
|
|
7360
7457
|
setQuery(e.target.value);
|
|
7361
7458
|
setActiveIndex(null);
|
|
7459
|
+
scrollVirtualListToStart();
|
|
7362
7460
|
},
|
|
7363
7461
|
onKeyDown: (e) => {
|
|
7364
7462
|
if (e.key === "ArrowDown") {
|
|
7365
7463
|
e.preventDefault();
|
|
7366
|
-
|
|
7367
|
-
const next = prev === null ? 0 : prev + 1;
|
|
7368
|
-
return next >= filteredOptions.length ? 0 : next;
|
|
7369
|
-
});
|
|
7464
|
+
moveActiveIndex(1);
|
|
7370
7465
|
} else if (e.key === "ArrowUp") {
|
|
7371
7466
|
e.preventDefault();
|
|
7372
|
-
|
|
7373
|
-
const next = prev === null ? filteredOptions.length - 1 : prev - 1;
|
|
7374
|
-
return next < 0 ? filteredOptions.length - 1 : next;
|
|
7375
|
-
});
|
|
7467
|
+
moveActiveIndex(-1);
|
|
7376
7468
|
} else if (e.key === "Enter") {
|
|
7377
7469
|
e.preventDefault();
|
|
7378
|
-
if (activeIndex !== null &&
|
|
7379
|
-
handleSelect(
|
|
7470
|
+
if (activeIndex !== null && renderLimitedOptions[activeIndex] && !getOptionDisabled(renderLimitedOptions[activeIndex])) {
|
|
7471
|
+
handleSelect(renderLimitedOptions[activeIndex]);
|
|
7380
7472
|
}
|
|
7381
7473
|
} else if (e.key === "Escape") {
|
|
7382
7474
|
e.preventDefault();
|
|
@@ -7399,7 +7491,10 @@ var Combobox = ({
|
|
|
7399
7491
|
"button",
|
|
7400
7492
|
{
|
|
7401
7493
|
type: "button",
|
|
7402
|
-
onClick: () =>
|
|
7494
|
+
onClick: () => {
|
|
7495
|
+
setQuery("");
|
|
7496
|
+
scrollVirtualListToStart();
|
|
7497
|
+
},
|
|
7403
7498
|
className: "absolute right-3 top-1/2 -translate-y-1/2 p-0.5 rounded-md hover:bg-muted text-muted-foreground hover:text-foreground transition-colors",
|
|
7404
7499
|
children: /* @__PURE__ */ jsx29(X9, { className: cn(size === "sm" ? "h-3 w-3" : size === "lg" ? "h-4 w-4" : "h-3.5 w-3.5") })
|
|
7405
7500
|
}
|
|
@@ -7409,14 +7504,22 @@ var Combobox = ({
|
|
|
7409
7504
|
"div",
|
|
7410
7505
|
{
|
|
7411
7506
|
ref: optionsViewportRef,
|
|
7507
|
+
id: `${resolvedId}-listbox`,
|
|
7412
7508
|
role: "listbox",
|
|
7413
7509
|
"aria-labelledby": labelId,
|
|
7414
|
-
className: "overflow-y-auto overscroll-contain",
|
|
7510
|
+
className: cn("overflow-y-auto overscroll-contain", (!useOverlayScrollbar || virtualized) && comboboxScrollClassName),
|
|
7415
7511
|
style: { maxHeight },
|
|
7416
7512
|
children: /* @__PURE__ */ jsx29("div", { className: cn(size === "sm" ? "p-1" : size === "lg" ? "p-2" : "p-1.5"), children: loading2 ? /* @__PURE__ */ jsx29("div", { className: "px-3 py-10 text-center", children: /* @__PURE__ */ jsxs22("div", { className: "flex flex-col items-center gap-3 animate-in fade-in-0 zoom-in-95 duration-300", children: [
|
|
7417
7513
|
/* @__PURE__ */ jsx29("div", { className: "relative", children: /* @__PURE__ */ jsx29("div", { className: "w-10 h-10 rounded-full border-2 border-primary/20 border-t-primary animate-spin" }) }),
|
|
7418
7514
|
/* @__PURE__ */ jsx29("span", { className: "text-sm text-muted-foreground", children: loadingText })
|
|
7419
|
-
] }) }) :
|
|
7515
|
+
] }) }) : shouldPromptForSearch ? /* @__PURE__ */ jsx29("div", { className: "px-3 py-10 text-center", children: /* @__PURE__ */ jsxs22("div", { className: "flex flex-col items-center gap-3 animate-in fade-in-0 zoom-in-95 duration-300", children: [
|
|
7516
|
+
/* @__PURE__ */ jsx29("div", { className: "w-12 h-12 rounded-full bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsx29(Search4, { className: "h-6 w-6 text-muted-foreground/60" }) }),
|
|
7517
|
+
/* @__PURE__ */ jsx29("div", { className: "space-y-1", children: /* @__PURE__ */ jsxs22("span", { className: "block text-sm font-medium text-foreground", children: [
|
|
7518
|
+
"Type at least ",
|
|
7519
|
+
minSearchLength,
|
|
7520
|
+
" characters to search"
|
|
7521
|
+
] }) })
|
|
7522
|
+
] }) }) : renderLimitedOptions.length > 0 ? groupedOptions ? (
|
|
7420
7523
|
// Render grouped options with global index tracking
|
|
7421
7524
|
(() => {
|
|
7422
7525
|
let globalIndex = 0;
|
|
@@ -7430,7 +7533,14 @@ var Combobox = ({
|
|
|
7430
7533
|
})()
|
|
7431
7534
|
) : (
|
|
7432
7535
|
// Render flat options
|
|
7433
|
-
/* @__PURE__ */ jsx29(
|
|
7536
|
+
/* @__PURE__ */ jsx29(
|
|
7537
|
+
"ul",
|
|
7538
|
+
{
|
|
7539
|
+
className: "space-y-0.5",
|
|
7540
|
+
style: canVirtualize ? { height: `${optionVirtualizer.getTotalSize()}px`, position: "relative" } : void 0,
|
|
7541
|
+
children: canVirtualize ? virtualItems.map((virtualItem) => renderOptionItem(renderLimitedOptions[virtualItem.index], virtualItem.index, virtualItem)) : renderLimitedOptions.map((item, index) => renderOptionItem(item, index))
|
|
7542
|
+
}
|
|
7543
|
+
)
|
|
7434
7544
|
) : /* @__PURE__ */ jsx29("div", { className: "px-3 py-10 text-center", children: /* @__PURE__ */ jsxs22("div", { className: "flex flex-col items-center gap-3 animate-in fade-in-0 zoom-in-95 duration-300", children: [
|
|
7435
7545
|
/* @__PURE__ */ jsx29("div", { className: "w-12 h-12 rounded-full bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsx29(SearchX, { className: "h-6 w-6 text-muted-foreground/60" }) }),
|
|
7436
7546
|
/* @__PURE__ */ jsxs22("div", { className: "space-y-1", children: [
|
|
@@ -7441,7 +7551,10 @@ var Combobox = ({
|
|
|
7441
7551
|
"button",
|
|
7442
7552
|
{
|
|
7443
7553
|
type: "button",
|
|
7444
|
-
onClick: () =>
|
|
7554
|
+
onClick: () => {
|
|
7555
|
+
setQuery("");
|
|
7556
|
+
scrollVirtualListToStart();
|
|
7557
|
+
},
|
|
7445
7558
|
className: "px-3 py-1.5 text-xs font-medium text-primary bg-primary/10 rounded-full hover:bg-primary/20 transition-colors",
|
|
7446
7559
|
children: "Clear search"
|
|
7447
7560
|
}
|
|
@@ -7511,7 +7624,13 @@ var Combobox = ({
|
|
|
7511
7624
|
tabIndex: 0,
|
|
7512
7625
|
"aria-label": "Clear selection",
|
|
7513
7626
|
onClick: handleClear,
|
|
7514
|
-
onKeyDown: (e) =>
|
|
7627
|
+
onKeyDown: (e) => {
|
|
7628
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
7629
|
+
e.preventDefault();
|
|
7630
|
+
e.stopPropagation();
|
|
7631
|
+
clearValue();
|
|
7632
|
+
}
|
|
7633
|
+
},
|
|
7515
7634
|
className: cn(
|
|
7516
7635
|
"opacity-0 group-hover:opacity-100 transition-all duration-200",
|
|
7517
7636
|
"p-1 rounded-lg hover:bg-destructive/10 text-muted-foreground hover:text-destructive"
|
|
@@ -8287,7 +8406,7 @@ function formatDateSmart(date, locale = "en") {
|
|
|
8287
8406
|
}
|
|
8288
8407
|
|
|
8289
8408
|
// src/components/DatePicker.tsx
|
|
8290
|
-
import { Calendar, ChevronLeft as ChevronLeft2, ChevronRight as ChevronRight3, Sparkles
|
|
8409
|
+
import { Calendar, ChevronLeft as ChevronLeft2, ChevronRight as ChevronRight3, Sparkles, X as XIcon } from "lucide-react";
|
|
8291
8410
|
import * as React28 from "react";
|
|
8292
8411
|
import { useId as useId7 } from "react";
|
|
8293
8412
|
import { Fragment as Fragment6, jsx as jsx34, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
@@ -8666,7 +8785,7 @@ var DatePicker = ({
|
|
|
8666
8785
|
isDateDisabled(/* @__PURE__ */ new Date()) && "opacity-50 cursor-not-allowed hover:scale-100 active:scale-100"
|
|
8667
8786
|
),
|
|
8668
8787
|
children: [
|
|
8669
|
-
/* @__PURE__ */ jsx34(
|
|
8788
|
+
/* @__PURE__ */ jsx34(Sparkles, { className: sizeStyles8[size].actionIcon }),
|
|
8670
8789
|
todayLabel || t("today")
|
|
8671
8790
|
]
|
|
8672
8791
|
}
|
|
@@ -9246,7 +9365,7 @@ var DateRangePicker = ({
|
|
|
9246
9365
|
isTodayUnavailable && "opacity-50 cursor-not-allowed hover:scale-100 active:scale-100"
|
|
9247
9366
|
),
|
|
9248
9367
|
children: [
|
|
9249
|
-
/* @__PURE__ */ jsx34(
|
|
9368
|
+
/* @__PURE__ */ jsx34(Sparkles, { className: sizeStyles8[size].actionIcon }),
|
|
9250
9369
|
t("today")
|
|
9251
9370
|
]
|
|
9252
9371
|
}
|
|
@@ -15391,8 +15510,23 @@ function CalendarTimeline({
|
|
|
15391
15510
|
// src/components/MultiCombobox.tsx
|
|
15392
15511
|
import * as React39 from "react";
|
|
15393
15512
|
import { useId as useId9 } from "react";
|
|
15394
|
-
import {
|
|
15513
|
+
import { useVirtualizer as useVirtualizer2 } from "@tanstack/react-virtual";
|
|
15514
|
+
import { ChevronDown as ChevronDown4, Search as Search5, Check as Check6, SearchX as SearchX2, Loader2 as Loader23, X as X13, Sparkles as Sparkles2 } from "lucide-react";
|
|
15395
15515
|
import { Fragment as Fragment13, jsx as jsx45, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
15516
|
+
var comboboxScrollClassName2 = [
|
|
15517
|
+
"scrollbar-thin",
|
|
15518
|
+
"[scrollbar-width:thin]",
|
|
15519
|
+
"[scrollbar-color:color-mix(in_oklch,var(--muted-foreground)_28%,transparent)_transparent]",
|
|
15520
|
+
"[&::-webkit-scrollbar]:w-2",
|
|
15521
|
+
"[&::-webkit-scrollbar-track]:bg-transparent",
|
|
15522
|
+
"[&::-webkit-scrollbar-thumb]:rounded-full",
|
|
15523
|
+
"[&::-webkit-scrollbar-thumb]:border-2",
|
|
15524
|
+
"[&::-webkit-scrollbar-thumb]:border-solid",
|
|
15525
|
+
"[&::-webkit-scrollbar-thumb]:border-transparent",
|
|
15526
|
+
"[&::-webkit-scrollbar-thumb]:bg-clip-padding",
|
|
15527
|
+
"[&::-webkit-scrollbar-thumb]:bg-muted-foreground/25",
|
|
15528
|
+
"[&::-webkit-scrollbar-thumb:hover]:bg-muted-foreground/45"
|
|
15529
|
+
].join(" ");
|
|
15396
15530
|
var MultiCombobox = ({
|
|
15397
15531
|
id,
|
|
15398
15532
|
options,
|
|
@@ -15421,10 +15555,20 @@ var MultiCombobox = ({
|
|
|
15421
15555
|
groupBy,
|
|
15422
15556
|
renderOption,
|
|
15423
15557
|
renderTag,
|
|
15558
|
+
selectedOptions: selectedOptionsProp,
|
|
15424
15559
|
error,
|
|
15425
15560
|
helperText,
|
|
15426
15561
|
maxTagsVisible = 3,
|
|
15427
|
-
useOverlayScrollbar =
|
|
15562
|
+
useOverlayScrollbar = true,
|
|
15563
|
+
virtualized = false,
|
|
15564
|
+
estimatedItemHeight = 44,
|
|
15565
|
+
overscan = 8,
|
|
15566
|
+
searchMode = "auto",
|
|
15567
|
+
onSearchChange,
|
|
15568
|
+
searchDebounceMs = 0,
|
|
15569
|
+
minSearchLength = 0,
|
|
15570
|
+
maxInitialOptions,
|
|
15571
|
+
showSearchPromptWhenEmptyQuery = false
|
|
15428
15572
|
}) => {
|
|
15429
15573
|
const tv = useSmartTranslations("ValidationInput");
|
|
15430
15574
|
const [query, setQuery] = React39.useState("");
|
|
@@ -15434,7 +15578,7 @@ var MultiCombobox = ({
|
|
|
15434
15578
|
const inputRef = React39.useRef(null);
|
|
15435
15579
|
const listRef = React39.useRef([]);
|
|
15436
15580
|
const optionsListRef = React39.useRef(null);
|
|
15437
|
-
useOverlayScrollbarTarget(optionsListRef, { enabled: useOverlayScrollbar });
|
|
15581
|
+
useOverlayScrollbarTarget(optionsListRef, { enabled: useOverlayScrollbar && !virtualized });
|
|
15438
15582
|
const triggerRef = React39.useRef(null);
|
|
15439
15583
|
useShadCNAnimations();
|
|
15440
15584
|
const normalizedOptions = React39.useMemo(
|
|
@@ -15443,23 +15587,52 @@ var MultiCombobox = ({
|
|
|
15443
15587
|
),
|
|
15444
15588
|
[options]
|
|
15445
15589
|
);
|
|
15446
|
-
const enableSearch = normalizedOptions.length > 10;
|
|
15447
|
-
const
|
|
15448
|
-
|
|
15449
|
-
|
|
15450
|
-
|
|
15451
|
-
|
|
15452
|
-
|
|
15590
|
+
const enableSearch = normalizedOptions.length > 10 || searchMode === "manual" || minSearchLength > 0 || !!onSearchChange;
|
|
15591
|
+
const trimmedQuery = query.trim();
|
|
15592
|
+
const queryMeetsMinimum = trimmedQuery.length >= minSearchLength;
|
|
15593
|
+
const shouldPromptForSearch = minSearchLength > 0 && !queryMeetsMinimum && (searchMode === "manual" || showSearchPromptWhenEmptyQuery);
|
|
15594
|
+
const filtered = React39.useMemo(() => {
|
|
15595
|
+
if (shouldPromptForSearch) return [];
|
|
15596
|
+
if (!enableSearch || searchMode === "manual") return normalizedOptions;
|
|
15597
|
+
const normalizedQuery = trimmedQuery.toLowerCase();
|
|
15598
|
+
if (!normalizedQuery) return normalizedOptions;
|
|
15599
|
+
return normalizedOptions.filter(
|
|
15600
|
+
(opt) => opt.label.toLowerCase().includes(normalizedQuery) || opt.description?.toLowerCase().includes(normalizedQuery)
|
|
15601
|
+
);
|
|
15602
|
+
}, [enableSearch, normalizedOptions, searchMode, shouldPromptForSearch, trimmedQuery]);
|
|
15603
|
+
const renderLimitedOptions = React39.useMemo(() => {
|
|
15604
|
+
if (trimmedQuery || maxInitialOptions === void 0 || maxInitialOptions < 1) {
|
|
15605
|
+
return filtered;
|
|
15606
|
+
}
|
|
15607
|
+
return filtered.slice(0, maxInitialOptions);
|
|
15608
|
+
}, [filtered, maxInitialOptions, trimmedQuery]);
|
|
15609
|
+
const canVirtualize = virtualized && !groupBy;
|
|
15610
|
+
const optionVirtualizer = useVirtualizer2({
|
|
15611
|
+
count: canVirtualize ? renderLimitedOptions.length : 0,
|
|
15612
|
+
getScrollElement: () => optionsListRef.current,
|
|
15613
|
+
estimateSize: () => estimatedItemHeight,
|
|
15614
|
+
initialRect: { width: 0, height: maxHeight },
|
|
15615
|
+
overscan,
|
|
15616
|
+
enabled: canVirtualize
|
|
15617
|
+
});
|
|
15618
|
+
const virtualItems = canVirtualize ? optionVirtualizer.getVirtualItems() : [];
|
|
15619
|
+
const scrollVirtualListToIndex = React39.useCallback((index) => {
|
|
15620
|
+
if (!canVirtualize || renderLimitedOptions.length === 0) return;
|
|
15621
|
+
optionVirtualizer.scrollToIndex(index, { align: "auto" });
|
|
15622
|
+
}, [canVirtualize, optionVirtualizer, renderLimitedOptions.length]);
|
|
15623
|
+
const scrollVirtualListToStart = React39.useCallback(() => {
|
|
15624
|
+
scrollVirtualListToIndex(0);
|
|
15625
|
+
}, [scrollVirtualListToIndex]);
|
|
15453
15626
|
const groupedOptions = React39.useMemo(() => {
|
|
15454
15627
|
if (!groupBy) return null;
|
|
15455
15628
|
const groups = /* @__PURE__ */ new Map();
|
|
15456
|
-
|
|
15629
|
+
renderLimitedOptions.forEach((opt) => {
|
|
15457
15630
|
const group = groupBy(opt);
|
|
15458
15631
|
if (!groups.has(group)) groups.set(group, []);
|
|
15459
15632
|
groups.get(group).push(opt);
|
|
15460
15633
|
});
|
|
15461
15634
|
return groups;
|
|
15462
|
-
}, [
|
|
15635
|
+
}, [renderLimitedOptions, groupBy]);
|
|
15463
15636
|
const toggleSelect = (optionValue) => {
|
|
15464
15637
|
const option = normalizedOptions.find((o) => o.value === optionValue);
|
|
15465
15638
|
if (option?.disabled || disabledOptions.includes(optionValue)) return;
|
|
@@ -15477,11 +15650,26 @@ var MultiCombobox = ({
|
|
|
15477
15650
|
};
|
|
15478
15651
|
const handleKeyDown2 = (e) => {
|
|
15479
15652
|
if (!open) setOpen(true);
|
|
15480
|
-
if (e.key === "
|
|
15653
|
+
if (e.key === "ArrowDown") {
|
|
15654
|
+
e.preventDefault();
|
|
15655
|
+
if (renderLimitedOptions.length === 0) return;
|
|
15656
|
+
const next = activeIndex === null ? 0 : (activeIndex + 1) % renderLimitedOptions.length;
|
|
15657
|
+
setActiveIndex(next);
|
|
15658
|
+
scrollVirtualListToIndex(next);
|
|
15659
|
+
} else if (e.key === "ArrowUp") {
|
|
15481
15660
|
e.preventDefault();
|
|
15482
|
-
if (
|
|
15483
|
-
|
|
15661
|
+
if (renderLimitedOptions.length === 0) return;
|
|
15662
|
+
const next = activeIndex === null ? renderLimitedOptions.length - 1 : (activeIndex - 1 + renderLimitedOptions.length) % renderLimitedOptions.length;
|
|
15663
|
+
setActiveIndex(next);
|
|
15664
|
+
scrollVirtualListToIndex(next);
|
|
15665
|
+
} else if (e.key === "Enter") {
|
|
15666
|
+
e.preventDefault();
|
|
15667
|
+
if (activeIndex !== null && renderLimitedOptions[activeIndex]) {
|
|
15668
|
+
toggleSelect(renderLimitedOptions[activeIndex].value);
|
|
15484
15669
|
}
|
|
15670
|
+
} else if (e.key === "Escape") {
|
|
15671
|
+
e.preventDefault();
|
|
15672
|
+
setOpen(false);
|
|
15485
15673
|
}
|
|
15486
15674
|
};
|
|
15487
15675
|
const handleClearAll = () => {
|
|
@@ -15498,8 +15686,24 @@ var MultiCombobox = ({
|
|
|
15498
15686
|
setTimeout(() => {
|
|
15499
15687
|
inputRef.current?.focus();
|
|
15500
15688
|
}, 100);
|
|
15689
|
+
} else if (!open) {
|
|
15690
|
+
setQuery("");
|
|
15691
|
+
setActiveIndex(null);
|
|
15692
|
+
scrollVirtualListToStart();
|
|
15693
|
+
}
|
|
15694
|
+
}, [enableSearch, open, scrollVirtualListToStart]);
|
|
15695
|
+
React39.useEffect(() => {
|
|
15696
|
+
if (!onSearchChange) return void 0;
|
|
15697
|
+
const timeoutId = window.setTimeout(() => onSearchChange(query), searchDebounceMs);
|
|
15698
|
+
return () => window.clearTimeout(timeoutId);
|
|
15699
|
+
}, [onSearchChange, query, searchDebounceMs]);
|
|
15700
|
+
React39.useEffect(() => {
|
|
15701
|
+
if (process.env.NODE_ENV !== "production" && normalizedOptions.length > 300 && !virtualized && searchMode !== "manual" && maxInitialOptions === void 0) {
|
|
15702
|
+
console.warn(
|
|
15703
|
+
'[Underverse UI] MultiCombobox received more than 300 options without virtualization, manual search, or maxInitialOptions. Use virtualized, searchMode="manual", or maxInitialOptions to avoid rendering a large dropdown.'
|
|
15704
|
+
);
|
|
15501
15705
|
}
|
|
15502
|
-
}, [
|
|
15706
|
+
}, [maxInitialOptions, normalizedOptions.length, searchMode, virtualized]);
|
|
15503
15707
|
const sizeStyles8 = {
|
|
15504
15708
|
sm: {
|
|
15505
15709
|
trigger: "h-8 px-3 py-1.5 text-sm md:h-7 md:text-xs",
|
|
@@ -15533,25 +15737,38 @@ var MultiCombobox = ({
|
|
|
15533
15737
|
const labelId = label ? `${resolvedId}-label` : void 0;
|
|
15534
15738
|
const labelSize = size === "sm" ? "text-xs" : size === "lg" ? "text-base" : "text-sm";
|
|
15535
15739
|
const listboxId = `${resolvedId}-listbox`;
|
|
15536
|
-
const renderOptionItem = (item, index) => {
|
|
15740
|
+
const renderOptionItem = (item, index, virtualItem) => {
|
|
15537
15741
|
const isSelected = value.includes(item.value);
|
|
15538
15742
|
const isDisabled = item.disabled || disabledOptions.includes(item.value);
|
|
15539
15743
|
const optionIcon = item.icon;
|
|
15540
15744
|
const optionDesc = item.description;
|
|
15745
|
+
const itemStyle = {
|
|
15746
|
+
animationDelay: open ? `${Math.min(index * 20, 200)}ms` : "0ms",
|
|
15747
|
+
...virtualItem ? {
|
|
15748
|
+
position: "absolute",
|
|
15749
|
+
top: 0,
|
|
15750
|
+
left: 0,
|
|
15751
|
+
width: "100%",
|
|
15752
|
+
transform: `translateY(${virtualItem.start}px)`
|
|
15753
|
+
} : {}
|
|
15754
|
+
};
|
|
15755
|
+
const measureRef = virtualItem ? optionVirtualizer.measureElement : void 0;
|
|
15541
15756
|
if (renderOption) {
|
|
15542
15757
|
return /* @__PURE__ */ jsx45(
|
|
15543
15758
|
"li",
|
|
15544
15759
|
{
|
|
15545
15760
|
ref: (node) => {
|
|
15761
|
+
measureRef?.(node);
|
|
15546
15762
|
listRef.current[index] = node;
|
|
15547
15763
|
},
|
|
15764
|
+
"data-index": virtualItem?.index,
|
|
15765
|
+
style: itemStyle,
|
|
15548
15766
|
onClick: (e) => {
|
|
15549
15767
|
e.preventDefault();
|
|
15550
15768
|
e.stopPropagation();
|
|
15551
15769
|
if (!isDisabled) toggleSelect(item.value);
|
|
15552
15770
|
inputRef.current?.focus();
|
|
15553
15771
|
},
|
|
15554
|
-
style: { animationDelay: open ? `${Math.min(index * 20, 200)}ms` : "0ms" },
|
|
15555
15772
|
className: cn("dropdown-item", isDisabled && "opacity-50 cursor-not-allowed pointer-events-none"),
|
|
15556
15773
|
children: renderOption(item, isSelected)
|
|
15557
15774
|
},
|
|
@@ -15562,15 +15779,17 @@ var MultiCombobox = ({
|
|
|
15562
15779
|
"li",
|
|
15563
15780
|
{
|
|
15564
15781
|
ref: (node) => {
|
|
15782
|
+
measureRef?.(node);
|
|
15565
15783
|
listRef.current[index] = node;
|
|
15566
15784
|
},
|
|
15785
|
+
"data-index": virtualItem?.index,
|
|
15786
|
+
style: itemStyle,
|
|
15567
15787
|
onClick: (e) => {
|
|
15568
15788
|
e.preventDefault();
|
|
15569
15789
|
e.stopPropagation();
|
|
15570
15790
|
if (!isDisabled) toggleSelect(item.value);
|
|
15571
15791
|
inputRef.current?.focus();
|
|
15572
15792
|
},
|
|
15573
|
-
style: { animationDelay: open ? `${Math.min(index * 20, 200)}ms` : "0ms" },
|
|
15574
15793
|
className: cn(
|
|
15575
15794
|
"dropdown-item flex cursor-pointer items-center gap-3 rounded-full transition-all duration-200",
|
|
15576
15795
|
sizeStyles8[size].item,
|
|
@@ -15634,6 +15853,7 @@ var MultiCombobox = ({
|
|
|
15634
15853
|
onChange: (e) => {
|
|
15635
15854
|
setQuery(e.target.value);
|
|
15636
15855
|
setActiveIndex(null);
|
|
15856
|
+
scrollVirtualListToStart();
|
|
15637
15857
|
},
|
|
15638
15858
|
onKeyDown: handleKeyDown2,
|
|
15639
15859
|
placeholder: searchPlaceholder,
|
|
@@ -15644,7 +15864,10 @@ var MultiCombobox = ({
|
|
|
15644
15864
|
"button",
|
|
15645
15865
|
{
|
|
15646
15866
|
type: "button",
|
|
15647
|
-
onClick: () =>
|
|
15867
|
+
onClick: () => {
|
|
15868
|
+
setQuery("");
|
|
15869
|
+
scrollVirtualListToStart();
|
|
15870
|
+
},
|
|
15648
15871
|
className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors",
|
|
15649
15872
|
children: /* @__PURE__ */ jsx45(X13, { className: "w-4 h-4" })
|
|
15650
15873
|
}
|
|
@@ -15658,37 +15881,63 @@ var MultiCombobox = ({
|
|
|
15658
15881
|
"aria-multiselectable": "true",
|
|
15659
15882
|
ref: optionsListRef,
|
|
15660
15883
|
style: { maxHeight },
|
|
15661
|
-
className: cn(
|
|
15884
|
+
className: cn(
|
|
15885
|
+
"overflow-y-auto p-1.5",
|
|
15886
|
+
(!useOverlayScrollbar || virtualized) && comboboxScrollClassName2,
|
|
15887
|
+
size === "lg" ? "text-base" : size === "sm" ? "text-xs" : "text-sm"
|
|
15888
|
+
),
|
|
15662
15889
|
children: loading2 ? /* @__PURE__ */ jsx45("li", { className: "px-3 py-8 text-center", children: /* @__PURE__ */ jsxs35("div", { className: "flex flex-col items-center gap-3 animate-in fade-in-0 zoom-in-95 duration-300", children: [
|
|
15663
15890
|
/* @__PURE__ */ jsxs35("div", { className: "relative", children: [
|
|
15664
|
-
/* @__PURE__ */ jsx45(
|
|
15665
|
-
/* @__PURE__ */ jsx45(
|
|
15891
|
+
/* @__PURE__ */ jsx45(Loader23, { className: "h-8 w-8 animate-spin text-primary" }),
|
|
15892
|
+
/* @__PURE__ */ jsx45(Sparkles2, { className: "h-4 w-4 text-primary/60 absolute -top-1 -right-1 animate-pulse" })
|
|
15666
15893
|
] }),
|
|
15667
15894
|
/* @__PURE__ */ jsx45("span", { className: "text-muted-foreground font-medium", children: loadingText })
|
|
15668
|
-
] }) }) :
|
|
15895
|
+
] }) }) : shouldPromptForSearch ? /* @__PURE__ */ jsx45("li", { className: "px-3 py-8 text-center text-muted-foreground", children: /* @__PURE__ */ jsxs35("div", { className: "flex flex-col items-center gap-3 animate-in fade-in-0 zoom-in-95 duration-300", children: [
|
|
15896
|
+
/* @__PURE__ */ jsx45(Search5, { className: "h-10 w-10 opacity-30 text-muted-foreground" }),
|
|
15897
|
+
/* @__PURE__ */ jsxs35("span", { className: "font-medium block text-foreground", children: [
|
|
15898
|
+
"Type at least ",
|
|
15899
|
+
minSearchLength,
|
|
15900
|
+
" characters to search"
|
|
15901
|
+
] })
|
|
15902
|
+
] }) }) : renderLimitedOptions.length ? groupedOptions ? (
|
|
15669
15903
|
// Render grouped options
|
|
15670
15904
|
Array.from(groupedOptions.entries()).map(([group, items]) => /* @__PURE__ */ jsxs35("li", { className: "mb-2", children: [
|
|
15671
15905
|
/* @__PURE__ */ jsx45("div", { className: "px-3 py-1.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider sticky top-0 bg-popover/95 backdrop-blur-sm", children: group }),
|
|
15672
|
-
/* @__PURE__ */ jsx45("ul", { children: items.map((item) => renderOptionItem(item,
|
|
15906
|
+
/* @__PURE__ */ jsx45("ul", { children: items.map((item) => renderOptionItem(item, renderLimitedOptions.indexOf(item))) })
|
|
15673
15907
|
] }, group))
|
|
15674
15908
|
) : (
|
|
15675
15909
|
// Render flat options
|
|
15676
|
-
|
|
15910
|
+
canVirtualize ? /* @__PURE__ */ jsx45("li", { role: "presentation", className: "list-none p-0", children: /* @__PURE__ */ jsx45("ul", { className: "relative", style: { height: `${optionVirtualizer.getTotalSize()}px` }, children: virtualItems.map((virtualItem) => renderOptionItem(renderLimitedOptions[virtualItem.index], virtualItem.index, virtualItem)) }) }) : renderLimitedOptions.map((item, index) => renderOptionItem(item, index))
|
|
15677
15911
|
) : /* @__PURE__ */ jsx45("li", { className: cn("px-3 py-8 text-center text-muted-foreground"), children: /* @__PURE__ */ jsxs35("div", { className: "flex flex-col items-center gap-3 animate-in fade-in-0 zoom-in-95 duration-300", children: [
|
|
15678
15912
|
/* @__PURE__ */ jsx45(SearchX2, { className: "h-10 w-10 opacity-30 text-muted-foreground" }),
|
|
15679
15913
|
/* @__PURE__ */ jsxs35("div", { className: "space-y-1", children: [
|
|
15680
15914
|
/* @__PURE__ */ jsx45("span", { className: "font-medium block", children: emptyText }),
|
|
15681
15915
|
query && /* @__PURE__ */ jsx45("span", { className: "text-xs opacity-60", children: "Try a different search term" })
|
|
15682
15916
|
] }),
|
|
15683
|
-
query && /* @__PURE__ */ jsxs35(
|
|
15684
|
-
|
|
15685
|
-
|
|
15686
|
-
|
|
15917
|
+
query && /* @__PURE__ */ jsxs35(
|
|
15918
|
+
"button",
|
|
15919
|
+
{
|
|
15920
|
+
type: "button",
|
|
15921
|
+
onClick: () => {
|
|
15922
|
+
setQuery("");
|
|
15923
|
+
scrollVirtualListToStart();
|
|
15924
|
+
},
|
|
15925
|
+
className: "text-xs text-primary hover:underline flex items-center gap-1",
|
|
15926
|
+
children: [
|
|
15927
|
+
/* @__PURE__ */ jsx45(X13, { className: "w-3 h-3" }),
|
|
15928
|
+
"Clear search"
|
|
15929
|
+
]
|
|
15930
|
+
}
|
|
15931
|
+
)
|
|
15687
15932
|
] }) })
|
|
15688
15933
|
}
|
|
15689
15934
|
)
|
|
15690
15935
|
] });
|
|
15691
|
-
const
|
|
15936
|
+
const selectedOptionFallbackMap = React39.useMemo(
|
|
15937
|
+
() => new Map((selectedOptionsProp ?? []).map((option) => [option.value, option])),
|
|
15938
|
+
[selectedOptionsProp]
|
|
15939
|
+
);
|
|
15940
|
+
const selectedOptions = value.map((v) => normalizedOptions.find((o) => o.value === v) ?? selectedOptionFallbackMap.get(v)).filter(Boolean);
|
|
15692
15941
|
const visibleTags = maxTagsVisible ? selectedOptions.slice(0, maxTagsVisible) : selectedOptions;
|
|
15693
15942
|
const hiddenCount = maxTagsVisible ? Math.max(0, selectedOptions.length - maxTagsVisible) : 0;
|
|
15694
15943
|
const triggerButton = /* @__PURE__ */ jsxs35(
|
|
@@ -17732,8 +17981,8 @@ function CategoryTreeSelect(props) {
|
|
|
17732
17981
|
}
|
|
17733
17982
|
|
|
17734
17983
|
// src/components/ImageUpload.tsx
|
|
17735
|
-
import { useState as useState32, useRef as useRef21, useCallback as
|
|
17736
|
-
import { Upload, X as X15, Image as ImageIcon, Loader2 as
|
|
17984
|
+
import { useState as useState32, useRef as useRef21, useCallback as useCallback18 } from "react";
|
|
17985
|
+
import { Upload, X as X15, Image as ImageIcon, Loader2 as Loader24, Check as Check7 } from "lucide-react";
|
|
17737
17986
|
import { jsx as jsx50, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
17738
17987
|
function ImageUpload({
|
|
17739
17988
|
onUpload,
|
|
@@ -17761,7 +18010,7 @@ function ImageUpload({
|
|
|
17761
18010
|
md: "w-24 h-24",
|
|
17762
18011
|
lg: "w-32 h-32"
|
|
17763
18012
|
};
|
|
17764
|
-
const handleDragOver =
|
|
18013
|
+
const handleDragOver = useCallback18(
|
|
17765
18014
|
(e) => {
|
|
17766
18015
|
e.preventDefault();
|
|
17767
18016
|
if (!disabled) {
|
|
@@ -17770,11 +18019,11 @@ function ImageUpload({
|
|
|
17770
18019
|
},
|
|
17771
18020
|
[disabled]
|
|
17772
18021
|
);
|
|
17773
|
-
const handleDragLeave =
|
|
18022
|
+
const handleDragLeave = useCallback18((e) => {
|
|
17774
18023
|
e.preventDefault();
|
|
17775
18024
|
setIsDragging(false);
|
|
17776
18025
|
}, []);
|
|
17777
|
-
const handleFiles =
|
|
18026
|
+
const handleFiles = useCallback18(
|
|
17778
18027
|
async (files) => {
|
|
17779
18028
|
if (files.length === 0) return;
|
|
17780
18029
|
const validFiles = files.filter((file) => {
|
|
@@ -17841,7 +18090,7 @@ function ImageUpload({
|
|
|
17841
18090
|
},
|
|
17842
18091
|
[maxSize, addToast, onUpload]
|
|
17843
18092
|
);
|
|
17844
|
-
const handleDrop =
|
|
18093
|
+
const handleDrop = useCallback18(
|
|
17845
18094
|
(e) => {
|
|
17846
18095
|
e.preventDefault();
|
|
17847
18096
|
setIsDragging(false);
|
|
@@ -17851,7 +18100,7 @@ function ImageUpload({
|
|
|
17851
18100
|
},
|
|
17852
18101
|
[disabled, handleFiles]
|
|
17853
18102
|
);
|
|
17854
|
-
const handleFileSelect =
|
|
18103
|
+
const handleFileSelect = useCallback18(
|
|
17855
18104
|
(e) => {
|
|
17856
18105
|
const files = Array.from(e.target.files || []);
|
|
17857
18106
|
handleFiles(files);
|
|
@@ -17883,7 +18132,7 @@ function ImageUpload({
|
|
|
17883
18132
|
onDrop: handleDrop,
|
|
17884
18133
|
children: [
|
|
17885
18134
|
uploading && /* @__PURE__ */ jsx50("div", { className: "absolute inset-0 bg-background/80 flex items-center justify-center rounded-2xl md:rounded-3xl", children: /* @__PURE__ */ jsxs40("div", { className: "flex items-center gap-3", children: [
|
|
17886
|
-
/* @__PURE__ */ jsx50(
|
|
18135
|
+
/* @__PURE__ */ jsx50(Loader24, { className: "w-6 h-6 animate-spin text-primary" }),
|
|
17887
18136
|
/* @__PURE__ */ jsx50("span", { className: "text-sm font-medium", children: "Uploading..." })
|
|
17888
18137
|
] }) }),
|
|
17889
18138
|
/* @__PURE__ */ jsxs40("div", { className: "space-y-4", children: [
|
|
@@ -17967,11 +18216,11 @@ import {
|
|
|
17967
18216
|
FileSpreadsheet,
|
|
17968
18217
|
FileText,
|
|
17969
18218
|
FileVideo,
|
|
17970
|
-
Loader2 as
|
|
18219
|
+
Loader2 as Loader25,
|
|
17971
18220
|
Trash2,
|
|
17972
18221
|
Upload as Upload2
|
|
17973
18222
|
} from "lucide-react";
|
|
17974
|
-
import { useCallback as
|
|
18223
|
+
import { useCallback as useCallback19, useMemo as useMemo20, useRef as useRef22, useState as useState33 } from "react";
|
|
17975
18224
|
import { Fragment as Fragment16, jsx as jsx51, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
17976
18225
|
var formatFileSize = (bytes) => {
|
|
17977
18226
|
if (bytes === 0) return "0 Bytes";
|
|
@@ -18116,7 +18365,7 @@ function FileUpload({
|
|
|
18116
18365
|
[]
|
|
18117
18366
|
);
|
|
18118
18367
|
const currentSize = sizeConfig[size];
|
|
18119
|
-
const handleDragOver =
|
|
18368
|
+
const handleDragOver = useCallback19(
|
|
18120
18369
|
(e) => {
|
|
18121
18370
|
e.preventDefault();
|
|
18122
18371
|
e.stopPropagation();
|
|
@@ -18126,12 +18375,12 @@ function FileUpload({
|
|
|
18126
18375
|
},
|
|
18127
18376
|
[disabled]
|
|
18128
18377
|
);
|
|
18129
|
-
const handleDragLeave =
|
|
18378
|
+
const handleDragLeave = useCallback19((e) => {
|
|
18130
18379
|
e.preventDefault();
|
|
18131
18380
|
e.stopPropagation();
|
|
18132
18381
|
setIsDragging(false);
|
|
18133
18382
|
}, []);
|
|
18134
|
-
const processFiles =
|
|
18383
|
+
const processFiles = useCallback19(
|
|
18135
18384
|
async (fileList) => {
|
|
18136
18385
|
if (fileList.length === 0) return;
|
|
18137
18386
|
const remainingSlots = maxFiles - files.length;
|
|
@@ -18204,7 +18453,7 @@ function FileUpload({
|
|
|
18204
18453
|
},
|
|
18205
18454
|
[files, maxFiles, maxSize, uploadHandler, onUpload, onChange, addToast, t]
|
|
18206
18455
|
);
|
|
18207
|
-
const handleDrop =
|
|
18456
|
+
const handleDrop = useCallback19(
|
|
18208
18457
|
(e) => {
|
|
18209
18458
|
e.preventDefault();
|
|
18210
18459
|
e.stopPropagation();
|
|
@@ -18215,7 +18464,7 @@ function FileUpload({
|
|
|
18215
18464
|
},
|
|
18216
18465
|
[disabled, processFiles]
|
|
18217
18466
|
);
|
|
18218
|
-
const handleFileSelect =
|
|
18467
|
+
const handleFileSelect = useCallback19(
|
|
18219
18468
|
(e) => {
|
|
18220
18469
|
const selectedFiles = Array.from(e.target.files || []);
|
|
18221
18470
|
processFiles(selectedFiles);
|
|
@@ -18225,7 +18474,7 @@ function FileUpload({
|
|
|
18225
18474
|
},
|
|
18226
18475
|
[processFiles]
|
|
18227
18476
|
);
|
|
18228
|
-
const handleRemove =
|
|
18477
|
+
const handleRemove = useCallback19(
|
|
18229
18478
|
(fileId) => {
|
|
18230
18479
|
setFiles((prev) => {
|
|
18231
18480
|
const fileToRemove = prev.find((f) => f.id === fileId);
|
|
@@ -18243,7 +18492,7 @@ function FileUpload({
|
|
|
18243
18492
|
const handleBrowseClick = () => {
|
|
18244
18493
|
fileInputRef.current?.click();
|
|
18245
18494
|
};
|
|
18246
|
-
const handleRetry =
|
|
18495
|
+
const handleRetry = useCallback19(
|
|
18247
18496
|
(fileEntry) => {
|
|
18248
18497
|
if (!uploadHandler || !fileEntry.file) return;
|
|
18249
18498
|
processFiles([fileEntry.file]);
|
|
@@ -18282,7 +18531,7 @@ function FileUpload({
|
|
|
18282
18531
|
/* @__PURE__ */ jsxs41("div", { className: "flex items-center gap-2", children: [
|
|
18283
18532
|
/* @__PURE__ */ jsx51("span", { className: cn("text-muted-foreground", size === "lg" ? "text-sm" : "text-xs"), children: file.formattedSize }),
|
|
18284
18533
|
file.status === "uploading" && /* @__PURE__ */ jsxs41("span", { className: "flex items-center gap-1 text-primary text-xs", children: [
|
|
18285
|
-
/* @__PURE__ */ jsx51(
|
|
18534
|
+
/* @__PURE__ */ jsx51(Loader25, { className: "w-3 h-3 animate-spin" }),
|
|
18286
18535
|
file.progress,
|
|
18287
18536
|
"%"
|
|
18288
18537
|
] }),
|
|
@@ -25924,7 +26173,7 @@ var EditorToolbar = ({
|
|
|
25924
26173
|
};
|
|
25925
26174
|
|
|
25926
26175
|
// src/components/UEditor/menus.tsx
|
|
25927
|
-
import { useCallback as
|
|
26176
|
+
import { useCallback as useCallback22, useEffect as useEffect34, useMemo as useMemo23, useRef as useRef31, useState as useState45 } from "react";
|
|
25928
26177
|
import { createPortal as createPortal8 } from "react-dom";
|
|
25929
26178
|
import {
|
|
25930
26179
|
AlignCenter as AlignCenter2,
|
|
@@ -26216,7 +26465,7 @@ var CustomBubbleMenu = ({
|
|
|
26216
26465
|
const menuRef = useRef31(null);
|
|
26217
26466
|
const keepOpenRef = useRef31(false);
|
|
26218
26467
|
const showTimeoutRef = useRef31(null);
|
|
26219
|
-
const setKeepOpen =
|
|
26468
|
+
const setKeepOpen = useCallback22((next) => {
|
|
26220
26469
|
keepOpenRef.current = next;
|
|
26221
26470
|
if (next) setIsVisible(true);
|
|
26222
26471
|
}, []);
|