@underverse-ui/underverse 1.0.106 → 1.0.109
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 +307 -90
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +333 -116
- package/dist/index.js.map +1 -1
- package/package.json +4 -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) => {
|
|
@@ -7203,9 +7204,19 @@ var Combobox = ({
|
|
|
7203
7204
|
groupBy,
|
|
7204
7205
|
renderOption,
|
|
7205
7206
|
renderValue,
|
|
7207
|
+
selectedOption: selectedOptionProp,
|
|
7206
7208
|
error,
|
|
7207
7209
|
helperText,
|
|
7208
|
-
useOverlayScrollbar = false
|
|
7210
|
+
useOverlayScrollbar = false,
|
|
7211
|
+
virtualized = false,
|
|
7212
|
+
estimatedItemHeight = 44,
|
|
7213
|
+
overscan = 8,
|
|
7214
|
+
searchMode = "auto",
|
|
7215
|
+
onSearchChange,
|
|
7216
|
+
searchDebounceMs = 0,
|
|
7217
|
+
minSearchLength = 0,
|
|
7218
|
+
maxInitialOptions,
|
|
7219
|
+
showSearchPromptWhenEmptyQuery = false
|
|
7209
7220
|
}) => {
|
|
7210
7221
|
const tv = useSmartTranslations("ValidationInput");
|
|
7211
7222
|
const [open, setOpen] = React24.useState(false);
|
|
@@ -7219,12 +7230,53 @@ var Combobox = ({
|
|
|
7219
7230
|
const autoId = useId6();
|
|
7220
7231
|
const resolvedId = id ? String(id) : `combobox-${autoId}`;
|
|
7221
7232
|
const labelId = label ? `${resolvedId}-label` : void 0;
|
|
7222
|
-
const enableSearch = options.length > 10;
|
|
7233
|
+
const enableSearch = options.length > 10 || searchMode === "manual" || minSearchLength > 0 || !!onSearchChange;
|
|
7234
|
+
const trimmedQuery = query.trim();
|
|
7235
|
+
const queryMeetsMinimum = trimmedQuery.length >= minSearchLength;
|
|
7236
|
+
const shouldPromptForSearch = minSearchLength > 0 && !queryMeetsMinimum && (searchMode === "manual" || showSearchPromptWhenEmptyQuery);
|
|
7223
7237
|
const filteredOptions = React24.useMemo(
|
|
7224
|
-
() =>
|
|
7225
|
-
|
|
7238
|
+
() => {
|
|
7239
|
+
if (shouldPromptForSearch) return [];
|
|
7240
|
+
if (!enableSearch || searchMode === "manual") return options;
|
|
7241
|
+
const normalizedQuery = trimmedQuery.toLowerCase();
|
|
7242
|
+
if (!normalizedQuery) return options;
|
|
7243
|
+
return options.filter((o) => getOptionLabel(o).toLowerCase().includes(normalizedQuery));
|
|
7244
|
+
},
|
|
7245
|
+
[enableSearch, options, searchMode, shouldPromptForSearch, trimmedQuery]
|
|
7246
|
+
);
|
|
7247
|
+
const renderLimitedOptions = React24.useMemo(
|
|
7248
|
+
() => {
|
|
7249
|
+
if (trimmedQuery || maxInitialOptions === void 0 || maxInitialOptions < 1) {
|
|
7250
|
+
return filteredOptions;
|
|
7251
|
+
}
|
|
7252
|
+
return filteredOptions.slice(0, maxInitialOptions);
|
|
7253
|
+
},
|
|
7254
|
+
[filteredOptions, maxInitialOptions, trimmedQuery]
|
|
7226
7255
|
);
|
|
7256
|
+
const canVirtualize = virtualized && !groupBy;
|
|
7257
|
+
const optionVirtualizer = useVirtualizer({
|
|
7258
|
+
count: canVirtualize ? renderLimitedOptions.length : 0,
|
|
7259
|
+
getScrollElement: () => optionsViewportRef.current,
|
|
7260
|
+
estimateSize: () => estimatedItemHeight,
|
|
7261
|
+
initialRect: { width: 0, height: maxHeight },
|
|
7262
|
+
overscan,
|
|
7263
|
+
enabled: canVirtualize
|
|
7264
|
+
});
|
|
7265
|
+
const virtualItems = canVirtualize ? optionVirtualizer.getVirtualItems() : [];
|
|
7227
7266
|
const triggerRef = React24.useRef(null);
|
|
7267
|
+
const scrollVirtualListToIndex = React24.useCallback((index) => {
|
|
7268
|
+
if (!canVirtualize || renderLimitedOptions.length === 0) return;
|
|
7269
|
+
optionVirtualizer.scrollToIndex(index, { align: "auto" });
|
|
7270
|
+
}, [canVirtualize, optionVirtualizer, renderLimitedOptions.length]);
|
|
7271
|
+
const scrollVirtualListToStart = React24.useCallback(() => {
|
|
7272
|
+
scrollVirtualListToIndex(0);
|
|
7273
|
+
}, [scrollVirtualListToIndex]);
|
|
7274
|
+
const moveActiveIndex = React24.useCallback((direction) => {
|
|
7275
|
+
if (renderLimitedOptions.length === 0) return;
|
|
7276
|
+
const next = activeIndex === null ? direction === 1 ? 0 : renderLimitedOptions.length - 1 : (activeIndex + direction + renderLimitedOptions.length) % renderLimitedOptions.length;
|
|
7277
|
+
setActiveIndex(next);
|
|
7278
|
+
scrollVirtualListToIndex(next);
|
|
7279
|
+
}, [activeIndex, renderLimitedOptions.length, scrollVirtualListToIndex]);
|
|
7228
7280
|
const handleSelect = (option) => {
|
|
7229
7281
|
if (getOptionDisabled(option)) return;
|
|
7230
7282
|
const val = getOptionValue(option);
|
|
@@ -7237,6 +7289,9 @@ var Combobox = ({
|
|
|
7237
7289
|
};
|
|
7238
7290
|
const handleClear = (e) => {
|
|
7239
7291
|
e.stopPropagation();
|
|
7292
|
+
clearValue();
|
|
7293
|
+
};
|
|
7294
|
+
const clearValue = () => {
|
|
7240
7295
|
onChange(null);
|
|
7241
7296
|
setOpen(false);
|
|
7242
7297
|
};
|
|
@@ -7244,13 +7299,26 @@ var Combobox = ({
|
|
|
7244
7299
|
if (!open) {
|
|
7245
7300
|
setQuery("");
|
|
7246
7301
|
setActiveIndex(null);
|
|
7302
|
+
scrollVirtualListToStart();
|
|
7247
7303
|
} else if (enableSearch) {
|
|
7248
7304
|
setTimeout(() => {
|
|
7249
7305
|
inputRef.current?.focus();
|
|
7250
7306
|
}, 100);
|
|
7251
7307
|
}
|
|
7252
|
-
}, [open,
|
|
7253
|
-
|
|
7308
|
+
}, [enableSearch, open, scrollVirtualListToStart]);
|
|
7309
|
+
React24.useEffect(() => {
|
|
7310
|
+
if (!onSearchChange) return void 0;
|
|
7311
|
+
const timeoutId = window.setTimeout(() => onSearchChange(query), searchDebounceMs);
|
|
7312
|
+
return () => window.clearTimeout(timeoutId);
|
|
7313
|
+
}, [onSearchChange, query, searchDebounceMs]);
|
|
7314
|
+
React24.useEffect(() => {
|
|
7315
|
+
if (process.env.NODE_ENV !== "production" && options.length > 300 && !virtualized && searchMode !== "manual" && maxInitialOptions === void 0) {
|
|
7316
|
+
console.warn(
|
|
7317
|
+
'[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.'
|
|
7318
|
+
);
|
|
7319
|
+
}
|
|
7320
|
+
}, [maxInitialOptions, options.length, searchMode, virtualized]);
|
|
7321
|
+
const selectedOption = findOptionByValue(options, value) ?? (selectedOptionProp && getOptionValue(selectedOptionProp) === value ? selectedOptionProp : void 0);
|
|
7254
7322
|
const displayValue = selectedOption ? getOptionLabel(selectedOption) : "";
|
|
7255
7323
|
const selectedIcon = selectedOption ? getOptionIcon(selectedOption) : void 0;
|
|
7256
7324
|
const hasValue = value !== void 0 && value !== null && value !== "";
|
|
@@ -7263,13 +7331,13 @@ var Combobox = ({
|
|
|
7263
7331
|
const groupedOptions = React24.useMemo(() => {
|
|
7264
7332
|
if (!groupBy) return null;
|
|
7265
7333
|
const groups = {};
|
|
7266
|
-
|
|
7334
|
+
renderLimitedOptions.forEach((opt) => {
|
|
7267
7335
|
const group = groupBy(opt);
|
|
7268
7336
|
if (!groups[group]) groups[group] = [];
|
|
7269
7337
|
groups[group].push(opt);
|
|
7270
7338
|
});
|
|
7271
7339
|
return groups;
|
|
7272
|
-
}, [
|
|
7340
|
+
}, [renderLimitedOptions, groupBy]);
|
|
7273
7341
|
const itemSizeStyles = {
|
|
7274
7342
|
sm: "px-2.5 py-1.5 text-xs gap-2",
|
|
7275
7343
|
md: "px-3 py-2.5 text-sm gap-3",
|
|
@@ -7285,60 +7353,75 @@ var Combobox = ({
|
|
|
7285
7353
|
md: "h-4 w-4",
|
|
7286
7354
|
lg: "h-5 w-5"
|
|
7287
7355
|
};
|
|
7288
|
-
const renderOptionItem = (item, index) => {
|
|
7356
|
+
const renderOptionItem = (item, index, virtualItem) => {
|
|
7289
7357
|
const itemValue = getOptionValue(item);
|
|
7290
7358
|
const itemLabel = getOptionLabel(item);
|
|
7291
7359
|
const itemIcon = getOptionIcon(item);
|
|
7292
7360
|
const itemDescription = getOptionDescription(item);
|
|
7293
7361
|
const itemDisabled = getOptionDisabled(item);
|
|
7294
7362
|
const isSelected = itemValue === value;
|
|
7295
|
-
return /* @__PURE__ */ jsx29(
|
|
7296
|
-
"
|
|
7363
|
+
return /* @__PURE__ */ jsx29(
|
|
7364
|
+
"li",
|
|
7297
7365
|
{
|
|
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
|
-
|
|
7366
|
+
ref: virtualItem ? optionVirtualizer.measureElement : void 0,
|
|
7367
|
+
"data-index": virtualItem?.index,
|
|
7368
|
+
className: "list-none",
|
|
7369
|
+
style: virtualItem ? {
|
|
7370
|
+
position: "absolute",
|
|
7371
|
+
top: 0,
|
|
7372
|
+
left: 0,
|
|
7373
|
+
width: "100%",
|
|
7374
|
+
transform: `translateY(${virtualItem.start}px)`
|
|
7375
|
+
} : void 0,
|
|
7376
|
+
children: /* @__PURE__ */ jsxs22(
|
|
7377
|
+
"button",
|
|
7378
|
+
{
|
|
7379
|
+
id: `${resolvedId}-item-${index}`,
|
|
7380
|
+
type: "button",
|
|
7381
|
+
role: "option",
|
|
7382
|
+
tabIndex: -1,
|
|
7383
|
+
disabled: itemDisabled,
|
|
7384
|
+
"aria-selected": isSelected,
|
|
7385
|
+
onClick: () => handleSelect(item),
|
|
7386
|
+
style: {
|
|
7387
|
+
animationDelay: open ? `${Math.min(index * 15, 150)}ms` : "0ms"
|
|
7388
|
+
},
|
|
7389
|
+
className: cn(
|
|
7390
|
+
"dropdown-item group flex w-full items-center rounded-full text-left",
|
|
7391
|
+
itemSizeStyles[size],
|
|
7392
|
+
"outline-none focus:outline-none focus-visible:outline-none",
|
|
7393
|
+
"transition-all duration-150",
|
|
7394
|
+
!itemDisabled && "cursor-pointer hover:bg-accent/70 hover:shadow-sm",
|
|
7395
|
+
!itemDisabled && "focus:bg-accent/80 focus:text-accent-foreground",
|
|
7396
|
+
index === activeIndex && !itemDisabled && "bg-accent/60",
|
|
7397
|
+
isSelected && "bg-primary/10 text-primary font-medium",
|
|
7398
|
+
itemDisabled && "opacity-50 cursor-not-allowed"
|
|
7399
|
+
),
|
|
7400
|
+
children: [
|
|
7401
|
+
itemIcon && /* @__PURE__ */ jsx29(
|
|
7402
|
+
"span",
|
|
7403
|
+
{
|
|
7404
|
+
className: cn("shrink-0 flex items-center justify-center", iconSizeStyles[size], isSelected ? "text-primary" : "text-muted-foreground"),
|
|
7405
|
+
children: itemIcon
|
|
7406
|
+
}
|
|
7407
|
+
),
|
|
7408
|
+
renderOption ? /* @__PURE__ */ jsx29("div", { className: "flex-1 min-w-0", children: renderOption(item, isSelected) }) : /* @__PURE__ */ jsxs22("div", { className: "flex-1 min-w-0", children: [
|
|
7409
|
+
/* @__PURE__ */ jsx29("span", { className: "block truncate", children: itemLabel }),
|
|
7410
|
+
itemDescription && /* @__PURE__ */ jsx29("span", { className: cn("block text-muted-foreground truncate mt-0.5", size === "sm" ? "text-[10px]" : "text-xs"), children: itemDescription })
|
|
7411
|
+
] }),
|
|
7412
|
+
isSelected && showSelectedIcon && /* @__PURE__ */ jsx29("span", { className: "shrink-0 ml-auto", children: /* @__PURE__ */ jsx29(Check3, { className: cn(checkIconSizeStyles[size], "text-primary") }) })
|
|
7413
|
+
]
|
|
7414
|
+
}
|
|
7415
|
+
)
|
|
7416
|
+
},
|
|
7417
|
+
`${itemValue}-${index}`
|
|
7418
|
+
);
|
|
7335
7419
|
};
|
|
7336
7420
|
const dropdownBody = /* @__PURE__ */ jsxs22(
|
|
7337
7421
|
"div",
|
|
7338
7422
|
{
|
|
7339
7423
|
"data-combobox-dropdown": true,
|
|
7340
7424
|
"data-state": open ? "open" : "closed",
|
|
7341
|
-
id: `${resolvedId}-listbox`,
|
|
7342
7425
|
className: "w-full rounded-2xl md:rounded-3xl overflow-hidden",
|
|
7343
7426
|
children: [
|
|
7344
7427
|
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 +7442,19 @@ var Combobox = ({
|
|
|
7359
7442
|
onChange: (e) => {
|
|
7360
7443
|
setQuery(e.target.value);
|
|
7361
7444
|
setActiveIndex(null);
|
|
7445
|
+
scrollVirtualListToStart();
|
|
7362
7446
|
},
|
|
7363
7447
|
onKeyDown: (e) => {
|
|
7364
7448
|
if (e.key === "ArrowDown") {
|
|
7365
7449
|
e.preventDefault();
|
|
7366
|
-
|
|
7367
|
-
const next = prev === null ? 0 : prev + 1;
|
|
7368
|
-
return next >= filteredOptions.length ? 0 : next;
|
|
7369
|
-
});
|
|
7450
|
+
moveActiveIndex(1);
|
|
7370
7451
|
} else if (e.key === "ArrowUp") {
|
|
7371
7452
|
e.preventDefault();
|
|
7372
|
-
|
|
7373
|
-
const next = prev === null ? filteredOptions.length - 1 : prev - 1;
|
|
7374
|
-
return next < 0 ? filteredOptions.length - 1 : next;
|
|
7375
|
-
});
|
|
7453
|
+
moveActiveIndex(-1);
|
|
7376
7454
|
} else if (e.key === "Enter") {
|
|
7377
7455
|
e.preventDefault();
|
|
7378
|
-
if (activeIndex !== null &&
|
|
7379
|
-
handleSelect(
|
|
7456
|
+
if (activeIndex !== null && renderLimitedOptions[activeIndex] && !getOptionDisabled(renderLimitedOptions[activeIndex])) {
|
|
7457
|
+
handleSelect(renderLimitedOptions[activeIndex]);
|
|
7380
7458
|
}
|
|
7381
7459
|
} else if (e.key === "Escape") {
|
|
7382
7460
|
e.preventDefault();
|
|
@@ -7399,7 +7477,10 @@ var Combobox = ({
|
|
|
7399
7477
|
"button",
|
|
7400
7478
|
{
|
|
7401
7479
|
type: "button",
|
|
7402
|
-
onClick: () =>
|
|
7480
|
+
onClick: () => {
|
|
7481
|
+
setQuery("");
|
|
7482
|
+
scrollVirtualListToStart();
|
|
7483
|
+
},
|
|
7403
7484
|
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
7485
|
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
7486
|
}
|
|
@@ -7409,6 +7490,7 @@ var Combobox = ({
|
|
|
7409
7490
|
"div",
|
|
7410
7491
|
{
|
|
7411
7492
|
ref: optionsViewportRef,
|
|
7493
|
+
id: `${resolvedId}-listbox`,
|
|
7412
7494
|
role: "listbox",
|
|
7413
7495
|
"aria-labelledby": labelId,
|
|
7414
7496
|
className: "overflow-y-auto overscroll-contain",
|
|
@@ -7416,7 +7498,14 @@ var Combobox = ({
|
|
|
7416
7498
|
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
7499
|
/* @__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
7500
|
/* @__PURE__ */ jsx29("span", { className: "text-sm text-muted-foreground", children: loadingText })
|
|
7419
|
-
] }) }) :
|
|
7501
|
+
] }) }) : 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: [
|
|
7502
|
+
/* @__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" }) }),
|
|
7503
|
+
/* @__PURE__ */ jsx29("div", { className: "space-y-1", children: /* @__PURE__ */ jsxs22("span", { className: "block text-sm font-medium text-foreground", children: [
|
|
7504
|
+
"Type at least ",
|
|
7505
|
+
minSearchLength,
|
|
7506
|
+
" characters to search"
|
|
7507
|
+
] }) })
|
|
7508
|
+
] }) }) : renderLimitedOptions.length > 0 ? groupedOptions ? (
|
|
7420
7509
|
// Render grouped options with global index tracking
|
|
7421
7510
|
(() => {
|
|
7422
7511
|
let globalIndex = 0;
|
|
@@ -7430,7 +7519,14 @@ var Combobox = ({
|
|
|
7430
7519
|
})()
|
|
7431
7520
|
) : (
|
|
7432
7521
|
// Render flat options
|
|
7433
|
-
/* @__PURE__ */ jsx29(
|
|
7522
|
+
/* @__PURE__ */ jsx29(
|
|
7523
|
+
"ul",
|
|
7524
|
+
{
|
|
7525
|
+
className: "space-y-0.5",
|
|
7526
|
+
style: canVirtualize ? { height: `${optionVirtualizer.getTotalSize()}px`, position: "relative" } : void 0,
|
|
7527
|
+
children: canVirtualize ? virtualItems.map((virtualItem) => renderOptionItem(renderLimitedOptions[virtualItem.index], virtualItem.index, virtualItem)) : renderLimitedOptions.map((item, index) => renderOptionItem(item, index))
|
|
7528
|
+
}
|
|
7529
|
+
)
|
|
7434
7530
|
) : /* @__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
7531
|
/* @__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
7532
|
/* @__PURE__ */ jsxs22("div", { className: "space-y-1", children: [
|
|
@@ -7441,7 +7537,10 @@ var Combobox = ({
|
|
|
7441
7537
|
"button",
|
|
7442
7538
|
{
|
|
7443
7539
|
type: "button",
|
|
7444
|
-
onClick: () =>
|
|
7540
|
+
onClick: () => {
|
|
7541
|
+
setQuery("");
|
|
7542
|
+
scrollVirtualListToStart();
|
|
7543
|
+
},
|
|
7445
7544
|
className: "px-3 py-1.5 text-xs font-medium text-primary bg-primary/10 rounded-full hover:bg-primary/20 transition-colors",
|
|
7446
7545
|
children: "Clear search"
|
|
7447
7546
|
}
|
|
@@ -7511,7 +7610,13 @@ var Combobox = ({
|
|
|
7511
7610
|
tabIndex: 0,
|
|
7512
7611
|
"aria-label": "Clear selection",
|
|
7513
7612
|
onClick: handleClear,
|
|
7514
|
-
onKeyDown: (e) =>
|
|
7613
|
+
onKeyDown: (e) => {
|
|
7614
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
7615
|
+
e.preventDefault();
|
|
7616
|
+
e.stopPropagation();
|
|
7617
|
+
clearValue();
|
|
7618
|
+
}
|
|
7619
|
+
},
|
|
7515
7620
|
className: cn(
|
|
7516
7621
|
"opacity-0 group-hover:opacity-100 transition-all duration-200",
|
|
7517
7622
|
"p-1 rounded-lg hover:bg-destructive/10 text-muted-foreground hover:text-destructive"
|
|
@@ -8287,7 +8392,7 @@ function formatDateSmart(date, locale = "en") {
|
|
|
8287
8392
|
}
|
|
8288
8393
|
|
|
8289
8394
|
// src/components/DatePicker.tsx
|
|
8290
|
-
import { Calendar, ChevronLeft as ChevronLeft2, ChevronRight as ChevronRight3, Sparkles
|
|
8395
|
+
import { Calendar, ChevronLeft as ChevronLeft2, ChevronRight as ChevronRight3, Sparkles, X as XIcon } from "lucide-react";
|
|
8291
8396
|
import * as React28 from "react";
|
|
8292
8397
|
import { useId as useId7 } from "react";
|
|
8293
8398
|
import { Fragment as Fragment6, jsx as jsx34, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
@@ -8666,7 +8771,7 @@ var DatePicker = ({
|
|
|
8666
8771
|
isDateDisabled(/* @__PURE__ */ new Date()) && "opacity-50 cursor-not-allowed hover:scale-100 active:scale-100"
|
|
8667
8772
|
),
|
|
8668
8773
|
children: [
|
|
8669
|
-
/* @__PURE__ */ jsx34(
|
|
8774
|
+
/* @__PURE__ */ jsx34(Sparkles, { className: sizeStyles8[size].actionIcon }),
|
|
8670
8775
|
todayLabel || t("today")
|
|
8671
8776
|
]
|
|
8672
8777
|
}
|
|
@@ -9246,7 +9351,7 @@ var DateRangePicker = ({
|
|
|
9246
9351
|
isTodayUnavailable && "opacity-50 cursor-not-allowed hover:scale-100 active:scale-100"
|
|
9247
9352
|
),
|
|
9248
9353
|
children: [
|
|
9249
|
-
/* @__PURE__ */ jsx34(
|
|
9354
|
+
/* @__PURE__ */ jsx34(Sparkles, { className: sizeStyles8[size].actionIcon }),
|
|
9250
9355
|
t("today")
|
|
9251
9356
|
]
|
|
9252
9357
|
}
|
|
@@ -15391,7 +15496,8 @@ function CalendarTimeline({
|
|
|
15391
15496
|
// src/components/MultiCombobox.tsx
|
|
15392
15497
|
import * as React39 from "react";
|
|
15393
15498
|
import { useId as useId9 } from "react";
|
|
15394
|
-
import {
|
|
15499
|
+
import { useVirtualizer as useVirtualizer2 } from "@tanstack/react-virtual";
|
|
15500
|
+
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
15501
|
import { Fragment as Fragment13, jsx as jsx45, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
15396
15502
|
var MultiCombobox = ({
|
|
15397
15503
|
id,
|
|
@@ -15421,10 +15527,20 @@ var MultiCombobox = ({
|
|
|
15421
15527
|
groupBy,
|
|
15422
15528
|
renderOption,
|
|
15423
15529
|
renderTag,
|
|
15530
|
+
selectedOptions: selectedOptionsProp,
|
|
15424
15531
|
error,
|
|
15425
15532
|
helperText,
|
|
15426
15533
|
maxTagsVisible = 3,
|
|
15427
|
-
useOverlayScrollbar = false
|
|
15534
|
+
useOverlayScrollbar = false,
|
|
15535
|
+
virtualized = false,
|
|
15536
|
+
estimatedItemHeight = 44,
|
|
15537
|
+
overscan = 8,
|
|
15538
|
+
searchMode = "auto",
|
|
15539
|
+
onSearchChange,
|
|
15540
|
+
searchDebounceMs = 0,
|
|
15541
|
+
minSearchLength = 0,
|
|
15542
|
+
maxInitialOptions,
|
|
15543
|
+
showSearchPromptWhenEmptyQuery = false
|
|
15428
15544
|
}) => {
|
|
15429
15545
|
const tv = useSmartTranslations("ValidationInput");
|
|
15430
15546
|
const [query, setQuery] = React39.useState("");
|
|
@@ -15443,23 +15559,52 @@ var MultiCombobox = ({
|
|
|
15443
15559
|
),
|
|
15444
15560
|
[options]
|
|
15445
15561
|
);
|
|
15446
|
-
const enableSearch = normalizedOptions.length > 10;
|
|
15447
|
-
const
|
|
15448
|
-
|
|
15449
|
-
|
|
15450
|
-
|
|
15451
|
-
|
|
15452
|
-
|
|
15562
|
+
const enableSearch = normalizedOptions.length > 10 || searchMode === "manual" || minSearchLength > 0 || !!onSearchChange;
|
|
15563
|
+
const trimmedQuery = query.trim();
|
|
15564
|
+
const queryMeetsMinimum = trimmedQuery.length >= minSearchLength;
|
|
15565
|
+
const shouldPromptForSearch = minSearchLength > 0 && !queryMeetsMinimum && (searchMode === "manual" || showSearchPromptWhenEmptyQuery);
|
|
15566
|
+
const filtered = React39.useMemo(() => {
|
|
15567
|
+
if (shouldPromptForSearch) return [];
|
|
15568
|
+
if (!enableSearch || searchMode === "manual") return normalizedOptions;
|
|
15569
|
+
const normalizedQuery = trimmedQuery.toLowerCase();
|
|
15570
|
+
if (!normalizedQuery) return normalizedOptions;
|
|
15571
|
+
return normalizedOptions.filter(
|
|
15572
|
+
(opt) => opt.label.toLowerCase().includes(normalizedQuery) || opt.description?.toLowerCase().includes(normalizedQuery)
|
|
15573
|
+
);
|
|
15574
|
+
}, [enableSearch, normalizedOptions, searchMode, shouldPromptForSearch, trimmedQuery]);
|
|
15575
|
+
const renderLimitedOptions = React39.useMemo(() => {
|
|
15576
|
+
if (trimmedQuery || maxInitialOptions === void 0 || maxInitialOptions < 1) {
|
|
15577
|
+
return filtered;
|
|
15578
|
+
}
|
|
15579
|
+
return filtered.slice(0, maxInitialOptions);
|
|
15580
|
+
}, [filtered, maxInitialOptions, trimmedQuery]);
|
|
15581
|
+
const canVirtualize = virtualized && !groupBy;
|
|
15582
|
+
const optionVirtualizer = useVirtualizer2({
|
|
15583
|
+
count: canVirtualize ? renderLimitedOptions.length : 0,
|
|
15584
|
+
getScrollElement: () => optionsListRef.current,
|
|
15585
|
+
estimateSize: () => estimatedItemHeight,
|
|
15586
|
+
initialRect: { width: 0, height: maxHeight },
|
|
15587
|
+
overscan,
|
|
15588
|
+
enabled: canVirtualize
|
|
15589
|
+
});
|
|
15590
|
+
const virtualItems = canVirtualize ? optionVirtualizer.getVirtualItems() : [];
|
|
15591
|
+
const scrollVirtualListToIndex = React39.useCallback((index) => {
|
|
15592
|
+
if (!canVirtualize || renderLimitedOptions.length === 0) return;
|
|
15593
|
+
optionVirtualizer.scrollToIndex(index, { align: "auto" });
|
|
15594
|
+
}, [canVirtualize, optionVirtualizer, renderLimitedOptions.length]);
|
|
15595
|
+
const scrollVirtualListToStart = React39.useCallback(() => {
|
|
15596
|
+
scrollVirtualListToIndex(0);
|
|
15597
|
+
}, [scrollVirtualListToIndex]);
|
|
15453
15598
|
const groupedOptions = React39.useMemo(() => {
|
|
15454
15599
|
if (!groupBy) return null;
|
|
15455
15600
|
const groups = /* @__PURE__ */ new Map();
|
|
15456
|
-
|
|
15601
|
+
renderLimitedOptions.forEach((opt) => {
|
|
15457
15602
|
const group = groupBy(opt);
|
|
15458
15603
|
if (!groups.has(group)) groups.set(group, []);
|
|
15459
15604
|
groups.get(group).push(opt);
|
|
15460
15605
|
});
|
|
15461
15606
|
return groups;
|
|
15462
|
-
}, [
|
|
15607
|
+
}, [renderLimitedOptions, groupBy]);
|
|
15463
15608
|
const toggleSelect = (optionValue) => {
|
|
15464
15609
|
const option = normalizedOptions.find((o) => o.value === optionValue);
|
|
15465
15610
|
if (option?.disabled || disabledOptions.includes(optionValue)) return;
|
|
@@ -15477,11 +15622,26 @@ var MultiCombobox = ({
|
|
|
15477
15622
|
};
|
|
15478
15623
|
const handleKeyDown2 = (e) => {
|
|
15479
15624
|
if (!open) setOpen(true);
|
|
15480
|
-
if (e.key === "
|
|
15625
|
+
if (e.key === "ArrowDown") {
|
|
15481
15626
|
e.preventDefault();
|
|
15482
|
-
if (
|
|
15483
|
-
|
|
15627
|
+
if (renderLimitedOptions.length === 0) return;
|
|
15628
|
+
const next = activeIndex === null ? 0 : (activeIndex + 1) % renderLimitedOptions.length;
|
|
15629
|
+
setActiveIndex(next);
|
|
15630
|
+
scrollVirtualListToIndex(next);
|
|
15631
|
+
} else if (e.key === "ArrowUp") {
|
|
15632
|
+
e.preventDefault();
|
|
15633
|
+
if (renderLimitedOptions.length === 0) return;
|
|
15634
|
+
const next = activeIndex === null ? renderLimitedOptions.length - 1 : (activeIndex - 1 + renderLimitedOptions.length) % renderLimitedOptions.length;
|
|
15635
|
+
setActiveIndex(next);
|
|
15636
|
+
scrollVirtualListToIndex(next);
|
|
15637
|
+
} else if (e.key === "Enter") {
|
|
15638
|
+
e.preventDefault();
|
|
15639
|
+
if (activeIndex !== null && renderLimitedOptions[activeIndex]) {
|
|
15640
|
+
toggleSelect(renderLimitedOptions[activeIndex].value);
|
|
15484
15641
|
}
|
|
15642
|
+
} else if (e.key === "Escape") {
|
|
15643
|
+
e.preventDefault();
|
|
15644
|
+
setOpen(false);
|
|
15485
15645
|
}
|
|
15486
15646
|
};
|
|
15487
15647
|
const handleClearAll = () => {
|
|
@@ -15498,8 +15658,24 @@ var MultiCombobox = ({
|
|
|
15498
15658
|
setTimeout(() => {
|
|
15499
15659
|
inputRef.current?.focus();
|
|
15500
15660
|
}, 100);
|
|
15661
|
+
} else if (!open) {
|
|
15662
|
+
setQuery("");
|
|
15663
|
+
setActiveIndex(null);
|
|
15664
|
+
scrollVirtualListToStart();
|
|
15501
15665
|
}
|
|
15502
|
-
}, [open,
|
|
15666
|
+
}, [enableSearch, open, scrollVirtualListToStart]);
|
|
15667
|
+
React39.useEffect(() => {
|
|
15668
|
+
if (!onSearchChange) return void 0;
|
|
15669
|
+
const timeoutId = window.setTimeout(() => onSearchChange(query), searchDebounceMs);
|
|
15670
|
+
return () => window.clearTimeout(timeoutId);
|
|
15671
|
+
}, [onSearchChange, query, searchDebounceMs]);
|
|
15672
|
+
React39.useEffect(() => {
|
|
15673
|
+
if (process.env.NODE_ENV !== "production" && normalizedOptions.length > 300 && !virtualized && searchMode !== "manual" && maxInitialOptions === void 0) {
|
|
15674
|
+
console.warn(
|
|
15675
|
+
'[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.'
|
|
15676
|
+
);
|
|
15677
|
+
}
|
|
15678
|
+
}, [maxInitialOptions, normalizedOptions.length, searchMode, virtualized]);
|
|
15503
15679
|
const sizeStyles8 = {
|
|
15504
15680
|
sm: {
|
|
15505
15681
|
trigger: "h-8 px-3 py-1.5 text-sm md:h-7 md:text-xs",
|
|
@@ -15533,25 +15709,38 @@ var MultiCombobox = ({
|
|
|
15533
15709
|
const labelId = label ? `${resolvedId}-label` : void 0;
|
|
15534
15710
|
const labelSize = size === "sm" ? "text-xs" : size === "lg" ? "text-base" : "text-sm";
|
|
15535
15711
|
const listboxId = `${resolvedId}-listbox`;
|
|
15536
|
-
const renderOptionItem = (item, index) => {
|
|
15712
|
+
const renderOptionItem = (item, index, virtualItem) => {
|
|
15537
15713
|
const isSelected = value.includes(item.value);
|
|
15538
15714
|
const isDisabled = item.disabled || disabledOptions.includes(item.value);
|
|
15539
15715
|
const optionIcon = item.icon;
|
|
15540
15716
|
const optionDesc = item.description;
|
|
15717
|
+
const itemStyle = {
|
|
15718
|
+
animationDelay: open ? `${Math.min(index * 20, 200)}ms` : "0ms",
|
|
15719
|
+
...virtualItem ? {
|
|
15720
|
+
position: "absolute",
|
|
15721
|
+
top: 0,
|
|
15722
|
+
left: 0,
|
|
15723
|
+
width: "100%",
|
|
15724
|
+
transform: `translateY(${virtualItem.start}px)`
|
|
15725
|
+
} : {}
|
|
15726
|
+
};
|
|
15727
|
+
const measureRef = virtualItem ? optionVirtualizer.measureElement : void 0;
|
|
15541
15728
|
if (renderOption) {
|
|
15542
15729
|
return /* @__PURE__ */ jsx45(
|
|
15543
15730
|
"li",
|
|
15544
15731
|
{
|
|
15545
15732
|
ref: (node) => {
|
|
15733
|
+
measureRef?.(node);
|
|
15546
15734
|
listRef.current[index] = node;
|
|
15547
15735
|
},
|
|
15736
|
+
"data-index": virtualItem?.index,
|
|
15737
|
+
style: itemStyle,
|
|
15548
15738
|
onClick: (e) => {
|
|
15549
15739
|
e.preventDefault();
|
|
15550
15740
|
e.stopPropagation();
|
|
15551
15741
|
if (!isDisabled) toggleSelect(item.value);
|
|
15552
15742
|
inputRef.current?.focus();
|
|
15553
15743
|
},
|
|
15554
|
-
style: { animationDelay: open ? `${Math.min(index * 20, 200)}ms` : "0ms" },
|
|
15555
15744
|
className: cn("dropdown-item", isDisabled && "opacity-50 cursor-not-allowed pointer-events-none"),
|
|
15556
15745
|
children: renderOption(item, isSelected)
|
|
15557
15746
|
},
|
|
@@ -15562,15 +15751,17 @@ var MultiCombobox = ({
|
|
|
15562
15751
|
"li",
|
|
15563
15752
|
{
|
|
15564
15753
|
ref: (node) => {
|
|
15754
|
+
measureRef?.(node);
|
|
15565
15755
|
listRef.current[index] = node;
|
|
15566
15756
|
},
|
|
15757
|
+
"data-index": virtualItem?.index,
|
|
15758
|
+
style: itemStyle,
|
|
15567
15759
|
onClick: (e) => {
|
|
15568
15760
|
e.preventDefault();
|
|
15569
15761
|
e.stopPropagation();
|
|
15570
15762
|
if (!isDisabled) toggleSelect(item.value);
|
|
15571
15763
|
inputRef.current?.focus();
|
|
15572
15764
|
},
|
|
15573
|
-
style: { animationDelay: open ? `${Math.min(index * 20, 200)}ms` : "0ms" },
|
|
15574
15765
|
className: cn(
|
|
15575
15766
|
"dropdown-item flex cursor-pointer items-center gap-3 rounded-full transition-all duration-200",
|
|
15576
15767
|
sizeStyles8[size].item,
|
|
@@ -15634,6 +15825,7 @@ var MultiCombobox = ({
|
|
|
15634
15825
|
onChange: (e) => {
|
|
15635
15826
|
setQuery(e.target.value);
|
|
15636
15827
|
setActiveIndex(null);
|
|
15828
|
+
scrollVirtualListToStart();
|
|
15637
15829
|
},
|
|
15638
15830
|
onKeyDown: handleKeyDown2,
|
|
15639
15831
|
placeholder: searchPlaceholder,
|
|
@@ -15644,7 +15836,10 @@ var MultiCombobox = ({
|
|
|
15644
15836
|
"button",
|
|
15645
15837
|
{
|
|
15646
15838
|
type: "button",
|
|
15647
|
-
onClick: () =>
|
|
15839
|
+
onClick: () => {
|
|
15840
|
+
setQuery("");
|
|
15841
|
+
scrollVirtualListToStart();
|
|
15842
|
+
},
|
|
15648
15843
|
className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors",
|
|
15649
15844
|
children: /* @__PURE__ */ jsx45(X13, { className: "w-4 h-4" })
|
|
15650
15845
|
}
|
|
@@ -15661,34 +15856,56 @@ var MultiCombobox = ({
|
|
|
15661
15856
|
className: cn("overflow-y-auto p-1.5", size === "lg" ? "text-base" : size === "sm" ? "text-xs" : "text-sm"),
|
|
15662
15857
|
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
15858
|
/* @__PURE__ */ jsxs35("div", { className: "relative", children: [
|
|
15664
|
-
/* @__PURE__ */ jsx45(
|
|
15665
|
-
/* @__PURE__ */ jsx45(
|
|
15859
|
+
/* @__PURE__ */ jsx45(Loader23, { className: "h-8 w-8 animate-spin text-primary" }),
|
|
15860
|
+
/* @__PURE__ */ jsx45(Sparkles2, { className: "h-4 w-4 text-primary/60 absolute -top-1 -right-1 animate-pulse" })
|
|
15666
15861
|
] }),
|
|
15667
15862
|
/* @__PURE__ */ jsx45("span", { className: "text-muted-foreground font-medium", children: loadingText })
|
|
15668
|
-
] }) }) :
|
|
15863
|
+
] }) }) : 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: [
|
|
15864
|
+
/* @__PURE__ */ jsx45(Search5, { className: "h-10 w-10 opacity-30 text-muted-foreground" }),
|
|
15865
|
+
/* @__PURE__ */ jsxs35("span", { className: "font-medium block text-foreground", children: [
|
|
15866
|
+
"Type at least ",
|
|
15867
|
+
minSearchLength,
|
|
15868
|
+
" characters to search"
|
|
15869
|
+
] })
|
|
15870
|
+
] }) }) : renderLimitedOptions.length ? groupedOptions ? (
|
|
15669
15871
|
// Render grouped options
|
|
15670
15872
|
Array.from(groupedOptions.entries()).map(([group, items]) => /* @__PURE__ */ jsxs35("li", { className: "mb-2", children: [
|
|
15671
15873
|
/* @__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,
|
|
15874
|
+
/* @__PURE__ */ jsx45("ul", { children: items.map((item) => renderOptionItem(item, renderLimitedOptions.indexOf(item))) })
|
|
15673
15875
|
] }, group))
|
|
15674
15876
|
) : (
|
|
15675
15877
|
// Render flat options
|
|
15676
|
-
|
|
15878
|
+
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
15879
|
) : /* @__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
15880
|
/* @__PURE__ */ jsx45(SearchX2, { className: "h-10 w-10 opacity-30 text-muted-foreground" }),
|
|
15679
15881
|
/* @__PURE__ */ jsxs35("div", { className: "space-y-1", children: [
|
|
15680
15882
|
/* @__PURE__ */ jsx45("span", { className: "font-medium block", children: emptyText }),
|
|
15681
15883
|
query && /* @__PURE__ */ jsx45("span", { className: "text-xs opacity-60", children: "Try a different search term" })
|
|
15682
15884
|
] }),
|
|
15683
|
-
query && /* @__PURE__ */ jsxs35(
|
|
15684
|
-
|
|
15685
|
-
|
|
15686
|
-
|
|
15885
|
+
query && /* @__PURE__ */ jsxs35(
|
|
15886
|
+
"button",
|
|
15887
|
+
{
|
|
15888
|
+
type: "button",
|
|
15889
|
+
onClick: () => {
|
|
15890
|
+
setQuery("");
|
|
15891
|
+
scrollVirtualListToStart();
|
|
15892
|
+
},
|
|
15893
|
+
className: "text-xs text-primary hover:underline flex items-center gap-1",
|
|
15894
|
+
children: [
|
|
15895
|
+
/* @__PURE__ */ jsx45(X13, { className: "w-3 h-3" }),
|
|
15896
|
+
"Clear search"
|
|
15897
|
+
]
|
|
15898
|
+
}
|
|
15899
|
+
)
|
|
15687
15900
|
] }) })
|
|
15688
15901
|
}
|
|
15689
15902
|
)
|
|
15690
15903
|
] });
|
|
15691
|
-
const
|
|
15904
|
+
const selectedOptionFallbackMap = React39.useMemo(
|
|
15905
|
+
() => new Map((selectedOptionsProp ?? []).map((option) => [option.value, option])),
|
|
15906
|
+
[selectedOptionsProp]
|
|
15907
|
+
);
|
|
15908
|
+
const selectedOptions = value.map((v) => normalizedOptions.find((o) => o.value === v) ?? selectedOptionFallbackMap.get(v)).filter(Boolean);
|
|
15692
15909
|
const visibleTags = maxTagsVisible ? selectedOptions.slice(0, maxTagsVisible) : selectedOptions;
|
|
15693
15910
|
const hiddenCount = maxTagsVisible ? Math.max(0, selectedOptions.length - maxTagsVisible) : 0;
|
|
15694
15911
|
const triggerButton = /* @__PURE__ */ jsxs35(
|
|
@@ -17732,8 +17949,8 @@ function CategoryTreeSelect(props) {
|
|
|
17732
17949
|
}
|
|
17733
17950
|
|
|
17734
17951
|
// 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
|
|
17952
|
+
import { useState as useState32, useRef as useRef21, useCallback as useCallback18 } from "react";
|
|
17953
|
+
import { Upload, X as X15, Image as ImageIcon, Loader2 as Loader24, Check as Check7 } from "lucide-react";
|
|
17737
17954
|
import { jsx as jsx50, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
17738
17955
|
function ImageUpload({
|
|
17739
17956
|
onUpload,
|
|
@@ -17761,7 +17978,7 @@ function ImageUpload({
|
|
|
17761
17978
|
md: "w-24 h-24",
|
|
17762
17979
|
lg: "w-32 h-32"
|
|
17763
17980
|
};
|
|
17764
|
-
const handleDragOver =
|
|
17981
|
+
const handleDragOver = useCallback18(
|
|
17765
17982
|
(e) => {
|
|
17766
17983
|
e.preventDefault();
|
|
17767
17984
|
if (!disabled) {
|
|
@@ -17770,11 +17987,11 @@ function ImageUpload({
|
|
|
17770
17987
|
},
|
|
17771
17988
|
[disabled]
|
|
17772
17989
|
);
|
|
17773
|
-
const handleDragLeave =
|
|
17990
|
+
const handleDragLeave = useCallback18((e) => {
|
|
17774
17991
|
e.preventDefault();
|
|
17775
17992
|
setIsDragging(false);
|
|
17776
17993
|
}, []);
|
|
17777
|
-
const handleFiles =
|
|
17994
|
+
const handleFiles = useCallback18(
|
|
17778
17995
|
async (files) => {
|
|
17779
17996
|
if (files.length === 0) return;
|
|
17780
17997
|
const validFiles = files.filter((file) => {
|
|
@@ -17841,7 +18058,7 @@ function ImageUpload({
|
|
|
17841
18058
|
},
|
|
17842
18059
|
[maxSize, addToast, onUpload]
|
|
17843
18060
|
);
|
|
17844
|
-
const handleDrop =
|
|
18061
|
+
const handleDrop = useCallback18(
|
|
17845
18062
|
(e) => {
|
|
17846
18063
|
e.preventDefault();
|
|
17847
18064
|
setIsDragging(false);
|
|
@@ -17851,7 +18068,7 @@ function ImageUpload({
|
|
|
17851
18068
|
},
|
|
17852
18069
|
[disabled, handleFiles]
|
|
17853
18070
|
);
|
|
17854
|
-
const handleFileSelect =
|
|
18071
|
+
const handleFileSelect = useCallback18(
|
|
17855
18072
|
(e) => {
|
|
17856
18073
|
const files = Array.from(e.target.files || []);
|
|
17857
18074
|
handleFiles(files);
|
|
@@ -17883,7 +18100,7 @@ function ImageUpload({
|
|
|
17883
18100
|
onDrop: handleDrop,
|
|
17884
18101
|
children: [
|
|
17885
18102
|
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(
|
|
18103
|
+
/* @__PURE__ */ jsx50(Loader24, { className: "w-6 h-6 animate-spin text-primary" }),
|
|
17887
18104
|
/* @__PURE__ */ jsx50("span", { className: "text-sm font-medium", children: "Uploading..." })
|
|
17888
18105
|
] }) }),
|
|
17889
18106
|
/* @__PURE__ */ jsxs40("div", { className: "space-y-4", children: [
|
|
@@ -17967,11 +18184,11 @@ import {
|
|
|
17967
18184
|
FileSpreadsheet,
|
|
17968
18185
|
FileText,
|
|
17969
18186
|
FileVideo,
|
|
17970
|
-
Loader2 as
|
|
18187
|
+
Loader2 as Loader25,
|
|
17971
18188
|
Trash2,
|
|
17972
18189
|
Upload as Upload2
|
|
17973
18190
|
} from "lucide-react";
|
|
17974
|
-
import { useCallback as
|
|
18191
|
+
import { useCallback as useCallback19, useMemo as useMemo20, useRef as useRef22, useState as useState33 } from "react";
|
|
17975
18192
|
import { Fragment as Fragment16, jsx as jsx51, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
17976
18193
|
var formatFileSize = (bytes) => {
|
|
17977
18194
|
if (bytes === 0) return "0 Bytes";
|
|
@@ -18116,7 +18333,7 @@ function FileUpload({
|
|
|
18116
18333
|
[]
|
|
18117
18334
|
);
|
|
18118
18335
|
const currentSize = sizeConfig[size];
|
|
18119
|
-
const handleDragOver =
|
|
18336
|
+
const handleDragOver = useCallback19(
|
|
18120
18337
|
(e) => {
|
|
18121
18338
|
e.preventDefault();
|
|
18122
18339
|
e.stopPropagation();
|
|
@@ -18126,12 +18343,12 @@ function FileUpload({
|
|
|
18126
18343
|
},
|
|
18127
18344
|
[disabled]
|
|
18128
18345
|
);
|
|
18129
|
-
const handleDragLeave =
|
|
18346
|
+
const handleDragLeave = useCallback19((e) => {
|
|
18130
18347
|
e.preventDefault();
|
|
18131
18348
|
e.stopPropagation();
|
|
18132
18349
|
setIsDragging(false);
|
|
18133
18350
|
}, []);
|
|
18134
|
-
const processFiles =
|
|
18351
|
+
const processFiles = useCallback19(
|
|
18135
18352
|
async (fileList) => {
|
|
18136
18353
|
if (fileList.length === 0) return;
|
|
18137
18354
|
const remainingSlots = maxFiles - files.length;
|
|
@@ -18204,7 +18421,7 @@ function FileUpload({
|
|
|
18204
18421
|
},
|
|
18205
18422
|
[files, maxFiles, maxSize, uploadHandler, onUpload, onChange, addToast, t]
|
|
18206
18423
|
);
|
|
18207
|
-
const handleDrop =
|
|
18424
|
+
const handleDrop = useCallback19(
|
|
18208
18425
|
(e) => {
|
|
18209
18426
|
e.preventDefault();
|
|
18210
18427
|
e.stopPropagation();
|
|
@@ -18215,7 +18432,7 @@ function FileUpload({
|
|
|
18215
18432
|
},
|
|
18216
18433
|
[disabled, processFiles]
|
|
18217
18434
|
);
|
|
18218
|
-
const handleFileSelect =
|
|
18435
|
+
const handleFileSelect = useCallback19(
|
|
18219
18436
|
(e) => {
|
|
18220
18437
|
const selectedFiles = Array.from(e.target.files || []);
|
|
18221
18438
|
processFiles(selectedFiles);
|
|
@@ -18225,7 +18442,7 @@ function FileUpload({
|
|
|
18225
18442
|
},
|
|
18226
18443
|
[processFiles]
|
|
18227
18444
|
);
|
|
18228
|
-
const handleRemove =
|
|
18445
|
+
const handleRemove = useCallback19(
|
|
18229
18446
|
(fileId) => {
|
|
18230
18447
|
setFiles((prev) => {
|
|
18231
18448
|
const fileToRemove = prev.find((f) => f.id === fileId);
|
|
@@ -18243,7 +18460,7 @@ function FileUpload({
|
|
|
18243
18460
|
const handleBrowseClick = () => {
|
|
18244
18461
|
fileInputRef.current?.click();
|
|
18245
18462
|
};
|
|
18246
|
-
const handleRetry =
|
|
18463
|
+
const handleRetry = useCallback19(
|
|
18247
18464
|
(fileEntry) => {
|
|
18248
18465
|
if (!uploadHandler || !fileEntry.file) return;
|
|
18249
18466
|
processFiles([fileEntry.file]);
|
|
@@ -18282,7 +18499,7 @@ function FileUpload({
|
|
|
18282
18499
|
/* @__PURE__ */ jsxs41("div", { className: "flex items-center gap-2", children: [
|
|
18283
18500
|
/* @__PURE__ */ jsx51("span", { className: cn("text-muted-foreground", size === "lg" ? "text-sm" : "text-xs"), children: file.formattedSize }),
|
|
18284
18501
|
file.status === "uploading" && /* @__PURE__ */ jsxs41("span", { className: "flex items-center gap-1 text-primary text-xs", children: [
|
|
18285
|
-
/* @__PURE__ */ jsx51(
|
|
18502
|
+
/* @__PURE__ */ jsx51(Loader25, { className: "w-3 h-3 animate-spin" }),
|
|
18286
18503
|
file.progress,
|
|
18287
18504
|
"%"
|
|
18288
18505
|
] }),
|
|
@@ -25924,7 +26141,7 @@ var EditorToolbar = ({
|
|
|
25924
26141
|
};
|
|
25925
26142
|
|
|
25926
26143
|
// src/components/UEditor/menus.tsx
|
|
25927
|
-
import { useCallback as
|
|
26144
|
+
import { useCallback as useCallback22, useEffect as useEffect34, useMemo as useMemo23, useRef as useRef31, useState as useState45 } from "react";
|
|
25928
26145
|
import { createPortal as createPortal8 } from "react-dom";
|
|
25929
26146
|
import {
|
|
25930
26147
|
AlignCenter as AlignCenter2,
|
|
@@ -26216,7 +26433,7 @@ var CustomBubbleMenu = ({
|
|
|
26216
26433
|
const menuRef = useRef31(null);
|
|
26217
26434
|
const keepOpenRef = useRef31(false);
|
|
26218
26435
|
const showTimeoutRef = useRef31(null);
|
|
26219
|
-
const setKeepOpen =
|
|
26436
|
+
const setKeepOpen = useCallback22((next) => {
|
|
26220
26437
|
keepOpenRef.current = next;
|
|
26221
26438
|
if (next) setIsVisible(true);
|
|
26222
26439
|
}, []);
|