@underverse-ui/underverse 0.2.58 → 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 +464 -163
- 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 +503 -202
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2845,19 +2845,25 @@ var Modal = ({
|
|
|
2845
2845
|
setIsMounted(true);
|
|
2846
2846
|
return () => setIsMounted(false);
|
|
2847
2847
|
}, []);
|
|
2848
|
+
const animationRef = React11.useRef(false);
|
|
2848
2849
|
React11.useEffect(() => {
|
|
2849
2850
|
if (isOpen) {
|
|
2851
|
+
if (animationRef.current) return;
|
|
2852
|
+
animationRef.current = true;
|
|
2850
2853
|
setIsVisible(true);
|
|
2851
2854
|
setIsAnimating(true);
|
|
2852
2855
|
requestAnimationFrame(() => {
|
|
2853
2856
|
setIsAnimating(false);
|
|
2854
2857
|
});
|
|
2855
|
-
} else
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2858
|
+
} else {
|
|
2859
|
+
animationRef.current = false;
|
|
2860
|
+
if (isVisible) {
|
|
2861
|
+
setIsAnimating(true);
|
|
2862
|
+
const hideTimer = setTimeout(() => {
|
|
2863
|
+
setIsVisible(false);
|
|
2864
|
+
}, 200);
|
|
2865
|
+
return () => clearTimeout(hideTimer);
|
|
2866
|
+
}
|
|
2861
2867
|
}
|
|
2862
2868
|
}, [isOpen, isVisible]);
|
|
2863
2869
|
React11.useEffect(() => {
|
|
@@ -4824,20 +4830,42 @@ var DropdownMenu = ({
|
|
|
4824
4830
|
}
|
|
4825
4831
|
);
|
|
4826
4832
|
};
|
|
4827
|
-
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)(
|
|
4828
4845
|
"button",
|
|
4829
4846
|
{
|
|
4830
4847
|
onClick,
|
|
4831
4848
|
disabled,
|
|
4849
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
4832
4850
|
className: cn(
|
|
4833
|
-
"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",
|
|
4834
4852
|
"hover:bg-accent hover:text-accent-foreground",
|
|
4835
4853
|
"focus:bg-accent focus:text-accent-foreground focus:outline-none",
|
|
4836
4854
|
"disabled:opacity-50 disabled:cursor-not-allowed",
|
|
4837
4855
|
destructive && "text-destructive hover:bg-destructive/10 focus:bg-destructive/10",
|
|
4856
|
+
active && "bg-primary/10 text-primary",
|
|
4838
4857
|
className
|
|
4839
4858
|
),
|
|
4840
|
-
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
|
+
]
|
|
4841
4869
|
}
|
|
4842
4870
|
);
|
|
4843
4871
|
var DropdownMenuSeparator = ({ className }) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cn("h-px bg-border my-1", className) });
|
|
@@ -4882,6 +4910,15 @@ var getOptionLabel = (option) => {
|
|
|
4882
4910
|
var getOptionValue = (option) => {
|
|
4883
4911
|
return typeof option === "string" ? option : option.value;
|
|
4884
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
|
+
};
|
|
4885
4922
|
var findOptionByValue = (options, value) => {
|
|
4886
4923
|
return options.find((opt) => getOptionValue(opt) === value);
|
|
4887
4924
|
};
|
|
@@ -4896,13 +4933,21 @@ var Combobox = ({
|
|
|
4896
4933
|
className,
|
|
4897
4934
|
disabled = false,
|
|
4898
4935
|
size = "md",
|
|
4936
|
+
variant = "default",
|
|
4899
4937
|
allowClear = false,
|
|
4900
4938
|
usePortal = true,
|
|
4901
4939
|
label,
|
|
4902
4940
|
required,
|
|
4903
4941
|
fontBold = false,
|
|
4904
4942
|
loading: loading2 = false,
|
|
4905
|
-
loadingText = "Loading..."
|
|
4943
|
+
loadingText = "Loading...",
|
|
4944
|
+
showSelectedIcon = true,
|
|
4945
|
+
maxHeight = 280,
|
|
4946
|
+
groupBy,
|
|
4947
|
+
renderOption,
|
|
4948
|
+
renderValue,
|
|
4949
|
+
error,
|
|
4950
|
+
helperText
|
|
4906
4951
|
}) => {
|
|
4907
4952
|
const [open, setOpen] = React20.useState(false);
|
|
4908
4953
|
const [query, setQuery] = React20.useState("");
|
|
@@ -4920,6 +4965,7 @@ var Combobox = ({
|
|
|
4920
4965
|
);
|
|
4921
4966
|
const triggerRef = React20.useRef(null);
|
|
4922
4967
|
const handleSelect = (option) => {
|
|
4968
|
+
if (getOptionDisabled(option)) return;
|
|
4923
4969
|
const val = getOptionValue(option);
|
|
4924
4970
|
if (val !== void 0 && val !== null) {
|
|
4925
4971
|
onChange(val);
|
|
@@ -4944,9 +4990,64 @@ var Combobox = ({
|
|
|
4944
4990
|
}, [open, enableSearch]);
|
|
4945
4991
|
const selectedOption = findOptionByValue(options, value);
|
|
4946
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
|
+
};
|
|
4947
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: [
|
|
4948
|
-
enableSearch && /* @__PURE__ */ (0, import_jsx_runtime25.
|
|
4949
|
-
/* @__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" }),
|
|
4950
5051
|
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
4951
5052
|
"input",
|
|
4952
5053
|
{
|
|
@@ -4971,7 +5072,7 @@ var Combobox = ({
|
|
|
4971
5072
|
});
|
|
4972
5073
|
} else if (e.key === "Enter") {
|
|
4973
5074
|
e.preventDefault();
|
|
4974
|
-
if (activeIndex !== null && filteredOptions[activeIndex]) {
|
|
5075
|
+
if (activeIndex !== null && filteredOptions[activeIndex] && !getOptionDisabled(filteredOptions[activeIndex])) {
|
|
4975
5076
|
handleSelect(filteredOptions[activeIndex]);
|
|
4976
5077
|
}
|
|
4977
5078
|
} else if (e.key === "Escape") {
|
|
@@ -4980,63 +5081,69 @@ var Combobox = ({
|
|
|
4980
5081
|
}
|
|
4981
5082
|
},
|
|
4982
5083
|
placeholder: searchPlaceholder,
|
|
4983
|
-
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
|
+
),
|
|
4984
5091
|
"aria-autocomplete": "list",
|
|
4985
5092
|
"aria-activedescendant": activeIndex != null ? `combobox-item-${activeIndex}` : void 0
|
|
4986
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
|
+
}
|
|
4987
5103
|
)
|
|
4988
|
-
] }),
|
|
4989
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "
|
|
4990
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
4991
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "text-sm text-muted-foreground", children: loadingText
|
|
4992
|
-
] }) }) : filteredOptions.length > 0 ?
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
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",
|
|
4998
5125
|
{
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
"aria-selected": isSelected,
|
|
5006
|
-
onClick: () => handleSelect(item),
|
|
5007
|
-
style: {
|
|
5008
|
-
animationDelay: open ? `${Math.min(index * 20, 200)}ms` : "0ms"
|
|
5009
|
-
},
|
|
5010
|
-
className: cn(
|
|
5011
|
-
"dropdown-item group flex cursor-pointer items-center justify-between rounded-lg px-2.5 py-1.5 text-sm",
|
|
5012
|
-
"outline-none focus:outline-none focus-visible:outline-none",
|
|
5013
|
-
"hover:bg-accent hover:text-accent-foreground",
|
|
5014
|
-
"focus:bg-accent focus:text-accent-foreground",
|
|
5015
|
-
"data-disabled:pointer-events-none data-disabled:opacity-50",
|
|
5016
|
-
index === activeIndex && "bg-accent text-accent-foreground",
|
|
5017
|
-
isSelected && "bg-accent text-accent-foreground"
|
|
5018
|
-
),
|
|
5019
|
-
children: [
|
|
5020
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "truncate", children: itemLabel }),
|
|
5021
|
-
isSelected && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.Check, { className: "h-4 w-4 text-primary" })
|
|
5022
|
-
]
|
|
5023
|
-
},
|
|
5024
|
-
`${itemValue}-${index}`
|
|
5025
|
-
);
|
|
5026
|
-
}) : /* @__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: [
|
|
5027
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.SearchX, { className: "h-8 w-8 opacity-40 text-muted-foreground" }),
|
|
5028
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "text-sm", children: emptyText }),
|
|
5029
|
-
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
|
+
)
|
|
5030
5132
|
] }) }) }) })
|
|
5031
5133
|
] });
|
|
5032
5134
|
const sizeStyles8 = {
|
|
5033
|
-
// Keep consistent with Input size heights
|
|
5034
5135
|
sm: "h-8 py-1.5 text-sm md:h-7 md:text-xs",
|
|
5035
5136
|
md: "h-10 py-2 text-sm",
|
|
5036
5137
|
lg: "h-12 py-3 text-base"
|
|
5037
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
|
+
};
|
|
5038
5145
|
const labelSize = size === "sm" ? "text-xs" : size === "lg" ? "text-base" : "text-sm";
|
|
5039
|
-
const radiusClass = size === "sm" ? "rounded-
|
|
5146
|
+
const radiusClass = size === "sm" ? "rounded-lg" : "rounded-xl";
|
|
5040
5147
|
const verticalGap = size === "sm" ? "space-y-1.5" : "space-y-2";
|
|
5041
5148
|
const triggerButtonBaseProps = {
|
|
5042
5149
|
ref: triggerRef,
|
|
@@ -5046,6 +5153,7 @@ var Combobox = ({
|
|
|
5046
5153
|
"aria-haspopup": "listbox",
|
|
5047
5154
|
"aria-expanded": open,
|
|
5048
5155
|
"aria-controls": `${resolvedId}-listbox`,
|
|
5156
|
+
"aria-invalid": !!error,
|
|
5049
5157
|
id: resolvedId,
|
|
5050
5158
|
"aria-labelledby": labelId,
|
|
5051
5159
|
onKeyDown: (e) => {
|
|
@@ -5059,18 +5167,21 @@ var Combobox = ({
|
|
|
5059
5167
|
}
|
|
5060
5168
|
},
|
|
5061
5169
|
className: cn(
|
|
5062
|
-
"flex w-full items-center justify-between
|
|
5170
|
+
"group flex w-full items-center justify-between px-3 transition-all duration-200",
|
|
5063
5171
|
radiusClass,
|
|
5064
5172
|
sizeStyles8[size],
|
|
5065
|
-
|
|
5173
|
+
variantStyles6[variant],
|
|
5174
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:border-primary",
|
|
5066
5175
|
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
5067
|
-
"
|
|
5176
|
+
open && "ring-2 ring-primary/20 border-primary",
|
|
5177
|
+
!!error && "border-destructive focus-visible:ring-destructive/30",
|
|
5068
5178
|
className
|
|
5069
5179
|
)
|
|
5070
5180
|
};
|
|
5071
5181
|
const triggerContents = /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
|
|
5072
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className:
|
|
5073
|
-
/* @__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: [
|
|
5074
5185
|
allowClear && value && !disabled && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
5075
5186
|
"div",
|
|
5076
5187
|
{
|
|
@@ -5079,11 +5190,23 @@ var Combobox = ({
|
|
|
5079
5190
|
"aria-label": "Clear selection",
|
|
5080
5191
|
onClick: handleClear,
|
|
5081
5192
|
onKeyDown: (e) => (e.key === "Enter" || e.key === " ") && handleClear(e),
|
|
5082
|
-
className:
|
|
5083
|
-
|
|
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" })
|
|
5084
5198
|
}
|
|
5085
5199
|
),
|
|
5086
|
-
/* @__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
|
+
)
|
|
5087
5210
|
] })
|
|
5088
5211
|
] });
|
|
5089
5212
|
const triggerButtonForPopover = /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { ...triggerButtonBaseProps, children: triggerContents });
|
|
@@ -5130,7 +5253,18 @@ var Combobox = ({
|
|
|
5130
5253
|
}
|
|
5131
5254
|
) : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "relative", children: [
|
|
5132
5255
|
triggerButtonInline,
|
|
5133
|
-
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
|
|
5134
5268
|
] })
|
|
5135
5269
|
] });
|
|
5136
5270
|
};
|
|
@@ -7466,7 +7600,8 @@ var MultiCombobox = ({
|
|
|
7466
7600
|
options,
|
|
7467
7601
|
value,
|
|
7468
7602
|
onChange,
|
|
7469
|
-
placeholder = "
|
|
7603
|
+
placeholder = "Select...",
|
|
7604
|
+
searchPlaceholder = "Search...",
|
|
7470
7605
|
maxSelected,
|
|
7471
7606
|
disabledOptions = [],
|
|
7472
7607
|
showTags = true,
|
|
@@ -7474,13 +7609,22 @@ var MultiCombobox = ({
|
|
|
7474
7609
|
className,
|
|
7475
7610
|
disabled = false,
|
|
7476
7611
|
size = "md",
|
|
7612
|
+
variant = "default",
|
|
7477
7613
|
label,
|
|
7478
7614
|
title,
|
|
7479
7615
|
required,
|
|
7480
7616
|
displayFormat = (option) => option.label,
|
|
7481
7617
|
loading: loading2 = false,
|
|
7482
7618
|
loadingText = "Loading...",
|
|
7483
|
-
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
|
|
7484
7628
|
}) => {
|
|
7485
7629
|
const [query, setQuery] = React27.useState("");
|
|
7486
7630
|
const [open, setOpen] = React27.useState(false);
|
|
@@ -7490,16 +7634,31 @@ var MultiCombobox = ({
|
|
|
7490
7634
|
const triggerRef = React27.useRef(null);
|
|
7491
7635
|
useShadCNAnimations();
|
|
7492
7636
|
const normalizedOptions = React27.useMemo(
|
|
7493
|
-
() => 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
|
+
),
|
|
7494
7640
|
[options]
|
|
7495
7641
|
);
|
|
7496
7642
|
const enableSearch = normalizedOptions.length > 10;
|
|
7497
7643
|
const filtered = React27.useMemo(
|
|
7498
|
-
() => enableSearch ? normalizedOptions.filter(
|
|
7644
|
+
() => enableSearch ? normalizedOptions.filter(
|
|
7645
|
+
(opt) => opt.label.toLowerCase().includes(query.toLowerCase()) || opt.description?.toLowerCase().includes(query.toLowerCase())
|
|
7646
|
+
) : normalizedOptions,
|
|
7499
7647
|
[normalizedOptions, query, enableSearch]
|
|
7500
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]);
|
|
7501
7659
|
const toggleSelect = (optionValue) => {
|
|
7502
|
-
|
|
7660
|
+
const option = normalizedOptions.find((o) => o.value === optionValue);
|
|
7661
|
+
if (option?.disabled || disabledOptions.includes(optionValue)) return;
|
|
7503
7662
|
if (value.includes(optionValue)) {
|
|
7504
7663
|
onChange(value.filter((v) => v !== optionValue));
|
|
7505
7664
|
} else {
|
|
@@ -7541,39 +7700,121 @@ var MultiCombobox = ({
|
|
|
7541
7700
|
md: {
|
|
7542
7701
|
trigger: "h-10 px-4 py-2 text-sm",
|
|
7543
7702
|
icon: "h-4 w-4",
|
|
7544
|
-
search: "px-
|
|
7703
|
+
search: "px-10 py-2 text-sm",
|
|
7545
7704
|
item: "text-sm px-3 py-2",
|
|
7546
7705
|
tag: "px-2 py-1 text-xs"
|
|
7547
7706
|
},
|
|
7548
7707
|
lg: {
|
|
7549
7708
|
trigger: "h-12 px-5 py-3 text-base",
|
|
7550
7709
|
icon: "h-5 w-5",
|
|
7551
|
-
search: "px-
|
|
7710
|
+
search: "px-10 py-3 text-base",
|
|
7552
7711
|
item: "text-base px-3 py-3",
|
|
7553
7712
|
tag: "px-2.5 py-1 text-sm"
|
|
7554
7713
|
}
|
|
7555
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
|
+
};
|
|
7556
7720
|
const autoId = (0, import_react16.useId)();
|
|
7557
7721
|
const resolvedId = id ? String(id) : `multicombobox-${autoId}`;
|
|
7558
7722
|
const labelId = label ? `${resolvedId}-label` : void 0;
|
|
7559
7723
|
const labelSize = size === "sm" ? "text-xs" : size === "lg" ? "text-base" : "text-sm";
|
|
7560
7724
|
const listboxId = `${resolvedId}-listbox`;
|
|
7561
|
-
const
|
|
7562
|
-
|
|
7563
|
-
|
|
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",
|
|
7564
7752
|
{
|
|
7565
|
-
|
|
7753
|
+
ref: (node) => {
|
|
7754
|
+
listRef.current[index] = node;
|
|
7755
|
+
},
|
|
7566
7756
|
onClick: (e) => {
|
|
7567
7757
|
e.preventDefault();
|
|
7568
7758
|
e.stopPropagation();
|
|
7569
|
-
|
|
7759
|
+
if (!isDisabled) toggleSelect(item.value);
|
|
7760
|
+
inputRef.current?.focus();
|
|
7570
7761
|
},
|
|
7571
|
-
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
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) }),
|
|
7577
7818
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
7578
7819
|
"input",
|
|
7579
7820
|
{
|
|
@@ -7584,8 +7825,17 @@ var MultiCombobox = ({
|
|
|
7584
7825
|
setActiveIndex(null);
|
|
7585
7826
|
},
|
|
7586
7827
|
onKeyDown: handleKeyDown,
|
|
7587
|
-
placeholder,
|
|
7588
|
-
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" })
|
|
7589
7839
|
}
|
|
7590
7840
|
)
|
|
7591
7841
|
] }),
|
|
@@ -7594,50 +7844,41 @@ var MultiCombobox = ({
|
|
|
7594
7844
|
{
|
|
7595
7845
|
id: listboxId,
|
|
7596
7846
|
role: "listbox",
|
|
7597
|
-
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
|
|
7617
|
-
|
|
7618
|
-
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
),
|
|
7626
|
-
children: [
|
|
7627
|
-
item.label,
|
|
7628
|
-
isSelected && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.Check, { className: sizeStyles8[size].icon })
|
|
7629
|
-
]
|
|
7630
|
-
},
|
|
7631
|
-
item.value
|
|
7632
|
-
);
|
|
7633
|
-
}) : /* @__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: [
|
|
7634
|
-
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.SearchX, { className: "h-8 w-8 opacity-40 text-muted-foreground" }),
|
|
7635
|
-
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { children: emptyText }),
|
|
7636
|
-
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
|
+
] })
|
|
7637
7875
|
] }) })
|
|
7638
7876
|
}
|
|
7639
7877
|
)
|
|
7640
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;
|
|
7641
7882
|
const triggerButton = /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
7642
7883
|
"button",
|
|
7643
7884
|
{
|
|
@@ -7650,50 +7891,99 @@ var MultiCombobox = ({
|
|
|
7650
7891
|
"aria-haspopup": "listbox",
|
|
7651
7892
|
"aria-expanded": open,
|
|
7652
7893
|
"aria-controls": listboxId,
|
|
7894
|
+
"aria-invalid": !!error,
|
|
7653
7895
|
className: cn(
|
|
7654
|
-
"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",
|
|
7655
7897
|
"px-3 py-2",
|
|
7656
|
-
|
|
7657
|
-
"
|
|
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"
|
|
7658
7903
|
),
|
|
7659
7904
|
children: [
|
|
7660
|
-
/* @__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 ?
|
|
7661
|
-
|
|
7662
|
-
|
|
7663
|
-
|
|
7664
|
-
|
|
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)(
|
|
7665
7911
|
"span",
|
|
7666
7912
|
{
|
|
7667
|
-
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
|
|
7679
|
-
|
|
7680
|
-
|
|
7681
|
-
|
|
7682
|
-
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
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: [
|
|
7688
7954
|
value.length,
|
|
7955
|
+
" item",
|
|
7956
|
+
value.length > 1 ? "s" : "",
|
|
7689
7957
|
" selected"
|
|
7690
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-muted-foreground", children: placeholder
|
|
7691
|
-
/* @__PURE__ */ (0, import_jsx_runtime33.
|
|
7692
|
-
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
|
|
7696
|
-
|
|
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
|
+
] })
|
|
7697
7987
|
]
|
|
7698
7988
|
}
|
|
7699
7989
|
);
|
|
@@ -7740,7 +8030,18 @@ var MultiCombobox = ({
|
|
|
7740
8030
|
contentClassName: "p-0",
|
|
7741
8031
|
children: dropdownBody
|
|
7742
8032
|
}
|
|
7743
|
-
)
|
|
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
|
+
] })
|
|
7744
8045
|
] });
|
|
7745
8046
|
};
|
|
7746
8047
|
|