@underverse-ui/underverse 0.2.59 → 0.2.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -4830,20 +4830,42 @@ var DropdownMenu = ({
4830
4830
  }
4831
4831
  );
4832
4832
  };
4833
- 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)(
4834
4845
  "button",
4835
4846
  {
4836
4847
  onClick,
4837
4848
  disabled,
4849
+ onMouseDown: (e) => e.preventDefault(),
4838
4850
  className: cn(
4839
- "flex w-full items-center gap-2 px-3 py-2 text-sm rounded-lg transition-colors",
4851
+ "flex w-full items-center gap-2 px-3 py-2 text-sm rounded-lg transition-colors group",
4840
4852
  "hover:bg-accent hover:text-accent-foreground",
4841
4853
  "focus:bg-accent focus:text-accent-foreground focus:outline-none",
4842
4854
  "disabled:opacity-50 disabled:cursor-not-allowed",
4843
4855
  destructive && "text-destructive hover:bg-destructive/10 focus:bg-destructive/10",
4856
+ active && "bg-primary/10 text-primary",
4844
4857
  className
4845
4858
  ),
4846
- children
4859
+ children: [
4860
+ Icon && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Icon, { className: cn("w-4 h-4 shrink-0", active ? "text-primary" : "opacity-60 group-hover:opacity-100") }),
4861
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex-1 text-left", children: [
4862
+ label && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cn("font-medium", description && "leading-tight"), children: label }),
4863
+ description && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "text-xs text-muted-foreground", children: description }),
4864
+ children
4865
+ ] }),
4866
+ shortcut && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "ml-2 text-xs text-muted-foreground opacity-60", children: shortcut }),
4867
+ active && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("svg", { className: "w-4 h-4 text-primary shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("polyline", { points: "20 6 9 17 4 12" }) })
4868
+ ]
4847
4869
  }
4848
4870
  );
4849
4871
  var DropdownMenuSeparator = ({ className }) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cn("h-px bg-border my-1", className) });
