@underverse-ui/underverse 0.2.59 → 0.2.60
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/dist/index.cjs +452 -157
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +36 -4
- package/dist/index.d.ts +36 -4
- package/dist/index.js +491 -196
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4830,20 +4830,42 @@ var DropdownMenu = ({
|
|
|
4830
4830
|
}
|
|
4831
4831
|
);
|
|
4832
4832
|
};
|
|
4833
|
-
var DropdownMenuItem = ({
|
|
4833
|
+
var DropdownMenuItem = ({
|
|
4834
|
+
children,
|
|
4835
|
+
label,
|
|
4836
|
+
description,
|
|
4837
|
+
icon: Icon,
|
|
4838
|
+
onClick,
|
|
4839
|
+
disabled,
|
|
4840
|
+
destructive,
|
|
4841
|
+
active,
|
|
4842
|
+
shortcut,
|
|
4843
|
+
className
|
|
4844
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
4834
4845
|
"button",
|
|
4835
4846
|
{
|
|
4836
4847
|
onClick,
|
|
4837
4848
|
disabled,
|
|
4849
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
4838
4850
|
className: cn(
|
|
4839
|
-
"flex w-full items-center gap-2 px-3 py-2 text-sm rounded-lg transition-colors",
|
|
4851
|
+
"flex w-full items-center gap-2 px-3 py-2 text-sm rounded-lg transition-colors group",
|
|
4840
4852
|
"hover:bg-accent hover:text-accent-foreground",
|
|
4841
4853
|
"focus:bg-accent focus:text-accent-foreground focus:outline-none",
|
|
4842
4854
|
"disabled:opacity-50 disabled:cursor-not-allowed",
|
|
4843
4855
|
destructive && "text-destructive hover:bg-destructive/10 focus:bg-destructive/10",
|
|
4856
|
+
active && "bg-primary/10 text-primary",
|
|
4844
4857
|
className
|
|
4845
4858
|
),
|
|
4846
|
-
children
|
|
4859
|
+
children: [
|
|
4860
|
+
Icon && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Icon, { className: cn("w-4 h-4 shrink-0", active ? "text-primary" : "opacity-60 group-hover:opacity-100") }),
|
|
4861
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex-1 text-left", children: [
|
|
4862
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cn("font-medium", description && "leading-tight"), children: label }),
|
|
4863
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "text-xs text-muted-foreground", children: description }),
|
|
4864
|
+
children
|
|
4865
|
+
] }),
|
|
4866
|
+
shortcut && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "ml-2 text-xs text-muted-foreground opacity-60", children: shortcut }),
|
|
4867
|
+
active && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("svg", { className: "w-4 h-4 text-primary shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("polyline", { points: "20 6 9 17 4 12" }) })
|
|
4868
|
+
]
|
|
4847
4869
|
}
|
|
4848
4870
|
);
|
|
4849
4871
|
var DropdownMenuSeparator = ({ className }) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cn("h-px bg-border my-1", className) });
|
|
@@ -4888,6 +4910,15 @@ var getOptionLabel = (option) => {
|
|
|
4888
4910
|
var getOptionValue = (option) => {
|
|
4889
4911
|
return typeof option === "string" ? option : option.value;
|
|
4890
4912
|
};
|
|
4913
|
+
var getOptionIcon = (option) => {
|
|
4914
|
+
return typeof option === "string" ? void 0 : option.icon;
|
|
4915
|
+
};
|
|
4916
|
+
var getOptionDescription = (option) => {
|
|
4917
|
+
return typeof option === "string" ? void 0 : option.description;
|
|
4918
|
+
};
|
|
4919
|
+
var getOptionDisabled = (option) => {
|
|
4920
|
+
return typeof option === "string" ? false : option.disabled ?? false;
|
|
4921
|
+
};
|
|
4891
4922
|
var findOptionByValue = (options, value) => {
|
|
4892
4923
|
return options.find((opt) => getOptionValue(opt) === value);
|
|
4893
4924
|
};
|
|
@@ -4902,13 +4933,21 @@ var Combobox = ({
|
|
|
4902
4933
|
className,
|
|
4903
4934
|
disabled = false,
|
|
4904
4935
|
size = "md",
|
|
4936
|
+
variant = "default",
|
|
4905
4937
|
allowClear = false,
|
|
4906
4938
|
usePortal = true,
|
|
4907
4939
|
label,
|
|
4908
4940
|
required,
|
|
4909
4941
|
fontBold = false,
|
|
4910
4942
|
loading: loading2 = false,
|
|
4911
|
-
loadingText = "Loading..."
|
|
4943
|
+
loadingText = "Loading...",
|
|
4944
|
+
showSelectedIcon = true,
|
|
4945
|
+
maxHeight = 280,
|
|
4946
|
+
groupBy,
|
|
4947
|
+
renderOption,
|
|
4948
|
+
renderValue,
|
|
4949
|
+
error,
|
|
4950
|
+
helperText
|
|
4912
4951
|
}) => {
|
|
4913
4952
|
const [open, setOpen] = React20.useState(false);
|
|
4914
4953
|
const [query, setQuery] = React20.useState("");
|
|
@@ -4926,6 +4965,7 @@ var Combobox = ({
|
|
|
4926
4965
|
);
|
|
4927
4966
|
const triggerRef = React20.useRef(null);
|
|
4928
4967
|
const handleSelect = (option) => {
|
|
4968
|
+
if (getOptionDisabled(option)) return;
|
|
4929
4969
|
const val = getOptionValue(option);
|
|
4930
4970
|
if (val !== void 0 && val !== null) {
|
|
4931
4971
|
onChange(val);
|
|
@@ -4950,9 +4990,64 @@ var Combobox = ({
|
|
|
4950
4990
|
}, [open, enableSearch]);
|
|
4951
4991
|
const selectedOption = findOptionByValue(options, value);
|
|
4952
4992
|
const displayValue = selectedOption ? getOptionLabel(selectedOption) : "";
|
|
4993
|
+
const selectedIcon = selectedOption ? getOptionIcon(selectedOption) : void 0;
|
|
4994
|
+
const groupedOptions = React20.useMemo(() => {
|
|
4995
|
+
if (!groupBy) return null;
|
|
4996
|
+
const groups = {};
|
|
4997
|
+
filteredOptions.forEach((opt) => {
|
|
4998
|
+
const group = groupBy(opt);
|
|
4999
|
+
if (!groups[group]) groups[group] = [];
|
|
5000
|
+
groups[group].push(opt);
|
|
5001
|
+
});
|
|
5002
|
+
return groups;
|
|
5003
|
+
}, [filteredOptions, groupBy]);
|
|
5004
|
+
const renderOptionItem = (item, index) => {
|
|
5005
|
+
const itemValue = getOptionValue(item);
|
|
5006
|
+
const itemLabel = getOptionLabel(item);
|
|
5007
|
+
const itemIcon = getOptionIcon(item);
|
|
5008
|
+
const itemDescription = getOptionDescription(item);
|
|
5009
|
+
const itemDisabled = getOptionDisabled(item);
|
|
5010
|
+
const isSelected = itemValue === value;
|
|
5011
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
5012
|
+
"li",
|
|
5013
|
+
{
|
|
5014
|
+
ref: (node) => {
|
|
5015
|
+
listRef.current[index] = node;
|
|
5016
|
+
},
|
|
5017
|
+
id: `combobox-item-${index}`,
|
|
5018
|
+
role: "option",
|
|
5019
|
+
tabIndex: -1,
|
|
5020
|
+
"aria-selected": isSelected,
|
|
5021
|
+
"aria-disabled": itemDisabled,
|
|
5022
|
+
onClick: () => !itemDisabled && handleSelect(item),
|
|
5023
|
+
style: {
|
|
5024
|
+
animationDelay: open ? `${Math.min(index * 15, 150)}ms` : "0ms"
|
|
5025
|
+
},
|
|
5026
|
+
className: cn(
|
|
5027
|
+
"dropdown-item group flex cursor-pointer items-center gap-3 rounded-xl px-3 py-2.5 text-sm",
|
|
5028
|
+
"outline-none focus:outline-none focus-visible:outline-none",
|
|
5029
|
+
"transition-all duration-150",
|
|
5030
|
+
!itemDisabled && "hover:bg-accent/80 hover:shadow-sm",
|
|
5031
|
+
!itemDisabled && "focus:bg-accent focus:text-accent-foreground",
|
|
5032
|
+
index === activeIndex && !itemDisabled && "bg-accent/60",
|
|
5033
|
+
isSelected && "bg-primary/10 text-primary font-medium",
|
|
5034
|
+
itemDisabled && "opacity-50 cursor-not-allowed"
|
|
5035
|
+
),
|
|
5036
|
+
children: [
|
|
5037
|
+
itemIcon && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: cn("shrink-0 w-5 h-5 flex items-center justify-center", isSelected ? "text-primary" : "text-muted-foreground"), children: itemIcon }),
|
|
5038
|
+
renderOption ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "flex-1 min-w-0", children: renderOption(item, isSelected) }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex-1 min-w-0", children: [
|
|
5039
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "block truncate", children: itemLabel }),
|
|
5040
|
+
itemDescription && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "block text-xs text-muted-foreground truncate mt-0.5", children: itemDescription })
|
|
5041
|
+
] }),
|
|
5042
|
+
isSelected && showSelectedIcon && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "shrink-0 ml-auto", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.Check, { className: "h-4 w-4 text-primary" }) })
|
|
5043
|
+
]
|
|
5044
|
+
},
|
|
5045
|
+
`${itemValue}-${index}`
|
|
5046
|
+
);
|
|
5047
|
+
};
|
|
4953
5048
|
const dropdownBody = /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { "data-combobox-dropdown": true, "data-state": open ? "open" : "closed", role: "listbox", id: `${resolvedId}-listbox`, className: "w-full", children: [
|
|
4954
|
-
enableSearch && /* @__PURE__ */ (0, import_jsx_runtime25.
|
|
4955
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.Search, { className: "absolute left-
|
|
5049
|
+
enableSearch && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "relative p-2.5 border-b border-border/30", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "relative", children: [
|
|
5050
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground/60 transition-colors peer-focus:text-primary" }),
|
|
4956
5051
|
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
4957
5052
|
"input",
|
|
4958
5053
|
{
|
|
@@ -4977,7 +5072,7 @@ var Combobox = ({
|
|
|
4977
5072
|
});
|
|
4978
5073
|
} else if (e.key === "Enter") {
|
|
4979
5074
|
e.preventDefault();
|
|
4980
|
-
if (activeIndex !== null && filteredOptions[activeIndex]) {
|
|
5075
|
+
if (activeIndex !== null && filteredOptions[activeIndex] && !getOptionDisabled(filteredOptions[activeIndex])) {
|
|
4981
5076
|
handleSelect(filteredOptions[activeIndex]);
|
|
4982
5077
|
}
|
|
4983
5078
|
} else if (e.key === "Escape") {
|
|
@@ -4986,63 +5081,69 @@ var Combobox = ({
|
|
|
4986
5081
|
}
|
|
4987
5082
|
},
|
|
4988
5083
|
placeholder: searchPlaceholder,
|
|
4989
|
-
className:
|
|
5084
|
+
className: cn(
|
|
5085
|
+
"peer w-full rounded-xl bg-muted/40 py-2.5 pl-9 pr-3 text-sm",
|
|
5086
|
+
"border border-transparent",
|
|
5087
|
+
"focus:outline-none focus:bg-background focus:border-primary/30 focus:ring-2 focus:ring-primary/10",
|
|
5088
|
+
"transition-all duration-200",
|
|
5089
|
+
"placeholder:text-muted-foreground/50"
|
|
5090
|
+
),
|
|
4990
5091
|
"aria-autocomplete": "list",
|
|
4991
5092
|
"aria-activedescendant": activeIndex != null ? `combobox-item-${activeIndex}` : void 0
|
|
4992
5093
|
}
|
|
5094
|
+
),
|
|
5095
|
+
query && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
5096
|
+
"button",
|
|
5097
|
+
{
|
|
5098
|
+
type: "button",
|
|
5099
|
+
onClick: () => setQuery(""),
|
|
5100
|
+
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",
|
|
5101
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.X, { className: "h-3.5 w-3.5" })
|
|
5102
|
+
}
|
|
4993
5103
|
)
|
|
4994
|
-
] }),
|
|
4995
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "
|
|
4996
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
4997
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "text-sm text-muted-foreground", children: loadingText
|
|
4998
|
-
] }) }) : filteredOptions.length > 0 ?
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5104
|
+
] }) }),
|
|
5105
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "overflow-y-auto overscroll-contain", style: { maxHeight }, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "p-1.5", children: loading2 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "px-3 py-10 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col items-center gap-3 animate-in fade-in-0 zoom-in-95 duration-300", children: [
|
|
5106
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "relative", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "w-10 h-10 rounded-full border-2 border-primary/20 border-t-primary animate-spin" }) }),
|
|
5107
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "text-sm text-muted-foreground", children: loadingText })
|
|
5108
|
+
] }) }) : filteredOptions.length > 0 ? groupedOptions ? (
|
|
5109
|
+
// Render grouped options
|
|
5110
|
+
Object.entries(groupedOptions).map(([group, items], groupIndex) => /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: cn(groupIndex > 0 && "mt-2 pt-2 border-t border-border/30"), children: [
|
|
5111
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "px-3 py-1.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: group }),
|
|
5112
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("ul", { className: "space-y-0.5", children: items.map((item, index) => renderOptionItem(item, groupIndex * 100 + index)) })
|
|
5113
|
+
] }, group))
|
|
5114
|
+
) : (
|
|
5115
|
+
// Render flat options
|
|
5116
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("ul", { className: "space-y-0.5", children: filteredOptions.map((item, index) => renderOptionItem(item, index)) })
|
|
5117
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "px-3 py-10 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col items-center gap-3 animate-in fade-in-0 zoom-in-95 duration-300", children: [
|
|
5118
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "w-12 h-12 rounded-full bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.SearchX, { className: "h-6 w-6 text-muted-foreground/60" }) }),
|
|
5119
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "space-y-1", children: [
|
|
5120
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "block text-sm font-medium text-foreground", children: emptyText }),
|
|
5121
|
+
query && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "block text-xs text-muted-foreground", children: "Try a different search term" })
|
|
5122
|
+
] }),
|
|
5123
|
+
query && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
5124
|
+
"button",
|
|
5004
5125
|
{
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
"aria-selected": isSelected,
|
|
5012
|
-
onClick: () => handleSelect(item),
|
|
5013
|
-
style: {
|
|
5014
|
-
animationDelay: open ? `${Math.min(index * 20, 200)}ms` : "0ms"
|
|
5015
|
-
},
|
|
5016
|
-
className: cn(
|
|
5017
|
-
"dropdown-item group flex cursor-pointer items-center justify-between rounded-lg px-2.5 py-1.5 text-sm",
|
|
5018
|
-
"outline-none focus:outline-none focus-visible:outline-none",
|
|
5019
|
-
"hover:bg-accent hover:text-accent-foreground",
|
|
5020
|
-
"focus:bg-accent focus:text-accent-foreground",
|
|
5021
|
-
"data-disabled:pointer-events-none data-disabled:opacity-50",
|
|
5022
|
-
index === activeIndex && "bg-accent text-accent-foreground",
|
|
5023
|
-
isSelected && "bg-accent text-accent-foreground"
|
|
5024
|
-
),
|
|
5025
|
-
children: [
|
|
5026
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "truncate", children: itemLabel }),
|
|
5027
|
-
isSelected && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.Check, { className: "h-4 w-4 text-primary" })
|
|
5028
|
-
]
|
|
5029
|
-
},
|
|
5030
|
-
`${itemValue}-${index}`
|
|
5031
|
-
);
|
|
5032
|
-
}) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("li", { className: "px-3 py-8 text-center text-muted-foreground text-sm", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col items-center gap-2 animate-in fade-in-0 zoom-in-95 duration-300", children: [
|
|
5033
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.SearchX, { className: "h-8 w-8 opacity-40 text-muted-foreground" }),
|
|
5034
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "text-sm", children: emptyText }),
|
|
5035
|
-
query && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { type: "button", onClick: () => setQuery(""), className: "text-xs text-primary hover:underline", children: "Clear" })
|
|
5126
|
+
type: "button",
|
|
5127
|
+
onClick: () => setQuery(""),
|
|
5128
|
+
className: "px-3 py-1.5 text-xs font-medium text-primary bg-primary/10 rounded-full hover:bg-primary/20 transition-colors",
|
|
5129
|
+
children: "Clear search"
|
|
5130
|
+
}
|
|
5131
|
+
)
|
|
5036
5132
|
] }) }) }) })
|
|
5037
5133
|
] });
|
|
5038
5134
|
const sizeStyles8 = {
|
|
5039
|
-
// Keep consistent with Input size heights
|
|
5040
5135
|
sm: "h-8 py-1.5 text-sm md:h-7 md:text-xs",
|
|
5041
5136
|
md: "h-10 py-2 text-sm",
|
|
5042
5137
|
lg: "h-12 py-3 text-base"
|
|
5043
5138
|
};
|
|
5139
|
+
const variantStyles6 = {
|
|
5140
|
+
default: "border border-input bg-background hover:bg-accent/5 hover:border-primary/40",
|
|
5141
|
+
outline: "border-2 border-input bg-transparent hover:border-primary/60",
|
|
5142
|
+
ghost: "border-0 bg-transparent hover:bg-accent/50",
|
|
5143
|
+
filled: "border-0 bg-muted/50 hover:bg-muted/80"
|
|
5144
|
+
};
|
|
5044
5145
|
const labelSize = size === "sm" ? "text-xs" : size === "lg" ? "text-base" : "text-sm";
|
|
5045
|
-
const radiusClass = size === "sm" ? "rounded-
|
|
5146
|
+
const radiusClass = size === "sm" ? "rounded-lg" : "rounded-xl";
|
|
5046
5147
|
const verticalGap = size === "sm" ? "space-y-1.5" : "space-y-2";
|
|
5047
5148
|
const triggerButtonBaseProps = {
|
|
5048
5149
|
ref: triggerRef,
|
|
@@ -5052,6 +5153,7 @@ var Combobox = ({
|
|
|
5052
5153
|
"aria-haspopup": "listbox",
|
|
5053
5154
|
"aria-expanded": open,
|
|
5054
5155
|
"aria-controls": `${resolvedId}-listbox`,
|
|
5156
|
+
"aria-invalid": !!error,
|
|
5055
5157
|
id: resolvedId,
|
|
5056
5158
|
"aria-labelledby": labelId,
|
|
5057
5159
|
onKeyDown: (e) => {
|
|
@@ -5065,18 +5167,21 @@ var Combobox = ({
|
|
|
5065
5167
|
}
|
|
5066
5168
|
},
|
|
5067
5169
|
className: cn(
|
|
5068
|
-
"flex w-full items-center justify-between
|
|
5170
|
+
"group flex w-full items-center justify-between px-3 transition-all duration-200",
|
|
5069
5171
|
radiusClass,
|
|
5070
5172
|
sizeStyles8[size],
|
|
5071
|
-
|
|
5173
|
+
variantStyles6[variant],
|
|
5174
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:border-primary",
|
|
5072
5175
|
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
5073
|
-
"
|
|
5176
|
+
open && "ring-2 ring-primary/20 border-primary",
|
|
5177
|
+
!!error && "border-destructive focus-visible:ring-destructive/30",
|
|
5074
5178
|
className
|
|
5075
5179
|
)
|
|
5076
5180
|
};
|
|
5077
5181
|
const triggerContents = /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
|
|
5078
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className:
|
|
5079
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.
|
|
5182
|
+
selectedIcon && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "shrink-0 w-5 h-5 mr-2 flex items-center justify-center text-primary", children: selectedIcon }),
|
|
5183
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: cn("flex-1 truncate text-left", !displayValue && "text-muted-foreground", fontBold && "font-semibold"), children: renderValue && selectedOption ? renderValue(selectedOption) : displayValue || placeholder }),
|
|
5184
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex items-center gap-1.5 ml-2 shrink-0", children: [
|
|
5080
5185
|
allowClear && value && !disabled && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
5081
5186
|
"div",
|
|
5082
5187
|
{
|
|
@@ -5085,11 +5190,23 @@ var Combobox = ({
|
|
|
5085
5190
|
"aria-label": "Clear selection",
|
|
5086
5191
|
onClick: handleClear,
|
|
5087
5192
|
onKeyDown: (e) => (e.key === "Enter" || e.key === " ") && handleClear(e),
|
|
5088
|
-
className:
|
|
5089
|
-
|
|
5193
|
+
className: cn(
|
|
5194
|
+
"opacity-0 group-hover:opacity-100 transition-all duration-200",
|
|
5195
|
+
"p-1 rounded-lg hover:bg-destructive/10 text-muted-foreground hover:text-destructive"
|
|
5196
|
+
),
|
|
5197
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.X, { className: "h-3.5 w-3.5" })
|
|
5090
5198
|
}
|
|
5091
5199
|
),
|
|
5092
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
5200
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
5201
|
+
import_lucide_react12.ChevronDown,
|
|
5202
|
+
{
|
|
5203
|
+
className: cn(
|
|
5204
|
+
"h-4 w-4 text-muted-foreground transition-all duration-300 ease-out",
|
|
5205
|
+
open && "rotate-180 text-primary",
|
|
5206
|
+
!open && "group-hover:text-foreground"
|
|
5207
|
+
)
|
|
5208
|
+
}
|
|
5209
|
+
)
|
|
5093
5210
|
] })
|
|
5094
5211
|
] });
|
|
5095
5212
|
const triggerButtonForPopover = /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { ...triggerButtonBaseProps, children: triggerContents });
|
|
@@ -5136,7 +5253,18 @@ var Combobox = ({
|
|
|
5136
5253
|
}
|
|
5137
5254
|
) : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "relative", children: [
|
|
5138
5255
|
triggerButtonInline,
|
|
5139
|
-
open && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "absolute left-0 top-full mt-1 z-50 w-full", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "rounded-2xl border
|
|
5256
|
+
open && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "absolute left-0 top-full mt-1 z-50 w-full", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "rounded-2xl border text-popover-foreground shadow-md backdrop-blur-sm bg-popover/95 border-border/60", children: dropdownBody }) })
|
|
5257
|
+
] }),
|
|
5258
|
+
(helperText || error) && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("p", { className: cn("text-xs transition-colors duration-200 flex items-center gap-1.5", error ? "text-destructive" : "text-muted-foreground"), children: [
|
|
5259
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: "w-3.5 h-3.5 shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
5260
|
+
"path",
|
|
5261
|
+
{
|
|
5262
|
+
fillRule: "evenodd",
|
|
5263
|
+
d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z",
|
|
5264
|
+
clipRule: "evenodd"
|
|
5265
|
+
}
|
|
5266
|
+
) }),
|
|
5267
|
+
error || helperText
|
|
5140
5268
|
] })
|
|
5141
5269
|
] });
|
|
5142
5270
|
};
|
|
@@ -7472,7 +7600,8 @@ var MultiCombobox = ({
|
|
|
7472
7600
|
options,
|
|
7473
7601
|
value,
|
|
7474
7602
|
onChange,
|
|
7475
|
-
placeholder = "
|
|
7603
|
+
placeholder = "Select...",
|
|
7604
|
+
searchPlaceholder = "Search...",
|
|
7476
7605
|
maxSelected,
|
|
7477
7606
|
disabledOptions = [],
|
|
7478
7607
|
showTags = true,
|
|
@@ -7480,13 +7609,22 @@ var MultiCombobox = ({
|
|
|
7480
7609
|
className,
|
|
7481
7610
|
disabled = false,
|
|
7482
7611
|
size = "md",
|
|
7612
|
+
variant = "default",
|
|
7483
7613
|
label,
|
|
7484
7614
|
title,
|
|
7485
7615
|
required,
|
|
7486
7616
|
displayFormat = (option) => option.label,
|
|
7487
7617
|
loading: loading2 = false,
|
|
7488
7618
|
loadingText = "Loading...",
|
|
7489
|
-
emptyText = "No results found"
|
|
7619
|
+
emptyText = "No results found",
|
|
7620
|
+
showSelectedIcons = true,
|
|
7621
|
+
maxHeight = 280,
|
|
7622
|
+
groupBy,
|
|
7623
|
+
renderOption,
|
|
7624
|
+
renderTag,
|
|
7625
|
+
error,
|
|
7626
|
+
helperText,
|
|
7627
|
+
maxTagsVisible = 3
|
|
7490
7628
|
}) => {
|
|
7491
7629
|
const [query, setQuery] = React27.useState("");
|
|
7492
7630
|
const [open, setOpen] = React27.useState(false);
|
|
@@ -7496,16 +7634,31 @@ var MultiCombobox = ({
|
|
|
7496
7634
|
const triggerRef = React27.useRef(null);
|
|
7497
7635
|
useShadCNAnimations();
|
|
7498
7636
|
const normalizedOptions = React27.useMemo(
|
|
7499
|
-
() => options.map(
|
|
7637
|
+
() => options.map(
|
|
7638
|
+
(o) => typeof o === "string" ? { value: o, label: o } : { value: o.value, label: o.label, icon: o.icon, description: o.description, disabled: o.disabled, group: o.group }
|
|
7639
|
+
),
|
|
7500
7640
|
[options]
|
|
7501
7641
|
);
|
|
7502
7642
|
const enableSearch = normalizedOptions.length > 10;
|
|
7503
7643
|
const filtered = React27.useMemo(
|
|
7504
|
-
() => enableSearch ? normalizedOptions.filter(
|
|
7644
|
+
() => enableSearch ? normalizedOptions.filter(
|
|
7645
|
+
(opt) => opt.label.toLowerCase().includes(query.toLowerCase()) || opt.description?.toLowerCase().includes(query.toLowerCase())
|
|
7646
|
+
) : normalizedOptions,
|
|
7505
7647
|
[normalizedOptions, query, enableSearch]
|
|
7506
7648
|
);
|
|
7649
|
+
const groupedOptions = React27.useMemo(() => {
|
|
7650
|
+
if (!groupBy) return null;
|
|
7651
|
+
const groups = /* @__PURE__ */ new Map();
|
|
7652
|
+
filtered.forEach((opt) => {
|
|
7653
|
+
const group = groupBy(opt);
|
|
7654
|
+
if (!groups.has(group)) groups.set(group, []);
|
|
7655
|
+
groups.get(group).push(opt);
|
|
7656
|
+
});
|
|
7657
|
+
return groups;
|
|
7658
|
+
}, [filtered, groupBy]);
|
|
7507
7659
|
const toggleSelect = (optionValue) => {
|
|
7508
|
-
|
|
7660
|
+
const option = normalizedOptions.find((o) => o.value === optionValue);
|
|
7661
|
+
if (option?.disabled || disabledOptions.includes(optionValue)) return;
|
|
7509
7662
|
if (value.includes(optionValue)) {
|
|
7510
7663
|
onChange(value.filter((v) => v !== optionValue));
|
|
7511
7664
|
} else {
|
|
@@ -7547,39 +7700,121 @@ var MultiCombobox = ({
|
|
|
7547
7700
|
md: {
|
|
7548
7701
|
trigger: "h-10 px-4 py-2 text-sm",
|
|
7549
7702
|
icon: "h-4 w-4",
|
|
7550
|
-
search: "px-
|
|
7703
|
+
search: "px-10 py-2 text-sm",
|
|
7551
7704
|
item: "text-sm px-3 py-2",
|
|
7552
7705
|
tag: "px-2 py-1 text-xs"
|
|
7553
7706
|
},
|
|
7554
7707
|
lg: {
|
|
7555
7708
|
trigger: "h-12 px-5 py-3 text-base",
|
|
7556
7709
|
icon: "h-5 w-5",
|
|
7557
|
-
search: "px-
|
|
7710
|
+
search: "px-10 py-3 text-base",
|
|
7558
7711
|
item: "text-base px-3 py-3",
|
|
7559
7712
|
tag: "px-2.5 py-1 text-sm"
|
|
7560
7713
|
}
|
|
7561
7714
|
};
|
|
7715
|
+
const variantStyles6 = {
|
|
7716
|
+
default: "border border-input bg-background shadow-sm hover:border-primary/50",
|
|
7717
|
+
outline: "border-2 border-input bg-transparent hover:border-primary",
|
|
7718
|
+
ghost: "border border-transparent bg-muted/50 hover:bg-muted"
|
|
7719
|
+
};
|
|
7562
7720
|
const autoId = (0, import_react16.useId)();
|
|
7563
7721
|
const resolvedId = id ? String(id) : `multicombobox-${autoId}`;
|
|
7564
7722
|
const labelId = label ? `${resolvedId}-label` : void 0;
|
|
7565
7723
|
const labelSize = size === "sm" ? "text-xs" : size === "lg" ? "text-base" : "text-sm";
|
|
7566
7724
|
const listboxId = `${resolvedId}-listbox`;
|
|
7567
|
-
const
|
|
7568
|
-
|
|
7569
|
-
|
|
7725
|
+
const renderOptionItem = (item, index) => {
|
|
7726
|
+
const isSelected = value.includes(item.value);
|
|
7727
|
+
const isDisabled = item.disabled || disabledOptions.includes(item.value);
|
|
7728
|
+
const optionIcon = item.icon;
|
|
7729
|
+
const optionDesc = item.description;
|
|
7730
|
+
if (renderOption) {
|
|
7731
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
7732
|
+
"li",
|
|
7733
|
+
{
|
|
7734
|
+
ref: (node) => {
|
|
7735
|
+
listRef.current[index] = node;
|
|
7736
|
+
},
|
|
7737
|
+
onClick: (e) => {
|
|
7738
|
+
e.preventDefault();
|
|
7739
|
+
e.stopPropagation();
|
|
7740
|
+
if (!isDisabled) toggleSelect(item.value);
|
|
7741
|
+
inputRef.current?.focus();
|
|
7742
|
+
},
|
|
7743
|
+
style: { animationDelay: open ? `${Math.min(index * 20, 200)}ms` : "0ms" },
|
|
7744
|
+
className: cn("dropdown-item", isDisabled && "opacity-50 cursor-not-allowed pointer-events-none"),
|
|
7745
|
+
children: renderOption(item, isSelected)
|
|
7746
|
+
},
|
|
7747
|
+
item.value
|
|
7748
|
+
);
|
|
7749
|
+
}
|
|
7750
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
7751
|
+
"li",
|
|
7570
7752
|
{
|
|
7571
|
-
|
|
7753
|
+
ref: (node) => {
|
|
7754
|
+
listRef.current[index] = node;
|
|
7755
|
+
},
|
|
7572
7756
|
onClick: (e) => {
|
|
7573
7757
|
e.preventDefault();
|
|
7574
7758
|
e.stopPropagation();
|
|
7575
|
-
|
|
7759
|
+
if (!isDisabled) toggleSelect(item.value);
|
|
7760
|
+
inputRef.current?.focus();
|
|
7576
7761
|
},
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
|
|
7762
|
+
style: { animationDelay: open ? `${Math.min(index * 20, 200)}ms` : "0ms" },
|
|
7763
|
+
className: cn(
|
|
7764
|
+
"dropdown-item flex cursor-pointer items-center gap-3 rounded-xl transition-all duration-200",
|
|
7765
|
+
sizeStyles8[size].item,
|
|
7766
|
+
"hover:bg-linear-to-r hover:from-primary/5 hover:to-transparent",
|
|
7767
|
+
index === activeIndex && "bg-primary/5",
|
|
7768
|
+
isSelected && "bg-primary/5",
|
|
7769
|
+
isDisabled && "opacity-40 cursor-not-allowed pointer-events-none"
|
|
7770
|
+
),
|
|
7771
|
+
children: [
|
|
7772
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
7773
|
+
"span",
|
|
7774
|
+
{
|
|
7775
|
+
className: cn(
|
|
7776
|
+
"shrink-0 w-5 h-5 flex items-center justify-center rounded-md border-2 transition-all duration-200",
|
|
7777
|
+
isSelected ? "bg-primary border-primary text-primary-foreground" : "border-muted-foreground/30 bg-transparent"
|
|
7778
|
+
),
|
|
7779
|
+
children: isSelected && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.Check, { className: "w-3 h-3" })
|
|
7780
|
+
}
|
|
7781
|
+
),
|
|
7782
|
+
optionIcon && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "shrink-0 w-5 h-5 flex items-center justify-center text-muted-foreground", children: optionIcon }),
|
|
7783
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex-1 min-w-0", children: [
|
|
7784
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: cn("truncate", isSelected && "font-medium text-primary"), children: item.label }),
|
|
7785
|
+
optionDesc && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "text-xs text-muted-foreground truncate mt-0.5", children: optionDesc })
|
|
7786
|
+
] })
|
|
7787
|
+
]
|
|
7788
|
+
},
|
|
7789
|
+
item.value
|
|
7790
|
+
);
|
|
7791
|
+
};
|
|
7792
|
+
const dropdownBody = /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { "data-combobox-dropdown": true, "data-state": open ? "open" : "closed", className: "w-full", children: [
|
|
7793
|
+
value.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "px-3 py-2 border-b border-border/40 flex items-center justify-between bg-muted/30", children: [
|
|
7794
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { className: "text-xs font-medium text-muted-foreground", children: [
|
|
7795
|
+
value.length,
|
|
7796
|
+
" selected",
|
|
7797
|
+
maxSelected && ` / ${maxSelected} max`
|
|
7798
|
+
] }),
|
|
7799
|
+
showClear && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
7800
|
+
"button",
|
|
7801
|
+
{
|
|
7802
|
+
type: "button",
|
|
7803
|
+
onClick: (e) => {
|
|
7804
|
+
e.preventDefault();
|
|
7805
|
+
e.stopPropagation();
|
|
7806
|
+
handleClearAll();
|
|
7807
|
+
},
|
|
7808
|
+
className: "text-xs text-muted-foreground hover:text-destructive transition-colors cursor-pointer flex items-center gap-1",
|
|
7809
|
+
children: [
|
|
7810
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.X, { className: "w-3 h-3" }),
|
|
7811
|
+
"Clear all"
|
|
7812
|
+
]
|
|
7813
|
+
}
|
|
7814
|
+
)
|
|
7815
|
+
] }),
|
|
7816
|
+
enableSearch && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "relative border-b border-border/40", children: [
|
|
7817
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.Search, { className: cn("absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", sizeStyles8[size].icon) }),
|
|
7583
7818
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
7584
7819
|
"input",
|
|
7585
7820
|
{
|
|
@@ -7590,8 +7825,17 @@ var MultiCombobox = ({
|
|
|
7590
7825
|
setActiveIndex(null);
|
|
7591
7826
|
},
|
|
7592
7827
|
onKeyDown: handleKeyDown,
|
|
7593
|
-
placeholder,
|
|
7594
|
-
className: cn("w-full
|
|
7828
|
+
placeholder: searchPlaceholder,
|
|
7829
|
+
className: cn("w-full bg-transparent focus:outline-none cursor-text placeholder:text-muted-foreground/60", sizeStyles8[size].search)
|
|
7830
|
+
}
|
|
7831
|
+
),
|
|
7832
|
+
query && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
7833
|
+
"button",
|
|
7834
|
+
{
|
|
7835
|
+
type: "button",
|
|
7836
|
+
onClick: () => setQuery(""),
|
|
7837
|
+
className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors",
|
|
7838
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.X, { className: "w-4 h-4" })
|
|
7595
7839
|
}
|
|
7596
7840
|
)
|
|
7597
7841
|
] }),
|
|
@@ -7600,50 +7844,41 @@ var MultiCombobox = ({
|
|
|
7600
7844
|
{
|
|
7601
7845
|
id: listboxId,
|
|
7602
7846
|
role: "listbox",
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
|
|
7617
|
-
|
|
7618
|
-
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
),
|
|
7632
|
-
children: [
|
|
7633
|
-
item.label,
|
|
7634
|
-
isSelected && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.Check, { className: sizeStyles8[size].icon })
|
|
7635
|
-
]
|
|
7636
|
-
},
|
|
7637
|
-
item.value
|
|
7638
|
-
);
|
|
7639
|
-
}) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("li", { className: cn("px-3 py-8 text-center text-muted-foreground", size === "lg" ? "text-base" : size === "sm" ? "text-xs" : "text-sm"), children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-col items-center gap-2 animate-in fade-in-0 zoom-in-95 duration-300", children: [
|
|
7640
|
-
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.SearchX, { className: "h-8 w-8 opacity-40 text-muted-foreground" }),
|
|
7641
|
-
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { children: emptyText }),
|
|
7642
|
-
query && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("button", { type: "button", onClick: () => setQuery(""), className: "text-xs text-primary hover:underline", children: "Clear search" })
|
|
7847
|
+
"aria-multiselectable": "true",
|
|
7848
|
+
style: { maxHeight },
|
|
7849
|
+
className: cn("overflow-y-auto p-1.5", size === "lg" ? "text-base" : size === "sm" ? "text-xs" : "text-sm"),
|
|
7850
|
+
children: loading2 ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("li", { className: "px-3 py-8 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-col items-center gap-3 animate-in fade-in-0 zoom-in-95 duration-300", children: [
|
|
7851
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "relative", children: [
|
|
7852
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.Loader2, { className: "h-8 w-8 animate-spin text-primary" }),
|
|
7853
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.Sparkles, { className: "h-4 w-4 text-primary/60 absolute -top-1 -right-1 animate-pulse" })
|
|
7854
|
+
] }),
|
|
7855
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-muted-foreground font-medium", children: loadingText })
|
|
7856
|
+
] }) }) : filtered.length ? groupedOptions ? (
|
|
7857
|
+
// Render grouped options
|
|
7858
|
+
Array.from(groupedOptions.entries()).map(([group, items]) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("li", { className: "mb-2", children: [
|
|
7859
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("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 }),
|
|
7860
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("ul", { children: items.map((item) => renderOptionItem(item, filtered.indexOf(item))) })
|
|
7861
|
+
] }, group))
|
|
7862
|
+
) : (
|
|
7863
|
+
// Render flat options
|
|
7864
|
+
filtered.map((item, index) => renderOptionItem(item, index))
|
|
7865
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("li", { className: cn("px-3 py-8 text-center text-muted-foreground"), children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-col items-center gap-3 animate-in fade-in-0 zoom-in-95 duration-300", children: [
|
|
7866
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.SearchX, { className: "h-10 w-10 opacity-30 text-muted-foreground" }),
|
|
7867
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "space-y-1", children: [
|
|
7868
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "font-medium block", children: emptyText }),
|
|
7869
|
+
query && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-xs opacity-60", children: "Try a different search term" })
|
|
7870
|
+
] }),
|
|
7871
|
+
query && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("button", { type: "button", onClick: () => setQuery(""), className: "text-xs text-primary hover:underline flex items-center gap-1", children: [
|
|
7872
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.X, { className: "w-3 h-3" }),
|
|
7873
|
+
"Clear search"
|
|
7874
|
+
] })
|
|
7643
7875
|
] }) })
|
|
7644
7876
|
}
|
|
7645
7877
|
)
|
|
7646
7878
|
] });
|
|
7879
|
+
const selectedOptions = value.map((v) => normalizedOptions.find((o) => o.value === v)).filter(Boolean);
|
|
7880
|
+
const visibleTags = maxTagsVisible ? selectedOptions.slice(0, maxTagsVisible) : selectedOptions;
|
|
7881
|
+
const hiddenCount = maxTagsVisible ? Math.max(0, selectedOptions.length - maxTagsVisible) : 0;
|
|
7647
7882
|
const triggerButton = /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
7648
7883
|
"button",
|
|
7649
7884
|
{
|
|
@@ -7656,50 +7891,99 @@ var MultiCombobox = ({
|
|
|
7656
7891
|
"aria-haspopup": "listbox",
|
|
7657
7892
|
"aria-expanded": open,
|
|
7658
7893
|
"aria-controls": listboxId,
|
|
7894
|
+
"aria-invalid": !!error,
|
|
7659
7895
|
className: cn(
|
|
7660
|
-
"flex w-full items-center gap-2 rounded-2xl
|
|
7896
|
+
"group flex w-full items-center gap-2 rounded-2xl min-h-10 transition-all duration-200",
|
|
7661
7897
|
"px-3 py-2",
|
|
7662
|
-
|
|
7663
|
-
"
|
|
7898
|
+
variantStyles6[variant],
|
|
7899
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:border-primary",
|
|
7900
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
7901
|
+
open && "ring-2 ring-primary/20 border-primary",
|
|
7902
|
+
!!error && "border-destructive focus-visible:ring-destructive/30"
|
|
7664
7903
|
),
|
|
7665
7904
|
children: [
|
|
7666
|
-
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex items-center gap-1 flex-wrap min-h-6 flex-1", children: value.length > 0 ? showTags ?
|
|
7667
|
-
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7905
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex items-center gap-1.5 flex-wrap min-h-6 flex-1", children: value.length > 0 ? showTags ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
|
|
7906
|
+
visibleTags.map((option) => {
|
|
7907
|
+
if (renderTag) {
|
|
7908
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(React27.Fragment, { children: renderTag(option, () => handleRemove(option.value)) }, option.value);
|
|
7909
|
+
}
|
|
7910
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
7671
7911
|
"span",
|
|
7672
7912
|
{
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
|
|
7679
|
-
|
|
7680
|
-
|
|
7681
|
-
|
|
7682
|
-
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
|
|
7913
|
+
className: cn(
|
|
7914
|
+
"inline-flex items-center gap-1.5 bg-primary/10 text-primary rounded-lg transition-all duration-200",
|
|
7915
|
+
"hover:bg-primary/20",
|
|
7916
|
+
sizeStyles8[size].tag
|
|
7917
|
+
),
|
|
7918
|
+
children: [
|
|
7919
|
+
showSelectedIcons && option.icon && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "shrink-0 w-3.5 h-3.5 flex items-center justify-center", children: option.icon }),
|
|
7920
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "truncate max-w-24", children: displayFormat(option) }),
|
|
7921
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
7922
|
+
"span",
|
|
7923
|
+
{
|
|
7924
|
+
role: "button",
|
|
7925
|
+
tabIndex: 0,
|
|
7926
|
+
"aria-label": `Remove ${displayFormat(option)}`,
|
|
7927
|
+
onClick: (e) => {
|
|
7928
|
+
e.preventDefault();
|
|
7929
|
+
e.stopPropagation();
|
|
7930
|
+
handleRemove(option.value);
|
|
7931
|
+
},
|
|
7932
|
+
onKeyDown: (e) => {
|
|
7933
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
7934
|
+
e.preventDefault();
|
|
7935
|
+
e.stopPropagation();
|
|
7936
|
+
handleRemove(option.value);
|
|
7937
|
+
}
|
|
7938
|
+
},
|
|
7939
|
+
className: "hover:text-destructive transition-colors cursor-pointer select-none hover:bg-destructive/10 rounded-full p-0.5",
|
|
7940
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.X, { className: "w-3 h-3" })
|
|
7941
|
+
}
|
|
7942
|
+
)
|
|
7943
|
+
]
|
|
7944
|
+
},
|
|
7945
|
+
option.value
|
|
7946
|
+
);
|
|
7947
|
+
}),
|
|
7948
|
+
hiddenCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { className: cn("inline-flex items-center bg-muted text-muted-foreground rounded-lg", sizeStyles8[size].tag), children: [
|
|
7949
|
+
"+",
|
|
7950
|
+
hiddenCount,
|
|
7951
|
+
" more"
|
|
7952
|
+
] })
|
|
7953
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { className: "truncate text-sm font-medium", children: [
|
|
7694
7954
|
value.length,
|
|
7955
|
+
" item",
|
|
7956
|
+
value.length > 1 ? "s" : "",
|
|
7695
7957
|
" selected"
|
|
7696
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-muted-foreground", children: placeholder
|
|
7697
|
-
/* @__PURE__ */ (0, import_jsx_runtime33.
|
|
7698
|
-
|
|
7699
|
-
|
|
7700
|
-
|
|
7701
|
-
|
|
7702
|
-
|
|
7958
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-muted-foreground", children: placeholder }) }),
|
|
7959
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center gap-1.5 shrink-0", children: [
|
|
7960
|
+
showClear && value.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
7961
|
+
"div",
|
|
7962
|
+
{
|
|
7963
|
+
role: "button",
|
|
7964
|
+
tabIndex: 0,
|
|
7965
|
+
"aria-label": "Clear all",
|
|
7966
|
+
onClick: (e) => {
|
|
7967
|
+
e.preventDefault();
|
|
7968
|
+
e.stopPropagation();
|
|
7969
|
+
handleClearAll();
|
|
7970
|
+
},
|
|
7971
|
+
onKeyDown: (e) => (e.key === "Enter" || e.key === " ") && handleClearAll(),
|
|
7972
|
+
className: "opacity-0 group-hover:opacity-100 transition-all duration-200 p-1 rounded-lg hover:bg-destructive/10 text-muted-foreground hover:text-destructive",
|
|
7973
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.X, { className: "h-3.5 w-3.5" })
|
|
7974
|
+
}
|
|
7975
|
+
),
|
|
7976
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
7977
|
+
import_lucide_react18.ChevronDown,
|
|
7978
|
+
{
|
|
7979
|
+
className: cn(
|
|
7980
|
+
"h-4 w-4 text-muted-foreground transition-all duration-300 ease-out",
|
|
7981
|
+
open && "rotate-180 text-primary",
|
|
7982
|
+
!open && "group-hover:text-foreground"
|
|
7983
|
+
)
|
|
7984
|
+
}
|
|
7985
|
+
)
|
|
7986
|
+
] })
|
|
7703
7987
|
]
|
|
7704
7988
|
}
|
|
7705
7989
|
);
|
|
@@ -7746,7 +8030,18 @@ var MultiCombobox = ({
|
|
|
7746
8030
|
contentClassName: "p-0",
|
|
7747
8031
|
children: dropdownBody
|
|
7748
8032
|
}
|
|
7749
|
-
)
|
|
8033
|
+
),
|
|
8034
|
+
(helperText || error) && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("p", { className: cn("text-xs transition-colors duration-200 flex items-center gap-1.5", error ? "text-destructive" : "text-muted-foreground"), children: [
|
|
8035
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: "w-3.5 h-3.5 shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
8036
|
+
"path",
|
|
8037
|
+
{
|
|
8038
|
+
fillRule: "evenodd",
|
|
8039
|
+
d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z",
|
|
8040
|
+
clipRule: "evenodd"
|
|
8041
|
+
}
|
|
8042
|
+
) }),
|
|
8043
|
+
error || helperText
|
|
8044
|
+
] })
|
|
7750
8045
|
] });
|
|
7751
8046
|
};
|
|
7752
8047
|
|