@memelabui/ui 0.1.0 → 0.2.0

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
@@ -58,6 +58,94 @@ function focusSafely(el) {
58
58
  } catch {
59
59
  }
60
60
  }
61
+ function useClipboard(timeout = 2e3) {
62
+ const [copied, setCopied] = react.useState(false);
63
+ const timerRef = react.useRef(null);
64
+ react.useEffect(() => {
65
+ return () => {
66
+ if (timerRef.current !== null) {
67
+ clearTimeout(timerRef.current);
68
+ }
69
+ };
70
+ }, []);
71
+ const copy = react.useCallback(
72
+ async (text) => {
73
+ try {
74
+ await navigator.clipboard.writeText(text);
75
+ } catch {
76
+ return;
77
+ }
78
+ if (timerRef.current !== null) {
79
+ clearTimeout(timerRef.current);
80
+ }
81
+ setCopied(true);
82
+ timerRef.current = setTimeout(() => {
83
+ setCopied(false);
84
+ timerRef.current = null;
85
+ }, timeout);
86
+ },
87
+ [timeout]
88
+ );
89
+ return { copy, copied };
90
+ }
91
+ function useDisclosure(defaultOpen = false) {
92
+ const [isOpen, setIsOpen] = react.useState(defaultOpen);
93
+ const open = react.useCallback(() => setIsOpen(true), []);
94
+ const close = react.useCallback(() => setIsOpen(false), []);
95
+ const toggle = react.useCallback(() => setIsOpen((prev) => !prev), []);
96
+ return { isOpen, open, close, toggle };
97
+ }
98
+ function useMediaQuery(query) {
99
+ const [matches, setMatches] = react.useState(() => {
100
+ if (typeof window === "undefined") return false;
101
+ return window.matchMedia(query).matches;
102
+ });
103
+ react.useEffect(() => {
104
+ if (typeof window === "undefined") return;
105
+ const mediaQueryList = window.matchMedia(query);
106
+ setMatches(mediaQueryList.matches);
107
+ const handler = (event) => {
108
+ setMatches(event.matches);
109
+ };
110
+ mediaQueryList.addEventListener("change", handler);
111
+ return () => {
112
+ mediaQueryList.removeEventListener("change", handler);
113
+ };
114
+ }, [query]);
115
+ return matches;
116
+ }
117
+ function useDebounce(value, delayMs = 300) {
118
+ const [debouncedValue, setDebouncedValue] = react.useState(value);
119
+ react.useEffect(() => {
120
+ const timer = setTimeout(() => {
121
+ setDebouncedValue(value);
122
+ }, delayMs);
123
+ return () => {
124
+ clearTimeout(timer);
125
+ };
126
+ }, [value, delayMs]);
127
+ return debouncedValue;
128
+ }
129
+
130
+ // src/tokens/colors.ts
131
+ var colors = {
132
+ bg: "#0a0a0f",
133
+ fg: "#f9fafb",
134
+ surface: {
135
+ 0: "#0a0a0f",
136
+ 50: "#0f0f18",
137
+ 100: "#141420",
138
+ 200: "#1a1a2e",
139
+ 300: "#24243a",
140
+ 400: "#2a2a4a"
141
+ },
142
+ primary: { DEFAULT: "#8b5cf6", light: "#a78bfa", dark: "#7c3aed" },
143
+ accent: { DEFAULT: "#667eea", light: "#8b9cf7", dark: "#4c5fd4" },
144
+ glow: { purple: "#764ba2", pink: "#f093fb" },
145
+ success: "#10b981",
146
+ warning: "#f59e0b",
147
+ danger: "#f43f5e"
148
+ };
61
149
  var base = "inline-flex items-center justify-center gap-2 rounded-xl font-semibold leading-none transition-[transform,background-color,box-shadow,opacity] select-none [-webkit-tap-highlight-color:transparent] active:translate-y-[0.5px] disabled:opacity-60 disabled:pointer-events-none focus-visible:ring-2 focus-visible:ring-primary/40 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent";
62
150
  var sizeClass = {
63
151
  sm: "px-3 py-2 text-sm",
@@ -65,10 +153,10 @@ var sizeClass = {
65
153
  lg: "px-4 py-3 text-base"
66
154
  };
67
155
  var variantClass = {
68
- primary: "bg-gradient-to-r from-violet-600 to-purple-600 text-white shadow-[0_0_25px_rgba(139,92,246,0.35)] hover:shadow-[0_0_35px_rgba(139,92,246,0.5)] hover:scale-[1.02]",
69
- success: "bg-emerald-600 text-white shadow-[0_10px_18px_rgba(16,185,129,0.22)] hover:bg-emerald-700",
70
- warning: "bg-amber-600 text-white shadow-[0_10px_18px_rgba(245,158,11,0.22)] hover:bg-amber-700",
71
- danger: "bg-rose-600 text-white shadow-[0_10px_18px_rgba(244,63,94,0.22)] hover:bg-rose-700",
156
+ primary: "bg-gradient-to-r from-violet-600 to-purple-600 text-white shadow-glow hover:shadow-glow-lg hover:scale-[1.02]",
157
+ success: "bg-emerald-600 text-white shadow-lg shadow-emerald-600/20 hover:bg-emerald-700",
158
+ warning: "bg-amber-600 text-white shadow-lg shadow-amber-600/20 hover:bg-amber-700",
159
+ danger: "bg-rose-600 text-white shadow-lg shadow-rose-600/20 hover:bg-rose-700",
72
160
  secondary: "text-white bg-white/5 ring-1 ring-white/10 hover:bg-white/10 hover:ring-white/20 backdrop-blur-sm",
73
161
  ghost: "text-white/70 hover:text-white hover:bg-white/5"
74
162
  };
@@ -80,6 +168,7 @@ var Button = react.forwardRef(function Button2({ variant = "secondary", size = "
80
168
  type: type === "submit" ? "submit" : type === "reset" ? "reset" : "button",
81
169
  ...props,
82
170
  disabled: disabled || loading,
171
+ ...loading ? { "aria-busy": true } : {},
83
172
  className: cn(base, sizeClass[size], variantClass[variant], className),
84
173
  children: loading ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center justify-center gap-2", children: [
85
174
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
@@ -100,9 +189,9 @@ var sizeClass2 = {
100
189
  };
101
190
  var variantClass2 = {
102
191
  primary: "bg-primary text-white shadow-glow hover:brightness-[0.98]",
103
- success: "bg-emerald-600 text-white shadow-[0_10px_18px_rgba(16,185,129,0.22)] hover:bg-emerald-700",
104
- warning: "bg-amber-600 text-white shadow-[0_10px_18px_rgba(245,158,11,0.22)] hover:bg-amber-700",
105
- danger: "bg-rose-600 text-white shadow-[0_10px_18px_rgba(244,63,94,0.22)] hover:bg-rose-700",
192
+ success: "bg-emerald-600 text-white shadow-lg shadow-emerald-600/20 hover:bg-emerald-700",
193
+ warning: "bg-amber-600 text-white shadow-lg shadow-amber-600/20 hover:bg-amber-700",
194
+ danger: "bg-rose-600 text-white shadow-lg shadow-rose-600/20 hover:bg-rose-700",
106
195
  secondary: "text-white bg-white/10 shadow-sm ring-1 ring-white/10",
107
196
  ghost: "text-white hover:bg-white/10"
108
197
  };
@@ -119,69 +208,155 @@ var IconButton = react.forwardRef(function IconButton2({ icon, variant = "ghost"
119
208
  }
120
209
  );
121
210
  });
122
- var inputBase = "w-full rounded-xl px-3 py-2.5 text-sm bg-white/10 text-white shadow-sm outline-none placeholder-white/30 focus:ring-2 focus:ring-primary/40 transition-shadow";
211
+ var inputBase = "w-full rounded-xl px-3 py-2.5 text-sm bg-white/10 text-white shadow-sm outline-none placeholder-white/30 focus-visible:ring-2 focus-visible:ring-primary/40 transition-shadow";
123
212
  var Input = react.forwardRef(function Input2({ hasError, label, error, helperText, className, id: externalId, ...props }, ref) {
124
213
  const generatedId = react.useId();
125
214
  const inputId = externalId || generatedId;
126
215
  const showError = hasError || !!error;
216
+ const errorId = error ? `${inputId}-error` : void 0;
217
+ const helperId = helperText && !error ? `${inputId}-helper` : void 0;
127
218
  const input = /* @__PURE__ */ jsxRuntime.jsx(
128
219
  "input",
129
220
  {
221
+ ...props,
130
222
  ref,
131
223
  id: inputId,
132
- ...props,
224
+ "aria-invalid": showError || void 0,
225
+ "aria-describedby": errorId || helperId || void 0,
133
226
  className: cn(inputBase, showError && "ring-2 ring-rose-500/40 focus:ring-rose-500/40", className)
134
227
  }
135
228
  );
136
229
  if (!label && !error && !helperText) return input;
137
230
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
138
- label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: inputId, className: "block text-sm text-white/50 mb-1.5", children: label }),
231
+ label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: inputId, className: "block text-sm text-white/70 mb-1.5", children: label }),
139
232
  input,
140
- error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-rose-400 text-xs mt-1", children: error }),
141
- helperText && !error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-white/40 text-xs mt-1", children: helperText })
233
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: errorId, className: "text-rose-400 text-xs mt-1", children: error }),
234
+ helperText && !error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: helperId, className: "text-white/40 text-xs mt-1", children: helperText })
142
235
  ] });
