@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 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 if (isVisible) {
2856
- setIsAnimating(true);
2857
- const hideTimer = setTimeout(() => {
2858
- setIsVisible(false);
2859
- }, 200);
2860
- return () => clearTimeout(hideTimer);
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 = ({ children, onClick, disabled, destructive, className }) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
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.jsxs)("div", { className: "relative p-3 border-b border-border/50 bg-muted/20", children: [
4949
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.Search, { className: "absolute left-6 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground transition-colors" }),
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: "w-full rounded-full bg-background/50 py-2 pl-8 pr-3 text-sm border-0 focus:outline-none focus:bg-background/80 transition-colors placeholder:text-muted-foreground/60",
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: "max-h-64 overflow-y-auto overscroll-contain", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("ul", { className: "p-1 space-y-1", children: loading2 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("li", { className: "px-3 py-8 text-center", 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: [
4990
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.Loader2, { className: "h-6 w-6 animate-spin text-primary" }),
4991
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "text-sm text-muted-foreground", children: loadingText || "Loading\u2026" })
4992
- ] }) }) : filteredOptions.length > 0 ? filteredOptions.map((item, index) => {
4993
- const itemValue = getOptionValue(item);
4994
- const itemLabel = getOptionLabel(item);
4995
- const isSelected = itemValue === value;
4996
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
4997
- "li",
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
- ref: (node) => {
5000
- listRef.current[index] = node;
5001
- },
5002
- id: `combobox-item-${index}`,
5003
- role: "option",
5004
- tabIndex: -1,
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-full" : "rounded-full";
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 border border-input bg-background px-3",
5170
+ "group flex w-full items-center justify-between px-3 transition-all duration-200",
5063
5171
  radiusClass,
5064
5172
  sizeStyles8[size],
5065
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
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
- "hover:bg-accent/5 transition-colors hover:border-primary/40 focus:border-primary",
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: cn("truncate", !displayValue && "text-muted-foreground", fontBold && "font-bold"), children: displayValue || placeholder }),
5073
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex items-center gap-1 ml-2", children: [
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: "opacity-0 group-hover:opacity-100 transition-opacity p-0.5 rounded-md hover:bg-destructive/10 text-muted-foreground hover:text-destructive",
5083
- children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.X, { className: "h-3 w-3" })
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)(import_lucide_react12.ChevronDown, { className: cn("h-4 w-4 text-muted-foreground transition-all duration-200", open && "rotate-180 scale-110 text-primary") })
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 bg-popover text-popover-foreground shadow-md backdrop-blur-sm bg-popover/95 border-border/60", children: dropdownBody }) })
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 = "Search...",
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((o) => typeof o === "string" ? { value: o, label: o } : { value: o.value, label: o.label }),
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((opt) => opt.label.toLowerCase().includes(query.toLowerCase())) : normalizedOptions,
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
- if (disabledOptions.includes(optionValue)) return;
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-8 py-2 text-sm",
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-8 py-3 text-base",
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 dropdownBody = /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { "data-combobox-dropdown": true, "data-state": open ? "open" : "closed", className: "w-full", children: [
7562
- showClear && value.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "px-3 py-2 border-b border-border/60 flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
7563
- "button",
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
- type: "button",
7753
+ ref: (node) => {
7754
+ listRef.current[index] = node;
7755
+ },
7566
7756
  onClick: (e) => {
7567
7757
  e.preventDefault();
7568
7758
  e.stopPropagation();
7569
- handleClearAll();
7759
+ if (!isDisabled) toggleSelect(item.value);
7760
+ inputRef.current?.focus();
7570
7761
  },
7571
- className: "text-xs text-muted-foreground hover:underline cursor-pointer",
7572
- children: "Clear all"
7573
- }
7574
- ) }),
7575
- enableSearch && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "relative border-b border-border/60", children: [
7576
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.Search, { className: cn("absolute left-2 top-2.5 text-muted-foreground", sizeStyles8[size].icon) }),
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 rounded-t-xl bg-transparent focus:outline-none cursor-text", sizeStyles8[size].search)
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
- className: cn("max-h-60 overflow-y-auto p-1", size === "lg" ? "text-base" : size === "sm" ? "text-xs" : "text-sm"),
7598
- 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-2 animate-in fade-in-0 zoom-in-95 duration-300", children: [
7599
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.Loader2, { className: "h-6 w-6 animate-spin text-primary" }),
7600
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-muted-foreground", children: loadingText })
7601
- ] }) }) : filtered.length ? filtered.map((item, index) => {
7602
- const isSelected = value.includes(item.value);
7603
- const isDisabled = disabledOptions.includes(item.value);
7604
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
7605
- "li",
7606
- {
7607
- ref: (node) => {
7608
- listRef.current[index] = node;
7609
- },
7610
- onClick: (e) => {
7611
- e.preventDefault();
7612
- e.stopPropagation();
7613
- toggleSelect(item.value);
7614
- inputRef.current?.focus();
7615
- },
7616
- style: {
7617
- animationDelay: open ? `${Math.min(index * 20, 200)}ms` : "0ms"
7618
- },
7619
- className: cn(
7620
- "dropdown-item flex cursor-pointer items-center justify-between rounded-lg transition-colors",
7621
- sizeStyles8[size].item,
7622
- "hover:bg-accent hover:text-accent-foreground",
7623
- index === activeIndex && "bg-accent text-accent-foreground",
7624
- isDisabled && "opacity-50 cursor-not-allowed pointer-events-none"
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 border border-input bg-background shadow-sm min-h-10",
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
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
7657
- "disabled:cursor-not-allowed disabled:opacity-50"
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 ? value.map((itemValue) => {
7661
- const option = normalizedOptions.find((o) => o.value === itemValue);
7662
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { className: "inline-flex items-center gap-1 bg-accent text-accent-foreground rounded-lg px-2 py-1 text-xs", children: [
7663
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "truncate max-w-30", children: option ? displayFormat(option) : itemValue }),
7664
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
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
- role: "button",
7668
- tabIndex: 0,
7669
- "aria-label": `Remove ${option ? displayFormat(option) : itemValue}`,
7670
- onClick: (e) => {
7671
- e.preventDefault();
7672
- e.stopPropagation();
7673
- handleRemove(itemValue);
7674
- },
7675
- onKeyDown: (e) => {
7676
- if (e.key === "Enter" || e.key === " ") {
7677
- e.preventDefault();
7678
- e.stopPropagation();
7679
- handleRemove(itemValue);
7680
- }
7681
- },
7682
- className: "hover:text-destructive transition-colors cursor-pointer select-none",
7683
- children: "\xD7"
7684
- }
7685
- )
7686
- ] }, itemValue);
7687
- }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { className: "truncate text-sm", children: [
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 || "Select..." }) }),
7691
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
7692
- import_lucide_react18.ChevronDown,
7693
- {
7694
- className: cn("opacity-50 transition-all duration-200", sizeStyles8[size].icon, open && "rotate-180 scale-110 text-primary opacity-100")
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