@@ -4888,6 +4910,15 @@ var getOptionLabel = (option) => {
4888
4910
  var getOptionValue = (option) => {
4889
4911
  return typeof option === "string" ? option : option.value;
4890
4912
  };
4913
+ var getOptionIcon = (option) => {
4914
+ return typeof option === "string" ? void 0 : option.icon;
4915
+ };
4916
+ var getOptionDescription = (option) => {
4917
+ return typeof option === "string" ? void 0 : option.description;
4918
+ };
4919
+ var getOptionDisabled = (option) => {
4920
+ return typeof option === "string" ? false : option.disabled ?? false;
4921
+ };
4891
4922
  var findOptionByValue = (options, value) => {
4892
4923
  return options.find((opt) => getOptionValue(opt) === value);
4893
4924
  };
@@ -4902,13 +4933,21 @@ var Combobox = ({
4902
4933
  className,
4903
4934
  disabled = false,
4904
4935
  size = "md",
4936
+ variant = "default",
4905
4937
  allowClear = false,
4906
4938
  usePortal = true,
4907
4939
  label,
4908
4940
  required,
4909
4941
  fontBold = false,
4910
4942
  loading: loading2 = false,
4911
- loadingText = "Loading..."
4943
+ loadingText = "Loading...",
4944
+ showSelectedIcon = true,
4945
+ maxHeight = 280,
4946
+ groupBy,
4947
+ renderOption,
4948
+ renderValue,
4949
+ error,
4950
+ helperText
4912
4951
  }) => {
4913
4952
  const [open, setOpen] = React20.useState(false);
4914
4953
  const [query, setQuery] = React20.useState("");
@@ -4926,6 +4965,7 @@ var Combobox = ({
4926
4965
  );
4927
4966
  const triggerRef = React20.useRef(null);
4928
4967
  const handleSelect = (option) => {
4968
+ if (getOptionDisabled(option)) return;
4929
4969
  const val = getOptionValue(option);
4930
4970
  if (val !== void 0 && val !== null) {
4931
4971
  onChange(val);
@@ -4950,9 +4990,64 @@ var Combobox = ({
4950
4990
  }, [open, enableSearch]);
4951
4991
  const selectedOption = findOptionByValue(options, value);
4952
4992
  const displayValue = selectedOption ? getOptionLabel(selectedOption) : "";
4993
+ const selectedIcon = selectedOption ? getOptionIcon(selectedOption) : void 0;
4994
+ const groupedOptions = React20.useMemo(() => {
4995
+ if (!groupBy) return null;
4996
+ const groups = {};
4997
+ filteredOptions.forEach((opt) => {
4998
+ const group = groupBy(opt);
4999
+ if (!groups[group]) groups[group] = [];
5000
+ groups[group].push(opt);
5001
+ });
5002
+ return groups;
5003
+ }, [filteredOptions, groupBy]);
5004
+ const renderOptionItem = (item, index) => {
5005
+ const itemValue = getOptionValue(item);
5006
+ const itemLabel = getOptionLabel(item);
5007
+ const itemIcon = getOptionIcon(item);
5008
+ const itemDescription = getOptionDescription(item);
5009
+ const itemDisabled = getOptionDisabled(item);
5010
+ const isSelected = itemValue === value;
5011
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
5012
+ "li",
5013
+ {
5014
+ ref: (node) => {
5015
+ listRef.current[index] = node;
5016
+ },
5017
+ id: `combobox-item-${index}`,
5018
+ role: "option",
5019
+ tabIndex: -1,
5020
+ "aria-selected": isSelected,
5021
+ "aria-disabled": itemDisabled,
5022
+ onClick: () => !itemDisabled && handleSelect(item),
5023
+ style: {
5024
+ animationDelay: open ? `${Math.min(index * 15, 150)}ms` : "0ms"
5025
+ },
5026
+ className: cn(
5027
+ "dropdown-item group flex cursor-pointer items-center gap-3 rounded-xl px-3 py-2.5 text-sm",
5028
+ "outline-none focus:outline-none focus-visible:outline-none",
5029
+ "transition-all duration-150",
5030
+ !itemDisabled && "hover:bg-accent/80 hover:shadow-sm",
5031
+ !itemDisabled && "focus:bg-accent focus:text-accent-foreground",
5032
+ index === activeIndex && !itemDisabled && "bg-accent/60",
5033
+ isSelected && "bg-primary/10 text-primary font-medium",
5034
+ itemDisabled && "opacity-50 cursor-not-allowed"
5035
+ ),
5036
+ children: [
5037
+ itemIcon && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: cn("shrink-0 w-5 h-5 flex items-center justify-center", isSelected ? "text-primary" : "text-muted-foreground"), children: itemIcon }),
5038
+ renderOption ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "flex-1 min-w-0", children: renderOption(item, isSelected) }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex-1 min-w-0", children: [
5039
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "block truncate", children: itemLabel }),
5040
+ itemDescription && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "block text-xs text-muted-foreground truncate mt-0.5", children: itemDescription })
5041
+ ] }),
5042
+ isSelected && showSelectedIcon && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "shrink-0 ml-auto", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.Check, { className: "h-4 w-4 text-primary" }) })
5043
+ ]
5044
+ },
5045
+ `${itemValue}-${index}`
5046
+ );
5047
+ };
4953
5048
  const dropdownBody = /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { "data-combobox-dropdown": true, "data-state": open ? "open" : "closed", role: "listbox", id: `${resolvedId}-listbox`, className: "w-full", children: [
4954
- enableSearch && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "relative p-3 border-b border-border/50 bg-muted/20", children: [
4955
- /* @__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" }),
4956
5051
  /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
4957
5052
  "input",
4958
5053
  {
@@ -4977,7 +5072,7 @@ var Combobox = ({
4977
5072
  });
4978
5073
  } else if (e.key === "Enter") {
4979
5074
  e.preventDefault();
4980
- if (activeIndex !== null && filteredOptions[activeIndex]) {
5075
+ if (activeIndex !== null && filteredOptions[activeIndex] && !getOptionDisabled(filteredOptions[activeIndex])) {
4981
5076
  handleSelect(filteredOptions[activeIndex]);
4982
5077
  }
4983
5078
  } else if (e.key === "Escape") {
@@ -4986,63 +5081,69 @@ var Combobox = ({
4986
5081
  }
4987
5082
  },
4988
5083
  placeholder: searchPlaceholder,
4989
- className: "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
+ ),
4990
5091
  "aria-autocomplete": "list",
4991
5092
  "aria-activedescendant": activeIndex != null ? `combobox-item-${activeIndex}` : void 0
4992
5093
  }
5094
+ ),
5095
+ query && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5096
+ "button",
5097
+ {
5098
+ type: "button",
5099
+ onClick: () => setQuery(""),
5100
+ className: "absolute right-3 top-1/2 -translate-y-1/2 p-0.5 rounded-md hover:bg-muted text-muted-foreground hover:text-foreground transition-colors",
5101
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.X, { className: "h-3.5 w-3.5" })
5102
+ }
4993
5103
  )
4994
- ] }),
4995
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "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: [
4996
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.Loader2, { className: "h-6 w-6 animate-spin text-primary" }),
4997
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "text-sm text-muted-foreground", children: loadingText || "Loading\u2026" })
4998
- ] }) }) : filteredOptions.length > 0 ? filteredOptions.map((item, index) => {
4999
- const itemValue = getOptionValue(item);
5000
- const itemLabel = getOptionLabel(item);
5001
- const isSelected = itemValue === value;
5002
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
5003
- "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",
5004
5125
  {
5005
- ref: (node) => {
5006
- listRef.current[index] = node;
5007
- },
5008
- id: `combobox-item-${index}`,
5009
- role: "option",
5010
- tabIndex: -1,
5011
- "aria-selected": isSelected,
5012
- onClick: () => handleSelect(item),
5013
- style: {
5014
- animationDelay: open ? `${Math.min(index * 20, 200)}ms` : "0ms"
5015
- },
5016
- className: cn(
5017
- "dropdown-item group flex cursor-pointer items-center justify-between rounded-lg px-2.5 py-1.5 text-sm",
5018
- "outline-none focus:outline-none focus-visible:outline-none",
5019
- "hover:bg-accent hover:text-accent-foreground",
5020
- "focus:bg-accent focus:text-accent-foreground",
5021
- "data-disabled:pointer-events-none data-disabled:opacity-50",
5022
- index === activeIndex && "bg-accent text-accent-foreground",
5023
- isSelected && "bg-accent text-accent-foreground"
5024
- ),
5025
- children: [
5026
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "truncate", children: itemLabel }),
5027
- isSelected && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.Check, { className: "h-4 w-4 text-primary" })
5028
- ]
5029
- },
5030
- `${itemValue}-${index}`
5031
- );
5032
- }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("li", { className: "px-3 py-8 text-center text-muted-foreground text-sm", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col items-center gap-2 animate-in fade-in-0 zoom-in-95 duration-300", children: [
5033
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react12.SearchX, { className: "h-8 w-8 opacity-40 text-muted-foreground" }),
5034
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "text-sm", children: emptyText }),
5035
- query && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { type: "button", onClick: () => setQuery(""), className: "text-xs text-primary hover:underline", children: "Clear" })
5126
+ type: "button",
5127
+ onClick: () => setQuery(""),
5128
+ className: "px-3 py-1.5 text-xs font-medium text-primary bg-primary/10 rounded-full hover:bg-primary/20 transition-colors",
5129
+ children: "Clear search"
5130
+ }
5131
+ )
5036
5132
  ] }) }) }) })
5037
5133
  ] });
5038
5134
  const sizeStyles8 = {
5039
- // Keep consistent with Input size heights
5040
5135
  sm: "h-8 py-1.5 text-sm md:h-7 md:text-xs",
5041
5136
  md: "h-10 py-2 text-sm",
5042
5137
  lg: "h-12 py-3 text-base"
5043
5138
  };
5139
+ const variantStyles6 = {
5140
+ default: "border border-input bg-background hover:bg-accent/5 hover:border-primary/40",
5141
+ outline: "border-2 border-input bg-transparent hover:border-primary/60",
5142
+ ghost: "border-0 bg-transparent hover:bg-accent/50",
5143
+ filled: "border-0 bg-muted/50 hover:bg-muted/80"
5144
+ };
5044
5145
  const labelSize = size === "sm" ? "text-xs" : size === "lg" ? "text-base" : "text-sm";
5045
- const radiusClass = size === "sm" ? "rounded-full" : "rounded-full";
5146
+ const radiusClass = size === "sm" ? "rounded-lg" : "rounded-xl";
5046
5147
  const verticalGap = size === "sm" ? "space-y-1.5" : "space-y-2";
5047
5148
  const triggerButtonBaseProps = {
5048
5149
  ref: triggerRef,
@@ -5052,6 +5153,7 @@ var Combobox = ({
5052
5153
  "aria-haspopup": "listbox",
5053
5154
  "aria-expanded": open,
5054
5155
  "aria-controls": `${resolvedId}-listbox`,
5156
+ "aria-invalid": !!error,
5055
5157
  id: resolvedId,
5056
5158
  "aria-labelledby": labelId,
5057
5159
  onKeyDown: (e) => {
@@ -5065,18 +5167,21 @@ var Combobox = ({
5065
5167
  }
5066
5168
  },
5067
5169
  className: cn(
5068
- "flex w-full items-center justify-between border border-input bg-background px-3",
5170
+ "group flex w-full items-center justify-between px-3 transition-all duration-200",
5069
5171
  radiusClass,
5070
5172
  sizeStyles8[size],
5071
- "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",
5072
5175
  "disabled:cursor-not-allowed disabled:opacity-50",
5073
- "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",
5074
5178
  className
5075
5179
  )
5076
5180
  };
5077
5181
  const triggerContents = /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
5078
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: cn("truncate", !displayValue && "text-muted-foreground", fontBold && "font-bold"), children: displayValue || placeholder }),
5079
- /* @__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: [
5080
5185
  allowClear && value && !disabled && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5081
5186
  "div",
5082
5187
  {
@@ -5085,11 +5190,23 @@ var Combobox = ({
5085
5190
  "aria-label": "Clear selection",
5086
5191
  onClick: handleClear,
5087
5192
  onKeyDown: (e) => (e.key === "Enter" || e.key === " ") && handleClear(e),
5088
- className: "opacity-0 group-hover:opacity-100 transition-opacity p-0.5 rounded-md hover:bg-destructive/10 text-muted-foreground hover:text-destructive",
5089
- 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" })
5090
5198
  }
5091
5199
  ),
5092
- /* @__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
+ )
5093
5210
  ] })
5094
5211
  ] });
5095
5212
  const triggerButtonForPopover = /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { ...triggerButtonBaseProps, children: triggerContents });
@@ -5136,7 +5253,18 @@ var Combobox = ({
5136
5253
  }
5137
5254
  ) : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "relative", children: [
5138
5255
  triggerButtonInline,
5139
- open && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "absolute left-0 top-full mt-1 z-50 w-full", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "rounded-2xl border 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
5140
5268
  ] })
5141
5269
  ] });
5142
5270
  };
@@ -7472,7 +7600,8 @@ var MultiCombobox = ({
7472
7600
  options,
7473
7601
  value,
7474
7602
  onChange,
7475
- placeholder = "Search...",
7603
+ placeholder = "Select...",
7604
+ searchPlaceholder = "Search...",
7476
7605
  maxSelected,
7477
7606
  disabledOptions = [],
7478
7607
  showTags = true,
@@ -7480,13 +7609,22 @@ var MultiCombobox = ({
7480
7609
  className,
7481
7610
  disabled = false,
7482
7611
  size = "md",
7612
+ variant = "default",
7483
7613
  label,
7484
7614
  title,
7485
7615
  required,
7486
7616
  displayFormat = (option) => option.label,
7487
7617
  loading: loading2 = false,
7488
7618
  loadingText = "Loading...",
7489
- emptyText = "No results found"
7619
+ emptyText = "No results found",
7620
+ showSelectedIcons = true,
7621
+ maxHeight = 280,
7622
+ groupBy,
7623
+ renderOption,
7624
+ renderTag,
7625
+ error,
7626
+ helperText,
7627
+ maxTagsVisible = 3
7490
7628
  }) => {
7491
7629
  const [query, setQuery] = React27.useState("");
7492
7630
  const [open, setOpen] = React27.useState(false);
@@ -7496,16 +7634,31 @@ var MultiCombobox = ({
7496
7634
  const triggerRef = React27.useRef(null);
7497
7635
  useShadCNAnimations();
7498
7636
  const normalizedOptions = React27.useMemo(
7499
- () => options.map((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
+ ),
7500
7640
  [options]
7501
7641
  );
7502
7642
  const enableSearch = normalizedOptions.length > 10;
7503
7643
  const filtered = React27.useMemo(
7504
- () => 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,
7505
7647
  [normalizedOptions, query, enableSearch]
7506
7648
  );
7649
+ const groupedOptions = React27.useMemo(() => {
7650
+ if (!groupBy) return null;
7651
+ const groups = /* @__PURE__ */ new Map();
7652
+ filtered.forEach((opt) => {
7653
+ const group = groupBy(opt);
7654
+ if (!groups.has(group)) groups.set(group, []);
7655
+ groups.get(group).push(opt);
7656
+ });
7657
+ return groups;
7658
+ }, [filtered, groupBy]);
7507
7659
  const toggleSelect = (optionValue) => {
7508
- if (disabledOptions.includes(optionValue)) return;
7660
+ const option = normalizedOptions.find((o) => o.value === optionValue);
7661
+ if (option?.disabled || disabledOptions.includes(optionValue)) return;
7509
7662
  if (value.includes(optionValue)) {
7510
7663
  onChange(value.filter((v) => v !== optionValue));
7511
7664
  } else {
@@ -7547,39 +7700,121 @@ var MultiCombobox = ({
7547
7700
  md: {
7548
7701
  trigger: "h-10 px-4 py-2 text-sm",
7549
7702
  icon: "h-4 w-4",
7550
- search: "px-8 py-2 text-sm",
7703
+ search: "px-10 py-2 text-sm",
7551
7704
  item: "text-sm px-3 py-2",
7552
7705
  tag: "px-2 py-1 text-xs"
7553
7706
  },
7554
7707
  lg: {
7555
7708
  trigger: "h-12 px-5 py-3 text-base",
7556
7709
  icon: "h-5 w-5",
7557
- search: "px-8 py-3 text-base",
7710
+ search: "px-10 py-3 text-base",
7558
7711
  item: "text-base px-3 py-3",
7559
7712
  tag: "px-2.5 py-1 text-sm"
7560
7713
  }
7561
7714
  };
7715
+ const variantStyles6 = {
7716
+ default: "border border-input bg-background shadow-sm hover:border-primary/50",
7717
+ outline: "border-2 border-input bg-transparent hover:border-primary",
7718
+ ghost: "border border-transparent bg-muted/50 hover:bg-muted"
7719
+ };
7562
7720
  const autoId = (0, import_react16.useId)();
7563
7721
  const resolvedId = id ? String(id) : `multicombobox-${autoId}`;
7564
7722
  const labelId = label ? `${resolvedId}-label` : void 0;
7565
7723
  const labelSize = size === "sm" ? "text-xs" : size === "lg" ? "text-base" : "text-sm";
7566
7724
  const listboxId = `${resolvedId}-listbox`;
7567
- const dropdownBody = /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { "data-combobox-dropdown": true, "data-state": open ? "open" : "closed", className: "w-full", children: [
7568
- 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)(
7569
- "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",
7570
7752
  {
7571
- type: "button",
7753
+ ref: (node) => {
7754
+ listRef.current[index] = node;
7755
+ },
7572
7756
  onClick: (e) => {
7573
7757
  e.preventDefault();
7574
7758
  e.stopPropagation();
7575
- handleClearAll();
7759
+ if (!isDisabled) toggleSelect(item.value);
7760
+ inputRef.current?.focus();
7576
7761
  },
7577
- className: "text-xs text-muted-foreground hover:underline cursor-pointer",
7578
- children: "Clear all"
7579
- }
7580
- ) }),
7581
- enableSearch && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "relative border-b border-border/60", children: [
7582
- /* @__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) }),
7583
7818
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
7584
7819
  "input",
7585
7820
  {
@@ -7590,8 +7825,17 @@ var MultiCombobox = ({
7590
7825
  setActiveIndex(null);
7591
7826
  },
7592
7827
  onKeyDown: handleKeyDown,
7593
- placeholder,
7594
- className: cn("w-full 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" })
7595
7839
  }
7596
7840
  )
7597
7841
  ] }),
@@ -7600,50 +7844,41 @@ var MultiCombobox = ({
7600
7844
  {
7601
7845
  id: listboxId,
7602
7846
  role: "listbox",
7603
- className: cn("max-h-60 overflow-y-auto p-1", size === "lg" ? "text-base" : size === "sm" ? "text-xs" : "text-sm"),
7604
- 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: [
7605
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.Loader2, { className: "h-6 w-6 animate-spin text-primary" }),
7606
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-muted-foreground", children: loadingText })
7607
- ] }) }) : filtered.length ? filtered.map((item, index) => {
7608
- const isSelected = value.includes(item.value);
7609
- const isDisabled = disabledOptions.includes(item.value);
7610
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
7611
- "li",
7612
- {
7613
- ref: (node) => {
7614
- listRef.current[index] = node;
7615
- },
7616
- onClick: (e) => {
7617
- e.preventDefault();
7618
- e.stopPropagation();
7619
- toggleSelect(item.value);
7620
- inputRef.current?.focus();
7621
- },
7622
- style: {
7623
- animationDelay: open ? `${Math.min(index * 20, 200)}ms` : "0ms"
7624
- },
7625
- className: cn(
7626
- "dropdown-item flex cursor-pointer items-center justify-between rounded-lg transition-colors",
7627
- sizeStyles8[size].item,
7628
- "hover:bg-accent hover:text-accent-foreground",
7629
- index === activeIndex && "bg-accent text-accent-foreground",
7630
- isDisabled && "opacity-50 cursor-not-allowed pointer-events-none"
7631
- ),
7632
- children: [
7633
- item.label,
7634
- isSelected && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.Check, { className: sizeStyles8[size].icon })
7635
- ]
7636
- },
7637
- item.value
7638
- );
7639
- }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("li", { className: cn("px-3 py-8 text-center text-muted-foreground", size === "lg" ? "text-base" : size === "sm" ? "text-xs" : "text-sm"), children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-col items-center gap-2 animate-in fade-in-0 zoom-in-95 duration-300", children: [
7640
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.SearchX, { className: "h-8 w-8 opacity-40 text-muted-foreground" }),
7641
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { children: emptyText }),
7642
- query && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("button", { type: "button", onClick: () => setQuery(""), className: "text-xs text-primary hover:underline", children: "Clear search" })
7847
+ "aria-multiselectable": "true",
7848
+ style: { maxHeight },
7849
+ className: cn("overflow-y-auto p-1.5", size === "lg" ? "text-base" : size === "sm" ? "text-xs" : "text-sm"),
7850
+ children: loading2 ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("li", { className: "px-3 py-8 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-col items-center gap-3 animate-in fade-in-0 zoom-in-95 duration-300", children: [
7851
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "relative", children: [
7852
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.Loader2, { className: "h-8 w-8 animate-spin text-primary" }),
7853
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.Sparkles, { className: "h-4 w-4 text-primary/60 absolute -top-1 -right-1 animate-pulse" })
7854
+ ] }),
7855
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-muted-foreground font-medium", children: loadingText })
7856
+ ] }) }) : filtered.length ? groupedOptions ? (
7857
+ // Render grouped options
7858
+ Array.from(groupedOptions.entries()).map(([group, items]) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("li", { className: "mb-2", children: [
7859
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "px-3 py-1.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider sticky top-0 bg-popover/95 backdrop-blur-sm", children: group }),
7860
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("ul", { children: items.map((item) => renderOptionItem(item, filtered.indexOf(item))) })
7861
+ ] }, group))
7862
+ ) : (
7863
+ // Render flat options
7864
+ filtered.map((item, index) => renderOptionItem(item, index))
7865
+ ) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("li", { className: cn("px-3 py-8 text-center text-muted-foreground"), children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-col items-center gap-3 animate-in fade-in-0 zoom-in-95 duration-300", children: [
7866
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.SearchX, { className: "h-10 w-10 opacity-30 text-muted-foreground" }),
7867
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "space-y-1", children: [
7868
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "font-medium block", children: emptyText }),
7869
+ query && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-xs opacity-60", children: "Try a different search term" })
7870
+ ] }),
7871
+ query && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("button", { type: "button", onClick: () => setQuery(""), className: "text-xs text-primary hover:underline flex items-center gap-1", children: [
7872
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.X, { className: "w-3 h-3" }),
7873
+ "Clear search"
7874
+ ] })
7643
7875
  ] }) })
7644
7876
  }
7645
7877
  )
7646
7878
  ] });
7879
+ const selectedOptions = value.map((v) => normalizedOptions.find((o) => o.value === v)).filter(Boolean);
7880
+ const visibleTags = maxTagsVisible ? selectedOptions.slice(0, maxTagsVisible) : selectedOptions;
7881
+ const hiddenCount = maxTagsVisible ? Math.max(0, selectedOptions.length - maxTagsVisible) : 0;
7647
7882
  const triggerButton = /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
7648
7883
  "button",
7649
7884
  {
@@ -7656,50 +7891,99 @@ var MultiCombobox = ({
7656
7891
  "aria-haspopup": "listbox",
7657
7892
  "aria-expanded": open,
7658
7893
  "aria-controls": listboxId,
7894
+ "aria-invalid": !!error,
7659
7895
  className: cn(
7660
- "flex w-full items-center gap-2 rounded-2xl 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",
7661
7897
  "px-3 py-2",
7662
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
7663
- "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"
7664
7903
  ),
7665
7904
  children: [
7666
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex items-center gap-1 flex-wrap min-h-6 flex-1", children: value.length > 0 ? showTags ? value.map((itemValue) => {
7667
- const option = normalizedOptions.find((o) => o.value === itemValue);
7668
- 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: [
7669
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "truncate max-w-30", children: option ? displayFormat(option) : itemValue }),
7670
- /* @__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)(
7671
7911
  "span",
7672
7912
  {
7673
- role: "button",
7674
- tabIndex: 0,
7675
- "aria-label": `Remove ${option ? displayFormat(option) : itemValue}`,
7676
- onClick: (e) => {
7677
- e.preventDefault();
7678
- e.stopPropagation();
7679
- handleRemove(itemValue);
7680
- },
7681
- onKeyDown: (e) => {
7682
- if (e.key === "Enter" || e.key === " ") {
7683
- e.preventDefault();
7684
- e.stopPropagation();
7685
- handleRemove(itemValue);
7686
- }
7687
- },
7688
- className: "hover:text-destructive transition-colors cursor-pointer select-none",
7689
- children: "\xD7"
7690
- }
7691
- )
7692
- ] }, itemValue);
7693
- }) : /* @__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: [
7694
7954
  value.length,
7955
+ " item",
7956
+ value.length > 1 ? "s" : "",
7695
7957
  " selected"
7696
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-muted-foreground", children: placeholder || "Select..." }) }),
7697
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
7698
- import_lucide_react18.ChevronDown,
7699
- {
7700
- className: cn("opacity-50 transition-all duration-200", sizeStyles8[size].icon, open && "rotate-180 scale-110 text-primary opacity-100")
7701
- }
7702
- )
7958
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-muted-foreground", children: placeholder }) }),
7959
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center gap-1.5 shrink-0", children: [
7960
+ showClear && value.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
7961
+ "div",
7962
+ {
7963
+ role: "button",
7964
+ tabIndex: 0,
7965
+ "aria-label": "Clear all",
7966
+ onClick: (e) => {
7967
+ e.preventDefault();
7968
+ e.stopPropagation();
7969
+ handleClearAll();
7970
+ },
7971
+ onKeyDown: (e) => (e.key === "Enter" || e.key === " ") && handleClearAll(),
7972
+ className: "opacity-0 group-hover:opacity-100 transition-all duration-200 p-1 rounded-lg hover:bg-destructive/10 text-muted-foreground hover:text-destructive",
7973
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.X, { className: "h-3.5 w-3.5" })
7974
+ }
7975
+ ),
7976
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
7977
+ import_lucide_react18.ChevronDown,
7978
+ {
7979
+ className: cn(
7980
+ "h-4 w-4 text-muted-foreground transition-all duration-300 ease-out",
7981
+ open && "rotate-180 text-primary",
7982
+ !open && "group-hover:text-foreground"
7983
+ )
7984
+ }
7985
+ )
7986
+ ] })
7703
7987
  ]
7704
7988
  }
7705
7989
  );
@@ -7746,7 +8030,18 @@ var MultiCombobox = ({
7746
8030
  contentClassName: "p-0",
7747
8031
  children: dropdownBody
7748
8032
  }
7749
- )
8033
+ ),
8034
+ (helperText || error) && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("p", { className: cn("text-xs transition-colors duration-200 flex items-center gap-1.5", error ? "text-destructive" : "text-muted-foreground"), children: [
8035
+ error && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: "w-3.5 h-3.5 shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
8036
+ "path",
8037
+ {
8038
+ fillRule: "evenodd",
8039
+ d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z",
8040
+ clipRule: "evenodd"
8041
+ }
8042
+ ) }),
8043
+ error || helperText
8044
+ ] })
7750
8045
  ] });
7751
8046
  };
7752
8047