143
236
  });
144
- var selectBase = "w-full rounded-xl px-3 py-2.5 text-sm bg-white/10 text-white shadow-sm outline-none focus:ring-2 focus:ring-primary/40 transition-shadow";
145
- var Select = react.forwardRef(function Select2({ hasError, label, error, className, id: externalId, children, ...props }, ref) {
237
+ var inputBase2 = "w-full rounded-xl pl-10 pr-3 py-2.5 text-sm bg-white/10 text-white shadow-sm outline-none placeholder-white/30 focus-visible:ring-2 focus-visible:ring-primary/40 transition-shadow";
238
+ var SearchInput = react.forwardRef(function SearchInput2({ label, onClear, className, id: externalId, placeholder = "Search...", value, ...props }, ref) {
239
+ const generatedId = react.useId();
240
+ const inputId = externalId || generatedId;
241
+ const hasValue = value !== void 0 && value !== "";
242
+ const wrapper = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
243
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-white/40", children: /* @__PURE__ */ jsxRuntime.jsxs(
244
+ "svg",
245
+ {
246
+ xmlns: "http://www.w3.org/2000/svg",
247
+ width: "16",
248
+ height: "16",
249
+ viewBox: "0 0 24 24",
250
+ fill: "none",
251
+ stroke: "currentColor",
252
+ strokeWidth: "2",
253
+ strokeLinecap: "round",
254
+ strokeLinejoin: "round",
255
+ "aria-hidden": "true",
256
+ children: [
257
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "11", cy: "11", r: "8" }),
258
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "21", y1: "21", x2: "16.65", y2: "16.65" })
259
+ ]
260
+ }
261
+ ) }),
262
+ /* @__PURE__ */ jsxRuntime.jsx(
263
+ "input",
264
+ {
265
+ ref,
266
+ id: inputId,
267
+ type: "search",
268
+ value,
269
+ placeholder,
270
+ ...props,
271
+ className: cn(inputBase2, hasValue && "pr-9", className)
272
+ }
273
+ ),
274
+ hasValue && onClear && /* @__PURE__ */ jsxRuntime.jsx(
275
+ "button",
276
+ {
277
+ type: "button",
278
+ onClick: onClear,
279
+ "aria-label": "Clear search",
280
+ className: "absolute right-3 top-1/2 -translate-y-1/2 text-white/40 hover:text-white transition-colors",
281
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
282
+ "svg",
283
+ {
284
+ xmlns: "http://www.w3.org/2000/svg",
285
+ width: "14",
286
+ height: "14",
287
+ viewBox: "0 0 24 24",
288
+ fill: "none",
289
+ stroke: "currentColor",
290
+ strokeWidth: "2",
291
+ strokeLinecap: "round",
292
+ strokeLinejoin: "round",
293
+ "aria-hidden": "true",
294
+ children: [
295
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
296
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
297
+ ]
298
+ }
299
+ )
300
+ }
301
+ )
302
+ ] });
303
+ if (!label) return wrapper;
304
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
305
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: inputId, className: "block text-sm text-white/70 mb-1.5", children: label }),
306
+ wrapper
307
+ ] });
308
+ });
309
+ var selectBase = "w-full rounded-xl px-3 py-2.5 text-sm bg-white/10 text-white shadow-sm outline-none focus-visible:ring-2 focus-visible:ring-primary/40 transition-shadow";
310
+ var Select = react.forwardRef(function Select2({ hasError, label, error, helperText, className, id: externalId, children, ...props }, ref) {
146
311
  const generatedId = react.useId();
147
312
  const selectId = externalId || generatedId;
148
313
  const showError = hasError || !!error;
314
+ const errorId = error ? `${selectId}-error` : void 0;
315
+ const helperId = helperText && !error ? `${selectId}-helper` : void 0;
149
316
  const select = /* @__PURE__ */ jsxRuntime.jsx(
150
317
  "select",
151
318
  {
319
+ ...props,
152
320
  ref,
153
321
  id: selectId,
154
- ...props,
322
+ "aria-invalid": showError || void 0,
323
+ "aria-describedby": errorId || helperId || void 0,
155
324
  className: cn(selectBase, showError && "ring-2 ring-rose-500/40 focus:ring-rose-500/40", className),
156
325
  children
157
326
  }
158
327
  );
159
- if (!label && !error) return select;
328
+ if (!label && !error && !helperText) return select;
160
329
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
161
- label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: selectId, className: "block text-sm text-white/50 mb-1.5", children: label }),
330
+ label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: selectId, className: "block text-sm text-white/70 mb-1.5", children: label }),
162
331
  select,
163
- error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-rose-400 text-xs mt-1", children: error })
332
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: errorId, className: "text-rose-400 text-xs mt-1", children: error }),
333
+ helperText && !error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: helperId, className: "text-white/40 text-xs mt-1", children: helperText })
164
334
  ] });
165
335
  });
166
- var textareaBase = "w-full rounded-xl px-3 py-2.5 text-sm bg-white/10 text-white shadow-sm outline-none placeholder-white/30 focus:ring-2 focus:ring-primary/40 transition-shadow resize-y";
167
- var Textarea = react.forwardRef(function Textarea2({ hasError, label, error, className, id: externalId, ...props }, ref) {
336
+ var textareaBase = "w-full rounded-xl px-3 py-2.5 text-sm bg-white/10 text-white shadow-sm outline-none placeholder-white/30 focus-visible:ring-2 focus-visible:ring-primary/40 transition-shadow resize-y";
337
+ var Textarea = react.forwardRef(function Textarea2({ hasError, label, error, helperText, className, id: externalId, ...props }, ref) {
168
338
  const generatedId = react.useId();
169
339
  const textareaId = externalId || generatedId;
170
340
  const showError = hasError || !!error;
341
+ const errorId = error ? `${textareaId}-error` : void 0;
342
+ const helperId = helperText && !error ? `${textareaId}-helper` : void 0;
171
343
  const textarea = /* @__PURE__ */ jsxRuntime.jsx(
172
344
  "textarea",
173
345
  {
346
+ ...props,
174
347
  ref,
175
348
  id: textareaId,
176
- ...props,
349
+ "aria-invalid": showError || void 0,
350
+ "aria-describedby": errorId || helperId || void 0,
177
351
  className: cn(textareaBase, showError && "ring-2 ring-rose-500/40 focus:ring-rose-500/40", className)
178
352
  }
179
353
  );
180
- if (!label && !error) return textarea;
354
+ if (!label && !error && !helperText) return textarea;
181
355
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
182
- label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: textareaId, className: "block text-sm text-white/50 mb-1.5", children: label }),
356
+ label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: textareaId, className: "block text-sm text-white/70 mb-1.5", children: label }),
183
357
  textarea,
184
- error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-rose-400 text-xs mt-1", children: error })
358
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: errorId, className: "text-rose-400 text-xs mt-1", children: error }),
359
+ helperText && !error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: helperId, className: "text-white/40 text-xs mt-1", children: helperText })
185
360
  ] });
186
361
  });
187
362
  var base3 = "inline-flex items-center justify-center rounded-full font-semibold ring-1 ring-white/10";
@@ -211,11 +386,15 @@ var thumbSize = {
211
386
  sm: { base: "w-4 h-4", translate: "translate-x-4" },
212
387
  md: { base: "w-5 h-5", translate: "translate-x-5" }
213
388
  };
