@juv/codego-react-ui 1.1.3 → 1.1.5

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.js CHANGED
@@ -5,9 +5,25 @@ import { ChevronDown } from "lucide-react";
5
5
  // src/lib/utils.ts
6
6
  import { clsx } from "clsx";
7
7
  import { twMerge } from "tailwind-merge";
8
+ import * as ReactDOM from "react-dom";
8
9
  function cn(...inputs) {
9
10
  return twMerge(clsx(inputs));
10
11
  }
12
+ function getPortalPosition(triggerEl, dropdownHeight = 300, preferredPlacement = "bottom") {
13
+ const r = triggerEl.getBoundingClientRect();
14
+ const spaceBelow = window.innerHeight - r.bottom;
15
+ const spaceAbove = r.top;
16
+ const placement = preferredPlacement === "bottom" && spaceBelow < dropdownHeight && spaceAbove > spaceBelow ? "top" : preferredPlacement;
17
+ return {
18
+ top: placement === "bottom" ? r.bottom + 4 : r.top - 4,
19
+ left: r.left,
20
+ width: r.width,
21
+ placement
22
+ };
23
+ }
24
+ function FloatingPortal({ children }) {
25
+ return ReactDOM.createPortal(children, document.body);
26
+ }
11
27
 
12
28
  // src/components/ui/accordion.tsx