214
- function Toggle({ checked, onChange, disabled, label, size = "md", "aria-label": ariaLabel }) {
389
+ var Toggle = react.forwardRef(function Toggle2({ checked, onChange, disabled, label, size = "md", id: externalId, "aria-label": ariaLabel }, ref) {
390
+ const generatedId = react.useId();
391
+ const toggleId = externalId || generatedId;
215
392
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
216
393
  /* @__PURE__ */ jsxRuntime.jsx(
217
394
  "button",
218
395
  {
396
+ ref,
397
+ id: toggleId,
219
398
  type: "button",
220
399
  role: "switch",
221
400
  "aria-checked": checked,
@@ -240,20 +419,247 @@ function Toggle({ checked, onChange, disabled, label, size = "md", "aria-label":
240
419
  )
241
420
  }
242
421
  ),
243
- label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-white/60", children: label })
422
+ label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: toggleId, className: "text-sm text-white/60 cursor-pointer", children: label })
423
+ ] });
424
+ });
425
+ var Checkbox = react.forwardRef(function Checkbox2({ label, error, indeterminate, className, id: externalId, disabled, checked, onChange, ...props }, ref) {
426
+ const generatedId = react.useId();
427
+ const inputId = externalId || generatedId;
428
+ const errorId = error ? `${inputId}-error` : void 0;
429
+ const internalRef = react.useRef(null);
430
+ react.useEffect(() => {
431
+ const el = internalRef.current;
432
+ if (el) {
433
+ el.indeterminate = indeterminate ?? false;
434
+ }
435
+ }, [indeterminate]);
436
+ const setRefs = (el) => {
437
+ internalRef.current = el;
438
+ if (typeof ref === "function") {
439
+ ref(el);
440
+ } else if (ref) {
441
+ ref.current = el;
442
+ }
443
+ };
444
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("inline-flex flex-col gap-1", className), children: [
445
+ /* @__PURE__ */ jsxRuntime.jsxs(
446
+ "label",
447
+ {
448
+ htmlFor: inputId,
449
+ className: cn(
450
+ "inline-flex items-center gap-2.5",
451
+ disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"
452
+ ),
453
+ children: [
454
+ /* @__PURE__ */ jsxRuntime.jsx(
455
+ "input",
456
+ {
457
+ ...props,
458
+ ref: setRefs,
459
+ id: inputId,
460
+ type: "checkbox",
461
+ checked,
462
+ onChange,
463
+ disabled,
464
+ "aria-invalid": !!error || void 0,
465
+ "aria-describedby": errorId,
466
+ className: "sr-only peer"
467
+ }
468
+ ),
469
+ /* @__PURE__ */ jsxRuntime.jsxs(
470
+ "span",
471
+ {
472
+ className: cn(
473
+ "relative flex items-center justify-center w-5 h-5 rounded-md",
474
+ "bg-white/10 border border-white/20",
475
+ "transition-colors duration-150",
476
+ "peer-focus-visible:ring-2 peer-focus-visible:ring-primary/40 peer-focus-visible:ring-offset-1 peer-focus-visible:ring-offset-transparent",
477
+ checked && !indeterminate && "bg-primary border-primary",
478
+ indeterminate && "bg-primary border-primary"
479
+ ),
480
+ "aria-hidden": "true",
481
+ children: [
482
+ checked && !indeterminate && /* @__PURE__ */ jsxRuntime.jsx(
483
+ "svg",
484
+ {
485
+ viewBox: "0 0 12 10",
486
+ fill: "none",
487
+ stroke: "white",
488
+ strokeWidth: "1.8",
489
+ strokeLinecap: "round",
490
+ strokeLinejoin: "round",
491
+ className: "w-3 h-2.5",
492
+ children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "1,5 4.5,8.5 11,1" })
493
+ }
494
+ ),
495
+ indeterminate && /* @__PURE__ */ jsxRuntime.jsx(
496
+ "svg",
497
+ {
498
+ viewBox: "0 0 10 2",
499
+ fill: "none",
500
+ stroke: "white",
501
+ strokeWidth: "2",
502
+ strokeLinecap: "round",
503
+ className: "w-2.5 h-0.5",
504
+ children: /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "0", y1: "1", x2: "10", y2: "1" })
505
+ }
506
+ )
507
+ ]
508
+ }
509
+ ),
510
+ label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-white/80 select-none", children: label })
511
+ ]
512
+ }
513
+ ),
514
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: errorId, className: "text-rose-400 text-xs ml-7", children: error })
244
515
  ] });
516
+ });
517
+ var RadioGroupContext = react.createContext(null);
518
+ function useRadioGroup() {
519
+ const ctx = react.useContext(RadioGroupContext);
520
+ if (!ctx) {
521
+ throw new Error("RadioItem must be used inside a RadioGroup");
522
+ }
523
+ return ctx;
524
+ }
525
+ function RadioGroup({
526
+ value: controlledValue,
527
+ defaultValue,
528
+ onValueChange,
529
+ name: externalName,
530
+ disabled = false,
531
+ orientation = "vertical",
532
+ label,
533
+ error,
534
+ children,
535
+ className
536
+ }) {
537
+ const groupId = react.useId();
538
+ const generatedName = react.useId();
539
+ const name = externalName ?? generatedName;
540
+ const [uncontrolledValue, setUncontrolledValue] = react.useState(defaultValue);
541
+ const isControlled = controlledValue !== void 0;
542
+ const currentValue = isControlled ? controlledValue : uncontrolledValue;
543
+ const handleChange = react.useCallback(
544
+ (val) => {
545
+ if (!isControlled) setUncontrolledValue(val);
546
+ onValueChange?.(val);
547
+ },
548
+ [isControlled, onValueChange]
549
+ );
550
+ const labelId = label ? `${groupId}-label` : void 0;
551
+ const errorId = error ? `${groupId}-error` : void 0;
552
+ const ctxValue = react.useMemo(
553
+ () => ({ value: currentValue, onChange: handleChange, name, disabled, groupId }),
554
+ [currentValue, handleChange, name, disabled, groupId]
555
+ );
556
+ return /* @__PURE__ */ jsxRuntime.jsx(RadioGroupContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsxRuntime.jsxs(
557
+ "fieldset",
558
+ {
559
+ role: "radiogroup",
560
+ "aria-labelledby": labelId,
561
+ "aria-describedby": errorId,
562
+ "aria-disabled": disabled || void 0,
563
+ className: cn("border-none p-0 m-0", className),
564
+ children: [
565
+ label && /* @__PURE__ */ jsxRuntime.jsx("legend", { id: labelId, className: "text-sm text-white/50 mb-2 float-none p-0", children: label }),
566
+ /* @__PURE__ */ jsxRuntime.jsx(
567
+ "div",
568
+ {
569
+ className: cn(
570
+ "flex gap-3",
571
+ orientation === "vertical" ? "flex-col" : "flex-row flex-wrap"
572
+ ),
573
+ children
574
+ }
575
+ ),
576
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: errorId, className: "text-rose-400 text-xs mt-1.5", children: error })
577
+ ]
578
+ }
579
+ ) });
580
+ }
581
+ function RadioItem({ value, disabled: itemDisabled, children, className }) {
582
+ const { value: groupValue, onChange, name, disabled: groupDisabled, groupId } = useRadioGroup();
583
+ const inputId = react.useId();
584
+ const inputRef = react.useRef(null);
585
+ const isDisabled = groupDisabled || itemDisabled;
586
+ const isSelected = groupValue === value;
587
+ const handleKeyDown = (e) => {
588
+ if (e.key !== "ArrowDown" && e.key !== "ArrowUp" && e.key !== "ArrowRight" && e.key !== "ArrowLeft") {
589
+ return;
590
+ }
591
+ e.preventDefault();
592
+ const fieldset = inputRef.current?.closest("fieldset");
593
+ if (!fieldset) return;
594
+ const inputs = Array.from(
595
+ fieldset.querySelectorAll(`input[type="radio"][name="${name}"]:not(:disabled)`)
596
+ );
597
+ const currentIndex = inputs.indexOf(inputRef.current);
598
+ if (currentIndex === -1) return;
599
+ const forward = e.key === "ArrowDown" || e.key === "ArrowRight";
600
+ const nextIndex = forward ? (currentIndex + 1) % inputs.length : (currentIndex - 1 + inputs.length) % inputs.length;
601
+ const nextInput = inputs[nextIndex];
602
+ nextInput.focus();
603
+ onChange(nextInput.value);
604
+ };
605
+ return /* @__PURE__ */ jsxRuntime.jsxs(
606
+ "label",
607
+ {
608
+ htmlFor: inputId,
609
+ className: cn(
610
+ "inline-flex items-center gap-2.5",
611
+ isDisabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer",
612
+ className
613
+ ),
614
+ children: [
615
+ /* @__PURE__ */ jsxRuntime.jsx(
616
+ "input",
617
+ {
618
+ ref: inputRef,
619
+ id: inputId,
620
+ type: "radio",
621
+ name,
622
+ value,
623
+ checked: isSelected,
624
+ disabled: isDisabled,
625
+ onChange: () => onChange(value),
626
+ onKeyDown: handleKeyDown,
627
+ className: "sr-only peer",
628
+ "aria-describedby": void 0
629
+ }
630
+ ),
631
+ /* @__PURE__ */ jsxRuntime.jsx(
632
+ "span",
633
+ {
634
+ className: cn(
635
+ "relative flex items-center justify-center w-5 h-5 rounded-full",
636
+ "bg-white/10 border border-white/20",
637
+ "transition-colors duration-150",
638
+ "peer-focus-visible:ring-2 peer-focus-visible:ring-primary/40 peer-focus-visible:ring-offset-1 peer-focus-visible:ring-offset-transparent",
639
+ isSelected && "border-primary"
640
+ ),
641
+ "aria-hidden": "true",
642
+ children: isSelected && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-2.5 h-2.5 rounded-full bg-primary" })
643
+ }
644
+ ),
645
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-white/80 select-none", children })
646
+ ]
647
+ }
648
+ );
245
649
  }
246
650
  var sizeClass4 = {
247
651
  sm: "h-3 w-3 border",
248
652
  md: "h-4 w-4 border-2",
249
653
  lg: "h-6 w-6 border-2"
250
654
  };
251
- function Spinner({ className, size = "md" }) {
655
+ function Spinner({ className, size = "md", label }) {
252
656
  return /* @__PURE__ */ jsxRuntime.jsx(
253
657
  "span",
254
658
  {
255
659
  className: cn("inline-block rounded-full border-white/15 border-t-primary animate-spin", sizeClass4[size], className),
256
- "aria-hidden": "true"
660
+ role: label ? "status" : void 0,
661
+ "aria-hidden": label ? void 0 : "true",
662
+ "aria-label": label || void 0
257
663
  }
258
664
  );
259
665
  }
@@ -266,14 +672,170 @@ function Skeleton({ className, circle }) {
266
672
  }
267
673
  );
268
674
  }
269
- function Card({ hoverable, variant = "surface", className, ...props }) {
675
+ var TabsContext = react.createContext(null);
676
+ function useTabsContext() {
677
+ const ctx = react.useContext(TabsContext);
678
+ if (!ctx) throw new Error("Tabs sub-components must be used inside <Tabs>");
679
+ return ctx;
680
+ }
681
+ function Tabs({
682
+ defaultValue = "",
683
+ value,
684
+ onValueChange,
685
+ variant = "underline",
686
+ children,
687
+ className
688
+ }) {
689
+ const baseId = react.useId();
690
+ const isControlled = value !== void 0;
691
+ const [internalValue, setInternalValue] = react.useState(defaultValue);
692
+ const activeValue = isControlled ? value ?? "" : internalValue;
693
+ const setActiveValue = react.useCallback(
694
+ (next) => {
695
+ if (!isControlled) setInternalValue(next);
696
+ onValueChange?.(next);
697
+ },
698
+ [isControlled, onValueChange]
699
+ );
700
+ const ctxValue = react.useMemo(
701
+ () => ({ activeValue, setActiveValue, variant, baseId }),
702
+ [activeValue, setActiveValue, variant, baseId]
703
+ );
704
+ return /* @__PURE__ */ jsxRuntime.jsx(TabsContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex flex-col", className), children }) });
705
+ }
706
+ function TabList({ children, className }) {
707
+ const { variant } = useTabsContext();
708
+ const listRef = react.useRef(null);
709
+ const handleKeyDown = (e) => {
710
+ const list = listRef.current;
711
+ if (!list) return;
712
+ const tabs = Array.from(list.querySelectorAll('[role="tab"]:not([disabled])'));
713
+ if (tabs.length === 0) return;
714
+ const focused = document.activeElement;
715
+ if (!focused || !list.contains(focused)) return;
716
+ const currentIndex = tabs.indexOf(focused);
717
+ let nextIndex = currentIndex;
718
+ switch (e.key) {
719
+ case "ArrowRight":
720
+ e.preventDefault();
721
+ nextIndex = currentIndex < tabs.length - 1 ? currentIndex + 1 : 0;
722
+ break;
723
+ case "ArrowLeft":
724
+ e.preventDefault();
725
+ nextIndex = currentIndex > 0 ? currentIndex - 1 : tabs.length - 1;
726
+ break;
727
+ case "Home":
728
+ e.preventDefault();
729
+ nextIndex = 0;
730
+ break;
731
+ case "End":
732
+ e.preventDefault();
733
+ nextIndex = tabs.length - 1;
734
+ break;
735
+ case "Enter":
736
+ case " ":
737
+ e.preventDefault();
738
+ focused?.click();
739
+ return;
740
+ default:
741
+ return;
742
+ }
743
+ tabs[nextIndex]?.focus();
744
+ tabs[nextIndex]?.click();
745
+ };
270
746
  return /* @__PURE__ */ jsxRuntime.jsx(
271
747
  "div",
272
748
  {
749
+ ref: listRef,
750
+ role: "tablist",
751
+ onKeyDown: handleKeyDown,
752
+ className: cn(
753
+ "flex",
754
+ variant === "underline" && "border-b border-white/10",
755
+ variant === "pill" && "gap-1 p-1 rounded-xl bg-white/5 ring-1 ring-white/10 w-fit",
756
+ className
757
+ ),
758
+ children
759
+ }
760
+ );
761
+ }
762
+ function Tab({ value, disabled = false, children, className }) {
763
+ const { activeValue, setActiveValue, variant, baseId } = useTabsContext();
764
+ const isActive = activeValue === value;
765
+ return /* @__PURE__ */ jsxRuntime.jsx(
766
+ "button",
767
+ {
768
+ type: "button",
769
+ role: "tab",
770
+ id: `${baseId}-tab-${value}`,
771
+ "aria-selected": isActive,
772
+ "aria-controls": `${baseId}-panel-${value}`,
773
+ disabled,
774
+ tabIndex: isActive ? 0 : -1,
775
+ onClick: () => {
776
+ if (!disabled) setActiveValue(value);
777
+ },
778
+ className: cn(
779
+ "relative px-4 py-2.5 text-sm font-medium transition-all duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 focus-visible:ring-offset-transparent",
780
+ // underline variant
781
+ variant === "underline" && [
782
+ "border-b-2 -mb-px",
783
+ isActive ? "border-primary text-white" : "border-transparent text-white/40 hover:text-white/70 hover:border-white/20",
784
+ disabled && "opacity-40 cursor-not-allowed hover:text-white/40 hover:border-transparent"
785
+ ],
786
+ // pill variant
787
+ variant === "pill" && [
788
+ "rounded-lg",
789
+ isActive ? "bg-white/10 text-white shadow-sm" : "text-white/50 hover:text-white/80 hover:bg-white/5",
790
+ disabled && "opacity-40 cursor-not-allowed hover:bg-transparent hover:text-white/50"
791
+ ],
792
+ className
793
+ ),
794
+ children
795
+ }
796
+ );
797
+ }
798
+ function TabPanel({ value, children, className }) {
799
+ const { activeValue, baseId } = useTabsContext();
800
+ if (activeValue !== value) return null;
801
+ return /* @__PURE__ */ jsxRuntime.jsx(
802
+ "div",
803
+ {
804
+ role: "tabpanel",
805
+ id: `${baseId}-panel-${value}`,
806
+ "aria-labelledby": `${baseId}-tab-${value}`,
807
+ tabIndex: 0,
808
+ className: cn("mt-4 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary", className),
809
+ children
810
+ }
811
+ );
812
+ }
813
+ var Card = react.forwardRef(function Card2({ hoverable, variant = "surface", className, ...props }, ref) {
814
+ return /* @__PURE__ */ jsxRuntime.jsx(
815
+ "div",
816
+ {
817
+ ref,
273
818
  ...props,
274
819
  className: cn(variant === "glass" ? "glass" : "surface", hoverable && "surface-hover", className)
275
820
  }
276
821
  );
822
+ });
823
+ var scrollLockCount = 0;
824
+ var savedOverflow = "";
825
+ function lockScroll() {
826
+ if (typeof document === "undefined") return;
827
+ if (scrollLockCount === 0) {
828
+ savedOverflow = document.body.style.overflow;
829
+ document.body.style.overflow = "hidden";
830
+ }
831
+ scrollLockCount++;
832
+ }
833
+ function unlockScroll() {
834
+ if (typeof document === "undefined") return;
835
+ scrollLockCount = Math.max(0, scrollLockCount - 1);
836
+ if (scrollLockCount === 0) {
837
+ document.body.style.overflow = savedOverflow;
838
+ }
277
839
  }
278
840
  function Modal({
279
841
  isOpen,
@@ -306,6 +868,11 @@ function Modal({
306
868
  if (lastActive?.isConnected) focusSafely(lastActive);
307
869
  };
308
870
  }, [isOpen]);
871
+ react.useEffect(() => {
872
+ if (!isOpen) return;
873
+ lockScroll();
874
+ return () => unlockScroll();
875
+ }, [isOpen]);
309
876
  if (!isOpen) return null;
310
877
  return /* @__PURE__ */ jsxRuntime.jsx(
311
878
  "div",
@@ -331,7 +898,7 @@ function Modal({
331
898
  ref: dialogRef,
332
899
  className: cn(
333
900
  "w-full rounded-t-3xl sm:rounded-2xl shadow-xl ring-1 ring-white/10 animate-modal-pop focus:outline-none focus-visible:ring-2 focus-visible:ring-primary",
334
- useGlass && "glass bg-[#0f0f18]/80",
901
+ useGlass && "glass bg-surface-50/80",
335
902
  contentClassName
336
903
  ),
337
904
  onMouseDown: (e) => e.stopPropagation(),
@@ -444,17 +1011,17 @@ function Tooltip({ content, delayMs = 500, placement = "top", className, childre
444
1011
  const anchorRef = react.useRef(null);
445
1012
  const [open, setOpen] = react.useState(false);
446
1013
  const [pos, setPos] = react.useState(null);
447
- const contentStr = react.useMemo(() => {
1014
+ react.useMemo(() => {
448
1015
  if (typeof content === "string") return content.trim();
449
1016
  return "";
450
1017
  }, [content]);
451
- function clearTimer() {
1018
+ const clearTimer = react.useCallback(() => {
452
1019
  if (openTimerRef.current !== null) {
453
1020
  window.clearTimeout(openTimerRef.current);
454
1021
  openTimerRef.current = null;
455
1022
  }
456
- }
457
- function updatePosition() {
1023
+ }, []);
1024
+ const updatePosition = react.useCallback(() => {
458
1025
  const el = anchorRef.current;
459
1026
  if (!el) return;
460
1027
  const r = el.getBoundingClientRect();
@@ -469,17 +1036,17 @@ function Tooltip({ content, delayMs = 500, placement = "top", className, childre
469
1036
  top: Math.round(effPlacement === "top" ? topY : bottomY),
470
1037
  placement: effPlacement
471
1038
  });
472
- }
473
- function scheduleOpen() {
1039
+ }, [placement]);
1040
+ const scheduleOpen = react.useCallback(() => {
474
1041
  clearTimer();
475
1042
  openTimerRef.current = window.setTimeout(() => {
476
1043
  setOpen(true);
477
1044
  }, Math.max(0, delayMs));
478
- }
479
- function close() {
1045
+ }, [clearTimer, delayMs]);
1046
+ const close = react.useCallback(() => {
480
1047
  clearTimer();
481
1048
  setOpen(false);
482
- }
1049
+ }, [clearTimer]);
483
1050
  react.useEffect(() => {
484
1051
  if (!open) return;
485
1052
  updatePosition();
@@ -491,45 +1058,52 @@ function Tooltip({ content, delayMs = 500, placement = "top", className, childre
491
1058
  window.removeEventListener("scroll", onScroll, true);
492
1059
  window.removeEventListener("resize", onResize);
493
1060
  };
494
- }, [open]);
1061
+ }, [open, updatePosition]);
495
1062
  react.useEffect(() => {
496
1063
  return () => clearTimer();
497
- }, []);
1064
+ }, [clearTimer]);
1065
+ if (!react.isValidElement(children)) return children;
498
1066
  const child = react.cloneElement(children, {
499
1067
  ref: (node) => {
500
1068
  anchorRef.current = node;
501
- const prevRef = children.ref;
1069
+ const childProps = children.props;
1070
+ const prevRef = childProps.ref;
502
1071
  if (typeof prevRef === "function") prevRef(node);
503
1072
  else if (prevRef && typeof prevRef === "object") prevRef.current = node;
504
1073
  },
505
1074
  onMouseEnter: (e) => {
506
- children.props.onMouseEnter?.(e);
1075
+ const childProps = children.props;
1076
+ if (typeof childProps.onMouseEnter === "function") childProps.onMouseEnter(e);
507
1077
  scheduleOpen();
508
1078
  },
509
1079
  onMouseLeave: (e) => {
510
- children.props.onMouseLeave?.(e);
1080
+ const childProps = children.props;
1081
+ if (typeof childProps.onMouseLeave === "function") childProps.onMouseLeave(e);
511
1082
  close();
512
1083
  },
513
1084
  onFocus: (e) => {
514
- children.props.onFocus?.(e);
1085
+ const childProps = children.props;
1086
+ if (typeof childProps.onFocus === "function") childProps.onFocus(e);
515
1087
  scheduleOpen();
516
1088
  },
517
1089
  onBlur: (e) => {
518
- children.props.onBlur?.(e);
1090
+ const childProps = children.props;
1091
+ if (typeof childProps.onBlur === "function") childProps.onBlur(e);
519
1092
  close();
520
1093
  },
521
- ...contentStr ? { "aria-describedby": tooltipId } : {}
1094
+ ...open ? { "aria-describedby": tooltipId } : {}
522
1095
  });
523
1096
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
524
1097
  child,
525
- open && content ? reactDom.createPortal(
1098
+ open && content && typeof document !== "undefined" ? reactDom.createPortal(
526
1099
  /* @__PURE__ */ jsxRuntime.jsx(
527
1100
  "div",
528
1101
  {
529
1102
  id: tooltipId,
530
1103
  role: "tooltip",
531
1104
  className: cn(
532
- "fixed z-[9999] max-w-[320px] rounded-lg bg-gray-900 px-3 py-2 text-xs leading-snug text-white shadow-xl pointer-events-none",
1105
+ "fixed z-[9999] max-w-[320px] rounded-lg px-3 py-2 text-xs leading-snug shadow-xl pointer-events-none",
1106
+ "bg-surface-100 text-white ring-1 ring-white/10",
533
1107
  className
534
1108
  ),
535
1109
  style: pos ? {
@@ -555,14 +1129,18 @@ function EmptyState({ icon: Icon, title, description, actionLabel, onAction, chi
555
1129
  }
556
1130
  function CollapsibleSection({ title, defaultOpen = true, children, right, className }) {
557
1131
  const [isOpen, setIsOpen] = react.useState(defaultOpen);
1132
+ const contentId = react.useId();
1133
+ const titleId = `${contentId}-title`;
558
1134
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("rounded-xl ring-1 ring-white/10 bg-white/5 overflow-hidden", className), children: [
559
1135
  /* @__PURE__ */ jsxRuntime.jsxs(
560
1136
  "button",
561
1137
  {
562
1138
  type: "button",
1139
+ id: titleId,
563
1140
  onClick: () => setIsOpen((prev) => !prev),
564
1141
  className: "flex w-full items-center justify-between gap-3 px-4 py-3 text-left hover:bg-white/[0.03] transition-colors",
565
1142
  "aria-expanded": isOpen,
1143
+ "aria-controls": contentId,
566
1144
  children: [
567
1145
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
568
1146
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -586,6 +1164,9 @@ function CollapsibleSection({ title, defaultOpen = true, children, right, classN
586
1164
  /* @__PURE__ */ jsxRuntime.jsx(
587
1165
  "div",
588
1166
  {
1167
+ id: contentId,
1168
+ role: "region",
1169
+ "aria-labelledby": titleId,
589
1170
  className: cn(
590
1171
  "grid transition-[grid-template-rows] duration-200 ease-out",
591
1172
  isOpen ? "grid-rows-[1fr]" : "grid-rows-[0fr]"
@@ -595,6 +1176,349 @@ function CollapsibleSection({ title, defaultOpen = true, children, right, classN
595
1176
  )
596
1177
  ] });
597
1178
  }
1179
+ var DropdownContext = react.createContext(null);
1180
+ function useDropdownContext(component) {
1181
+ const ctx = react.useContext(DropdownContext);
1182
+ if (!ctx) {
1183
+ throw new Error(`<${component}> must be rendered inside <Dropdown>`);
1184
+ }
1185
+ return ctx;
1186
+ }
1187
+ function Dropdown({ children, className }) {
1188
+ const [open, setOpen] = react.useState(false);
1189
+ const triggerId = react.useId();
1190
+ const menuId = react.useId();
1191
+ const triggerRef = react.useRef(null);
1192
+ const toggleOpen = react.useCallback(() => setOpen((prev) => !prev), []);
1193
+ const ctxValue = react.useMemo(
1194
+ () => ({ open, setOpen, toggleOpen, triggerId, menuId, triggerRef }),
1195
+ [open, setOpen, toggleOpen, triggerId, menuId, triggerRef]
1196
+ );
1197
+ return /* @__PURE__ */ jsxRuntime.jsx(DropdownContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("relative inline-block", className), children }) });
1198
+ }
1199
+ function DropdownTrigger({ children, className }) {
1200
+ const { open, toggleOpen, triggerId, menuId, triggerRef } = useDropdownContext("DropdownTrigger");
1201
+ if (!react.isValidElement(children)) return children;
1202
+ return react.cloneElement(children, {
1203
+ id: triggerId,
1204
+ "aria-haspopup": "menu",
1205
+ "aria-expanded": open,
1206
+ "aria-controls": open ? menuId : void 0,
1207
+ className: cn(children.props.className, className),
1208
+ ref: (node) => {
1209
+ triggerRef.current = node;
1210
+ const childRef = children.ref;
1211
+ if (typeof childRef === "function") childRef(node);
1212
+ else if (childRef && typeof childRef === "object") childRef.current = node;
1213
+ },
1214
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1215
+ onClick: (e) => {
1216
+ const childProps = children.props;
1217
+ if (typeof childProps.onClick === "function") childProps.onClick(e);
1218
+ toggleOpen();
1219
+ }
1220
+ });
1221
+ }
1222
+ function DropdownMenu({ children, className, align = "left" }) {
1223
+ const { open, setOpen, menuId, triggerId, triggerRef } = useDropdownContext("DropdownMenu");
1224
+ const menuRef = react.useRef(null);
1225
+ const [pos, setPos] = react.useState(null);
1226
+ const [visible, setVisible] = react.useState(false);
1227
+ const updatePosition = react.useCallback(() => {
1228
+ const trigger = triggerRef.current;
1229
+ if (!trigger) return;
1230
+ const rect = trigger.getBoundingClientRect();
1231
+ const left = align === "right" ? rect.right : rect.left;
1232
+ setPos({
1233
+ top: Math.round(rect.bottom + 6),
1234
+ left: Math.round(left)
1235
+ });
1236
+ }, [align, triggerRef]);
1237
+ react.useEffect(() => {
1238
+ if (!open) {
1239
+ setVisible(false);
1240
+ return;
1241
+ }
1242
+ updatePosition();
1243
+ const raf = window.requestAnimationFrame(() => {
1244
+ setVisible(true);
1245
+ const menuEl = menuRef.current;
1246
+ if (!menuEl) return;
1247
+ const items = getFocusableElements(menuEl);
1248
+ focusSafely(items[0] ?? menuEl);
1249
+ });
1250
+ return () => window.cancelAnimationFrame(raf);
1251
+ }, [open, updatePosition]);
1252
+ react.useEffect(() => {
1253
+ if (!open) return;
1254
+ function handlePointerDown(e) {
1255
+ const target = e.target;
1256
+ if (menuRef.current?.contains(target)) return;
1257
+ if (triggerRef.current?.contains(target)) return;
1258
+ setOpen(false);
1259
+ }
1260
+ document.addEventListener("pointerdown", handlePointerDown, true);
1261
+ return () => document.removeEventListener("pointerdown", handlePointerDown, true);
1262
+ }, [open, setOpen, triggerRef]);
1263
+ react.useEffect(() => {
1264
+ if (!open) return;
1265
+ const onScroll = () => updatePosition();
1266
+ const onResize = () => updatePosition();
1267
+ window.addEventListener("scroll", onScroll, true);
1268
+ window.addEventListener("resize", onResize);
1269
+ return () => {
1270
+ window.removeEventListener("scroll", onScroll, true);
1271
+ window.removeEventListener("resize", onResize);
1272
+ };
1273
+ }, [open, updatePosition]);
1274
+ if (!open) return null;
1275
+ const panel = /* @__PURE__ */ jsxRuntime.jsx(
1276
+ "div",
1277
+ {
1278
+ ref: menuRef,
1279
+ id: menuId,
1280
+ role: "menu",
1281
+ "aria-labelledby": triggerId,
1282
+ tabIndex: -1,
1283
+ className: cn(
1284
+ // Base glass panel
1285
+ "fixed z-[9999] min-w-[10rem] py-1",
1286
+ "rounded-xl shadow-xl ring-1 ring-white/10",
1287
+ "bg-surface-50/90 backdrop-blur-md",
1288
+ // Animation
1289
+ "transition-[opacity,transform] duration-150 ease-out",
1290
+ visible ? "opacity-100 translate-y-0" : "opacity-0 -translate-y-1",
1291
+ className
1292
+ ),
1293
+ style: pos ? {
1294
+ top: pos.top,
1295
+ ...align === "right" ? { right: `calc(100vw - ${pos.left}px)` } : { left: pos.left }
1296
+ } : { top: 0, left: 0, visibility: "hidden" },
1297
+ onKeyDown: (e) => {
1298
+ const menuEl = menuRef.current;
1299
+ if (!menuEl) return;
1300
+ if (e.key === "Escape" || e.key === "Tab") {
1301
+ if (e.key === "Escape") {
1302
+ e.preventDefault();
1303
+ e.stopPropagation();
1304
+ focusSafely(triggerRef.current);
1305
+ }
1306
+ setOpen(false);
1307
+ return;
1308
+ }
1309
+ const items = getFocusableElements(menuEl).filter(
1310
+ (el) => el.getAttribute("role") === "menuitem" && el.getAttribute("aria-disabled") !== "true"
1311
+ );
1312
+ if (items.length === 0) return;
1313
+ const active = document.activeElement;
1314
+ const currentIndex = items.findIndex((el) => el === active);
1315
+ if (e.key === "ArrowDown") {
1316
+ e.preventDefault();
1317
+ const next = currentIndex < items.length - 1 ? currentIndex + 1 : 0;
1318
+ focusSafely(items[next]);
1319
+ } else if (e.key === "ArrowUp") {
1320
+ e.preventDefault();
1321
+ const prev = currentIndex > 0 ? currentIndex - 1 : items.length - 1;
1322
+ focusSafely(items[prev]);
1323
+ } else if (e.key === "Home") {
1324
+ e.preventDefault();
1325
+ focusSafely(items[0]);
1326
+ } else if (e.key === "End") {
1327
+ e.preventDefault();
1328
+ focusSafely(items[items.length - 1]);
1329
+ }
1330
+ },
1331
+ children
1332
+ }
1333
+ );
1334
+ if (typeof document === "undefined") return null;
1335
+ return reactDom.createPortal(panel, document.body);
1336
+ }
1337
+ function DropdownItem({ onSelect, disabled = false, children, className }) {
1338
+ const { setOpen, triggerRef } = useDropdownContext("DropdownItem");
1339
+ const handleSelect = react.useCallback(() => {
1340
+ if (disabled) return;
1341
+ setOpen(false);
1342
+ focusSafely(triggerRef.current);
1343
+ onSelect?.();
1344
+ }, [disabled, onSelect, setOpen, triggerRef]);
1345
+ return /* @__PURE__ */ jsxRuntime.jsx(
1346
+ "div",
1347
+ {
1348
+ role: "menuitem",
1349
+ tabIndex: disabled ? -1 : 0,
1350
+ "aria-disabled": disabled ? "true" : void 0,
1351
+ className: cn(
1352
+ "flex items-center gap-2 w-full px-3 py-2 text-sm text-left cursor-pointer select-none",
1353
+ "text-white/80 transition-colors duration-100",
1354
+ "focus:outline-none focus-visible:bg-white/10",
1355
+ disabled ? "opacity-40 pointer-events-none cursor-not-allowed" : "hover:bg-white/10 hover:text-white focus-visible:text-white",
1356
+ className
1357
+ ),
1358
+ onClick: handleSelect,
1359
+ onKeyDown: (e) => {
1360
+ if (e.key === "Enter" || e.key === " ") {
1361
+ e.preventDefault();
1362
+ handleSelect();
1363
+ }
1364
+ },
1365
+ children
1366
+ }
1367
+ );
1368
+ }
1369
+ function DropdownSeparator({ className }) {
1370
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "separator", className: cn("border-t border-white/10 my-1", className) });
1371
+ }
1372
+ function matchesAccept(file, accept) {
1373
+ const types = accept.split(",").map((t) => t.trim().toLowerCase());
1374
+ const fileName = file.name.toLowerCase();
1375
+ const mimeType = file.type.toLowerCase();
1376
+ return types.some((t) => {
1377
+ if (t.startsWith(".")) return fileName.endsWith(t);
1378
+ if (t.endsWith("/*")) return mimeType.startsWith(t.slice(0, -1));
1379
+ return mimeType === t;
1380
+ });
1381
+ }
1382
+ function DropZone({
1383
+ onFilesDropped,
1384
+ accept,
1385
+ maxFiles,
1386
+ maxSize,
1387
+ disabled = false,
1388
+ children,
1389
+ className,
1390
+ "aria-label": ariaLabel = "File drop zone"
1391
+ }) {
1392
+ const [isDragging, setIsDragging] = react.useState(false);
1393
+ const inputRef = react.useRef(null);
1394
+ const dragCounterRef = react.useRef(0);
1395
+ const processFiles = react.useCallback(
1396
+ (fileList) => {
1397
+ if (!fileList || disabled) return;
1398
+ let files = Array.from(fileList);
1399
+ if (accept) {
1400
+ files = files.filter((f) => matchesAccept(f, accept));
1401
+ }
1402
+ if (maxSize !== void 0) {
1403
+ files = files.filter((f) => f.size <= maxSize);
1404
+ }
1405
+ if (maxFiles !== void 0) {
1406
+ files = files.slice(0, maxFiles);
1407
+ }
1408
+ onFilesDropped(files);
1409
+ },
1410
+ [accept, disabled, maxFiles, maxSize, onFilesDropped]
1411
+ );
1412
+ const handleDragEnter = react.useCallback(
1413
+ (e) => {
1414
+ e.preventDefault();
1415
+ dragCounterRef.current++;
1416
+ if (!disabled) setIsDragging(true);
1417
+ },
1418
+ [disabled]
1419
+ );
1420
+ const handleDragOver = react.useCallback(
1421
+ (e) => {
1422
+ e.preventDefault();
1423
+ },
1424
+ []
1425
+ );
1426
+ const handleDragLeave = react.useCallback((e) => {
1427
+ e.preventDefault();
1428
+ dragCounterRef.current--;
1429
+ if (dragCounterRef.current === 0) setIsDragging(false);
1430
+ }, []);
1431
+ const handleDrop = react.useCallback(
1432
+ (e) => {
1433
+ e.preventDefault();
1434
+ dragCounterRef.current = 0;
1435
+ setIsDragging(false);
1436
+ if (disabled) return;
1437
+ processFiles(e.dataTransfer.files);
1438
+ },
1439
+ [disabled, processFiles]
1440
+ );
1441
+ const handleClick = react.useCallback(() => {
1442
+ if (!disabled) inputRef.current?.click();
1443
+ }, [disabled]);
1444
+ const handleKeyDown = react.useCallback(
1445
+ (e) => {
1446
+ if (disabled) return;
1447
+ if (e.key === "Enter" || e.key === " ") {
1448
+ e.preventDefault();
1449
+ inputRef.current?.click();
1450
+ }
1451
+ },
1452
+ [disabled]
1453
+ );
1454
+ const handleInputChange = react.useCallback(() => {
1455
+ processFiles(inputRef.current?.files ?? null);
1456
+ if (inputRef.current) inputRef.current.value = "";
1457
+ }, [processFiles]);
1458
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1459
+ "div",
1460
+ {
1461
+ role: "button",
1462
+ tabIndex: disabled ? -1 : 0,
1463
+ "aria-label": ariaLabel,
1464
+ "aria-disabled": disabled,
1465
+ onClick: handleClick,
1466
+ onKeyDown: handleKeyDown,
1467
+ onDragEnter: handleDragEnter,
1468
+ onDragOver: handleDragOver,
1469
+ onDragLeave: handleDragLeave,
1470
+ onDrop: handleDrop,
1471
+ className: cn(
1472
+ "flex flex-col items-center justify-center gap-3 rounded-2xl border-2 border-dashed px-6 py-10 text-center transition-colors cursor-pointer select-none outline-none",
1473
+ "border-white/20 bg-white/5 backdrop-blur-sm",
1474
+ "hover:border-primary/50 hover:bg-white/[0.08]",
1475
+ "focus-visible:ring-2 focus-visible:ring-primary/40 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent",
1476
+ isDragging && "border-primary bg-primary/10",
1477
+ disabled && "pointer-events-none opacity-50",
1478
+ className
1479
+ ),
1480
+ children: [
1481
+ /* @__PURE__ */ jsxRuntime.jsx(
1482
+ "input",
1483
+ {
1484
+ ref: inputRef,
1485
+ type: "file",
1486
+ tabIndex: -1,
1487
+ className: "sr-only",
1488
+ accept,
1489
+ multiple: maxFiles !== 1,
1490
+ onChange: handleInputChange,
1491
+ onClick: (e) => e.stopPropagation(),
1492
+ "aria-hidden": "true"
1493
+ }
1494
+ ),
1495
+ children ?? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1496
+ /* @__PURE__ */ jsxRuntime.jsx(
1497
+ "svg",
1498
+ {
1499
+ xmlns: "http://www.w3.org/2000/svg",
1500
+ className: "h-10 w-10 text-white/40",
1501
+ fill: "none",
1502
+ viewBox: "0 0 24 24",
1503
+ stroke: "currentColor",
1504
+ strokeWidth: 1.5,
1505
+ "aria-hidden": "true",
1506
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1507
+ "path",
1508
+ {
1509
+ strokeLinecap: "round",
1510
+ strokeLinejoin: "round",
1511
+ d: "M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5"
1512
+ }
1513
+ )
1514
+ }
1515
+ ),
1516
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-white/50", children: "Drop files here or click to browse" })
1517
+ ] })
1518
+ ]
1519
+ }
1520
+ );
1521
+ }
598
1522
  var maxWidthClass = {
599
1523
  sm: "max-w-2xl",
600
1524
  md: "max-w-4xl",
@@ -607,7 +1531,7 @@ function defaultBackground() {
607
1531
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 overflow-hidden", children: [
608
1532
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -top-32 -left-32 h-[600px] w-[600px] rounded-full bg-violet-600/20 blur-[140px]" }),
609
1533
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-20 -right-32 h-[500px] w-[500px] rounded-full bg-purple-500/[0.12] blur-[120px]" }),
610
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-0 left-1/2 h-[400px] w-[500px] -translate-x-1/2 rounded-full bg-indigo-500/[0.10] blur-[120px]" })
1534
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-0 left-1/2 h-[400px] w-[500px] -translate-x-1/2 rounded-full bg-accent/[0.10] blur-[120px]" })
611
1535
  ] });
612
1536
  }
613
1537
  function PageShell({
@@ -641,11 +1565,8 @@ function PageShell({
641
1565
  }
642
1566
  function Navbar({ logo, children, className, glass = true }) {
643
1567
  return /* @__PURE__ */ jsxRuntime.jsx("header", { className: cn("fixed top-0 w-full z-50", glass && "glass", className), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-7xl mx-auto px-6 h-16 flex items-center justify-between", children: [
644
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-3", children: logo || /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
645
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-9 h-9 rounded-xl animated-gradient" }),
646
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-lg font-bold tracking-tight", children: "MemeLab" })
647
- ] }) }),
648
- children && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-4", children })
1568
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-3", children: logo ?? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-9 h-9 rounded-xl animated-gradient" }) }),
1569
+ children && /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Main navigation", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-4", children }) })
649
1570
  ] }) });