13
29
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -800,6 +816,8 @@ function ColorPicker({
800
816
  const [opacity, setOpacity] = React7.useState(100);
801
817
  const [recent, setRecent] = React7.useState([]);
802
818
  const ref = React7.useRef(null);
819
+ const triggerRef = React7.useRef(null);
820
+ const [dropStyle, setDropStyle] = React7.useState({});
803
821
  const color = controlled ?? internal;
804
822
  React7.useEffect(() => {
805
823
  setHex(color);
@@ -811,6 +829,19 @@ function ColorPicker({
811
829
  document.addEventListener("mousedown", handler);
812
830
  return () => document.removeEventListener("mousedown", handler);
813
831
  }, []);
832
+ function openPicker() {
833
+ if (triggerRef.current) {
834
+ const pos = getPortalPosition(triggerRef.current, 280);
835
+ setDropStyle({
836
+ position: "fixed",
837
+ top: pos.placement === "bottom" ? pos.top : void 0,
838
+ bottom: pos.placement === "top" ? window.innerHeight - pos.top : void 0,
839
+ left: pos.left,
840
+ zIndex: 9999
841
+ });
842
+ }
843
+ setOpen(true);
844
+ }
814
845
  function apply(c) {
815
846
  if (!controlled) setInternal(c);
816
847
  onChange?.(c);
@@ -825,9 +856,10 @@ function ColorPicker({
825
856
  /* @__PURE__ */ jsxs8(
826
857
  "button",
827
858
  {
859
+ ref: triggerRef,
828
860
  type: "button",
829
861
  disabled,
830
- onClick: () => setOpen((v) => !v),
862
+ onClick: () => open ? setOpen(false) : openPicker(),
831
863
  className: cn(
832
864
  "flex items-center gap-2 h-9 px-3 rounded-xl border border-border bg-background text-sm transition-colors hover:border-primary/40 focus:outline-none focus:ring-2 focus:ring-ring",
833
865
  disabled && "opacity-50 cursor-not-allowed pointer-events-none"
@@ -838,7 +870,7 @@ function ColorPicker({
838
870
  ]
839
871
  }
840
872
  ),
841
- open && /* @__PURE__ */ jsxs8("div", { className: "absolute z-50 mt-1 w-56 rounded-xl border border-border glass shadow-2xl p-3 space-y-3", children: [
873
+ open && /* @__PURE__ */ jsx9(FloatingPortal, { children: /* @__PURE__ */ jsxs8("div", { className: "w-56 rounded-xl border border-border glass shadow-2xl p-3 space-y-3", style: dropStyle, children: [
842
874
  /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2", children: [
843
875
  /* @__PURE__ */ jsx9(
844
876
  "input",
@@ -893,10 +925,7 @@ function ColorPicker({
893
925
  apply(s);
894
926
  setHex(s);
895
927
  },
896
- className: cn(
897
- "h-6 w-6 rounded-md border transition-transform hover:scale-110",
898
- color === s ? "border-primary ring-1 ring-primary" : "border-border/60"
899
- ),
928
+ className: cn("h-6 w-6 rounded-md border transition-transform hover:scale-110", color === s ? "border-primary ring-1 ring-primary" : "border-border/60"),
900
929
  style: { backgroundColor: s },
901
930
  title: s
902
931
  },
@@ -920,7 +949,7 @@ function ColorPicker({
920
949
  s
921
950
  )) })
922
951
  ] })
923
- ] })
952
+ ] }) })
924
953
  ] });
925
954
  }
926
955
 
@@ -947,7 +976,9 @@ function Combobox({
947
976
  const [open, setOpen] = React8.useState(false);
948
977
  const [query, setQuery] = React8.useState("");
949
978
  const ref = React8.useRef(null);
979
+ const triggerRef = React8.useRef(null);
950
980
  const inputRef = React8.useRef(null);
981
+ const [dropStyle, setDropStyle] = React8.useState({});
951
982
  const selected = controlled ?? internal;
952
983
  function update(val) {
953
984
  if (!controlled) setInternal(val);
@@ -971,6 +1002,20 @@ function Combobox({
971
1002
  e.stopPropagation();
972
1003
  update(multiple ? [] : "");
973
1004
  }
1005
+ const openDropdown = () => {
1006
+ if (triggerRef.current) {
1007
+ const pos = getPortalPosition(triggerRef.current, 300);
1008
+ setDropStyle({
1009
+ position: "fixed",
1010
+ top: pos.placement === "bottom" ? pos.top : void 0,
1011
+ bottom: pos.placement === "top" ? window.innerHeight - pos.top : void 0,
1012
+ left: pos.left,
1013
+ width: pos.width,
1014
+ zIndex: 9999
1015
+ });
1016
+ }
1017
+ setOpen(true);
1018
+ };
974
1019
  React8.useEffect(() => {
975
1020
  function handler(e) {
976
1021
  if (ref.current && !ref.current.contains(e.target)) {
@@ -984,13 +1029,10 @@ function Combobox({
984
1029
  React8.useEffect(() => {
985
1030
  if (open) setTimeout(() => inputRef.current?.focus(), 50);
986
1031
  }, [open]);
987
- const filtered = options.filter(
988
- (o) => o.label.toLowerCase().includes(query.toLowerCase())
989
- );
1032
+ const filtered = options.filter((o) => o.label.toLowerCase().includes(query.toLowerCase()));
990
1033
  const groups = Array.from(new Set(filtered.map((o) => o.group ?? ""))).filter(Boolean);
991
1034
  const ungrouped = filtered.filter((o) => !o.group);
992
1035
  const showCreate = creatable && query && !options.find((o) => o.label.toLowerCase() === query.toLowerCase());
993
- const selectedLabels = multiple ? selected.map((v) => options.find((o) => o.value === v)?.label ?? v) : options.find((o) => o.value === selected)?.label;
994
1036
  const hasValue = multiple ? selected.length > 0 : !!selected;
995
1037
  function renderOptions(opts) {
996
1038
  return opts.map((opt) => /* @__PURE__ */ jsxs9(
@@ -1018,9 +1060,10 @@ function Combobox({
1018
1060
  /* @__PURE__ */ jsxs9(
1019
1061
  "button",
1020
1062
  {
1063
+ ref: triggerRef,
1021
1064
  type: "button",
1022
1065
  disabled,
1023
- onClick: () => setOpen((v) => !v),
1066
+ onClick: () => open ? setOpen(false) : openDropdown(),
1024
1067
  className: cn(
1025
1068
  "flex w-full items-center justify-between gap-2 rounded-xl border border-border bg-background px-3 py-2 text-sm transition-colors",
1026
1069
  "hover:border-primary/40 focus:outline-none focus:ring-2 focus:ring-ring",
@@ -1028,7 +1071,7 @@ function Combobox({
1028
1071
  disabled && "opacity-50 cursor-not-allowed pointer-events-none"
1029
1072
  ),
1030
1073
  children: [
1031
- /* @__PURE__ */ jsx10("span", { className: cn("flex-1 truncate text-left", !hasValue && "text-muted-foreground"), children: multiple ? selected.length > 0 ? `${selected.length} selected` : placeholder : selectedLabels || placeholder }),
1074
+ /* @__PURE__ */ jsx10("span", { className: cn("flex-1 truncate text-left", !hasValue && "text-muted-foreground"), children: multiple ? selected.length > 0 ? `${selected.length} selected` : placeholder : options.find((o) => o.value === selected)?.label || placeholder }),
1032
1075
  /* @__PURE__ */ jsxs9("span", { className: "flex items-center gap-1 shrink-0", children: [
1033
1076
  clearable && hasValue && /* @__PURE__ */ jsx10("span", { onClick: clear, className: "text-muted-foreground hover:text-foreground transition-colors", children: /* @__PURE__ */ jsx10(X2, { className: "h-3.5 w-3.5" }) }),
1034
1077
  /* @__PURE__ */ jsx10(ChevronDown2, { className: cn("h-4 w-4 text-muted-foreground transition-transform", open && "rotate-180") })
@@ -1036,50 +1079,57 @@ function Combobox({
1036
1079
  ]
1037
1080
  }
1038
1081
  ),
1039
- open && /* @__PURE__ */ jsxs9("div", { className: "absolute z-50 mt-1 w-full rounded-xl border border-border glass shadow-2xl overflow-hidden", children: [
1040
- /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2 border-b border-border px-3 py-2", children: [
1041
- /* @__PURE__ */ jsx10(Search, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
1042
- /* @__PURE__ */ jsx10(
1043
- "input",
1044
- {
1045
- ref: inputRef,
1046
- value: query,
1047
- onChange: (e) => setQuery(e.target.value),
1048
- placeholder: searchPlaceholder,
1049
- className: "flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
1050
- }
1051
- )
1052
- ] }),
1053
- /* @__PURE__ */ jsxs9("div", { className: "overflow-y-auto p-1", style: { maxHeight }, children: [
1054
- showCreate && /* @__PURE__ */ jsxs9(
1055
- "button",
1056
- {
1057
- type: "button",
1058
- onClick: () => {
1059
- toggle(query);
1060
- setQuery("");
1061
- },
1062
- className: "flex w-full items-center gap-2 px-3 py-2 text-sm rounded-lg hover:bg-accent text-primary",
1063
- children: [
1064
- /* @__PURE__ */ jsx10(Plus, { className: "h-3.5 w-3.5" }),
1065
- 'Create "',
1066
- query,
1067
- '"'
1068
- ]
1069
- }
1070
- ),
1071
- groups.map((group) => /* @__PURE__ */ jsxs9("div", { children: [
1072
- /* @__PURE__ */ jsx10("p", { className: "px-3 py-1 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: group }),
1073
- renderOptions(filtered.filter((o) => o.group === group))
1074
- ] }, group)),
1075
- renderOptions(ungrouped),
1076
- filtered.length === 0 && !showCreate && /* @__PURE__ */ jsx10("p", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: "No results" })
1077
- ] }),
1078
- multiple && selected.length > 0 && /* @__PURE__ */ jsx10("div", { className: "border-t border-border px-3 py-2 flex flex-wrap gap-1", children: selected.map((v) => /* @__PURE__ */ jsxs9("span", { className: "inline-flex items-center gap-1 rounded-full bg-primary/15 text-primary text-xs px-2 py-0.5", children: [
1079
- options.find((o) => o.value === v)?.label ?? v,
1080
- /* @__PURE__ */ jsx10("button", { type: "button", onClick: () => toggle(v), className: "opacity-60 hover:opacity-100", children: /* @__PURE__ */ jsx10(X2, { className: "h-3 w-3" }) })
1081
- ] }, v)) })
1082
- ] })
1082
+ open && /* @__PURE__ */ jsx10(FloatingPortal, { children: /* @__PURE__ */ jsxs9(
1083
+ "div",
1084
+ {
1085
+ className: "rounded-xl border border-border glass shadow-2xl overflow-hidden",
1086
+ style: dropStyle,
1087
+ children: [
1088
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2 border-b border-border px-3 py-2", children: [
1089
+ /* @__PURE__ */ jsx10(Search, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
1090
+ /* @__PURE__ */ jsx10(
1091
+ "input",
1092
+ {
1093
+ ref: inputRef,
1094
+ value: query,
1095
+ onChange: (e) => setQuery(e.target.value),
1096
+ placeholder: searchPlaceholder,
1097
+ className: "flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
1098
+ }
1099
+ )
1100
+ ] }),
1101
+ /* @__PURE__ */ jsxs9("div", { className: "overflow-y-auto p-1", style: { maxHeight }, children: [
1102
+ showCreate && /* @__PURE__ */ jsxs9(
1103
+ "button",
1104
+ {
1105
+ type: "button",
1106
+ onClick: () => {
1107
+ toggle(query);
1108
+ setQuery("");
1109
+ },
1110
+ className: "flex w-full items-center gap-2 px-3 py-2 text-sm rounded-lg hover:bg-accent text-primary",
1111
+ children: [
1112
+ /* @__PURE__ */ jsx10(Plus, { className: "h-3.5 w-3.5" }),
1113
+ 'Create "',
1114
+ query,
1115
+ '"'
1116
+ ]
1117
+ }
1118
+ ),
1119
+ groups.map((group) => /* @__PURE__ */ jsxs9("div", { children: [
1120
+ /* @__PURE__ */ jsx10("p", { className: "px-3 py-1 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: group }),
1121
+ renderOptions(filtered.filter((o) => o.group === group))
1122
+ ] }, group)),
1123
+ renderOptions(ungrouped),
1124
+ filtered.length === 0 && !showCreate && /* @__PURE__ */ jsx10("p", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: "No results" })
1125
+ ] }),
1126
+ multiple && selected.length > 0 && /* @__PURE__ */ jsx10("div", { className: "border-t border-border px-3 py-2 flex flex-wrap gap-1", children: selected.map((v) => /* @__PURE__ */ jsxs9("span", { className: "inline-flex items-center gap-1 rounded-full bg-primary/15 text-primary text-xs px-2 py-0.5", children: [
1127
+ options.find((o) => o.value === v)?.label ?? v,
1128
+ /* @__PURE__ */ jsx10("button", { type: "button", onClick: () => toggle(v), className: "opacity-60 hover:opacity-100", children: /* @__PURE__ */ jsx10(X2, { className: "h-3 w-3" }) })
1129
+ ] }, v)) })
1130
+ ]
1131
+ }
1132
+ ) })
1083
1133
  ] });
1084
1134
  }
1085
1135
 
@@ -2053,6 +2103,20 @@ function parseValue(value, mode) {
2053
2103
  return null;
2054
2104
  }
2055
2105
  }
2106
+ function getAnchorStyle(anchorEl, wide = false) {
2107
+ if (!anchorEl) return { position: "fixed", top: 0, left: 0, zIndex: 9999 };
2108
+ const r = anchorEl.getBoundingClientRect();
2109
+ const spaceBelow = window.innerHeight - r.bottom;
2110
+ const dropH = wide ? 320 : 300;
2111
+ const placement = spaceBelow < dropH && r.top > spaceBelow ? "top" : "bottom";
2112
+ return {
2113
+ position: "fixed",
2114
+ top: placement === "bottom" ? r.bottom + 4 : void 0,
2115
+ bottom: placement === "top" ? window.innerHeight - r.top + 4 : void 0,
2116
+ left: r.left,
2117
+ zIndex: 9999
2118
+ };
2119
+ }
2056
2120
  function TimePicker({
2057
2121
  selected,
2058
2122
  disabledDateTimes,
@@ -2120,25 +2184,9 @@ function Calendar2({
2120
2184
  const isDisabled = (d) => disabledDates?.includes(format(d, "yyyy-MM-dd")) ?? false;
2121
2185
  return /* @__PURE__ */ jsxs15("div", { className: "p-3 select-none", children: [
2122
2186
  /* @__PURE__ */ jsxs15("div", { className: "flex items-center justify-between mb-3", children: [
2123
- /* @__PURE__ */ jsx16(
2124
- "button",
2125
- {
2126
- type: "button",
2127
- onClick: onPrevMonth,
2128
- className: "p-1 rounded hover:bg-white/10 text-muted-foreground hover:text-foreground transition-colors",
2129
- children: /* @__PURE__ */ jsx16(ChevronLeft3, { size: 16 })
2130
- }
2131
- ),
2187
+ /* @__PURE__ */ jsx16("button", { type: "button", onClick: onPrevMonth, className: "p-1 rounded hover:bg-white/10 text-muted-foreground hover:text-foreground transition-colors", children: /* @__PURE__ */ jsx16(ChevronLeft3, { size: 16 }) }),
2132
2188
  /* @__PURE__ */ jsx16("span", { className: "text-sm font-semibold text-foreground", children: format(month, "MMMM yyyy") }),
2133
- /* @__PURE__ */ jsx16(
2134
- "button",
2135
- {
2136
- type: "button",
2137
- onClick: onNextMonth,
2138
- className: "p-1 rounded hover:bg-white/10 text-muted-foreground hover:text-foreground transition-colors",
2139
- children: /* @__PURE__ */ jsx16(ChevronRight5, { size: 16 })
2140
- }
2141
- )
2189
+ /* @__PURE__ */ jsx16("button", { type: "button", onClick: onNextMonth, className: "p-1 rounded hover:bg-white/10 text-muted-foreground hover:text-foreground transition-colors", children: /* @__PURE__ */ jsx16(ChevronRight5, { size: 16 }) })
2142
2190
  ] }),
2143
2191
  /* @__PURE__ */ jsx16("div", { className: "grid grid-cols-7 mb-1", children: DAYS2.map((d) => /* @__PURE__ */ jsx16("div", { className: "text-center text-xs text-muted-foreground py-1", children: d }, d)) }),
2144
2192
  /* @__PURE__ */ jsx16("div", { className: "grid grid-cols-7 gap-y-1", children: days.map((d, i) => {
@@ -2173,136 +2221,44 @@ function DatePickerPopup({
2173
2221
  disabledDates,
2174
2222
  disabledDateTimes,
2175
2223
  onChange,
2176
- onClose
2224
+ onClose,
2225
+ anchorEl
2177
2226
  }) {
2178
2227
  const parsed = parseValue(value, mode);
2179
2228
  const [month, setMonth] = React13.useState(parsed ?? /* @__PURE__ */ new Date());
2180
2229
  const [selected, setSelected] = React13.useState(parsed);
2181
- const ref = React13.useRef(null);
2230
+ const anchorStyle = getAnchorStyle(anchorEl ?? null, mode === "dateTime");
2182
2231
  const commit = (d) => {
2183
- if (mode === "date") {
2184
- onChange(format(d, "yyyy-MM-dd"));
2185
- } else if (mode === "dateTime") {
2186
- onChange(format(d, "yyyy-MM-dd'T'HH:mm"));
2187
- } else {
2188
- onChange(format(d, "HH:mm"));
2189
- }
2232
+ if (mode === "date") onChange(format(d, "yyyy-MM-dd"));
2233
+ else if (mode === "dateTime") onChange(format(d, "yyyy-MM-dd'T'HH:mm"));
2234
+ else onChange(format(d, "HH:mm"));
2190
2235
  setSelected(d);
2191
2236
  };
2192
2237
  const handleDayClick = (d) => {
2193
2238
  const base = selected ?? /* @__PURE__ */ new Date();
2194
- const merged = setMinutes(setHours(d, getHours(base)), getMinutes(base));
2195
- commit(merged);
2239
+ commit(setMinutes(setHours(d, getHours(base)), getMinutes(base)));
2196
2240
  };
2197
2241
  const handleTimeChange = (h, m) => {
2198
- const base = selected ?? /* @__PURE__ */ new Date();
2199
- const merged = setMinutes(setHours(base, h), m);
2200
- commit(merged);
2242
+ commit(setMinutes(setHours(selected ?? /* @__PURE__ */ new Date(), h), m));
2201
2243
  };
2202
2244
  const datePrefix = selected ? format(selected, "yyyy-MM-dd") : void 0;
2203
- if (mode === "dateTime") {
2204
- return /* @__PURE__ */ jsxs15(
2205
- "div",
2245
+ const footer = (label) => /* @__PURE__ */ jsxs15("div", { className: "border-t border-white/10 px-3 py-2 flex items-center justify-between", children: [
2246
+ /* @__PURE__ */ jsx16("span", { className: "text-xs text-muted-foreground", children: label }),
2247
+ /* @__PURE__ */ jsx16(
2248
+ "button",
2206
2249
  {
2207
- ref,
2208
- className: "absolute z-50 mt-1 rounded-xl border border-white/10 bg-card shadow-xl backdrop-blur-sm",
2209
- style: { top: "100%", left: 0 },
2210
- children: [
2211
- /* @__PURE__ */ jsxs15("div", { className: "flex", children: [
2212
- /* @__PURE__ */ jsxs15("div", { className: "flex-1 min-w-[240px]", children: [
2213
- /* @__PURE__ */ jsx16("p", { className: "text-xs font-medium text-muted-foreground px-3 pt-3 pb-1", children: "Date" }),
2214
- /* @__PURE__ */ jsx16(
2215
- Calendar2,
2216
- {
2217
- selected,
2218
- month,
2219
- disabledDates,
2220
- onDayClick: handleDayClick,
2221
- onPrevMonth: () => setMonth((m) => subMonths(m, 1)),
2222
- onNextMonth: () => setMonth((m) => addMonths(m, 1))
2223
- }
2224
- )
2225
- ] }),
2226
- /* @__PURE__ */ jsx16("div", { className: "w-px bg-white/10 my-3" }),
2227
- /* @__PURE__ */ jsxs15("div", { className: "flex-1 min-w-[140px] flex flex-col", children: [
2228
- /* @__PURE__ */ jsx16("p", { className: "text-xs font-medium text-muted-foreground px-3 pt-3 pb-1", children: "Time" }),
2229
- /* @__PURE__ */ jsxs15("div", { className: "flex gap-2 px-3 pb-3 flex-1", children: [
2230
- /* @__PURE__ */ jsxs15("div", { className: "flex-1", children: [
2231
- /* @__PURE__ */ jsx16("p", { className: "text-xs text-muted-foreground mb-1 text-center", children: "HH" }),
2232
- /* @__PURE__ */ jsx16("div", { className: "grid grid-cols-1 gap-1 max-h-52 overflow-y-auto", children: HOURS.map((h) => /* @__PURE__ */ jsx16(
2233
- "button",
2234
- {
2235
- type: "button",
2236
- disabled: MINUTES.every((m) => {
2237
- if (!disabledDateTimes || !datePrefix) return false;
2238
- return disabledDateTimes.includes(`${datePrefix}T${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`);
2239
- }),
2240
- onClick: () => handleTimeChange(h, selected ? getMinutes(selected) : 0),
2241
- className: cn(
2242
- "text-xs rounded py-1 transition-colors",
2243
- selected && getHours(selected) === h ? "bg-primary text-primary-foreground" : "hover:bg-white/10 text-foreground",
2244
- MINUTES.every((m) => {
2245
- if (!disabledDateTimes || !datePrefix) return false;
2246
- return disabledDateTimes.includes(`${datePrefix}T${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`);
2247
- }) && "opacity-30 cursor-not-allowed line-through"
2248
- ),
2249
- children: String(h).padStart(2, "0")
2250
- },
2251
- h
2252
- )) })
2253
- ] }),
2254
- /* @__PURE__ */ jsxs15("div", { className: "w-12", children: [
2255
- /* @__PURE__ */ jsx16("p", { className: "text-xs text-muted-foreground mb-1 text-center", children: "MM" }),
2256
- /* @__PURE__ */ jsx16("div", { className: "grid grid-cols-1 gap-1 max-h-52 overflow-y-auto", children: MINUTES.map((m) => {
2257
- const h = selected ? getHours(selected) : 0;
2258
- const isDisabled = disabledDateTimes && datePrefix ? disabledDateTimes.includes(`${datePrefix}T${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`) : false;
2259
- return /* @__PURE__ */ jsxs15(
2260
- "button",
2261
- {
2262
- type: "button",
2263
- disabled: isDisabled,
2264
- onClick: () => handleTimeChange(h, m),
2265
- className: cn(
2266
- "text-xs rounded py-1 transition-colors",
2267
- selected && getMinutes(selected) === m ? "bg-primary text-primary-foreground" : "hover:bg-white/10 text-foreground",
2268
- isDisabled && "opacity-30 cursor-not-allowed line-through"
2269
- ),
2270
- children: [
2271
- ":",
2272
- String(m).padStart(2, "0")
2273
- ]
2274
- },
2275
- m
2276
- );
2277
- }) })
2278
- ] })
2279
- ] })
2280
- ] })
2281
- ] }),
2282
- /* @__PURE__ */ jsxs15("div", { className: "border-t border-white/10 px-3 py-2 flex items-center justify-between", children: [
2283
- /* @__PURE__ */ jsx16("span", { className: "text-xs text-muted-foreground", children: selected ? format(selected, "MMM d, yyyy HH:mm") : "No date selected" }),
2284
- /* @__PURE__ */ jsx16(
2285
- "button",
2286
- {
2287
- type: "button",
2288
- onClick: onClose,
2289
- className: "text-xs rounded-lg bg-primary text-primary-foreground px-3 py-1 hover:opacity-90 transition-opacity",
2290
- children: "Done"
2291
- }
2292
- )
2293
- ] })
2294
- ]
2250
+ type: "button",
2251
+ onClick: onClose,
2252
+ className: "text-xs rounded-lg bg-primary text-primary-foreground px-3 py-1 hover:opacity-90 transition-opacity",
2253
+ children: "Done"
2295
2254
  }
2296
- );
2297
- }
2298
- return /* @__PURE__ */ jsxs15(
2299
- "div",
2300
- {
2301
- ref,
2302
- className: "absolute z-50 mt-1 rounded-xl border border-white/10 bg-card shadow-xl backdrop-blur-sm min-w-[260px]",
2303
- style: { top: "100%", left: 0 },
2304
- children: [
2305
- mode === "date" && /* @__PURE__ */ jsxs15(Fragment3, { children: [
2255
+ )
2256
+ ] });
2257
+ if (mode === "dateTime") {
2258
+ return /* @__PURE__ */ jsx16(FloatingPortal, { children: /* @__PURE__ */ jsxs15("div", { className: "rounded-xl border border-white/10 bg-card shadow-xl backdrop-blur-sm", style: anchorStyle, children: [
2259
+ /* @__PURE__ */ jsxs15("div", { className: "flex", children: [
2260
+ /* @__PURE__ */ jsxs15("div", { className: "flex-1 min-w-[240px]", children: [
2261
+ /* @__PURE__ */ jsx16("p", { className: "text-xs font-medium text-muted-foreground px-3 pt-3 pb-1", children: "Date" }),
2306
2262
  /* @__PURE__ */ jsx16(
2307
2263
  Calendar2,
2308
2264
  {
@@ -2313,46 +2269,91 @@ function DatePickerPopup({
2313
2269
  onPrevMonth: () => setMonth((m) => subMonths(m, 1)),
2314
2270
  onNextMonth: () => setMonth((m) => addMonths(m, 1))
2315
2271
  }
2316
- ),
2317
- /* @__PURE__ */ jsxs15("div", { className: "border-t border-white/10 px-3 py-2 flex items-center justify-between", children: [
2318
- /* @__PURE__ */ jsx16("span", { className: "text-xs text-muted-foreground", children: selected ? format(selected, "MMM d, yyyy") : "No date selected" }),
2319
- /* @__PURE__ */ jsx16(
2320
- "button",
2321
- {
2322
- type: "button",
2323
- onClick: onClose,
2324
- className: "text-xs rounded-lg bg-primary text-primary-foreground px-3 py-1 hover:opacity-90 transition-opacity",
2325
- children: "Done"
2326
- }
2327
- )
2328
- ] })
2272
+ )
2329
2273
  ] }),
2330
- mode === "time" && /* @__PURE__ */ jsxs15(Fragment3, { children: [
2331
- /* @__PURE__ */ jsx16(
2332
- TimePicker,
2333
- {
2334
- selected,
2335
- disabledDateTimes,
2336
- datePrefix,
2337
- onChange: handleTimeChange
2338
- }
2339
- ),
2340
- /* @__PURE__ */ jsxs15("div", { className: "border-t border-white/10 px-3 py-2 flex items-center justify-between", children: [
2341
- /* @__PURE__ */ jsx16("span", { className: "text-xs text-muted-foreground", children: selected ? format(selected, "HH:mm") : "No time selected" }),
2342
- /* @__PURE__ */ jsx16(
2343
- "button",
2344
- {
2345
- type: "button",
2346
- onClick: onClose,
2347
- className: "text-xs rounded-lg bg-primary text-primary-foreground px-3 py-1 hover:opacity-90 transition-opacity",
2348
- children: "Done"
2349
- }
2350
- )
2274
+ /* @__PURE__ */ jsx16("div", { className: "w-px bg-white/10 my-3" }),
2275
+ /* @__PURE__ */ jsxs15("div", { className: "flex-1 min-w-[140px] flex flex-col", children: [
2276
+ /* @__PURE__ */ jsx16("p", { className: "text-xs font-medium text-muted-foreground px-3 pt-3 pb-1", children: "Time" }),
2277
+ /* @__PURE__ */ jsxs15("div", { className: "flex gap-2 px-3 pb-3 flex-1", children: [
2278
+ /* @__PURE__ */ jsxs15("div", { className: "flex-1", children: [
2279
+ /* @__PURE__ */ jsx16("p", { className: "text-xs text-muted-foreground mb-1 text-center", children: "HH" }),
2280
+ /* @__PURE__ */ jsx16("div", { className: "grid grid-cols-1 gap-1 max-h-52 overflow-y-auto", children: HOURS.map((h) => /* @__PURE__ */ jsx16(
2281
+ "button",
2282
+ {
2283
+ type: "button",
2284
+ disabled: MINUTES.every((m) => {
2285
+ if (!disabledDateTimes || !datePrefix) return false;
2286
+ return disabledDateTimes.includes(`${datePrefix}T${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`);
2287
+ }),
2288
+ onClick: () => handleTimeChange(h, selected ? getMinutes(selected) : 0),
2289
+ className: cn(
2290
+ "text-xs rounded py-1 transition-colors",
2291
+ selected && getHours(selected) === h ? "bg-primary text-primary-foreground" : "hover:bg-white/10 text-foreground"
2292
+ ),
2293
+ children: String(h).padStart(2, "0")
2294
+ },
2295
+ h
2296
+ )) })
2297
+ ] }),
2298
+ /* @__PURE__ */ jsxs15("div", { className: "w-12", children: [
2299
+ /* @__PURE__ */ jsx16("p", { className: "text-xs text-muted-foreground mb-1 text-center", children: "MM" }),
2300
+ /* @__PURE__ */ jsx16("div", { className: "grid grid-cols-1 gap-1 max-h-52 overflow-y-auto", children: MINUTES.map((m) => {
2301
+ const h = selected ? getHours(selected) : 0;
2302
+ const dis = disabledDateTimes && datePrefix ? disabledDateTimes.includes(`${datePrefix}T${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`) : false;
2303
+ return /* @__PURE__ */ jsxs15(
2304
+ "button",
2305
+ {
2306
+ type: "button",
2307
+ disabled: dis,
2308
+ onClick: () => handleTimeChange(h, m),
2309
+ className: cn(
2310
+ "text-xs rounded py-1 transition-colors",
2311
+ selected && getMinutes(selected) === m ? "bg-primary text-primary-foreground" : "hover:bg-white/10 text-foreground",
2312
+ dis && "opacity-30 cursor-not-allowed line-through"
2313
+ ),
2314
+ children: [
2315
+ ":",
2316
+ String(m).padStart(2, "0")
2317
+ ]
2318
+ },
2319
+ m
2320
+ );
2321
+ }) })
2322
+ ] })
2351
2323
  ] })
2352
2324
  ] })
2353
- ]
2354
- }
2355
- );
2325
+ ] }),
2326
+ footer(selected ? format(selected, "MMM d, yyyy HH:mm") : "No date selected")
2327
+ ] }) });
2328
+ }
2329
+ return /* @__PURE__ */ jsx16(FloatingPortal, { children: /* @__PURE__ */ jsxs15("div", { className: "rounded-xl border border-white/10 bg-card shadow-xl backdrop-blur-sm min-w-[260px]", style: anchorStyle, children: [
2330
+ mode === "date" && /* @__PURE__ */ jsxs15(Fragment3, { children: [
2331
+ /* @__PURE__ */ jsx16(
2332
+ Calendar2,
2333
+ {
2334
+ selected,
2335
+ month,
2336
+ disabledDates,
2337
+ onDayClick: handleDayClick,
2338
+ onPrevMonth: () => setMonth((m) => subMonths(m, 1)),
2339
+ onNextMonth: () => setMonth((m) => addMonths(m, 1))
2340
+ }
2341
+ ),
2342
+ footer(selected ? format(selected, "MMM d, yyyy") : "No date selected")
2343
+ ] }),
2344
+ mode === "time" && /* @__PURE__ */ jsxs15(Fragment3, { children: [
2345
+ /* @__PURE__ */ jsx16(
2346
+ TimePicker,
2347
+ {
2348
+ selected,
2349
+ disabledDateTimes,
2350
+ datePrefix,
2351
+ onChange: handleTimeChange
2352
+ }
2353
+ ),
2354
+ footer(selected ? format(selected, "HH:mm") : "No time selected")
2355
+ ] })
2356
+ ] }) });
2356
2357
  }
2357
2358
 
2358
2359
  // src/components/ui/date-range-picker.tsx
@@ -2390,6 +2391,8 @@ function DateRangePicker({
2390
2391
  const [viewMonth, setViewMonth] = React14.useState(today.getMonth());
2391
2392
  const [viewYear, setViewYear] = React14.useState(today.getFullYear());
2392
2393
  const ref = React14.useRef(null);
2394
+ const triggerRef = React14.useRef(null);
2395
+ const [dropStyle, setDropStyle] = React14.useState({});
2393
2396
  const range2 = controlled ?? internal;
2394
2397
  React14.useEffect(() => {
2395
2398
  function handler(e) {
@@ -2402,6 +2405,19 @@ function DateRangePicker({
2402
2405
  if (!controlled) setInternal(r);
2403
2406
  onChange?.(r);
2404
2407
  }
2408
+ function openPicker() {
2409
+ if (triggerRef.current) {
2410
+ const pos = getPortalPosition(triggerRef.current, 340);
2411
+ setDropStyle({
2412
+ position: "fixed",
2413
+ top: pos.placement === "bottom" ? pos.top : void 0,
2414
+ bottom: pos.placement === "top" ? window.innerHeight - pos.top : void 0,
2415
+ left: pos.left,
2416
+ zIndex: 9999
2417
+ });
2418
+ }
2419
+ setOpen(true);
2420
+ }
2405
2421
  function selectDay(d) {
2406
2422
  if (selecting === "from") {
2407
2423
  update({ from: d, to: null });
@@ -2436,9 +2452,10 @@ function DateRangePicker({
2436
2452
  /* @__PURE__ */ jsxs16(
2437
2453
  "button",
2438
2454
  {
2455
+ ref: triggerRef,
2439
2456
  type: "button",
2440
2457
  disabled,
2441
- onClick: () => setOpen((v) => !v),
2458
+ onClick: () => open ? setOpen(false) : openPicker(),
2442
2459
  className: cn(
2443
2460
  "flex w-full items-center gap-2 rounded-xl border border-border bg-background px-3 py-2 text-sm transition-colors",
2444
2461
  "hover:border-primary/40 focus:outline-none focus:ring-2 focus:ring-ring",
@@ -2464,25 +2481,41 @@ function DateRangePicker({
2464
2481
  ]
2465
2482
  }
2466
2483
  ),
2467
- open && /* @__PURE__ */ jsxs16("div", { className: "absolute z-50 mt-1 rounded-xl border border-border glass shadow-2xl p-4 w-72", children: [
2484
+ open && /* @__PURE__ */ jsx17(FloatingPortal, { children: /* @__PURE__ */ jsxs16("div", { className: "rounded-xl border border-border glass shadow-2xl p-4 w-72", style: dropStyle, children: [
2468
2485
  /* @__PURE__ */ jsxs16("div", { className: "flex items-center justify-between mb-3", children: [
2469
- /* @__PURE__ */ jsx17("button", { type: "button", onClick: () => {
2470
- if (viewMonth === 0) {
2471
- setViewMonth(11);
2472
- setViewYear((y) => y - 1);
2473
- } else setViewMonth((m) => m - 1);
2474
- }, className: "p-1 rounded-md hover:bg-accent transition-colors", children: /* @__PURE__ */ jsx17(ChevronLeft4, { className: "h-4 w-4" }) }),
2486
+ /* @__PURE__ */ jsx17(
2487
+ "button",
2488
+ {
2489
+ type: "button",
2490
+ onClick: () => {
2491
+ if (viewMonth === 0) {
2492
+ setViewMonth(11);
2493
+ setViewYear((y) => y - 1);
2494
+ } else setViewMonth((m) => m - 1);
2495
+ },
2496
+ className: "p-1 rounded-md hover:bg-accent transition-colors",
2497
+ children: /* @__PURE__ */ jsx17(ChevronLeft4, { className: "h-4 w-4" })
2498
+ }
2499
+ ),
2475
2500
  /* @__PURE__ */ jsxs16("span", { className: "text-sm font-semibold", children: [
2476
2501
  MONTHS2[viewMonth],
2477
2502
  " ",
2478
2503
  viewYear
2479
2504
  ] }),
2480
- /* @__PURE__ */ jsx17("button", { type: "button", onClick: () => {
2481
- if (viewMonth === 11) {
2482
- setViewMonth(0);
2483
- setViewYear((y) => y + 1);
2484
- } else setViewMonth((m) => m + 1);
2485
- }, className: "p-1 rounded-md hover:bg-accent transition-colors", children: /* @__PURE__ */ jsx17(ChevronRight6, { className: "h-4 w-4" }) })
2505
+ /* @__PURE__ */ jsx17(
2506
+ "button",
2507
+ {
2508
+ type: "button",
2509
+ onClick: () => {
2510
+ if (viewMonth === 11) {
2511
+ setViewMonth(0);
2512
+ setViewYear((y) => y + 1);
2513
+ } else setViewMonth((m) => m + 1);
2514
+ },
2515
+ className: "p-1 rounded-md hover:bg-accent transition-colors",
2516
+ children: /* @__PURE__ */ jsx17(ChevronRight6, { className: "h-4 w-4" })
2517
+ }
2518
+ )
2486
2519
  ] }),
2487
2520
  /* @__PURE__ */ jsx17("div", { className: "grid grid-cols-7 mb-1", children: DAYS3.map((d) => /* @__PURE__ */ jsx17("div", { className: "text-center text-[10px] font-semibold text-muted-foreground py-1", children: d }, d)) }),
2488
2521
  /* @__PURE__ */ jsx17("div", { className: "grid grid-cols-7 gap-y-0.5", children: days.map((d, i) => {
@@ -2517,7 +2550,7 @@ function DateRangePicker({
2517
2550
  );
2518
2551
  }) }),
2519
2552
  /* @__PURE__ */ jsx17("p", { className: "mt-3 text-center text-xs text-muted-foreground", children: selecting === "from" ? "Select start date" : "Select end date" })
2520
- ] })
2553
+ ] }) })
2521
2554
  ] });
2522
2555
  }
2523
2556
 
@@ -2619,10 +2652,10 @@ function Drawer({
2619
2652
  import * as React16 from "react";
2620
2653
  import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
2621
2654
  var widthMap = {
2622
- sm: "w-40",
2623
- md: "w-56",
2624
- lg: "w-72",
2625
- auto: "w-auto min-w-[10rem]"
2655
+ sm: 160,
2656
+ md: 224,
2657
+ lg: 288,
2658
+ auto: void 0
2626
2659
  };
2627
2660
  function Dropdown({
2628
2661
  trigger,
@@ -2636,25 +2669,40 @@ function Dropdown({
2636
2669
  className
2637
2670
  }) {
2638
2671
  const [isOpen, setIsOpen] = React16.useState(false);
2639
- const containerRef = React16.useRef(null);
2640
- const toggle = () => {
2672
+ const triggerRef = React16.useRef(null);
2673
+ const [dropStyle, setDropStyle] = React16.useState({});
2674
+ const open = () => {
2641
2675
  if (disabled) return;
2642
- const next = !isOpen;
2643
- setIsOpen(next);
2644
- onOpenChange?.(next);
2676
+ if (triggerRef.current) {
2677
+ const r = triggerRef.current.getBoundingClientRect();
2678
+ const pos = getPortalPosition(triggerRef.current, 320, placement);
2679
+ const w = widthMap[width];
2680
+ let left = r.left;
2681
+ if (align === "right") left = r.right - (w ?? r.width);
2682
+ if (align === "center") left = r.left + r.width / 2 - (w ?? r.width) / 2;
2683
+ setDropStyle({
2684
+ position: "fixed",
2685
+ top: pos.placement === "bottom" ? pos.top : void 0,
2686
+ bottom: pos.placement === "top" ? window.innerHeight - r.top + 4 : void 0,
2687
+ left,
2688
+ width: w,
2689
+ minWidth: w ? void 0 : "10rem",
2690
+ zIndex: 9999
2691
+ });
2692
+ }
2693
+ setIsOpen(true);
2694
+ onOpenChange?.(true);
2645
2695
  };
2646
2696
  const close = () => {
2647
2697
  setIsOpen(false);
2648
2698
  onOpenChange?.(false);
2649
2699
  };
2650
2700
  React16.useEffect(() => {
2651
- const handleClickOutside = (event) => {
2652
- if (containerRef.current && !containerRef.current.contains(event.target)) {
2653
- close();
2654
- }
2701
+ const handleClickOutside = (e) => {
2702
+ if (triggerRef.current && !triggerRef.current.contains(e.target)) close();
2655
2703
  };
2656
- const handleKeyDown = (event) => {
2657
- if (event.key === "Escape") close();
2704
+ const handleKeyDown = (e) => {
2705
+ if (e.key === "Escape") close();
2658
2706
  };
2659
2707
  document.addEventListener("mousedown", handleClickOutside);
2660
2708
  document.addEventListener("keydown", handleKeyDown);
@@ -2663,39 +2711,30 @@ function Dropdown({
2663
2711
  document.removeEventListener("keydown", handleKeyDown);
2664
2712
  };
2665
2713
  }, [isOpen]);
2666
- return /* @__PURE__ */ jsxs18("div", { className: "relative inline-block text-left", ref: containerRef, children: [
2714
+ return /* @__PURE__ */ jsxs18("div", { className: "relative inline-block text-left", ref: triggerRef, children: [
2667
2715
  /* @__PURE__ */ jsx19(
2668
2716
  "div",
2669
2717
  {
2670
- onClick: toggle,
2718
+ onClick: () => isOpen ? close() : open(),
2671
2719
  "aria-expanded": isOpen,
2672
2720
  "aria-haspopup": "true",
2673
2721
  className: cn(disabled && "opacity-50 cursor-not-allowed pointer-events-none"),
2674
2722
  children: trigger
2675
2723
  }
2676
2724
  ),
2677
- isOpen && /* @__PURE__ */ jsx19(
2725
+ isOpen && /* @__PURE__ */ jsx19(FloatingPortal, { children: /* @__PURE__ */ jsx19(
2678
2726
  "div",
2679
2727
  {
2680
2728
  className: cn(
2681
- "absolute z-50 rounded-xl border border-white/10 bg-background/90 backdrop-blur-2xl text-popover-foreground",
2729
+ "rounded-xl border border-white/10 bg-background/90 backdrop-blur-2xl text-popover-foreground",
2682
2730
  "shadow-[0_8px_32px_rgba(0,0,0,0.35)] ring-1 ring-white/5",
2683
2731
  "animate-in fade-in-0 zoom-in-95 duration-150",
2684
- widthMap[width],
2685
- placement === "top" ? "bottom-full mb-2" : "top-full mt-2",
2686
- align === "right" ? "right-0" : align === "center" ? "left-1/2 -translate-x-1/2" : "left-0",
2687
2732
  className
2688
2733
  ),
2689
- children: /* @__PURE__ */ jsx19(
2690
- "div",
2691
- {
2692
- className: "py-1.5",
2693
- onClick: closeOnSelect ? close : void 0,
2694
- children
2695
- }
2696
- )
2734
+ style: dropStyle,
2735
+ children: /* @__PURE__ */ jsx19("div", { className: "py-1.5", onClick: closeOnSelect ? close : void 0, children })
2697
2736
  }
2698
- )
2737
+ ) })
2699
2738
  ] });
2700
2739
  }
2701
2740
  function DropdownItem({
@@ -2728,10 +2767,7 @@ function DropdownItem({
2728
2767
  function DropdownSeparator({ className }) {
2729
2768
  return /* @__PURE__ */ jsx19("div", { className: cn("my-1.5 h-px bg-white/8", className) });
2730
2769
  }
2731
- function DropdownLabel({
2732
- children,
2733
- className
2734
- }) {
2770
+ function DropdownLabel({ children, className }) {
2735
2771
  return /* @__PURE__ */ jsx19("p", { className: cn("px-3.5 py-1.5 text-xs font-semibold uppercase tracking-wider text-muted-foreground/60", className), children });
2736
2772
  }
2737
2773
 
@@ -3593,6 +3629,7 @@ var Input = React18.forwardRef(
3593
3629
  value: internalValue,
3594
3630
  disabledDates,
3595
3631
  disabledDateTimes,
3632
+ anchorEl: containerRef.current,
3596
3633
  onChange: (v) => {
3597
3634
  setInternalValue(v);
3598
3635
  setValidationError(null);
@@ -3984,30 +4021,43 @@ function Select({
3984
4021
  const [search, setSearch] = React23.useState("");
3985
4022
  const [isSearching, setIsSearching] = React23.useState(false);
3986
4023
  const containerRef = React23.useRef(null);
4024
+ const triggerRef = React23.useRef(null);
4025
+ const [dropStyle, setDropStyle] = React23.useState({});
3987
4026
  const getKey = (opt) => String(Object.keys(opt)[0]);
3988
4027
  const getLabel = (opt) => Object.values(opt)[0];
3989
4028
  const selectedValues = React23.useMemo(() => {
3990
- if (multiple && Array.isArray(value)) {
3991
- return value;
3992
- } else if (typeof value === "string") {
3993
- return [value];
3994
- }
4029
+ if (multiple && Array.isArray(value)) return value;
4030
+ if (typeof value === "string") return [value];
3995
4031
  return [];
3996
4032
  }, [value, multiple]);
3997
- const selectedOptions = React23.useMemo(() => {
3998
- return options.filter((opt) => selectedValues.includes(getKey(opt)));
3999
- }, [options, selectedValues]);
4000
- const filteredOptions = searchable ? options.filter(
4001
- (opt) => getLabel(opt).toLowerCase().includes(search.toLowerCase())
4002
- ) : options;
4033
+ const selectedOptions = React23.useMemo(
4034
+ () => options.filter((opt) => selectedValues.includes(getKey(opt))),
4035
+ [options, selectedValues]
4036
+ );
4037
+ const filteredOptions = searchable ? options.filter((opt) => getLabel(opt).toLowerCase().includes(search.toLowerCase())) : options;
4038
+ const openDropdown = () => {
4039
+ if (disabled) return;
4040
+ if (triggerRef.current) {
4041
+ const pos = getPortalPosition(triggerRef.current, 260);
4042
+ setDropStyle({
4043
+ position: "fixed",
4044
+ top: pos.placement === "bottom" ? pos.top : void 0,
4045
+ bottom: pos.placement === "top" ? window.innerHeight - pos.top : void 0,
4046
+ left: pos.left,
4047
+ width: pos.width,
4048
+ zIndex: 9999
4049
+ });
4050
+ }
4051
+ setIsOpen(true);
4052
+ };
4003
4053
  React23.useEffect(() => {
4004
- const handleClickOutside = (event) => {
4005
- if (containerRef.current && !containerRef.current.contains(event.target)) {
4054
+ const handler = (e) => {
4055
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
4006
4056
  setIsOpen(false);
4007
4057
  }
4008
4058
  };
4009
- document.addEventListener("mousedown", handleClickOutside);
4010
- return () => document.removeEventListener("mousedown", handleClickOutside);
4059
+ document.addEventListener("mousedown", handler);
4060
+ return () => document.removeEventListener("mousedown", handler);
4011
4061
  }, []);
4012
4062
  const handleSelect = (optionValue) => {
4013
4063
  if (multiple) {
@@ -4021,10 +4071,7 @@ function Select({
4021
4071
  };
4022
4072
  const handleRemove = (optionValue, e) => {
4023
4073
  e.stopPropagation();
4024
- if (multiple) {
4025
- const newValues = selectedValues.filter((v) => v !== optionValue);
4026
- onChange?.(newValues);
4027
- }
4074
+ if (multiple) onChange?.(selectedValues.filter((v) => v !== optionValue));
4028
4075
  };
4029
4076
  const handleReorder = (fromIndex, toIndex) => {
4030
4077
  if (!multiple || !reorderable || !Array.isArray(value)) return;
@@ -4038,16 +4085,13 @@ function Select({
4038
4085
  e.dataTransfer.setData("text/plain", index.toString());
4039
4086
  };
4040
4087
  const handleDragOver = (e) => {
4041
- if (!reorderable) return;
4042
- e.preventDefault();
4088
+ if (reorderable) e.preventDefault();
4043
4089
  };
4044
4090
  const handleDrop = (e, toIndex) => {
4045
4091
  if (!reorderable) return;
4046
4092
  e.preventDefault();
4047
4093
  const fromIndex = parseInt(e.dataTransfer.getData("text/plain"));
4048
- if (fromIndex !== toIndex) {
4049
- handleReorder(fromIndex, toIndex);
4050
- }
4094
+ if (fromIndex !== toIndex) handleReorder(fromIndex, toIndex);
4051
4095
  };
4052
4096
  if (native) {
4053
4097
  if (multiple) {
@@ -4057,58 +4101,29 @@ function Select({
4057
4101
  "w-full rounded-xl border border-slate-900/30 bg-background/50 backdrop-blur-sm overflow-hidden ring-offset-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2",
4058
4102
  disabled && "opacity-50 pointer-events-none"
4059
4103
  ), children: [
4060
- selectedValues.length > 0 && /* @__PURE__ */ jsx28("div", { className: "flex flex-wrap gap-1 px-3 pt-2 pb-1 border-b border-slate-900/10", children: selectedOptions.map((opt) => /* @__PURE__ */ jsxs24(
4061
- "span",
4062
- {
4063
- className: "inline-flex items-center gap-1 rounded-sm bg-primary/15 text-primary px-2 py-0.5 text-xs",
4064
- children: [
4065
- getLabel(opt),
4066
- /* @__PURE__ */ jsx28(
4067
- "button",
4068
- {
4069
- type: "button",
4070
- disabled,
4071
- onClick: () => onChange?.(selectedValues.filter((v) => v !== getKey(opt))),
4072
- className: "hover:text-destructive",
4073
- children: /* @__PURE__ */ jsx28(X7, { className: "h-3 w-3" })
4074
- }
4075
- )
4076
- ]
4077
- },
4078
- getKey(opt)
4079
- )) }),
4104
+ selectedValues.length > 0 && /* @__PURE__ */ jsx28("div", { className: "flex flex-wrap gap-1 px-3 pt-2 pb-1 border-b border-slate-900/10", children: selectedOptions.map((opt) => /* @__PURE__ */ jsxs24("span", { className: "inline-flex items-center gap-1 rounded-sm bg-primary/15 text-primary px-2 py-0.5 text-xs", children: [
4105
+ getLabel(opt),
4106
+ /* @__PURE__ */ jsx28("button", { type: "button", disabled, onClick: () => onChange?.(selectedValues.filter((v) => v !== getKey(opt))), className: "hover:text-destructive", children: /* @__PURE__ */ jsx28(X7, { className: "h-3 w-3" }) })
4107
+ ] }, getKey(opt))) }),
4080
4108
  /* @__PURE__ */ jsx28("div", { className: "max-h-48 overflow-y-auto p-1", children: options.map((option) => {
4081
4109
  const checked = selectedValues.includes(getKey(option));
4082
- return /* @__PURE__ */ jsxs24(
4083
- "label",
4084
- {
4085
- className: cn(
4086
- "flex items-center gap-3 rounded-lg px-3 py-2 text-sm cursor-pointer transition-colors hover:bg-accent",
4087
- checked && "bg-primary/8 text-primary"
4088
- ),
4089
- children: [
4090
- /* @__PURE__ */ jsx28("span", { className: cn(
4091
- "flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors",
4092
- checked ? "bg-primary border-primary text-primary-foreground" : "border-slate-900/30 bg-background/50"
4093
- ), children: checked && /* @__PURE__ */ jsx28(Check5, { className: "h-3 w-3" }) }),
4094
- /* @__PURE__ */ jsx28(
4095
- "input",
4096
- {
4097
- type: "checkbox",
4098
- className: "sr-only",
4099
- checked,
4100
- disabled,
4101
- onChange: () => {
4102
- const newValues = checked ? selectedValues.filter((v) => v !== getKey(option)) : [...selectedValues, getKey(option)];
4103
- onChange?.(newValues);
4104
- }
4105
- }
4106
- ),
4107
- getLabel(option)
4108
- ]
4109
- },
4110
- getKey(option)
4111
- );
4110
+ return /* @__PURE__ */ jsxs24("label", { className: cn("flex items-center gap-3 rounded-lg px-3 py-2 text-sm cursor-pointer transition-colors hover:bg-accent", checked && "bg-primary/8 text-primary"), children: [
4111
+ /* @__PURE__ */ jsx28("span", { className: cn("flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors", checked ? "bg-primary border-primary text-primary-foreground" : "border-slate-900/30 bg-background/50"), children: checked && /* @__PURE__ */ jsx28(Check5, { className: "h-3 w-3" }) }),
4112
+ /* @__PURE__ */ jsx28(
4113
+ "input",
4114
+ {
4115
+ type: "checkbox",
4116
+ className: "sr-only",
4117
+ checked,
4118
+ disabled,
4119
+ onChange: () => {
4120
+ const newValues = checked ? selectedValues.filter((v) => v !== getKey(option)) : [...selectedValues, getKey(option)];
4121
+ onChange?.(newValues);
4122
+ }
4123
+ }
4124
+ ),
4125
+ getLabel(option)
4126
+ ] }, getKey(option));
4112
4127
  }) })
4113
4128
  ] })
4114
4129
  ] });
@@ -4139,14 +4154,15 @@ function Select({
4139
4154
  /* @__PURE__ */ jsx28(
4140
4155
  "button",
4141
4156
  {
4157
+ ref: triggerRef,
4142
4158
  type: "button",
4143
4159
  className: cn(
4144
4160
  "relative flex w-full items-center min-h-[2.5rem] rounded-xl border border-slate-900/30 bg-background/50 backdrop-blur-sm px-4 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 transition-colors hover:bg-background/80",
4145
4161
  multiple && selectedValues.length > 0 && "flex-wrap gap-1",
4146
- (suffixIcon || !suffixIcon) && "pr-10",
4162
+ "pr-10",
4147
4163
  disabled && "cursor-not-allowed opacity-50"
4148
4164
  ),
4149
- onClick: () => !disabled && setIsOpen(!isOpen),
4165
+ onClick: () => isOpen ? setIsOpen(false) : openDropdown(),
4150
4166
  disabled,
4151
4167
  children: multiple && selectedOptions.length > 0 ? /* @__PURE__ */ jsx28("div", { className: "flex flex-wrap gap-1 flex-1 min-w-0", children: selectedOptions.map((option, index) => /* @__PURE__ */ jsxs24(
4152
4168
  "span",
@@ -4155,22 +4171,11 @@ function Select({
4155
4171
  onDragStart: (e) => handleDragStart(e, index),
4156
4172
  onDragOver: handleDragOver,
4157
4173
  onDrop: (e) => handleDrop(e, index),
4158
- className: cn(
4159
- "inline-flex items-center gap-1 rounded-sm bg-primary/15 text-primary px-2 py-1 text-xs",
4160
- reorderable && "cursor-move"
4161
- ),
4174
+ className: cn("inline-flex items-center gap-1 rounded-sm bg-primary/15 text-primary px-2 py-1 text-xs", reorderable && "cursor-move"),
4162
4175
  children: [
4163
4176
  reorderable && /* @__PURE__ */ jsx28(GripVertical3, { className: "h-3 w-3 opacity-50" }),
4164
4177
  getLabel(option),
4165
- /* @__PURE__ */ jsx28(
4166
- "button",
4167
- {
4168
- type: "button",
4169
- onClick: (e) => handleRemove(getKey(option), e),
4170
- className: "ml-1 hover:text-destructive",
4171
- children: /* @__PURE__ */ jsx28(X7, { className: "h-3 w-3" })
4172
- }
4173
- )
4178
+ /* @__PURE__ */ jsx28("button", { type: "button", onClick: (e) => handleRemove(getKey(option), e), className: "ml-1 hover:text-destructive", children: /* @__PURE__ */ jsx28(X7, { className: "h-3 w-3" }) })
4174
4179
  ]
4175
4180
  },
4176
4181
  getKey(option)
@@ -4183,50 +4188,56 @@ function Select({
4183
4188
  {
4184
4189
  type: "button",
4185
4190
  disabled,
4186
- onClick: () => !disabled && setIsOpen(!isOpen),
4191
+ onClick: () => isOpen ? setIsOpen(false) : openDropdown(),
4187
4192
  className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors z-10 disabled:opacity-50",
4188
- "aria-label": "Open options",
4189
4193
  style: iconColor ? { color: iconColor } : void 0,
4190
4194
  children: suffixIcon
4191
4195
  }
4192
4196
  ),
4193
- isOpen && /* @__PURE__ */ jsxs24("div", { className: "absolute z-50 mt-1 max-h-60 w-full overflow-auto rounded-md border border-white/10 bg-background/80 backdrop-blur-xl text-popover-foreground shadow-lg animate-in fade-in-80", children: [
4194
- searchable && /* @__PURE__ */ jsxs24("div", { className: "sticky top-0 z-10 flex items-center border-b border-white/10 bg-background/90 px-3 py-2", children: [
4195
- /* @__PURE__ */ jsx28(Search4, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
4196
- /* @__PURE__ */ jsx28(
4197
- "input",
4198
- {
4199
- className: "flex h-8 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
4200
- placeholder: searchingMessage,
4201
- value: search,
4202
- onChange: (e) => {
4203
- setSearch(e.target.value);
4204
- setIsSearching(true);
4205
- setTimeout(() => setIsSearching(false), 300);
4206
- },
4207
- onClick: (e) => e.stopPropagation()
4208
- }
4209
- )
4210
- ] }),
4211
- /* @__PURE__ */ jsx28("div", { className: "p-1", children: isSearching ? /* @__PURE__ */ jsx28("div", { className: "py-6 text-center text-sm text-muted-foreground", children: loadingMessage }) : filteredOptions.length === 0 ? /* @__PURE__ */ jsx28("div", { className: "py-6 text-center text-sm text-muted-foreground", children: search ? noSearchResultsMessage : "No options available." }) : /* @__PURE__ */ jsxs24(Fragment5, { children: [
4212
- filteredOptions.map((option) => /* @__PURE__ */ jsxs24(
4213
- "div",
4214
- {
4215
- className: cn(
4216
- "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none hover:bg-accent hover:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
4217
- (multiple ? selectedValues.includes(getKey(option)) : value === getKey(option)) && "bg-accent text-accent-foreground"
4218
- ),
4219
- onClick: () => handleSelect(getKey(option)),
4220
- children: [
4221
- /* @__PURE__ */ jsx28("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: (multiple ? selectedValues.includes(getKey(option)) : value === getKey(option)) && /* @__PURE__ */ jsx28(Check5, { className: "h-4 w-4" }) }),
4222
- /* @__PURE__ */ jsx28("span", { className: "truncate", children: getLabel(option) })
4223
- ]
4224
- },
4225
- getKey(option)
4226
- )),
4227
- createOptionForm && /* @__PURE__ */ jsx28("div", { className: "border-t border-white/10 mt-1 pt-1", children: createOptionForm })
4228
- ] }) })
4229
- ] })
4197
+ isOpen && /* @__PURE__ */ jsx28(FloatingPortal, { children: /* @__PURE__ */ jsxs24(
4198
+ "div",
4199
+ {
4200
+ className: "max-h-60 overflow-auto rounded-md border border-white/10 bg-background/80 backdrop-blur-xl text-popover-foreground shadow-lg animate-in fade-in-80",
4201
+ style: dropStyle,
4202
+ children: [
4203
+ searchable && /* @__PURE__ */ jsxs24("div", { className: "sticky top-0 z-10 flex items-center border-b border-white/10 bg-background/90 px-3 py-2", children: [
4204
+ /* @__PURE__ */ jsx28(Search4, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
4205
+ /* @__PURE__ */ jsx28(
4206
+ "input",
4207
+ {
4208
+ className: "flex h-8 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground",
4209
+ placeholder: searchingMessage,
4210
+ value: search,
4211
+ onChange: (e) => {
4212
+ setSearch(e.target.value);
4213
+ setIsSearching(true);
4214
+ setTimeout(() => setIsSearching(false), 300);
4215
+ },
4216
+ onClick: (e) => e.stopPropagation()
4217
+ }
4218
+ )
4219
+ ] }),
4220
+ /* @__PURE__ */ jsx28("div", { className: "p-1", children: isSearching ? /* @__PURE__ */ jsx28("div", { className: "py-6 text-center text-sm text-muted-foreground", children: loadingMessage }) : filteredOptions.length === 0 ? /* @__PURE__ */ jsx28("div", { className: "py-6 text-center text-sm text-muted-foreground", children: search ? noSearchResultsMessage : "No options available." }) : /* @__PURE__ */ jsxs24(Fragment5, { children: [
4221
+ filteredOptions.map((option) => /* @__PURE__ */ jsxs24(
4222
+ "div",
4223
+ {
4224
+ className: cn(
4225
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none hover:bg-accent hover:text-accent-foreground",
4226
+ (multiple ? selectedValues.includes(getKey(option)) : value === getKey(option)) && "bg-accent text-accent-foreground"
4227
+ ),
4228
+ onClick: () => handleSelect(getKey(option)),
4229
+ children: [
4230
+ /* @__PURE__ */ jsx28("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: (multiple ? selectedValues.includes(getKey(option)) : value === getKey(option)) && /* @__PURE__ */ jsx28(Check5, { className: "h-4 w-4" }) }),
4231
+ /* @__PURE__ */ jsx28("span", { className: "truncate", children: getLabel(option) })
4232
+ ]
4233
+ },
4234
+ getKey(option)
4235
+ )),
4236
+ createOptionForm && /* @__PURE__ */ jsx28("div", { className: "border-t border-white/10 mt-1 pt-1", children: createOptionForm })
4237
+ ] }) })
4238
+ ]
4239
+ }
4240
+ ) })
4230
4241
  ] })
4231
4242
  ] });
4232
4243
  }
@@ -5069,7 +5080,7 @@ import { PanelLeftClose, PanelLeftOpen, Sun as Sun2, Moon } from "lucide-react";
5069
5080
 
5070
5081
  // src/components/ui/tooltip.tsx
5071
5082
  import * as React28 from "react";
5072
- import * as ReactDOM from "react-dom";
5083
+ import * as ReactDOM2 from "react-dom";
5073
5084
  import { Fragment as Fragment7, jsx as jsx33, jsxs as jsxs29 } from "react/jsx-runtime";
5074
5085
  function Tooltip({
5075
5086
  content,
@@ -5124,7 +5135,7 @@ function Tooltip({
5124
5135
  onBlur: () => setVisible(false),
5125
5136
  children: [
5126
5137
  children,
5127
- visible && ReactDOM.createPortal(
5138
+ visible && ReactDOM2.createPortal(
5128
5139
  /* @__PURE__ */ jsx33(
5129
5140
  "div",
5130
5141
  {
@@ -5399,16 +5410,28 @@ function PanelSidebarGroup({
5399
5410
  // src/components/ui/popover.tsx
5400
5411
  import * as React31 from "react";
5401
5412
  import { jsx as jsx36, jsxs as jsxs31 } from "react/jsx-runtime";
5402
- var PLACEMENT_CLASSES = {
5403
- "top": "bottom-full left-1/2 -translate-x-1/2 mb-2",
5404
- "bottom": "top-full left-1/2 -translate-x-1/2 mt-2",
5405
- "left": "right-full top-1/2 -translate-y-1/2 mr-2",
5406
- "right": "left-full top-1/2 -translate-y-1/2 ml-2",
5407
- "top-start": "bottom-full left-0 mb-2",
5408
- "top-end": "bottom-full right-0 mb-2",
5409
- "bottom-start": "top-full left-0 mt-2",
5410
- "bottom-end": "top-full right-0 mt-2"
5411
- };
5413
+ function calcStyle(triggerEl, placement) {
5414
+ const r = triggerEl.getBoundingClientRect();
5415
+ const GAP = 8;
5416
+ switch (placement) {
5417
+ case "bottom":
5418
+ return { top: r.bottom + GAP, left: r.left + r.width / 2, transform: "translateX(-50%)" };
5419
+ case "bottom-start":
5420
+ return { top: r.bottom + GAP, left: r.left };
5421
+ case "bottom-end":
5422
+ return { top: r.bottom + GAP, left: r.right, transform: "translateX(-100%)" };
5423
+ case "top":
5424
+ return { top: r.top - GAP, left: r.left + r.width / 2, transform: "translate(-50%, -100%)" };
5425
+ case "top-start":
5426
+ return { top: r.top - GAP, left: r.left, transform: "translateY(-100%)" };
5427
+ case "top-end":
5428
+ return { top: r.top - GAP, left: r.right, transform: "translate(-100%, -100%)" };
5429
+ case "left":
5430
+ return { top: r.top + r.height / 2, left: r.left - GAP, transform: "translate(-100%, -50%)" };
5431
+ case "right":
5432
+ return { top: r.top + r.height / 2, left: r.right + GAP, transform: "translateY(-50%)" };
5433
+ }
5434
+ }
5412
5435
  function Popover({
5413
5436
  trigger,
5414
5437
  content,
@@ -5420,11 +5443,18 @@ function Popover({
5420
5443
  }) {
5421
5444
  const [internal, setInternal] = React31.useState(false);
5422
5445
  const ref = React31.useRef(null);
5446
+ const [popStyle, setPopStyle] = React31.useState({});
5423
5447
  const open = controlled ?? internal;
5424
5448
  function setOpen(v) {
5425
5449
  if (controlled === void 0) setInternal(v);
5426
5450
  onOpenChange?.(v);
5427
5451
  }
5452
+ function openPopover() {
5453
+ if (ref.current) {
5454
+ setPopStyle({ position: "fixed", zIndex: 9999, ...calcStyle(ref.current, placement) });
5455
+ }
5456
+ setOpen(true);
5457
+ }
5428
5458
  React31.useEffect(() => {
5429
5459
  if (triggerOn !== "click") return;
5430
5460
  function handler(e) {
@@ -5433,14 +5463,17 @@ function Popover({
5433
5463
  document.addEventListener("mousedown", handler);
5434
5464
  return () => document.removeEventListener("mousedown", handler);
5435
5465
  }, [triggerOn]);
5436
- const hoverProps = triggerOn === "hover" ? { onMouseEnter: () => setOpen(true), onMouseLeave: () => setOpen(false) } : {};
5466
+ const hoverProps = triggerOn === "hover" ? { onMouseEnter: openPopover, onMouseLeave: () => setOpen(false) } : {};
5437
5467
  return /* @__PURE__ */ jsxs31("div", { ref, className: "relative inline-block", ...hoverProps, children: [
5438
- /* @__PURE__ */ jsx36("div", { onClick: () => triggerOn === "click" && setOpen(!open), children: trigger }),
5439
- open && /* @__PURE__ */ jsx36("div", { className: cn(
5440
- "absolute z-50 min-w-max rounded-xl border border-border glass shadow-2xl",
5441
- PLACEMENT_CLASSES[placement],
5442
- className
5443
- ), children: content })
5468
+ /* @__PURE__ */ jsx36("div", { onClick: () => triggerOn === "click" && (open ? setOpen(false) : openPopover()), children: trigger }),
5469
+ open && /* @__PURE__ */ jsx36(FloatingPortal, { children: /* @__PURE__ */ jsx36(
5470
+ "div",
5471
+ {
5472
+ className: cn("min-w-max rounded-xl border border-border glass shadow-2xl", className),
5473
+ style: popStyle,
5474
+ children: content
5475
+ }
5476
+ ) })
5444
5477
  ] });
5445
5478
  }
5446
5479
 
@@ -5727,7 +5760,7 @@ function RadioGroup({
5727
5760
  }
5728
5761
 
5729
5762
  // src/components/ui/repeater.tsx
5730
- import { Plus as Plus4, Trash2 as Trash22, GripVertical as GripVertical4 } from "lucide-react";
5763
+ import { Plus as Plus3, Trash2 as Trash22, GripVertical as GripVertical4 } from "lucide-react";
5731
5764
  import { jsx as jsx40, jsxs as jsxs35 } from "react/jsx-runtime";
5732
5765
  function Repeater({
5733
5766
  items,
@@ -5772,7 +5805,7 @@ function Repeater({
5772
5805
  onClick: onAdd,
5773
5806
  className: "group flex w-full items-center justify-center gap-2 rounded-xl border border-dashed border-border py-3 text-sm font-medium text-muted-foreground transition-all duration-200 hover:border-primary/50 hover:bg-primary/5 hover:text-primary",
5774
5807
  children: [
5775
- /* @__PURE__ */ jsx40("span", { className: "flex h-5 w-5 items-center justify-center rounded-full bg-muted group-hover:bg-primary/10 transition-colors", children: /* @__PURE__ */ jsx40(Plus4, { className: "h-3 w-3" }) }),
5808
+ /* @__PURE__ */ jsx40("span", { className: "flex h-5 w-5 items-center justify-center rounded-full bg-muted group-hover:bg-primary/10 transition-colors", children: /* @__PURE__ */ jsx40(Plus3, { className: "h-3 w-3" }) }),
5776
5809
  addButtonText
5777
5810
  ]
5778
5811
  }