650
1571
  }
651
1572
  function Sidebar({ children, collapsed = false, onToggle, className }) {
@@ -683,7 +1604,15 @@ function Sidebar({ children, collapsed = false, onToggle, className }) {
683
1604
  }
684
1605
  );
685
1606
  }
686
- function DashboardLayout({ children, navbar, sidebar, className }) {
1607
+ var maxWidthClass2 = {
1608
+ sm: "max-w-2xl",
1609
+ md: "max-w-4xl",
1610
+ lg: "max-w-5xl",
1611
+ xl: "max-w-7xl",
1612
+ "2xl": "max-w-[92rem]",
1613
+ full: "max-w-full"
1614
+ };
1615
+ function DashboardLayout({ children, navbar, sidebar, className, mainClassName, maxWidth = "lg" }) {
687
1616
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("min-h-screen bg-surface relative overflow-hidden", className), children: [
688
1617
  /* @__PURE__ */ jsxRuntime.jsxs("div", { "aria-hidden": "true", className: "pointer-events-none fixed inset-0", children: [
689
1618
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orb orb-purple w-[400px] h-[400px] -top-[150px] -left-[150px] opacity-15" }),
@@ -692,17 +1621,253 @@ function DashboardLayout({ children, navbar, sidebar, className }) {
692
1621
  navbar,
693
1622
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex", navbar && "pt-16"), children: [
694
1623
  sidebar,
695
- /* @__PURE__ */ jsxRuntime.jsx("main", { className: "flex-1 min-w-0 py-8 px-6", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-5xl mx-auto", children }) })
1624
+ /* @__PURE__ */ jsxRuntime.jsx("main", { className: cn("flex-1 min-w-0 py-8 px-6", mainClassName), children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("mx-auto", maxWidthClass2[maxWidth]), children }) })
696
1625
  ] })
697
1626
  ] });
698
1627
  }
1628
+ var ProgressButton = react.forwardRef(
1629
+ function ProgressButton2({ isLoading, loadingText, children, disabled, className, ...props }, ref) {
1630
+ return /* @__PURE__ */ jsxRuntime.jsx(
1631
+ Button,
1632
+ {
1633
+ ref,
1634
+ ...props,
1635
+ disabled: disabled || isLoading,
1636
+ "aria-busy": isLoading || void 0,
1637
+ className: cn("relative overflow-hidden", className),
1638
+ children: isLoading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1639
+ /* @__PURE__ */ jsxRuntime.jsx(
1640
+ "span",
1641
+ {
1642
+ "aria-hidden": "true",
1643
+ className: "absolute inset-0 bg-white/20 animate-[ml-shimmer_2s_ease-in-out_infinite] skew-x-[-20deg] pointer-events-none"
1644
+ }
1645
+ ),
1646
+ /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: "sm" }),
1647
+ loadingText ?? children
1648
+ ] }) : children
1649
+ }
1650
+ );
1651
+ }
1652
+ );
1653
+ function reducer(state, action) {
1654
+ switch (action.type) {
1655
+ case "ADD": {
1656
+ const next = [...state, action.toast];
1657
+ return next.length > action.maxToasts ? next.slice(-action.maxToasts) : next;
1658
+ }
1659
+ case "REMOVE":
1660
+ return state.filter((t) => t.id !== action.id);
1661
+ case "REMOVE_ALL":
1662
+ return [];
1663
+ default:
1664
+ return state;
1665
+ }
1666
+ }
1667
+ var ToastContext = react.createContext(null);
1668
+ var variantBarColor = {
1669
+ success: "bg-emerald-500",
1670
+ error: "bg-rose-500",
1671
+ warning: "bg-amber-500",
1672
+ info: "bg-primary"
1673
+ };
1674
+ var variantProgressColor = {
1675
+ success: "bg-emerald-500",
1676
+ error: "bg-rose-500",
1677
+ warning: "bg-amber-500",
1678
+ info: "bg-primary"
1679
+ };
1680
+ var variantIconLabel = {
1681
+ success: "\u2713",
1682
+ error: "\u2715",
1683
+ warning: "\u26A0",
1684
+ info: "i"
1685
+ };
1686
+ var variantIconColor = {
1687
+ success: "text-emerald-400",
1688
+ error: "text-rose-400",
1689
+ warning: "text-amber-400",
1690
+ info: "text-primary"
1691
+ };
1692
+ var positionClass = {
1693
+ "top-right": "top-4 right-4 items-end",
1694
+ "top-center": "top-4 left-1/2 -translate-x-1/2 items-center",
1695
+ "bottom-right": "bottom-4 right-4 items-end",
1696
+ "bottom-center": "bottom-4 left-1/2 -translate-x-1/2 items-center"
1697
+ };
1698
+ function ToastCard({ toast, onDismiss }) {
1699
+ const duration = toast.duration ?? 5e3;
1700
+ const [visible, setVisible] = react.useState(false);
1701
+ const [exiting, setExiting] = react.useState(false);
1702
+ const [started, setStarted] = react.useState(false);
1703
+ const dismissedRef = react.useRef(false);
1704
+ const timerRef = react.useRef(null);
1705
+ const exitTimerRef = react.useRef(null);
1706
+ react.useEffect(() => {
1707
+ let innerFrame;
1708
+ const frame = requestAnimationFrame(() => {
1709
+ setVisible(true);
1710
+ innerFrame = requestAnimationFrame(() => setStarted(true));
1711
+ });
1712
+ return () => {
1713
+ cancelAnimationFrame(frame);
1714
+ cancelAnimationFrame(innerFrame);
1715
+ };
1716
+ }, []);
1717
+ react.useEffect(() => {
1718
+ return () => {
1719
+ if (exitTimerRef.current !== null) clearTimeout(exitTimerRef.current);
1720
+ };
1721
+ }, []);
1722
+ const triggerDismiss = react.useCallback(() => {
1723
+ if (dismissedRef.current) return;
1724
+ dismissedRef.current = true;
1725
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
1726
+ setExiting(true);
1727
+ exitTimerRef.current = setTimeout(() => onDismiss(toast.id), 280);
1728
+ }, [onDismiss, toast.id]);
1729
+ react.useEffect(() => {
1730
+ if (duration <= 0) return;
1731
+ timerRef.current = setTimeout(triggerDismiss, duration);
1732
+ return () => {
1733
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
1734
+ };
1735
+ }, [duration, triggerDismiss]);
1736
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1737
+ "div",
1738
+ {
1739
+ role: "alert",
1740
+ "aria-live": "assertive",
1741
+ "aria-atomic": "true",
1742
+ "data-variant": toast.variant,
1743
+ className: cn(
1744
+ // Base layout
1745
+ "relative flex w-80 max-w-[calc(100vw-2rem)] overflow-hidden rounded-xl shadow-xl",
1746
+ // Glass background
1747
+ "bg-surface-100/95 backdrop-blur-md ring-1 ring-white/10",
1748
+ // Enter / exit transitions
1749
+ "transition-all duration-300",
1750
+ visible && !exiting ? "opacity-100 translate-x-0" : "opacity-0 translate-x-8",
1751
+ exiting && "scale-95"
1752
+ ),
1753
+ children: [
1754
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: cn("w-[3px] shrink-0 self-stretch", variantBarColor[toast.variant]) }),
1755
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 items-start gap-3 px-4 py-3 pr-9", children: [
1756
+ /* @__PURE__ */ jsxRuntime.jsx(
1757
+ "span",
1758
+ {
1759
+ "aria-hidden": "true",
1760
+ className: cn(
1761
+ "mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full text-[11px] font-bold ring-1",
1762
+ variantIconColor[toast.variant],
1763
+ "ring-current/30"
1764
+ ),
1765
+ children: variantIconLabel[toast.variant]
1766
+ }
1767
+ ),
1768
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
1769
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold leading-snug text-white", children: toast.title }),
1770
+ toast.description ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 text-xs leading-relaxed text-white/60", children: toast.description }) : null
1771
+ ] })
1772
+ ] }),
1773
+ /* @__PURE__ */ jsxRuntime.jsx(
1774
+ "button",
1775
+ {
1776
+ type: "button",
1777
+ "aria-label": "Dismiss notification",
1778
+ onClick: triggerDismiss,
1779
+ className: cn(
1780
+ "absolute right-2 top-2 flex h-6 w-6 items-center justify-center rounded-lg",
1781
+ "text-white/40 transition-colors hover:bg-white/10 hover:text-white/80",
1782
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40"
1783
+ ),
1784
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "10", height: "10", viewBox: "0 0 10 10", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M1 1l8 8M9 1L1 9", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) })
1785
+ }
1786
+ ),
1787
+ duration > 0 && /* @__PURE__ */ jsxRuntime.jsx(
1788
+ "span",
1789
+ {
1790
+ "aria-hidden": "true",
1791
+ className: cn(
1792
+ "absolute bottom-0 left-0 h-[2px] ease-linear",
1793
+ variantProgressColor[toast.variant],
1794
+ "opacity-60"
1795
+ ),
1796
+ style: {
1797
+ width: started ? "0%" : "100%",
1798
+ transitionProperty: "width",
1799
+ transitionDuration: started ? `${duration}ms` : "0ms"
1800
+ }
1801
+ }
1802
+ )
1803
+ ]
1804
+ }
1805
+ );
1806
+ }
1807
+ function ToastProvider({ children, position = "top-right", maxToasts = 5 }) {
1808
+ const [toasts, dispatch] = react.useReducer(reducer, []);
1809
+ const counterRef = react.useRef(0);
1810
+ const maxToastsRef = react.useRef(maxToasts);
1811
+ maxToastsRef.current = maxToasts;
1812
+ const toast = react.useCallback(
1813
+ (options) => {
1814
+ const id = `toast-${++counterRef.current}`;
1815
+ const newToast = {
1816
+ id,
1817
+ variant: options.variant,
1818
+ title: options.title,
1819
+ description: options.description,
1820
+ duration: options.duration ?? 5e3
1821
+ };
1822
+ dispatch({ type: "ADD", toast: newToast, maxToasts: maxToastsRef.current });
1823
+ return id;
1824
+ },
1825
+ []
1826
+ );
1827
+ const dismiss = react.useCallback((id) => {
1828
+ dispatch({ type: "REMOVE", id });
1829
+ }, []);
1830
+ const dismissAll = react.useCallback(() => {
1831
+ dispatch({ type: "REMOVE_ALL" });
1832
+ }, []);
1833
+ const value = react.useMemo(() => ({ toast, dismiss, dismissAll }), [toast, dismiss, dismissAll]);
1834
+ const container = typeof document !== "undefined" ? reactDom.createPortal(
1835
+ /* @__PURE__ */ jsxRuntime.jsx(
1836
+ "div",
1837
+ {
1838
+ "aria-label": "Notifications",
1839
+ className: cn("fixed z-[9999] flex flex-col gap-3 pointer-events-none", positionClass[position]),
1840
+ children: toasts.map((t) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsxRuntime.jsx(ToastCard, { toast: t, onDismiss: dismiss }) }, t.id))
1841
+ }
1842
+ ),
1843
+ document.body
1844
+ ) : null;
1845
+ return /* @__PURE__ */ jsxRuntime.jsxs(ToastContext.Provider, { value, children: [
1846
+ children,
1847
+ container
1848
+ ] });
1849
+ }
1850
+ function useToast() {
1851
+ const ctx = react.useContext(ToastContext);
1852
+ if (!ctx) {
1853
+ throw new Error("useToast must be used inside <ToastProvider>");
1854
+ }
1855
+ return ctx;
1856
+ }
699
1857
 
700
1858
  exports.Badge = Badge;
701
1859
  exports.Button = Button;
702
1860
  exports.Card = Card;
1861
+ exports.Checkbox = Checkbox;
703
1862
  exports.CollapsibleSection = CollapsibleSection;
704
1863
  exports.ConfirmDialog = ConfirmDialog;
705
1864
  exports.DashboardLayout = DashboardLayout;
1865
+ exports.DropZone = DropZone;
1866
+ exports.Dropdown = Dropdown;
1867
+ exports.DropdownItem = DropdownItem;
1868
+ exports.DropdownMenu = DropdownMenu;
1869
+ exports.DropdownSeparator = DropdownSeparator;
1870
+ exports.DropdownTrigger = DropdownTrigger;
706
1871
  exports.EmptyState = EmptyState;
707
1872
  exports.IconButton = IconButton;
708
1873
  exports.Input = Input;
@@ -710,13 +1875,28 @@ exports.Modal = Modal;
710
1875
  exports.Navbar = Navbar;
711
1876
  exports.PageShell = PageShell;
712
1877
  exports.Pill = Pill;
1878
+ exports.ProgressButton = ProgressButton;
1879
+ exports.RadioGroup = RadioGroup;
1880
+ exports.RadioItem = RadioItem;
1881
+ exports.SearchInput = SearchInput;
713
1882
  exports.Select = Select;
714
1883
  exports.Sidebar = Sidebar;
715
1884
  exports.Skeleton = Skeleton;
716
1885
  exports.Spinner = Spinner;
1886
+ exports.Tab = Tab;
1887
+ exports.TabList = TabList;
1888
+ exports.TabPanel = TabPanel;
1889
+ exports.Tabs = Tabs;
717
1890
  exports.Textarea = Textarea;
1891
+ exports.ToastProvider = ToastProvider;
718
1892
  exports.Toggle = Toggle;
719
1893
  exports.Tooltip = Tooltip;
720
1894
  exports.cn = cn;
1895
+ exports.colors = colors;
721
1896
  exports.focusSafely = focusSafely;
722
1897
  exports.getFocusableElements = getFocusableElements;
1898
+ exports.useClipboard = useClipboard;
1899
+ exports.useDebounce = useDebounce;
1900
+ exports.useDisclosure = useDisclosure;
1901
+ exports.useMediaQuery = useMediaQuery;
1902
+ exports.useToast = useToast;