@memelabui/ui 0.1.1 → 0.3.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/README.md +104 -29
- package/dist/index.cjs +2335 -86
- package/dist/index.d.cts +349 -9
- package/dist/index.d.ts +349 -9
- package/dist/index.js +2297 -92
- package/dist/preset/index.cjs +13 -45
- package/dist/preset/index.js +13 -41
- package/dist/styles/index.css +619 -90
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { forwardRef, useId, useRef, useEffect,
|
|
2
|
-
import {
|
|
1
|
+
import React, { forwardRef, useState, useId, useMemo, useRef, useEffect, createContext, useCallback, isValidElement, cloneElement, useReducer, useContext } from 'react';
|
|
2
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import { createPortal } from 'react-dom';
|
|
4
4
|
|
|
5
5
|
// src/utils/cn.ts
|
|
@@ -56,17 +56,153 @@ function focusSafely(el) {
|
|
|
56
56
|
} catch {
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
function useClipboard(timeout = 2e3) {
|
|
60
|
+
const [copied, setCopied] = useState(false);
|
|
61
|
+
const timerRef = useRef(null);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
return () => {
|
|
64
|
+
if (timerRef.current !== null) {
|
|
65
|
+
clearTimeout(timerRef.current);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}, []);
|
|
69
|
+
const copy = useCallback(
|
|
70
|
+
async (text) => {
|
|
71
|
+
try {
|
|
72
|
+
await navigator.clipboard.writeText(text);
|
|
73
|
+
} catch {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (timerRef.current !== null) {
|
|
77
|
+
clearTimeout(timerRef.current);
|
|
78
|
+
}
|
|
79
|
+
setCopied(true);
|
|
80
|
+
timerRef.current = setTimeout(() => {
|
|
81
|
+
setCopied(false);
|
|
82
|
+
timerRef.current = null;
|
|
83
|
+
}, timeout);
|
|
84
|
+
},
|
|
85
|
+
[timeout]
|
|
86
|
+
);
|
|
87
|
+
return { copy, copied };
|
|
88
|
+
}
|
|
89
|
+
function useDisclosure(defaultOpen = false) {
|
|
90
|
+
const [isOpen, setIsOpen] = useState(defaultOpen);
|
|
91
|
+
const open = useCallback(() => setIsOpen(true), []);
|
|
92
|
+
const close = useCallback(() => setIsOpen(false), []);
|
|
93
|
+
const toggle = useCallback(() => setIsOpen((prev) => !prev), []);
|
|
94
|
+
return { isOpen, open, close, toggle };
|
|
95
|
+
}
|
|
96
|
+
function useMediaQuery(query) {
|
|
97
|
+
const [matches, setMatches] = useState(() => {
|
|
98
|
+
if (typeof window === "undefined") return false;
|
|
99
|
+
return window.matchMedia(query).matches;
|
|
100
|
+
});
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (typeof window === "undefined") return;
|
|
103
|
+
const mediaQueryList = window.matchMedia(query);
|
|
104
|
+
setMatches(mediaQueryList.matches);
|
|
105
|
+
const handler = (event) => {
|
|
106
|
+
setMatches(event.matches);
|
|
107
|
+
};
|
|
108
|
+
mediaQueryList.addEventListener("change", handler);
|
|
109
|
+
return () => {
|
|
110
|
+
mediaQueryList.removeEventListener("change", handler);
|
|
111
|
+
};
|
|
112
|
+
}, [query]);
|
|
113
|
+
return matches;
|
|
114
|
+
}
|
|
115
|
+
function useDebounce(value, delayMs = 300) {
|
|
116
|
+
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
const timer = setTimeout(() => {
|
|
119
|
+
setDebouncedValue(value);
|
|
120
|
+
}, delayMs);
|
|
121
|
+
return () => {
|
|
122
|
+
clearTimeout(timer);
|
|
123
|
+
};
|
|
124
|
+
}, [value, delayMs]);
|
|
125
|
+
return debouncedValue;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/tokens/colors.ts
|
|
129
|
+
var colors = {
|
|
130
|
+
bg: "#0a0a0f",
|
|
131
|
+
fg: "#f9fafb",
|
|
132
|
+
surface: {
|
|
133
|
+
0: "#0a0a0f",
|
|
134
|
+
50: "#0f0f18",
|
|
135
|
+
100: "#141420",
|
|
136
|
+
200: "#1a1a2e",
|
|
137
|
+
300: "#24243a",
|
|
138
|
+
400: "#2a2a4a"
|
|
139
|
+
},
|
|
140
|
+
primary: { DEFAULT: "#8b5cf6", light: "#a78bfa", dark: "#7c3aed" },
|
|
141
|
+
accent: { DEFAULT: "#667eea", light: "#8b9cf7", dark: "#4c5fd4" },
|
|
142
|
+
glow: { purple: "#764ba2", pink: "#f093fb" },
|
|
143
|
+
success: "#10b981",
|
|
144
|
+
warning: "#f59e0b",
|
|
145
|
+
danger: "#f43f5e"
|
|
146
|
+
};
|
|
60
147
|
var sizeClass = {
|
|
148
|
+
sm: "w-8 h-8 text-xs",
|
|
149
|
+
md: "w-10 h-10 text-sm",
|
|
150
|
+
lg: "w-12 h-12 text-base",
|
|
151
|
+
xl: "w-16 h-16 text-lg"
|
|
152
|
+
};
|
|
153
|
+
function getInitials(name) {
|
|
154
|
+
if (!name) return "";
|
|
155
|
+
const parts = name.trim().split(/\s+/);
|
|
156
|
+
const first = parts[0]?.[0] ?? "";
|
|
157
|
+
const second = parts[1]?.[0] ?? "";
|
|
158
|
+
return (first + second).toUpperCase();
|
|
159
|
+
}
|
|
160
|
+
var Avatar = forwardRef(function Avatar2({ src, alt, name, size = "md", className }, ref) {
|
|
161
|
+
const [imgError, setImgError] = useState(false);
|
|
162
|
+
const showFallback = !src || imgError;
|
|
163
|
+
const initials = getInitials(name);
|
|
164
|
+
return /* @__PURE__ */ jsxs(
|
|
165
|
+
"div",
|
|
166
|
+
{
|
|
167
|
+
ref,
|
|
168
|
+
className: cn(
|
|
169
|
+
"relative inline-flex items-center justify-center shrink-0 rounded-full overflow-hidden ring-1 ring-white/10",
|
|
170
|
+
sizeClass[size],
|
|
171
|
+
className
|
|
172
|
+
),
|
|
173
|
+
children: [
|
|
174
|
+
!showFallback && /* @__PURE__ */ jsx(
|
|
175
|
+
"img",
|
|
176
|
+
{
|
|
177
|
+
src,
|
|
178
|
+
alt: alt ?? name ?? "",
|
|
179
|
+
onError: () => setImgError(true),
|
|
180
|
+
className: "w-full h-full object-cover"
|
|
181
|
+
}
|
|
182
|
+
),
|
|
183
|
+
showFallback && /* @__PURE__ */ jsx(
|
|
184
|
+
"span",
|
|
185
|
+
{
|
|
186
|
+
"aria-label": name,
|
|
187
|
+
className: "w-full h-full inline-flex items-center justify-center bg-gradient-to-br from-primary to-accent font-semibold text-white leading-none select-none",
|
|
188
|
+
children: initials
|
|
189
|
+
}
|
|
190
|
+
)
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
);
|
|
194
|
+
});
|
|
195
|
+
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";
|
|
196
|
+
var sizeClass2 = {
|
|
61
197
|
sm: "px-3 py-2 text-sm",
|
|
62
198
|
md: "px-3.5 py-2.5 text-sm",
|
|
63
199
|
lg: "px-4 py-3 text-base"
|
|
64
200
|
};
|
|
65
201
|
var variantClass = {
|
|
66
|
-
primary: "bg-gradient-to-r from-violet-600 to-purple-600 text-white shadow-
|
|
67
|
-
success: "bg-emerald-600 text-white shadow-
|
|
68
|
-
warning: "bg-amber-600 text-white shadow-
|
|
69
|
-
danger: "bg-rose-600 text-white shadow-
|
|
202
|
+
primary: "bg-gradient-to-r from-violet-600 to-purple-600 text-white shadow-glow hover:shadow-glow-lg hover:scale-[1.02]",
|
|
203
|
+
success: "bg-emerald-600 text-white shadow-lg shadow-emerald-600/20 hover:bg-emerald-700",
|
|
204
|
+
warning: "bg-amber-600 text-white shadow-lg shadow-amber-600/20 hover:bg-amber-700",
|
|
205
|
+
danger: "bg-rose-600 text-white shadow-lg shadow-rose-600/20 hover:bg-rose-700",
|
|
70
206
|
secondary: "text-white bg-white/5 ring-1 ring-white/10 hover:bg-white/10 hover:ring-white/20 backdrop-blur-sm",
|
|
71
207
|
ghost: "text-white/70 hover:text-white hover:bg-white/5"
|
|
72
208
|
};
|
|
@@ -78,7 +214,8 @@ var Button = forwardRef(function Button2({ variant = "secondary", size = "md", l
|
|
|
78
214
|
type: type === "submit" ? "submit" : type === "reset" ? "reset" : "button",
|
|
79
215
|
...props,
|
|
80
216
|
disabled: disabled || loading,
|
|
81
|
-
|
|
217
|
+
...loading ? { "aria-busy": true } : {},
|
|
218
|
+
className: cn(base, sizeClass2[size], variantClass[variant], className),
|
|
82
219
|
children: loading ? /* @__PURE__ */ jsxs("span", { className: "flex items-center justify-center gap-2", children: [
|
|
83
220
|
/* @__PURE__ */ jsx("span", { className: "w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
|
|
84
221
|
children
|
|
@@ -91,16 +228,16 @@ var Button = forwardRef(function Button2({ variant = "secondary", size = "md", l
|
|
|
91
228
|
);
|
|
92
229
|
});
|
|
93
230
|
var base2 = "inline-flex items-center justify-center 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";
|
|
94
|
-
var
|
|
231
|
+
var sizeClass3 = {
|
|
95
232
|
sm: "p-1.5 w-7 h-7",
|
|
96
233
|
md: "p-2 w-9 h-9",
|
|
97
234
|
lg: "p-2.5 w-11 h-11"
|
|
98
235
|
};
|
|
99
236
|
var variantClass2 = {
|
|
100
237
|
primary: "bg-primary text-white shadow-glow hover:brightness-[0.98]",
|
|
101
|
-
success: "bg-emerald-600 text-white shadow-
|
|
102
|
-
warning: "bg-amber-600 text-white shadow-
|
|
103
|
-
danger: "bg-rose-600 text-white shadow-
|
|
238
|
+
success: "bg-emerald-600 text-white shadow-lg shadow-emerald-600/20 hover:bg-emerald-700",
|
|
239
|
+
warning: "bg-amber-600 text-white shadow-lg shadow-amber-600/20 hover:bg-amber-700",
|
|
240
|
+
danger: "bg-rose-600 text-white shadow-lg shadow-rose-600/20 hover:bg-rose-700",
|
|
104
241
|
secondary: "text-white bg-white/10 shadow-sm ring-1 ring-white/10",
|
|
105
242
|
ghost: "text-white hover:bg-white/10"
|
|
106
243
|
};
|
|
@@ -112,78 +249,284 @@ var IconButton = forwardRef(function IconButton2({ icon, variant = "ghost", size
|
|
|
112
249
|
type: type === "submit" ? "submit" : type === "reset" ? "reset" : "button",
|
|
113
250
|
...props,
|
|
114
251
|
disabled,
|
|
115
|
-
className: cn(base2,
|
|
252
|
+
className: cn(base2, sizeClass3[size], variantClass2[variant], className),
|
|
116
253
|
children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: icon })
|
|
117
254
|
}
|
|
118
255
|
);
|
|
119
256
|
});
|
|
120
|
-
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";
|
|
257
|
+
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";
|
|
121
258
|
var Input = forwardRef(function Input2({ hasError, label, error, helperText, className, id: externalId, ...props }, ref) {
|
|
122
259
|
const generatedId = useId();
|
|
123
260
|
const inputId = externalId || generatedId;
|
|
124
261
|
const showError = hasError || !!error;
|
|
262
|
+
const errorId = error ? `${inputId}-error` : void 0;
|
|
263
|
+
const helperId = helperText && !error ? `${inputId}-helper` : void 0;
|
|
125
264
|
const input = /* @__PURE__ */ jsx(
|
|
126
265
|
"input",
|
|
127
266
|
{
|
|
267
|
+
...props,
|
|
128
268
|
ref,
|
|
129
269
|
id: inputId,
|
|
130
|
-
|
|
270
|
+
"aria-invalid": showError || void 0,
|
|
271
|
+
"aria-describedby": errorId || helperId || void 0,
|
|
131
272
|
className: cn(inputBase, showError && "ring-2 ring-rose-500/40 focus:ring-rose-500/40", className)
|
|
132
273
|
}
|
|
133
274
|
);
|
|
134
275
|
if (!label && !error && !helperText) return input;
|
|
135
276
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
136
|
-
label && /* @__PURE__ */ jsx("label", { htmlFor: inputId, className: "block text-sm text-white/
|
|
277
|
+
label && /* @__PURE__ */ jsx("label", { htmlFor: inputId, className: "block text-sm text-white/70 mb-1.5", children: label }),
|
|
137
278
|
input,
|
|
138
|
-
error && /* @__PURE__ */ jsx("p", { className: "text-rose-400 text-xs mt-1", children: error }),
|
|
139
|
-
helperText && !error && /* @__PURE__ */ jsx("p", { className: "text-white/40 text-xs mt-1", children: helperText })
|
|
279
|
+
error && /* @__PURE__ */ jsx("p", { id: errorId, className: "text-rose-400 text-xs mt-1", children: error }),
|
|
280
|
+
helperText && !error && /* @__PURE__ */ jsx("p", { id: helperId, className: "text-white/40 text-xs mt-1", children: helperText })
|
|
281
|
+
] });
|
|
282
|
+
});
|
|
283
|
+
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";
|
|
284
|
+
var SearchInput = forwardRef(function SearchInput2({ label, onClear, className, id: externalId, placeholder = "Search...", value, ...props }, ref) {
|
|
285
|
+
const generatedId = useId();
|
|
286
|
+
const inputId = externalId || generatedId;
|
|
287
|
+
const hasValue = value !== void 0 && value !== "";
|
|
288
|
+
const wrapper = /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
289
|
+
/* @__PURE__ */ jsx("span", { className: "absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-white/40", children: /* @__PURE__ */ jsxs(
|
|
290
|
+
"svg",
|
|
291
|
+
{
|
|
292
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
293
|
+
width: "16",
|
|
294
|
+
height: "16",
|
|
295
|
+
viewBox: "0 0 24 24",
|
|
296
|
+
fill: "none",
|
|
297
|
+
stroke: "currentColor",
|
|
298
|
+
strokeWidth: "2",
|
|
299
|
+
strokeLinecap: "round",
|
|
300
|
+
strokeLinejoin: "round",
|
|
301
|
+
"aria-hidden": "true",
|
|
302
|
+
children: [
|
|
303
|
+
/* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "8" }),
|
|
304
|
+
/* @__PURE__ */ jsx("line", { x1: "21", y1: "21", x2: "16.65", y2: "16.65" })
|
|
305
|
+
]
|
|
306
|
+
}
|
|
307
|
+
) }),
|
|
308
|
+
/* @__PURE__ */ jsx(
|
|
309
|
+
"input",
|
|
310
|
+
{
|
|
311
|
+
ref,
|
|
312
|
+
id: inputId,
|
|
313
|
+
type: "search",
|
|
314
|
+
value,
|
|
315
|
+
placeholder,
|
|
316
|
+
...props,
|
|
317
|
+
className: cn(inputBase2, hasValue && "pr-9", className)
|
|
318
|
+
}
|
|
319
|
+
),
|
|
320
|
+
hasValue && onClear && /* @__PURE__ */ jsx(
|
|
321
|
+
"button",
|
|
322
|
+
{
|
|
323
|
+
type: "button",
|
|
324
|
+
onClick: onClear,
|
|
325
|
+
"aria-label": "Clear search",
|
|
326
|
+
className: "absolute right-3 top-1/2 -translate-y-1/2 text-white/40 hover:text-white transition-colors",
|
|
327
|
+
children: /* @__PURE__ */ jsxs(
|
|
328
|
+
"svg",
|
|
329
|
+
{
|
|
330
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
331
|
+
width: "14",
|
|
332
|
+
height: "14",
|
|
333
|
+
viewBox: "0 0 24 24",
|
|
334
|
+
fill: "none",
|
|
335
|
+
stroke: "currentColor",
|
|
336
|
+
strokeWidth: "2",
|
|
337
|
+
strokeLinecap: "round",
|
|
338
|
+
strokeLinejoin: "round",
|
|
339
|
+
"aria-hidden": "true",
|
|
340
|
+
children: [
|
|
341
|
+
/* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
342
|
+
/* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
343
|
+
]
|
|
344
|
+
}
|
|
345
|
+
)
|
|
346
|
+
}
|
|
347
|
+
)
|
|
348
|
+
] });
|
|
349
|
+
if (!label) return wrapper;
|
|
350
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
351
|
+
/* @__PURE__ */ jsx("label", { htmlFor: inputId, className: "block text-sm text-white/70 mb-1.5", children: label }),
|
|
352
|
+
wrapper
|
|
140
353
|
] });
|
|
141
354
|
});
|
|
142
|
-
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";
|
|
143
|
-
var Select = forwardRef(function Select2({ hasError, label, error, className, id: externalId, children, ...props }, ref) {
|
|
355
|
+
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";
|
|
356
|
+
var Select = forwardRef(function Select2({ hasError, label, error, helperText, className, id: externalId, children, ...props }, ref) {
|
|
144
357
|
const generatedId = useId();
|
|
145
358
|
const selectId = externalId || generatedId;
|
|
146
359
|
const showError = hasError || !!error;
|
|
360
|
+
const errorId = error ? `${selectId}-error` : void 0;
|
|
361
|
+
const helperId = helperText && !error ? `${selectId}-helper` : void 0;
|
|
147
362
|
const select = /* @__PURE__ */ jsx(
|
|
148
363
|
"select",
|
|
149
364
|
{
|
|
365
|
+
...props,
|
|
150
366
|
ref,
|
|
151
367
|
id: selectId,
|
|
152
|
-
|
|
368
|
+
"aria-invalid": showError || void 0,
|
|
369
|
+
"aria-describedby": errorId || helperId || void 0,
|
|
153
370
|
className: cn(selectBase, showError && "ring-2 ring-rose-500/40 focus:ring-rose-500/40", className),
|
|
154
371
|
children
|
|
155
372
|
}
|
|
156
373
|
);
|
|
157
|
-
if (!label && !error) return select;
|
|
374
|
+
if (!label && !error && !helperText) return select;
|
|
158
375
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
159
|
-
label && /* @__PURE__ */ jsx("label", { htmlFor: selectId, className: "block text-sm text-white/
|
|
376
|
+
label && /* @__PURE__ */ jsx("label", { htmlFor: selectId, className: "block text-sm text-white/70 mb-1.5", children: label }),
|
|
160
377
|
select,
|
|
161
|
-
error && /* @__PURE__ */ jsx("p", { className: "text-rose-400 text-xs mt-1", children: error })
|
|
378
|
+
error && /* @__PURE__ */ jsx("p", { id: errorId, className: "text-rose-400 text-xs mt-1", children: error }),
|
|
379
|
+
helperText && !error && /* @__PURE__ */ jsx("p", { id: helperId, className: "text-white/40 text-xs mt-1", children: helperText })
|
|
162
380
|
] });
|
|
163
381
|
});
|
|
164
|
-
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";
|
|
165
|
-
var Textarea = forwardRef(function Textarea2({ hasError, label, error, className, id: externalId, ...props }, ref) {
|
|
382
|
+
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";
|
|
383
|
+
var Textarea = forwardRef(function Textarea2({ hasError, label, error, helperText, className, id: externalId, ...props }, ref) {
|
|
166
384
|
const generatedId = useId();
|
|
167
385
|
const textareaId = externalId || generatedId;
|
|
168
386
|
const showError = hasError || !!error;
|
|
387
|
+
const errorId = error ? `${textareaId}-error` : void 0;
|
|
388
|
+
const helperId = helperText && !error ? `${textareaId}-helper` : void 0;
|
|
169
389
|
const textarea = /* @__PURE__ */ jsx(
|
|
170
390
|
"textarea",
|
|
171
391
|
{
|
|
392
|
+
...props,
|
|
172
393
|
ref,
|
|
173
394
|
id: textareaId,
|
|
174
|
-
|
|
395
|
+
"aria-invalid": showError || void 0,
|
|
396
|
+
"aria-describedby": errorId || helperId || void 0,
|
|
175
397
|
className: cn(textareaBase, showError && "ring-2 ring-rose-500/40 focus:ring-rose-500/40", className)
|
|
176
398
|
}
|
|
177
399
|
);
|
|
178
|
-
if (!label && !error) return textarea;
|
|
400
|
+
if (!label && !error && !helperText) return textarea;
|
|
179
401
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
180
|
-
label && /* @__PURE__ */ jsx("label", { htmlFor: textareaId, className: "block text-sm text-white/
|
|
402
|
+
label && /* @__PURE__ */ jsx("label", { htmlFor: textareaId, className: "block text-sm text-white/70 mb-1.5", children: label }),
|
|
181
403
|
textarea,
|
|
182
|
-
error && /* @__PURE__ */ jsx("p", { className: "text-rose-400 text-xs mt-1", children: error })
|
|
404
|
+
error && /* @__PURE__ */ jsx("p", { id: errorId, className: "text-rose-400 text-xs mt-1", children: error }),
|
|
405
|
+
helperText && !error && /* @__PURE__ */ jsx("p", { id: helperId, className: "text-white/40 text-xs mt-1", children: helperText })
|
|
183
406
|
] });
|
|
184
407
|
});
|
|
408
|
+
function TagInput({
|
|
409
|
+
value,
|
|
410
|
+
onChange,
|
|
411
|
+
placeholder,
|
|
412
|
+
disabled = false,
|
|
413
|
+
label,
|
|
414
|
+
error,
|
|
415
|
+
maxTags,
|
|
416
|
+
className,
|
|
417
|
+
id: externalId
|
|
418
|
+
}) {
|
|
419
|
+
const generatedId = useId();
|
|
420
|
+
const inputId = externalId ?? generatedId;
|
|
421
|
+
const errorId = error ? `${inputId}-error` : void 0;
|
|
422
|
+
const inputRef = useRef(null);
|
|
423
|
+
const atMax = maxTags !== void 0 && value.length >= maxTags;
|
|
424
|
+
function addTag(raw) {
|
|
425
|
+
const tag = raw.trim();
|
|
426
|
+
if (!tag || value.includes(tag) || atMax) return;
|
|
427
|
+
onChange([...value, tag]);
|
|
428
|
+
}
|
|
429
|
+
function removeTag(index) {
|
|
430
|
+
onChange(value.filter((_, i) => i !== index));
|
|
431
|
+
}
|
|
432
|
+
function handleKeyDown(e) {
|
|
433
|
+
const input = inputRef.current;
|
|
434
|
+
if (!input) return;
|
|
435
|
+
if (e.key === "Enter" || e.key === "," || e.key === "Tab") {
|
|
436
|
+
if (input.value) {
|
|
437
|
+
e.preventDefault();
|
|
438
|
+
addTag(input.value);
|
|
439
|
+
input.value = "";
|
|
440
|
+
}
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
if (e.key === "Backspace" && !input.value && value.length > 0) {
|
|
444
|
+
removeTag(value.length - 1);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
function handlePaste(e) {
|
|
448
|
+
const pasted = e.clipboardData.getData("text");
|
|
449
|
+
if (!pasted.includes(",")) return;
|
|
450
|
+
e.preventDefault();
|
|
451
|
+
const parts = pasted.split(",");
|
|
452
|
+
const next = [...value];
|
|
453
|
+
for (const part of parts) {
|
|
454
|
+
const tag = part.trim();
|
|
455
|
+
if (tag && !next.includes(tag)) {
|
|
456
|
+
if (maxTags !== void 0 && next.length >= maxTags) break;
|
|
457
|
+
next.push(tag);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
onChange(next);
|
|
461
|
+
if (inputRef.current) inputRef.current.value = "";
|
|
462
|
+
}
|
|
463
|
+
const wrapper = /* @__PURE__ */ jsxs(
|
|
464
|
+
"div",
|
|
465
|
+
{
|
|
466
|
+
className: cn(
|
|
467
|
+
"flex flex-wrap items-center gap-1.5 min-h-[42px] w-full rounded-xl px-3 py-2 bg-white/10 ring-1 ring-white/10 transition-shadow focus-within:ring-2 focus-within:ring-primary/40",
|
|
468
|
+
error && "ring-2 ring-rose-500/40 focus-within:ring-rose-500/40",
|
|
469
|
+
disabled && "opacity-50 cursor-not-allowed",
|
|
470
|
+
className
|
|
471
|
+
),
|
|
472
|
+
onClick: () => inputRef.current?.focus(),
|
|
473
|
+
children: [
|
|
474
|
+
value.map((tag, i) => /* @__PURE__ */ jsxs(
|
|
475
|
+
"span",
|
|
476
|
+
{
|
|
477
|
+
className: "inline-flex items-center gap-1 bg-white/10 text-white/90 rounded-full text-xs px-2.5 py-1 ring-1 ring-white/10",
|
|
478
|
+
children: [
|
|
479
|
+
tag,
|
|
480
|
+
!disabled && /* @__PURE__ */ jsx(
|
|
481
|
+
"button",
|
|
482
|
+
{
|
|
483
|
+
type: "button",
|
|
484
|
+
"aria-label": `Remove ${tag}`,
|
|
485
|
+
onClick: (e) => {
|
|
486
|
+
e.stopPropagation();
|
|
487
|
+
removeTag(i);
|
|
488
|
+
},
|
|
489
|
+
className: "text-white/50 hover:text-white/90 transition-colors leading-none",
|
|
490
|
+
children: "\u2715"
|
|
491
|
+
}
|
|
492
|
+
)
|
|
493
|
+
]
|
|
494
|
+
},
|
|
495
|
+
`${tag}-${i}`
|
|
496
|
+
)),
|
|
497
|
+
/* @__PURE__ */ jsx(
|
|
498
|
+
"input",
|
|
499
|
+
{
|
|
500
|
+
ref: inputRef,
|
|
501
|
+
id: inputId,
|
|
502
|
+
type: "text",
|
|
503
|
+
disabled: disabled || atMax,
|
|
504
|
+
placeholder: value.length === 0 ? placeholder : void 0,
|
|
505
|
+
"aria-invalid": !!error || void 0,
|
|
506
|
+
"aria-describedby": errorId,
|
|
507
|
+
onKeyDown: handleKeyDown,
|
|
508
|
+
onPaste: handlePaste,
|
|
509
|
+
onBlur: (e) => {
|
|
510
|
+
if (e.target.value) {
|
|
511
|
+
addTag(e.target.value);
|
|
512
|
+
e.target.value = "";
|
|
513
|
+
}
|
|
514
|
+
},
|
|
515
|
+
className: "flex-1 min-w-[120px] bg-transparent text-sm text-white outline-none placeholder-white/30 disabled:cursor-not-allowed"
|
|
516
|
+
}
|
|
517
|
+
)
|
|
518
|
+
]
|
|
519
|
+
}
|
|
520
|
+
);
|
|
521
|
+
if (!label && !error) return wrapper;
|
|
522
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
523
|
+
label && /* @__PURE__ */ jsx("label", { htmlFor: inputId, className: "block text-sm text-white/70 mb-1.5", children: label }),
|
|
524
|
+
wrapper,
|
|
525
|
+
error && /* @__PURE__ */ jsx("p", { id: errorId, className: "text-rose-400 text-xs mt-1", children: error })
|
|
526
|
+
] });
|
|
527
|
+
}
|
|
185
528
|
var base3 = "inline-flex items-center justify-center rounded-full font-semibold ring-1 ring-white/10";
|
|
186
|
-
var
|
|
529
|
+
var sizeClass4 = {
|
|
187
530
|
sm: "text-xs px-2.5 py-1",
|
|
188
531
|
md: "text-sm px-3 py-1.5"
|
|
189
532
|
};
|
|
@@ -198,7 +541,7 @@ var variantClass3 = {
|
|
|
198
541
|
accent: "bg-accent/15 text-accent-light ring-accent/20"
|
|
199
542
|
};
|
|
200
543
|
var Badge = forwardRef(function Badge2({ children, variant = "neutral", size = "sm", className, ...props }, ref) {
|
|
201
|
-
return /* @__PURE__ */ jsx("span", { ref, ...props, className: cn(base3,
|
|
544
|
+
return /* @__PURE__ */ jsx("span", { ref, ...props, className: cn(base3, sizeClass4[size], variantClass3[variant], className), children });
|
|
202
545
|
});
|
|
203
546
|
var Pill = Badge;
|
|
204
547
|
var trackSize = {
|
|
@@ -209,11 +552,15 @@ var thumbSize = {
|
|
|
209
552
|
sm: { base: "w-4 h-4", translate: "translate-x-4" },
|
|
210
553
|
md: { base: "w-5 h-5", translate: "translate-x-5" }
|
|
211
554
|
};
|
|
212
|
-
|
|
555
|
+
var Toggle = forwardRef(function Toggle2({ checked, onChange, disabled, label, size = "md", id: externalId, "aria-label": ariaLabel }, ref) {
|
|
556
|
+
const generatedId = useId();
|
|
557
|
+
const toggleId = externalId || generatedId;
|
|
213
558
|
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
214
559
|
/* @__PURE__ */ jsx(
|
|
215
560
|
"button",
|
|
216
561
|
{
|
|
562
|
+
ref,
|
|
563
|
+
id: toggleId,
|
|
217
564
|
type: "button",
|
|
218
565
|
role: "switch",
|
|
219
566
|
"aria-checked": checked,
|
|
@@ -238,20 +585,415 @@ function Toggle({ checked, onChange, disabled, label, size = "md", "aria-label":
|
|
|
238
585
|
)
|
|
239
586
|
}
|
|
240
587
|
),
|
|
241
|
-
label && /* @__PURE__ */ jsx("
|
|
588
|
+
label && /* @__PURE__ */ jsx("label", { htmlFor: toggleId, className: "text-sm text-white/60 cursor-pointer", children: label })
|
|
589
|
+
] });
|
|
590
|
+
});
|
|
591
|
+
var Slider = forwardRef(function Slider2({
|
|
592
|
+
label,
|
|
593
|
+
showValue = false,
|
|
594
|
+
formatValue,
|
|
595
|
+
onChange,
|
|
596
|
+
disabled,
|
|
597
|
+
min = 0,
|
|
598
|
+
max = 100,
|
|
599
|
+
step = 1,
|
|
600
|
+
value,
|
|
601
|
+
defaultValue,
|
|
602
|
+
className,
|
|
603
|
+
id: externalId,
|
|
604
|
+
...props
|
|
605
|
+
}, ref) {
|
|
606
|
+
const generatedId = useId();
|
|
607
|
+
const sliderId = externalId || generatedId;
|
|
608
|
+
const numericValue = useMemo(() => {
|
|
609
|
+
const raw = value !== void 0 ? value : defaultValue;
|
|
610
|
+
return raw !== void 0 ? Number(raw) : Number(min);
|
|
611
|
+
}, [value, defaultValue, min]);
|
|
612
|
+
const percentage = useMemo(() => {
|
|
613
|
+
const numMin = Number(min);
|
|
614
|
+
const numMax = Number(max);
|
|
615
|
+
if (numMax === numMin) return 0;
|
|
616
|
+
return Math.max(0, Math.min(100, (numericValue - numMin) / (numMax - numMin) * 100));
|
|
617
|
+
}, [numericValue, min, max]);
|
|
618
|
+
const trackGradient = `linear-gradient(to right, var(--ml-primary, #8b5cf6) ${percentage}%, rgba(255,255,255,0.1) ${percentage}%)`;
|
|
619
|
+
const displayValue = formatValue ? formatValue(numericValue) : String(numericValue);
|
|
620
|
+
const handleChange = (e) => {
|
|
621
|
+
onChange?.(Number(e.target.value));
|
|
622
|
+
};
|
|
623
|
+
const inputEl = /* @__PURE__ */ jsx(
|
|
624
|
+
"input",
|
|
625
|
+
{
|
|
626
|
+
...props,
|
|
627
|
+
ref,
|
|
628
|
+
id: sliderId,
|
|
629
|
+
type: "range",
|
|
630
|
+
min,
|
|
631
|
+
max,
|
|
632
|
+
step,
|
|
633
|
+
value,
|
|
634
|
+
defaultValue: value === void 0 ? defaultValue ?? Number(min) : void 0,
|
|
635
|
+
disabled,
|
|
636
|
+
onChange: handleChange,
|
|
637
|
+
style: { background: trackGradient },
|
|
638
|
+
className: cn(
|
|
639
|
+
// Layout + reset
|
|
640
|
+
"w-full h-2 appearance-none rounded-full outline-none cursor-pointer",
|
|
641
|
+
// bg is set via inline style (trackGradient); keep transparent fallback for SSR
|
|
642
|
+
"bg-white/10",
|
|
643
|
+
// Webkit thumb
|
|
644
|
+
"[&::-webkit-slider-thumb]:appearance-none",
|
|
645
|
+
"[&::-webkit-slider-thumb]:w-4",
|
|
646
|
+
"[&::-webkit-slider-thumb]:h-4",
|
|
647
|
+
"[&::-webkit-slider-thumb]:rounded-full",
|
|
648
|
+
"[&::-webkit-slider-thumb]:bg-white",
|
|
649
|
+
"[&::-webkit-slider-thumb]:border-2",
|
|
650
|
+
"[&::-webkit-slider-thumb]:border-[var(--ml-primary,#8b5cf6)]",
|
|
651
|
+
"[&::-webkit-slider-thumb]:transition-shadow",
|
|
652
|
+
"[&::-webkit-slider-thumb]:duration-150",
|
|
653
|
+
// Moz thumb
|
|
654
|
+
"[&::-moz-range-thumb]:w-4",
|
|
655
|
+
"[&::-moz-range-thumb]:h-4",
|
|
656
|
+
"[&::-moz-range-thumb]:rounded-full",
|
|
657
|
+
"[&::-moz-range-thumb]:bg-white",
|
|
658
|
+
"[&::-moz-range-thumb]:border-2",
|
|
659
|
+
"[&::-moz-range-thumb]:border-[var(--ml-primary,#8b5cf6)]",
|
|
660
|
+
"[&::-moz-range-thumb]:transition-shadow",
|
|
661
|
+
"[&::-moz-range-thumb]:duration-150",
|
|
662
|
+
// Moz track — transparent so the gradient on the element shows through
|
|
663
|
+
"[&::-moz-range-track]:bg-transparent",
|
|
664
|
+
"[&::-moz-range-track]:rounded-full",
|
|
665
|
+
// Focus ring on thumb
|
|
666
|
+
"focus-visible:outline-none",
|
|
667
|
+
"focus-visible:[&::-webkit-slider-thumb]:ring-2",
|
|
668
|
+
"focus-visible:[&::-webkit-slider-thumb]:ring-primary/40",
|
|
669
|
+
"focus-visible:[&::-webkit-slider-thumb]:ring-offset-2",
|
|
670
|
+
"focus-visible:[&::-webkit-slider-thumb]:ring-offset-surface",
|
|
671
|
+
"focus-visible:[&::-moz-range-thumb]:ring-2",
|
|
672
|
+
"focus-visible:[&::-moz-range-thumb]:ring-primary/40",
|
|
673
|
+
// Disabled
|
|
674
|
+
disabled && "opacity-50 cursor-not-allowed",
|
|
675
|
+
className
|
|
676
|
+
)
|
|
677
|
+
}
|
|
678
|
+
);
|
|
679
|
+
if (!label && !showValue) return inputEl;
|
|
680
|
+
return /* @__PURE__ */ jsxs("div", { className: "w-full space-y-1.5", children: [
|
|
681
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
682
|
+
label && /* @__PURE__ */ jsx("label", { htmlFor: sliderId, className: "text-sm text-white/70", children: label }),
|
|
683
|
+
showValue && /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-white/90 tabular-nums ml-auto", children: displayValue })
|
|
684
|
+
] }),
|
|
685
|
+
inputEl
|
|
686
|
+
] });
|
|
687
|
+
});
|
|
688
|
+
var ColorInput = forwardRef(function ColorInput2({ value, onChange, label, disabled, className, id: idProp }, ref) {
|
|
689
|
+
const autoId = useId();
|
|
690
|
+
const id = idProp ?? autoId;
|
|
691
|
+
const nativePickerRef = useRef(null);
|
|
692
|
+
function handleSwatchClick() {
|
|
693
|
+
if (!disabled) {
|
|
694
|
+
nativePickerRef.current?.click();
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
function handleTextChange(e) {
|
|
698
|
+
onChange(e.target.value);
|
|
699
|
+
}
|
|
700
|
+
function handleNativeChange(e) {
|
|
701
|
+
onChange(e.target.value);
|
|
702
|
+
}
|
|
703
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1.5", className), children: [
|
|
704
|
+
label ? /* @__PURE__ */ jsx("label", { htmlFor: id, className: "block text-sm text-white/70", children: label }) : null,
|
|
705
|
+
/* @__PURE__ */ jsxs(
|
|
706
|
+
"div",
|
|
707
|
+
{
|
|
708
|
+
className: cn(
|
|
709
|
+
"flex items-center gap-2 bg-white/10 rounded-xl ring-1 ring-white/10 px-3 py-2 transition-shadow",
|
|
710
|
+
"focus-within:ring-2 focus-within:ring-primary/40",
|
|
711
|
+
disabled && "opacity-60 pointer-events-none"
|
|
712
|
+
),
|
|
713
|
+
children: [
|
|
714
|
+
/* @__PURE__ */ jsx(
|
|
715
|
+
"input",
|
|
716
|
+
{
|
|
717
|
+
type: "color",
|
|
718
|
+
ref: nativePickerRef,
|
|
719
|
+
value,
|
|
720
|
+
onChange: handleNativeChange,
|
|
721
|
+
disabled,
|
|
722
|
+
"aria-hidden": "true",
|
|
723
|
+
tabIndex: -1,
|
|
724
|
+
className: "sr-only"
|
|
725
|
+
}
|
|
726
|
+
),
|
|
727
|
+
/* @__PURE__ */ jsx(
|
|
728
|
+
"button",
|
|
729
|
+
{
|
|
730
|
+
type: "button",
|
|
731
|
+
onClick: handleSwatchClick,
|
|
732
|
+
disabled,
|
|
733
|
+
"aria-label": "Open color picker",
|
|
734
|
+
className: cn(
|
|
735
|
+
"w-8 h-8 rounded-lg border border-white/20 flex-shrink-0 transition-transform hover:scale-105 active:scale-95 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60"
|
|
736
|
+
),
|
|
737
|
+
style: { backgroundColor: value }
|
|
738
|
+
}
|
|
739
|
+
),
|
|
740
|
+
/* @__PURE__ */ jsx(
|
|
741
|
+
"input",
|
|
742
|
+
{
|
|
743
|
+
ref,
|
|
744
|
+
id,
|
|
745
|
+
type: "text",
|
|
746
|
+
value,
|
|
747
|
+
onChange: handleTextChange,
|
|
748
|
+
disabled,
|
|
749
|
+
spellCheck: false,
|
|
750
|
+
className: "flex-1 min-w-0 bg-transparent text-sm text-white placeholder:text-white/30 outline-none font-mono",
|
|
751
|
+
placeholder: "#000000"
|
|
752
|
+
}
|
|
753
|
+
)
|
|
754
|
+
]
|
|
755
|
+
}
|
|
756
|
+
)
|
|
757
|
+
] });
|
|
758
|
+
});
|
|
759
|
+
var Checkbox = forwardRef(function Checkbox2({ label, error, indeterminate, className, id: externalId, disabled, checked, onChange, ...props }, ref) {
|
|
760
|
+
const generatedId = useId();
|
|
761
|
+
const inputId = externalId || generatedId;
|
|
762
|
+
const errorId = error ? `${inputId}-error` : void 0;
|
|
763
|
+
const internalRef = useRef(null);
|
|
764
|
+
useEffect(() => {
|
|
765
|
+
const el = internalRef.current;
|
|
766
|
+
if (el) {
|
|
767
|
+
el.indeterminate = indeterminate ?? false;
|
|
768
|
+
}
|
|
769
|
+
}, [indeterminate]);
|
|
770
|
+
const setRefs = (el) => {
|
|
771
|
+
internalRef.current = el;
|
|
772
|
+
if (typeof ref === "function") {
|
|
773
|
+
ref(el);
|
|
774
|
+
} else if (ref) {
|
|
775
|
+
ref.current = el;
|
|
776
|
+
}
|
|
777
|
+
};
|
|
778
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("inline-flex flex-col gap-1", className), children: [
|
|
779
|
+
/* @__PURE__ */ jsxs(
|
|
780
|
+
"label",
|
|
781
|
+
{
|
|
782
|
+
htmlFor: inputId,
|
|
783
|
+
className: cn(
|
|
784
|
+
"inline-flex items-center gap-2.5",
|
|
785
|
+
disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"
|
|
786
|
+
),
|
|
787
|
+
children: [
|
|
788
|
+
/* @__PURE__ */ jsx(
|
|
789
|
+
"input",
|
|
790
|
+
{
|
|
791
|
+
...props,
|
|
792
|
+
ref: setRefs,
|
|
793
|
+
id: inputId,
|
|
794
|
+
type: "checkbox",
|
|
795
|
+
checked,
|
|
796
|
+
onChange,
|
|
797
|
+
disabled,
|
|
798
|
+
"aria-invalid": !!error || void 0,
|
|
799
|
+
"aria-describedby": errorId,
|
|
800
|
+
className: "sr-only peer"
|
|
801
|
+
}
|
|
802
|
+
),
|
|
803
|
+
/* @__PURE__ */ jsxs(
|
|
804
|
+
"span",
|
|
805
|
+
{
|
|
806
|
+
className: cn(
|
|
807
|
+
"relative flex items-center justify-center w-5 h-5 rounded-md",
|
|
808
|
+
"bg-white/10 border border-white/20",
|
|
809
|
+
"transition-colors duration-150",
|
|
810
|
+
"peer-focus-visible:ring-2 peer-focus-visible:ring-primary/40 peer-focus-visible:ring-offset-1 peer-focus-visible:ring-offset-transparent",
|
|
811
|
+
checked && !indeterminate && "bg-primary border-primary",
|
|
812
|
+
indeterminate && "bg-primary border-primary"
|
|
813
|
+
),
|
|
814
|
+
"aria-hidden": "true",
|
|
815
|
+
children: [
|
|
816
|
+
checked && !indeterminate && /* @__PURE__ */ jsx(
|
|
817
|
+
"svg",
|
|
818
|
+
{
|
|
819
|
+
viewBox: "0 0 12 10",
|
|
820
|
+
fill: "none",
|
|
821
|
+
stroke: "white",
|
|
822
|
+
strokeWidth: "1.8",
|
|
823
|
+
strokeLinecap: "round",
|
|
824
|
+
strokeLinejoin: "round",
|
|
825
|
+
className: "w-3 h-2.5",
|
|
826
|
+
children: /* @__PURE__ */ jsx("polyline", { points: "1,5 4.5,8.5 11,1" })
|
|
827
|
+
}
|
|
828
|
+
),
|
|
829
|
+
indeterminate && /* @__PURE__ */ jsx(
|
|
830
|
+
"svg",
|
|
831
|
+
{
|
|
832
|
+
viewBox: "0 0 10 2",
|
|
833
|
+
fill: "none",
|
|
834
|
+
stroke: "white",
|
|
835
|
+
strokeWidth: "2",
|
|
836
|
+
strokeLinecap: "round",
|
|
837
|
+
className: "w-2.5 h-0.5",
|
|
838
|
+
children: /* @__PURE__ */ jsx("line", { x1: "0", y1: "1", x2: "10", y2: "1" })
|
|
839
|
+
}
|
|
840
|
+
)
|
|
841
|
+
]
|
|
842
|
+
}
|
|
843
|
+
),
|
|
844
|
+
label && /* @__PURE__ */ jsx("span", { className: "text-sm text-white/80 select-none", children: label })
|
|
845
|
+
]
|
|
846
|
+
}
|
|
847
|
+
),
|
|
848
|
+
error && /* @__PURE__ */ jsx("p", { id: errorId, className: "text-rose-400 text-xs ml-7", children: error })
|
|
242
849
|
] });
|
|
850
|
+
});
|
|
851
|
+
var RadioGroupContext = createContext(null);
|
|
852
|
+
function useRadioGroup() {
|
|
853
|
+
const ctx = useContext(RadioGroupContext);
|
|
854
|
+
if (!ctx) {
|
|
855
|
+
throw new Error("RadioItem must be used inside a RadioGroup");
|
|
856
|
+
}
|
|
857
|
+
return ctx;
|
|
243
858
|
}
|
|
244
|
-
|
|
859
|
+
function RadioGroup({
|
|
860
|
+
value: controlledValue,
|
|
861
|
+
defaultValue,
|
|
862
|
+
onValueChange,
|
|
863
|
+
name: externalName,
|
|
864
|
+
disabled = false,
|
|
865
|
+
orientation = "vertical",
|
|
866
|
+
label,
|
|
867
|
+
error,
|
|
868
|
+
children,
|
|
869
|
+
className
|
|
870
|
+
}) {
|
|
871
|
+
const groupId = useId();
|
|
872
|
+
const generatedName = useId();
|
|
873
|
+
const name = externalName ?? generatedName;
|
|
874
|
+
const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
|
|
875
|
+
const isControlled = controlledValue !== void 0;
|
|
876
|
+
const currentValue = isControlled ? controlledValue : uncontrolledValue;
|
|
877
|
+
const handleChange = useCallback(
|
|
878
|
+
(val) => {
|
|
879
|
+
if (!isControlled) setUncontrolledValue(val);
|
|
880
|
+
onValueChange?.(val);
|
|
881
|
+
},
|
|
882
|
+
[isControlled, onValueChange]
|
|
883
|
+
);
|
|
884
|
+
const labelId = label ? `${groupId}-label` : void 0;
|
|
885
|
+
const errorId = error ? `${groupId}-error` : void 0;
|
|
886
|
+
const ctxValue = useMemo(
|
|
887
|
+
() => ({ value: currentValue, onChange: handleChange, name, disabled, groupId }),
|
|
888
|
+
[currentValue, handleChange, name, disabled, groupId]
|
|
889
|
+
);
|
|
890
|
+
return /* @__PURE__ */ jsx(RadioGroupContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsxs(
|
|
891
|
+
"fieldset",
|
|
892
|
+
{
|
|
893
|
+
role: "radiogroup",
|
|
894
|
+
"aria-labelledby": labelId,
|
|
895
|
+
"aria-describedby": errorId,
|
|
896
|
+
"aria-disabled": disabled || void 0,
|
|
897
|
+
className: cn("border-none p-0 m-0", className),
|
|
898
|
+
children: [
|
|
899
|
+
label && /* @__PURE__ */ jsx("legend", { id: labelId, className: "text-sm text-white/50 mb-2 float-none p-0", children: label }),
|
|
900
|
+
/* @__PURE__ */ jsx(
|
|
901
|
+
"div",
|
|
902
|
+
{
|
|
903
|
+
className: cn(
|
|
904
|
+
"flex gap-3",
|
|
905
|
+
orientation === "vertical" ? "flex-col" : "flex-row flex-wrap"
|
|
906
|
+
),
|
|
907
|
+
children
|
|
908
|
+
}
|
|
909
|
+
),
|
|
910
|
+
error && /* @__PURE__ */ jsx("p", { id: errorId, className: "text-rose-400 text-xs mt-1.5", children: error })
|
|
911
|
+
]
|
|
912
|
+
}
|
|
913
|
+
) });
|
|
914
|
+
}
|
|
915
|
+
function RadioItem({ value, disabled: itemDisabled, children, className }) {
|
|
916
|
+
const { value: groupValue, onChange, name, disabled: groupDisabled, groupId } = useRadioGroup();
|
|
917
|
+
const inputId = useId();
|
|
918
|
+
const inputRef = useRef(null);
|
|
919
|
+
const isDisabled = groupDisabled || itemDisabled;
|
|
920
|
+
const isSelected = groupValue === value;
|
|
921
|
+
const handleKeyDown = (e) => {
|
|
922
|
+
if (e.key !== "ArrowDown" && e.key !== "ArrowUp" && e.key !== "ArrowRight" && e.key !== "ArrowLeft") {
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
e.preventDefault();
|
|
926
|
+
const fieldset = inputRef.current?.closest("fieldset");
|
|
927
|
+
if (!fieldset) return;
|
|
928
|
+
const inputs = Array.from(
|
|
929
|
+
fieldset.querySelectorAll(`input[type="radio"][name="${name}"]:not(:disabled)`)
|
|
930
|
+
);
|
|
931
|
+
const currentIndex = inputs.indexOf(inputRef.current);
|
|
932
|
+
if (currentIndex === -1) return;
|
|
933
|
+
const forward = e.key === "ArrowDown" || e.key === "ArrowRight";
|
|
934
|
+
const nextIndex = forward ? (currentIndex + 1) % inputs.length : (currentIndex - 1 + inputs.length) % inputs.length;
|
|
935
|
+
const nextInput = inputs[nextIndex];
|
|
936
|
+
nextInput.focus();
|
|
937
|
+
onChange(nextInput.value);
|
|
938
|
+
};
|
|
939
|
+
return /* @__PURE__ */ jsxs(
|
|
940
|
+
"label",
|
|
941
|
+
{
|
|
942
|
+
htmlFor: inputId,
|
|
943
|
+
className: cn(
|
|
944
|
+
"inline-flex items-center gap-2.5",
|
|
945
|
+
isDisabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer",
|
|
946
|
+
className
|
|
947
|
+
),
|
|
948
|
+
children: [
|
|
949
|
+
/* @__PURE__ */ jsx(
|
|
950
|
+
"input",
|
|
951
|
+
{
|
|
952
|
+
ref: inputRef,
|
|
953
|
+
id: inputId,
|
|
954
|
+
type: "radio",
|
|
955
|
+
name,
|
|
956
|
+
value,
|
|
957
|
+
checked: isSelected,
|
|
958
|
+
disabled: isDisabled,
|
|
959
|
+
onChange: () => onChange(value),
|
|
960
|
+
onKeyDown: handleKeyDown,
|
|
961
|
+
className: "sr-only peer",
|
|
962
|
+
"aria-describedby": void 0
|
|
963
|
+
}
|
|
964
|
+
),
|
|
965
|
+
/* @__PURE__ */ jsx(
|
|
966
|
+
"span",
|
|
967
|
+
{
|
|
968
|
+
className: cn(
|
|
969
|
+
"relative flex items-center justify-center w-5 h-5 rounded-full",
|
|
970
|
+
"bg-white/10 border border-white/20",
|
|
971
|
+
"transition-colors duration-150",
|
|
972
|
+
"peer-focus-visible:ring-2 peer-focus-visible:ring-primary/40 peer-focus-visible:ring-offset-1 peer-focus-visible:ring-offset-transparent",
|
|
973
|
+
isSelected && "border-primary"
|
|
974
|
+
),
|
|
975
|
+
"aria-hidden": "true",
|
|
976
|
+
children: isSelected && /* @__PURE__ */ jsx("span", { className: "w-2.5 h-2.5 rounded-full bg-primary" })
|
|
977
|
+
}
|
|
978
|
+
),
|
|
979
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-white/80 select-none", children })
|
|
980
|
+
]
|
|
981
|
+
}
|
|
982
|
+
);
|
|
983
|
+
}
|
|
984
|
+
var sizeClass5 = {
|
|
245
985
|
sm: "h-3 w-3 border",
|
|
246
986
|
md: "h-4 w-4 border-2",
|
|
247
987
|
lg: "h-6 w-6 border-2"
|
|
248
988
|
};
|
|
249
|
-
function Spinner({ className, size = "md" }) {
|
|
989
|
+
function Spinner({ className, size = "md", label }) {
|
|
250
990
|
return /* @__PURE__ */ jsx(
|
|
251
991
|
"span",
|
|
252
992
|
{
|
|
253
|
-
className: cn("inline-block rounded-full border-white/15 border-t-primary animate-spin",
|
|
254
|
-
"
|
|
993
|
+
className: cn("inline-block rounded-full border-white/15 border-t-primary animate-spin", sizeClass5[size], className),
|
|
994
|
+
role: label ? "status" : void 0,
|
|
995
|
+
"aria-hidden": label ? void 0 : "true",
|
|
996
|
+
"aria-label": label || void 0
|
|
255
997
|
}
|
|
256
998
|
);
|
|
257
999
|
}
|
|
@@ -264,37 +1006,193 @@ function Skeleton({ className, circle }) {
|
|
|
264
1006
|
}
|
|
265
1007
|
);
|
|
266
1008
|
}
|
|
267
|
-
|
|
1009
|
+
var TabsContext = createContext(null);
|
|
1010
|
+
function useTabsContext() {
|
|
1011
|
+
const ctx = useContext(TabsContext);
|
|
1012
|
+
if (!ctx) throw new Error("Tabs sub-components must be used inside <Tabs>");
|
|
1013
|
+
return ctx;
|
|
1014
|
+
}
|
|
1015
|
+
function Tabs({
|
|
1016
|
+
defaultValue = "",
|
|
1017
|
+
value,
|
|
1018
|
+
onValueChange,
|
|
1019
|
+
variant = "underline",
|
|
1020
|
+
children,
|
|
1021
|
+
className
|
|
1022
|
+
}) {
|
|
1023
|
+
const baseId = useId();
|
|
1024
|
+
const isControlled = value !== void 0;
|
|
1025
|
+
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
1026
|
+
const activeValue = isControlled ? value ?? "" : internalValue;
|
|
1027
|
+
const setActiveValue = useCallback(
|
|
1028
|
+
(next) => {
|
|
1029
|
+
if (!isControlled) setInternalValue(next);
|
|
1030
|
+
onValueChange?.(next);
|
|
1031
|
+
},
|
|
1032
|
+
[isControlled, onValueChange]
|
|
1033
|
+
);
|
|
1034
|
+
const ctxValue = useMemo(
|
|
1035
|
+
() => ({ activeValue, setActiveValue, variant, baseId }),
|
|
1036
|
+
[activeValue, setActiveValue, variant, baseId]
|
|
1037
|
+
);
|
|
1038
|
+
return /* @__PURE__ */ jsx(TabsContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsx("div", { className: cn("flex flex-col", className), children }) });
|
|
1039
|
+
}
|
|
1040
|
+
function TabList({ children, className }) {
|
|
1041
|
+
const { variant } = useTabsContext();
|
|
1042
|
+
const listRef = useRef(null);
|
|
1043
|
+
const handleKeyDown = (e) => {
|
|
1044
|
+
const list = listRef.current;
|
|
1045
|
+
if (!list) return;
|
|
1046
|
+
const tabs = Array.from(list.querySelectorAll('[role="tab"]:not([disabled])'));
|
|
1047
|
+
if (tabs.length === 0) return;
|
|
1048
|
+
const focused = document.activeElement;
|
|
1049
|
+
if (!focused || !list.contains(focused)) return;
|
|
1050
|
+
const currentIndex = tabs.indexOf(focused);
|
|
1051
|
+
let nextIndex = currentIndex;
|
|
1052
|
+
switch (e.key) {
|
|
1053
|
+
case "ArrowRight":
|
|
1054
|
+
e.preventDefault();
|
|
1055
|
+
nextIndex = currentIndex < tabs.length - 1 ? currentIndex + 1 : 0;
|
|
1056
|
+
break;
|
|
1057
|
+
case "ArrowLeft":
|
|
1058
|
+
e.preventDefault();
|
|
1059
|
+
nextIndex = currentIndex > 0 ? currentIndex - 1 : tabs.length - 1;
|
|
1060
|
+
break;
|
|
1061
|
+
case "Home":
|
|
1062
|
+
e.preventDefault();
|
|
1063
|
+
nextIndex = 0;
|
|
1064
|
+
break;
|
|
1065
|
+
case "End":
|
|
1066
|
+
e.preventDefault();
|
|
1067
|
+
nextIndex = tabs.length - 1;
|
|
1068
|
+
break;
|
|
1069
|
+
case "Enter":
|
|
1070
|
+
case " ":
|
|
1071
|
+
e.preventDefault();
|
|
1072
|
+
focused?.click();
|
|
1073
|
+
return;
|
|
1074
|
+
default:
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
tabs[nextIndex]?.focus();
|
|
1078
|
+
tabs[nextIndex]?.click();
|
|
1079
|
+
};
|
|
268
1080
|
return /* @__PURE__ */ jsx(
|
|
269
1081
|
"div",
|
|
270
1082
|
{
|
|
271
|
-
|
|
272
|
-
|
|
1083
|
+
ref: listRef,
|
|
1084
|
+
role: "tablist",
|
|
1085
|
+
onKeyDown: handleKeyDown,
|
|
1086
|
+
className: cn(
|
|
1087
|
+
"flex",
|
|
1088
|
+
variant === "underline" && "border-b border-white/10",
|
|
1089
|
+
variant === "pill" && "gap-1 p-1 rounded-xl bg-white/5 ring-1 ring-white/10 w-fit",
|
|
1090
|
+
className
|
|
1091
|
+
),
|
|
1092
|
+
children
|
|
273
1093
|
}
|
|
274
1094
|
);
|
|
275
1095
|
}
|
|
276
|
-
function
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
1096
|
+
function Tab({ value, disabled = false, children, className }) {
|
|
1097
|
+
const { activeValue, setActiveValue, variant, baseId } = useTabsContext();
|
|
1098
|
+
const isActive = activeValue === value;
|
|
1099
|
+
return /* @__PURE__ */ jsx(
|
|
1100
|
+
"button",
|
|
1101
|
+
{
|
|
1102
|
+
type: "button",
|
|
1103
|
+
role: "tab",
|
|
1104
|
+
id: `${baseId}-tab-${value}`,
|
|
1105
|
+
"aria-selected": isActive,
|
|
1106
|
+
"aria-controls": `${baseId}-panel-${value}`,
|
|
1107
|
+
disabled,
|
|
1108
|
+
tabIndex: isActive ? 0 : -1,
|
|
1109
|
+
onClick: () => {
|
|
1110
|
+
if (!disabled) setActiveValue(value);
|
|
1111
|
+
},
|
|
1112
|
+
className: cn(
|
|
1113
|
+
"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",
|
|
1114
|
+
// underline variant
|
|
1115
|
+
variant === "underline" && [
|
|
1116
|
+
"border-b-2 -mb-px",
|
|
1117
|
+
isActive ? "border-primary text-white" : "border-transparent text-white/40 hover:text-white/70 hover:border-white/20",
|
|
1118
|
+
disabled && "opacity-40 cursor-not-allowed hover:text-white/40 hover:border-transparent"
|
|
1119
|
+
],
|
|
1120
|
+
// pill variant
|
|
1121
|
+
variant === "pill" && [
|
|
1122
|
+
"rounded-lg",
|
|
1123
|
+
isActive ? "bg-white/10 text-white shadow-sm" : "text-white/50 hover:text-white/80 hover:bg-white/5",
|
|
1124
|
+
disabled && "opacity-40 cursor-not-allowed hover:bg-transparent hover:text-white/50"
|
|
1125
|
+
],
|
|
1126
|
+
className
|
|
1127
|
+
),
|
|
1128
|
+
children
|
|
1129
|
+
}
|
|
1130
|
+
);
|
|
1131
|
+
}
|
|
1132
|
+
function TabPanel({ value, children, className }) {
|
|
1133
|
+
const { activeValue, baseId } = useTabsContext();
|
|
1134
|
+
if (activeValue !== value) return null;
|
|
1135
|
+
return /* @__PURE__ */ jsx(
|
|
1136
|
+
"div",
|
|
1137
|
+
{
|
|
1138
|
+
role: "tabpanel",
|
|
1139
|
+
id: `${baseId}-panel-${value}`,
|
|
1140
|
+
"aria-labelledby": `${baseId}-tab-${value}`,
|
|
1141
|
+
tabIndex: 0,
|
|
1142
|
+
className: cn("mt-4 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary", className),
|
|
1143
|
+
children
|
|
1144
|
+
}
|
|
1145
|
+
);
|
|
1146
|
+
}
|
|
1147
|
+
var Card = forwardRef(function Card2({ hoverable, variant = "surface", className, ...props }, ref) {
|
|
1148
|
+
return /* @__PURE__ */ jsx(
|
|
1149
|
+
"div",
|
|
1150
|
+
{
|
|
1151
|
+
ref,
|
|
1152
|
+
...props,
|
|
1153
|
+
className: cn(variant === "glass" ? "glass" : "surface", hoverable && "surface-hover", className)
|
|
1154
|
+
}
|
|
1155
|
+
);
|
|
1156
|
+
});
|
|
1157
|
+
var scrollLockCount = 0;
|
|
1158
|
+
var savedOverflow = "";
|
|
1159
|
+
function lockScroll() {
|
|
1160
|
+
if (typeof document === "undefined") return;
|
|
1161
|
+
if (scrollLockCount === 0) {
|
|
1162
|
+
savedOverflow = document.body.style.overflow;
|
|
1163
|
+
document.body.style.overflow = "hidden";
|
|
1164
|
+
}
|
|
1165
|
+
scrollLockCount++;
|
|
1166
|
+
}
|
|
1167
|
+
function unlockScroll() {
|
|
1168
|
+
if (typeof document === "undefined") return;
|
|
1169
|
+
scrollLockCount = Math.max(0, scrollLockCount - 1);
|
|
1170
|
+
if (scrollLockCount === 0) {
|
|
1171
|
+
document.body.style.overflow = savedOverflow;
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
function Modal({
|
|
1175
|
+
isOpen,
|
|
1176
|
+
onClose,
|
|
1177
|
+
children,
|
|
1178
|
+
ariaLabel,
|
|
1179
|
+
ariaLabelledBy,
|
|
1180
|
+
closeOnBackdrop = true,
|
|
1181
|
+
closeOnEsc = true,
|
|
1182
|
+
useGlass = true,
|
|
1183
|
+
overlayClassName,
|
|
1184
|
+
contentClassName,
|
|
1185
|
+
zIndexClassName = "z-50"
|
|
1186
|
+
}) {
|
|
1187
|
+
const dialogRef = useRef(null);
|
|
1188
|
+
const lastActiveElementRef = useRef(null);
|
|
1189
|
+
useEffect(() => {
|
|
1190
|
+
if (!isOpen) return;
|
|
1191
|
+
lastActiveElementRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
1192
|
+
const raf = window.requestAnimationFrame(() => {
|
|
1193
|
+
const dialogEl = dialogRef.current;
|
|
1194
|
+
if (!dialogEl) return;
|
|
1195
|
+
const focusables = getFocusableElements(dialogEl);
|
|
298
1196
|
focusSafely(focusables[0] ?? dialogEl);
|
|
299
1197
|
});
|
|
300
1198
|
return () => {
|
|
@@ -304,6 +1202,11 @@ function Modal({
|
|
|
304
1202
|
if (lastActive?.isConnected) focusSafely(lastActive);
|
|
305
1203
|
};
|
|
306
1204
|
}, [isOpen]);
|
|
1205
|
+
useEffect(() => {
|
|
1206
|
+
if (!isOpen) return;
|
|
1207
|
+
lockScroll();
|
|
1208
|
+
return () => unlockScroll();
|
|
1209
|
+
}, [isOpen]);
|
|
307
1210
|
if (!isOpen) return null;
|
|
308
1211
|
return /* @__PURE__ */ jsx(
|
|
309
1212
|
"div",
|
|
@@ -329,7 +1232,7 @@ function Modal({
|
|
|
329
1232
|
ref: dialogRef,
|
|
330
1233
|
className: cn(
|
|
331
1234
|
"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",
|
|
332
|
-
useGlass && "glass bg-
|
|
1235
|
+
useGlass && "glass bg-surface-50/80",
|
|
333
1236
|
contentClassName
|
|
334
1237
|
),
|
|
335
1238
|
onMouseDown: (e) => e.stopPropagation(),
|
|
@@ -442,17 +1345,17 @@ function Tooltip({ content, delayMs = 500, placement = "top", className, childre
|
|
|
442
1345
|
const anchorRef = useRef(null);
|
|
443
1346
|
const [open, setOpen] = useState(false);
|
|
444
1347
|
const [pos, setPos] = useState(null);
|
|
445
|
-
|
|
1348
|
+
useMemo(() => {
|
|
446
1349
|
if (typeof content === "string") return content.trim();
|
|
447
1350
|
return "";
|
|
448
1351
|
}, [content]);
|
|
449
|
-
|
|
1352
|
+
const clearTimer = useCallback(() => {
|
|
450
1353
|
if (openTimerRef.current !== null) {
|
|
451
1354
|
window.clearTimeout(openTimerRef.current);
|
|
452
1355
|
openTimerRef.current = null;
|
|
453
1356
|
}
|
|
454
|
-
}
|
|
455
|
-
|
|
1357
|
+
}, []);
|
|
1358
|
+
const updatePosition = useCallback(() => {
|
|
456
1359
|
const el = anchorRef.current;
|
|
457
1360
|
if (!el) return;
|
|
458
1361
|
const r = el.getBoundingClientRect();
|
|
@@ -467,17 +1370,17 @@ function Tooltip({ content, delayMs = 500, placement = "top", className, childre
|
|
|
467
1370
|
top: Math.round(effPlacement === "top" ? topY : bottomY),
|
|
468
1371
|
placement: effPlacement
|
|
469
1372
|
});
|
|
470
|
-
}
|
|
471
|
-
|
|
1373
|
+
}, [placement]);
|
|
1374
|
+
const scheduleOpen = useCallback(() => {
|
|
472
1375
|
clearTimer();
|
|
473
1376
|
openTimerRef.current = window.setTimeout(() => {
|
|
474
1377
|
setOpen(true);
|
|
475
1378
|
}, Math.max(0, delayMs));
|
|
476
|
-
}
|
|
477
|
-
|
|
1379
|
+
}, [clearTimer, delayMs]);
|
|
1380
|
+
const close = useCallback(() => {
|
|
478
1381
|
clearTimer();
|
|
479
1382
|
setOpen(false);
|
|
480
|
-
}
|
|
1383
|
+
}, [clearTimer]);
|
|
481
1384
|
useEffect(() => {
|
|
482
1385
|
if (!open) return;
|
|
483
1386
|
updatePosition();
|
|
@@ -489,45 +1392,52 @@ function Tooltip({ content, delayMs = 500, placement = "top", className, childre
|
|
|
489
1392
|
window.removeEventListener("scroll", onScroll, true);
|
|
490
1393
|
window.removeEventListener("resize", onResize);
|
|
491
1394
|
};
|
|
492
|
-
}, [open]);
|
|
1395
|
+
}, [open, updatePosition]);
|
|
493
1396
|
useEffect(() => {
|
|
494
1397
|
return () => clearTimer();
|
|
495
|
-
}, []);
|
|
1398
|
+
}, [clearTimer]);
|
|
1399
|
+
if (!isValidElement(children)) return children;
|
|
496
1400
|
const child = cloneElement(children, {
|
|
497
1401
|
ref: (node) => {
|
|
498
1402
|
anchorRef.current = node;
|
|
499
|
-
const
|
|
1403
|
+
const childProps = children.props;
|
|
1404
|
+
const prevRef = childProps.ref;
|
|
500
1405
|
if (typeof prevRef === "function") prevRef(node);
|
|
501
1406
|
else if (prevRef && typeof prevRef === "object") prevRef.current = node;
|
|
502
1407
|
},
|
|
503
1408
|
onMouseEnter: (e) => {
|
|
504
|
-
children.props
|
|
1409
|
+
const childProps = children.props;
|
|
1410
|
+
if (typeof childProps.onMouseEnter === "function") childProps.onMouseEnter(e);
|
|
505
1411
|
scheduleOpen();
|
|
506
1412
|
},
|
|
507
1413
|
onMouseLeave: (e) => {
|
|
508
|
-
children.props
|
|
1414
|
+
const childProps = children.props;
|
|
1415
|
+
if (typeof childProps.onMouseLeave === "function") childProps.onMouseLeave(e);
|
|
509
1416
|
close();
|
|
510
1417
|
},
|
|
511
1418
|
onFocus: (e) => {
|
|
512
|
-
children.props
|
|
1419
|
+
const childProps = children.props;
|
|
1420
|
+
if (typeof childProps.onFocus === "function") childProps.onFocus(e);
|
|
513
1421
|
scheduleOpen();
|
|
514
1422
|
},
|
|
515
1423
|
onBlur: (e) => {
|
|
516
|
-
children.props
|
|
1424
|
+
const childProps = children.props;
|
|
1425
|
+
if (typeof childProps.onBlur === "function") childProps.onBlur(e);
|
|
517
1426
|
close();
|
|
518
1427
|
},
|
|
519
|
-
...
|
|
1428
|
+
...open ? { "aria-describedby": tooltipId } : {}
|
|
520
1429
|
});
|
|
521
1430
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
522
1431
|
child,
|
|
523
|
-
open && content ? createPortal(
|
|
1432
|
+
open && content && typeof document !== "undefined" ? createPortal(
|
|
524
1433
|
/* @__PURE__ */ jsx(
|
|
525
1434
|
"div",
|
|
526
1435
|
{
|
|
527
1436
|
id: tooltipId,
|
|
528
1437
|
role: "tooltip",
|
|
529
1438
|
className: cn(
|
|
530
|
-
"fixed z-[9999] max-w-[320px] rounded-lg
|
|
1439
|
+
"fixed z-[9999] max-w-[320px] rounded-lg px-3 py-2 text-xs leading-snug shadow-xl pointer-events-none",
|
|
1440
|
+
"bg-surface-100 text-white ring-1 ring-white/10",
|
|
531
1441
|
className
|
|
532
1442
|
),
|
|
533
1443
|
style: pos ? {
|
|
@@ -553,14 +1463,18 @@ function EmptyState({ icon: Icon, title, description, actionLabel, onAction, chi
|
|
|
553
1463
|
}
|
|
554
1464
|
function CollapsibleSection({ title, defaultOpen = true, children, right, className }) {
|
|
555
1465
|
const [isOpen, setIsOpen] = useState(defaultOpen);
|
|
1466
|
+
const contentId = useId();
|
|
1467
|
+
const titleId = `${contentId}-title`;
|
|
556
1468
|
return /* @__PURE__ */ jsxs("div", { className: cn("rounded-xl ring-1 ring-white/10 bg-white/5 overflow-hidden", className), children: [
|
|
557
1469
|
/* @__PURE__ */ jsxs(
|
|
558
1470
|
"button",
|
|
559
1471
|
{
|
|
560
1472
|
type: "button",
|
|
1473
|
+
id: titleId,
|
|
561
1474
|
onClick: () => setIsOpen((prev) => !prev),
|
|
562
1475
|
className: "flex w-full items-center justify-between gap-3 px-4 py-3 text-left hover:bg-white/[0.03] transition-colors",
|
|
563
1476
|
"aria-expanded": isOpen,
|
|
1477
|
+
"aria-controls": contentId,
|
|
564
1478
|
children: [
|
|
565
1479
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
566
1480
|
/* @__PURE__ */ jsx(
|
|
@@ -584,6 +1498,9 @@ function CollapsibleSection({ title, defaultOpen = true, children, right, classN
|
|
|
584
1498
|
/* @__PURE__ */ jsx(
|
|
585
1499
|
"div",
|
|
586
1500
|
{
|
|
1501
|
+
id: contentId,
|
|
1502
|
+
role: "region",
|
|
1503
|
+
"aria-labelledby": titleId,
|
|
587
1504
|
className: cn(
|
|
588
1505
|
"grid transition-[grid-template-rows] duration-200 ease-out",
|
|
589
1506
|
isOpen ? "grid-rows-[1fr]" : "grid-rows-[0fr]"
|
|
@@ -593,6 +1510,747 @@ function CollapsibleSection({ title, defaultOpen = true, children, right, classN
|
|
|
593
1510
|
)
|
|
594
1511
|
] });
|
|
595
1512
|
}
|
|
1513
|
+
var DropdownContext = createContext(null);
|
|
1514
|
+
function useDropdownContext(component) {
|
|
1515
|
+
const ctx = useContext(DropdownContext);
|
|
1516
|
+
if (!ctx) {
|
|
1517
|
+
throw new Error(`<${component}> must be rendered inside <Dropdown>`);
|
|
1518
|
+
}
|
|
1519
|
+
return ctx;
|
|
1520
|
+
}
|
|
1521
|
+
function Dropdown({ children, className }) {
|
|
1522
|
+
const [open, setOpen] = useState(false);
|
|
1523
|
+
const triggerId = useId();
|
|
1524
|
+
const menuId = useId();
|
|
1525
|
+
const triggerRef = useRef(null);
|
|
1526
|
+
const toggleOpen = useCallback(() => setOpen((prev) => !prev), []);
|
|
1527
|
+
const ctxValue = useMemo(
|
|
1528
|
+
() => ({ open, setOpen, toggleOpen, triggerId, menuId, triggerRef }),
|
|
1529
|
+
[open, setOpen, toggleOpen, triggerId, menuId, triggerRef]
|
|
1530
|
+
);
|
|
1531
|
+
return /* @__PURE__ */ jsx(DropdownContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsx("div", { className: cn("relative inline-block", className), children }) });
|
|
1532
|
+
}
|
|
1533
|
+
function DropdownTrigger({ children, className }) {
|
|
1534
|
+
const { open, toggleOpen, triggerId, menuId, triggerRef } = useDropdownContext("DropdownTrigger");
|
|
1535
|
+
if (!isValidElement(children)) return children;
|
|
1536
|
+
return cloneElement(children, {
|
|
1537
|
+
id: triggerId,
|
|
1538
|
+
"aria-haspopup": "menu",
|
|
1539
|
+
"aria-expanded": open,
|
|
1540
|
+
"aria-controls": open ? menuId : void 0,
|
|
1541
|
+
className: cn(children.props.className, className),
|
|
1542
|
+
ref: (node) => {
|
|
1543
|
+
triggerRef.current = node;
|
|
1544
|
+
const childRef = children.ref;
|
|
1545
|
+
if (typeof childRef === "function") childRef(node);
|
|
1546
|
+
else if (childRef && typeof childRef === "object") childRef.current = node;
|
|
1547
|
+
},
|
|
1548
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1549
|
+
onClick: (e) => {
|
|
1550
|
+
const childProps = children.props;
|
|
1551
|
+
if (typeof childProps.onClick === "function") childProps.onClick(e);
|
|
1552
|
+
toggleOpen();
|
|
1553
|
+
}
|
|
1554
|
+
});
|
|
1555
|
+
}
|
|
1556
|
+
function DropdownMenu({ children, className, align = "left" }) {
|
|
1557
|
+
const { open, setOpen, menuId, triggerId, triggerRef } = useDropdownContext("DropdownMenu");
|
|
1558
|
+
const menuRef = useRef(null);
|
|
1559
|
+
const [pos, setPos] = useState(null);
|
|
1560
|
+
const [visible, setVisible] = useState(false);
|
|
1561
|
+
const updatePosition = useCallback(() => {
|
|
1562
|
+
const trigger = triggerRef.current;
|
|
1563
|
+
if (!trigger) return;
|
|
1564
|
+
const rect = trigger.getBoundingClientRect();
|
|
1565
|
+
const left = align === "right" ? rect.right : rect.left;
|
|
1566
|
+
setPos({
|
|
1567
|
+
top: Math.round(rect.bottom + 6),
|
|
1568
|
+
left: Math.round(left)
|
|
1569
|
+
});
|
|
1570
|
+
}, [align, triggerRef]);
|
|
1571
|
+
useEffect(() => {
|
|
1572
|
+
if (!open) {
|
|
1573
|
+
setVisible(false);
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1576
|
+
updatePosition();
|
|
1577
|
+
const raf = window.requestAnimationFrame(() => {
|
|
1578
|
+
setVisible(true);
|
|
1579
|
+
const menuEl = menuRef.current;
|
|
1580
|
+
if (!menuEl) return;
|
|
1581
|
+
const items = getFocusableElements(menuEl);
|
|
1582
|
+
focusSafely(items[0] ?? menuEl);
|
|
1583
|
+
});
|
|
1584
|
+
return () => window.cancelAnimationFrame(raf);
|
|
1585
|
+
}, [open, updatePosition]);
|
|
1586
|
+
useEffect(() => {
|
|
1587
|
+
if (!open) return;
|
|
1588
|
+
function handlePointerDown(e) {
|
|
1589
|
+
const target = e.target;
|
|
1590
|
+
if (menuRef.current?.contains(target)) return;
|
|
1591
|
+
if (triggerRef.current?.contains(target)) return;
|
|
1592
|
+
setOpen(false);
|
|
1593
|
+
}
|
|
1594
|
+
document.addEventListener("pointerdown", handlePointerDown, true);
|
|
1595
|
+
return () => document.removeEventListener("pointerdown", handlePointerDown, true);
|
|
1596
|
+
}, [open, setOpen, triggerRef]);
|
|
1597
|
+
useEffect(() => {
|
|
1598
|
+
if (!open) return;
|
|
1599
|
+
const onScroll = () => updatePosition();
|
|
1600
|
+
const onResize = () => updatePosition();
|
|
1601
|
+
window.addEventListener("scroll", onScroll, true);
|
|
1602
|
+
window.addEventListener("resize", onResize);
|
|
1603
|
+
return () => {
|
|
1604
|
+
window.removeEventListener("scroll", onScroll, true);
|
|
1605
|
+
window.removeEventListener("resize", onResize);
|
|
1606
|
+
};
|
|
1607
|
+
}, [open, updatePosition]);
|
|
1608
|
+
if (!open) return null;
|
|
1609
|
+
const panel = /* @__PURE__ */ jsx(
|
|
1610
|
+
"div",
|
|
1611
|
+
{
|
|
1612
|
+
ref: menuRef,
|
|
1613
|
+
id: menuId,
|
|
1614
|
+
role: "menu",
|
|
1615
|
+
"aria-labelledby": triggerId,
|
|
1616
|
+
tabIndex: -1,
|
|
1617
|
+
className: cn(
|
|
1618
|
+
// Base glass panel
|
|
1619
|
+
"fixed z-[9999] min-w-[10rem] py-1",
|
|
1620
|
+
"rounded-xl shadow-xl ring-1 ring-white/10",
|
|
1621
|
+
"bg-surface-50/90 backdrop-blur-md",
|
|
1622
|
+
// Animation
|
|
1623
|
+
"transition-[opacity,transform] duration-150 ease-out",
|
|
1624
|
+
visible ? "opacity-100 translate-y-0" : "opacity-0 -translate-y-1",
|
|
1625
|
+
className
|
|
1626
|
+
),
|
|
1627
|
+
style: pos ? {
|
|
1628
|
+
top: pos.top,
|
|
1629
|
+
...align === "right" ? { right: `calc(100vw - ${pos.left}px)` } : { left: pos.left }
|
|
1630
|
+
} : { top: 0, left: 0, visibility: "hidden" },
|
|
1631
|
+
onKeyDown: (e) => {
|
|
1632
|
+
const menuEl = menuRef.current;
|
|
1633
|
+
if (!menuEl) return;
|
|
1634
|
+
if (e.key === "Escape" || e.key === "Tab") {
|
|
1635
|
+
if (e.key === "Escape") {
|
|
1636
|
+
e.preventDefault();
|
|
1637
|
+
e.stopPropagation();
|
|
1638
|
+
focusSafely(triggerRef.current);
|
|
1639
|
+
}
|
|
1640
|
+
setOpen(false);
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1643
|
+
const items = getFocusableElements(menuEl).filter(
|
|
1644
|
+
(el) => el.getAttribute("role") === "menuitem" && el.getAttribute("aria-disabled") !== "true"
|
|
1645
|
+
);
|
|
1646
|
+
if (items.length === 0) return;
|
|
1647
|
+
const active = document.activeElement;
|
|
1648
|
+
const currentIndex = items.findIndex((el) => el === active);
|
|
1649
|
+
if (e.key === "ArrowDown") {
|
|
1650
|
+
e.preventDefault();
|
|
1651
|
+
const next = currentIndex < items.length - 1 ? currentIndex + 1 : 0;
|
|
1652
|
+
focusSafely(items[next]);
|
|
1653
|
+
} else if (e.key === "ArrowUp") {
|
|
1654
|
+
e.preventDefault();
|
|
1655
|
+
const prev = currentIndex > 0 ? currentIndex - 1 : items.length - 1;
|
|
1656
|
+
focusSafely(items[prev]);
|
|
1657
|
+
} else if (e.key === "Home") {
|
|
1658
|
+
e.preventDefault();
|
|
1659
|
+
focusSafely(items[0]);
|
|
1660
|
+
} else if (e.key === "End") {
|
|
1661
|
+
e.preventDefault();
|
|
1662
|
+
focusSafely(items[items.length - 1]);
|
|
1663
|
+
}
|
|
1664
|
+
},
|
|
1665
|
+
children
|
|
1666
|
+
}
|
|
1667
|
+
);
|
|
1668
|
+
if (typeof document === "undefined") return null;
|
|
1669
|
+
return createPortal(panel, document.body);
|
|
1670
|
+
}
|
|
1671
|
+
function DropdownItem({ onSelect, disabled = false, children, className }) {
|
|
1672
|
+
const { setOpen, triggerRef } = useDropdownContext("DropdownItem");
|
|
1673
|
+
const handleSelect = useCallback(() => {
|
|
1674
|
+
if (disabled) return;
|
|
1675
|
+
setOpen(false);
|
|
1676
|
+
focusSafely(triggerRef.current);
|
|
1677
|
+
onSelect?.();
|
|
1678
|
+
}, [disabled, onSelect, setOpen, triggerRef]);
|
|
1679
|
+
return /* @__PURE__ */ jsx(
|
|
1680
|
+
"div",
|
|
1681
|
+
{
|
|
1682
|
+
role: "menuitem",
|
|
1683
|
+
tabIndex: disabled ? -1 : 0,
|
|
1684
|
+
"aria-disabled": disabled ? "true" : void 0,
|
|
1685
|
+
className: cn(
|
|
1686
|
+
"flex items-center gap-2 w-full px-3 py-2 text-sm text-left cursor-pointer select-none",
|
|
1687
|
+
"text-white/80 transition-colors duration-100",
|
|
1688
|
+
"focus:outline-none focus-visible:bg-white/10",
|
|
1689
|
+
disabled ? "opacity-40 pointer-events-none cursor-not-allowed" : "hover:bg-white/10 hover:text-white focus-visible:text-white",
|
|
1690
|
+
className
|
|
1691
|
+
),
|
|
1692
|
+
onClick: handleSelect,
|
|
1693
|
+
onKeyDown: (e) => {
|
|
1694
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1695
|
+
e.preventDefault();
|
|
1696
|
+
handleSelect();
|
|
1697
|
+
}
|
|
1698
|
+
},
|
|
1699
|
+
children
|
|
1700
|
+
}
|
|
1701
|
+
);
|
|
1702
|
+
}
|
|
1703
|
+
function DropdownSeparator({ className }) {
|
|
1704
|
+
return /* @__PURE__ */ jsx("div", { role: "separator", className: cn("border-t border-white/10 my-1", className) });
|
|
1705
|
+
}
|
|
1706
|
+
function matchesAccept(file, accept) {
|
|
1707
|
+
const types = accept.split(",").map((t) => t.trim().toLowerCase());
|
|
1708
|
+
const fileName = file.name.toLowerCase();
|
|
1709
|
+
const mimeType = file.type.toLowerCase();
|
|
1710
|
+
return types.some((t) => {
|
|
1711
|
+
if (t.startsWith(".")) return fileName.endsWith(t);
|
|
1712
|
+
if (t.endsWith("/*")) return mimeType.startsWith(t.slice(0, -1));
|
|
1713
|
+
return mimeType === t;
|
|
1714
|
+
});
|
|
1715
|
+
}
|
|
1716
|
+
function DropZone({
|
|
1717
|
+
onFilesDropped,
|
|
1718
|
+
accept,
|
|
1719
|
+
maxFiles,
|
|
1720
|
+
maxSize,
|
|
1721
|
+
disabled = false,
|
|
1722
|
+
children,
|
|
1723
|
+
className,
|
|
1724
|
+
"aria-label": ariaLabel = "File drop zone"
|
|
1725
|
+
}) {
|
|
1726
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
1727
|
+
const inputRef = useRef(null);
|
|
1728
|
+
const dragCounterRef = useRef(0);
|
|
1729
|
+
const processFiles = useCallback(
|
|
1730
|
+
(fileList) => {
|
|
1731
|
+
if (!fileList || disabled) return;
|
|
1732
|
+
let files = Array.from(fileList);
|
|
1733
|
+
if (accept) {
|
|
1734
|
+
files = files.filter((f) => matchesAccept(f, accept));
|
|
1735
|
+
}
|
|
1736
|
+
if (maxSize !== void 0) {
|
|
1737
|
+
files = files.filter((f) => f.size <= maxSize);
|
|
1738
|
+
}
|
|
1739
|
+
if (maxFiles !== void 0) {
|
|
1740
|
+
files = files.slice(0, maxFiles);
|
|
1741
|
+
}
|
|
1742
|
+
onFilesDropped(files);
|
|
1743
|
+
},
|
|
1744
|
+
[accept, disabled, maxFiles, maxSize, onFilesDropped]
|
|
1745
|
+
);
|
|
1746
|
+
const handleDragEnter = useCallback(
|
|
1747
|
+
(e) => {
|
|
1748
|
+
e.preventDefault();
|
|
1749
|
+
dragCounterRef.current++;
|
|
1750
|
+
if (!disabled) setIsDragging(true);
|
|
1751
|
+
},
|
|
1752
|
+
[disabled]
|
|
1753
|
+
);
|
|
1754
|
+
const handleDragOver = useCallback(
|
|
1755
|
+
(e) => {
|
|
1756
|
+
e.preventDefault();
|
|
1757
|
+
},
|
|
1758
|
+
[]
|
|
1759
|
+
);
|
|
1760
|
+
const handleDragLeave = useCallback((e) => {
|
|
1761
|
+
e.preventDefault();
|
|
1762
|
+
dragCounterRef.current--;
|
|
1763
|
+
if (dragCounterRef.current === 0) setIsDragging(false);
|
|
1764
|
+
}, []);
|
|
1765
|
+
const handleDrop = useCallback(
|
|
1766
|
+
(e) => {
|
|
1767
|
+
e.preventDefault();
|
|
1768
|
+
dragCounterRef.current = 0;
|
|
1769
|
+
setIsDragging(false);
|
|
1770
|
+
if (disabled) return;
|
|
1771
|
+
processFiles(e.dataTransfer.files);
|
|
1772
|
+
},
|
|
1773
|
+
[disabled, processFiles]
|
|
1774
|
+
);
|
|
1775
|
+
const handleClick = useCallback(() => {
|
|
1776
|
+
if (!disabled) inputRef.current?.click();
|
|
1777
|
+
}, [disabled]);
|
|
1778
|
+
const handleKeyDown = useCallback(
|
|
1779
|
+
(e) => {
|
|
1780
|
+
if (disabled) return;
|
|
1781
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1782
|
+
e.preventDefault();
|
|
1783
|
+
inputRef.current?.click();
|
|
1784
|
+
}
|
|
1785
|
+
},
|
|
1786
|
+
[disabled]
|
|
1787
|
+
);
|
|
1788
|
+
const handleInputChange = useCallback(() => {
|
|
1789
|
+
processFiles(inputRef.current?.files ?? null);
|
|
1790
|
+
if (inputRef.current) inputRef.current.value = "";
|
|
1791
|
+
}, [processFiles]);
|
|
1792
|
+
return /* @__PURE__ */ jsxs(
|
|
1793
|
+
"div",
|
|
1794
|
+
{
|
|
1795
|
+
role: "button",
|
|
1796
|
+
tabIndex: disabled ? -1 : 0,
|
|
1797
|
+
"aria-label": ariaLabel,
|
|
1798
|
+
"aria-disabled": disabled,
|
|
1799
|
+
onClick: handleClick,
|
|
1800
|
+
onKeyDown: handleKeyDown,
|
|
1801
|
+
onDragEnter: handleDragEnter,
|
|
1802
|
+
onDragOver: handleDragOver,
|
|
1803
|
+
onDragLeave: handleDragLeave,
|
|
1804
|
+
onDrop: handleDrop,
|
|
1805
|
+
className: cn(
|
|
1806
|
+
"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",
|
|
1807
|
+
"border-white/20 bg-white/5 backdrop-blur-sm",
|
|
1808
|
+
"hover:border-primary/50 hover:bg-white/[0.08]",
|
|
1809
|
+
"focus-visible:ring-2 focus-visible:ring-primary/40 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent",
|
|
1810
|
+
isDragging && "border-primary bg-primary/10",
|
|
1811
|
+
disabled && "pointer-events-none opacity-50",
|
|
1812
|
+
className
|
|
1813
|
+
),
|
|
1814
|
+
children: [
|
|
1815
|
+
/* @__PURE__ */ jsx(
|
|
1816
|
+
"input",
|
|
1817
|
+
{
|
|
1818
|
+
ref: inputRef,
|
|
1819
|
+
type: "file",
|
|
1820
|
+
tabIndex: -1,
|
|
1821
|
+
className: "sr-only",
|
|
1822
|
+
accept,
|
|
1823
|
+
multiple: maxFiles !== 1,
|
|
1824
|
+
onChange: handleInputChange,
|
|
1825
|
+
onClick: (e) => e.stopPropagation(),
|
|
1826
|
+
"aria-hidden": "true"
|
|
1827
|
+
}
|
|
1828
|
+
),
|
|
1829
|
+
children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1830
|
+
/* @__PURE__ */ jsx(
|
|
1831
|
+
"svg",
|
|
1832
|
+
{
|
|
1833
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1834
|
+
className: "h-10 w-10 text-white/40",
|
|
1835
|
+
fill: "none",
|
|
1836
|
+
viewBox: "0 0 24 24",
|
|
1837
|
+
stroke: "currentColor",
|
|
1838
|
+
strokeWidth: 1.5,
|
|
1839
|
+
"aria-hidden": "true",
|
|
1840
|
+
children: /* @__PURE__ */ jsx(
|
|
1841
|
+
"path",
|
|
1842
|
+
{
|
|
1843
|
+
strokeLinecap: "round",
|
|
1844
|
+
strokeLinejoin: "round",
|
|
1845
|
+
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"
|
|
1846
|
+
}
|
|
1847
|
+
)
|
|
1848
|
+
}
|
|
1849
|
+
),
|
|
1850
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-white/50", children: "Drop files here or click to browse" })
|
|
1851
|
+
] })
|
|
1852
|
+
]
|
|
1853
|
+
}
|
|
1854
|
+
);
|
|
1855
|
+
}
|
|
1856
|
+
function FormField({ label, error, helperText, children, className, id: idProp }) {
|
|
1857
|
+
const autoId = useId();
|
|
1858
|
+
const id = idProp ?? autoId;
|
|
1859
|
+
const descIds = [];
|
|
1860
|
+
const errorId = error ? `${id}-error` : void 0;
|
|
1861
|
+
const helperId = helperText ? `${id}-helper` : void 0;
|
|
1862
|
+
if (errorId) descIds.push(errorId);
|
|
1863
|
+
if (helperId) descIds.push(helperId);
|
|
1864
|
+
const describedBy = descIds.length > 0 ? descIds.join(" ") : void 0;
|
|
1865
|
+
const child = cloneElement(children, {
|
|
1866
|
+
id,
|
|
1867
|
+
...describedBy ? { "aria-describedby": describedBy } : {},
|
|
1868
|
+
...error ? { "aria-invalid": true } : {}
|
|
1869
|
+
});
|
|
1870
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col", className), children: [
|
|
1871
|
+
label ? /* @__PURE__ */ jsx("label", { htmlFor: id, className: "block text-sm text-white/70 mb-1.5", children: label }) : null,
|
|
1872
|
+
child,
|
|
1873
|
+
error ? /* @__PURE__ */ jsx("p", { id: errorId, className: "text-rose-400 text-xs mt-1", role: "alert", children: error }) : null,
|
|
1874
|
+
helperText && !error ? /* @__PURE__ */ jsx("p", { id: helperId, className: "text-white/40 text-xs mt-1", children: helperText }) : null
|
|
1875
|
+
] });
|
|
1876
|
+
}
|
|
1877
|
+
function Divider({ orientation = "horizontal", label, className }) {
|
|
1878
|
+
if (orientation === "vertical") {
|
|
1879
|
+
return /* @__PURE__ */ jsx(
|
|
1880
|
+
"div",
|
|
1881
|
+
{
|
|
1882
|
+
role: "separator",
|
|
1883
|
+
"aria-orientation": "vertical",
|
|
1884
|
+
className: cn("border-l border-white/10 h-full mx-3 self-stretch", className)
|
|
1885
|
+
}
|
|
1886
|
+
);
|
|
1887
|
+
}
|
|
1888
|
+
if (label) {
|
|
1889
|
+
return /* @__PURE__ */ jsxs(
|
|
1890
|
+
"div",
|
|
1891
|
+
{
|
|
1892
|
+
role: "separator",
|
|
1893
|
+
"aria-orientation": "horizontal",
|
|
1894
|
+
className: cn("flex items-center gap-3 my-3 w-full", className),
|
|
1895
|
+
children: [
|
|
1896
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 border-t border-white/10" }),
|
|
1897
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-white/40 leading-none select-none", children: label }),
|
|
1898
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 border-t border-white/10" })
|
|
1899
|
+
]
|
|
1900
|
+
}
|
|
1901
|
+
);
|
|
1902
|
+
}
|
|
1903
|
+
return /* @__PURE__ */ jsx(
|
|
1904
|
+
"hr",
|
|
1905
|
+
{
|
|
1906
|
+
role: "separator",
|
|
1907
|
+
"aria-orientation": "horizontal",
|
|
1908
|
+
className: cn("border-t border-white/10 w-full my-3", className)
|
|
1909
|
+
}
|
|
1910
|
+
);
|
|
1911
|
+
}
|
|
1912
|
+
var Table = forwardRef(({ children, className }, ref) => /* @__PURE__ */ jsx("div", { className: "rounded-xl ring-1 ring-white/10 overflow-hidden bg-white/5", children: /* @__PURE__ */ jsx("table", { ref, className: cn("w-full text-sm text-white", className), children }) }));
|
|
1913
|
+
Table.displayName = "Table";
|
|
1914
|
+
var TableHeader = forwardRef(
|
|
1915
|
+
({ children, className }, ref) => /* @__PURE__ */ jsx("thead", { ref, className: cn("bg-white/5 border-b border-white/10", className), children })
|
|
1916
|
+
);
|
|
1917
|
+
TableHeader.displayName = "TableHeader";
|
|
1918
|
+
var TableBody = forwardRef(
|
|
1919
|
+
({ children, className }, ref) => /* @__PURE__ */ jsx("tbody", { ref, className: cn("divide-y divide-white/[0.06]", className), children })
|
|
1920
|
+
);
|
|
1921
|
+
TableBody.displayName = "TableBody";
|
|
1922
|
+
var TableRow = forwardRef(
|
|
1923
|
+
({ children, className, hoverable }, ref) => /* @__PURE__ */ jsx(
|
|
1924
|
+
"tr",
|
|
1925
|
+
{
|
|
1926
|
+
ref,
|
|
1927
|
+
className: cn(hoverable && "hover:bg-white/[0.03] transition-colors", className),
|
|
1928
|
+
children
|
|
1929
|
+
}
|
|
1930
|
+
)
|
|
1931
|
+
);
|
|
1932
|
+
TableRow.displayName = "TableRow";
|
|
1933
|
+
var TableHead = forwardRef(
|
|
1934
|
+
({ children, className }, ref) => /* @__PURE__ */ jsx(
|
|
1935
|
+
"th",
|
|
1936
|
+
{
|
|
1937
|
+
ref,
|
|
1938
|
+
className: cn(
|
|
1939
|
+
"px-4 py-3 text-left text-xs font-semibold text-white/50 uppercase tracking-wider",
|
|
1940
|
+
className
|
|
1941
|
+
),
|
|
1942
|
+
children
|
|
1943
|
+
}
|
|
1944
|
+
)
|
|
1945
|
+
);
|
|
1946
|
+
TableHead.displayName = "TableHead";
|
|
1947
|
+
var alignClass = {
|
|
1948
|
+
left: "text-left",
|
|
1949
|
+
center: "text-center",
|
|
1950
|
+
right: "text-right"
|
|
1951
|
+
};
|
|
1952
|
+
var TableCell = forwardRef(
|
|
1953
|
+
({ children, className, align = "left" }, ref) => /* @__PURE__ */ jsx("td", { ref, className: cn("px-4 py-3 text-white/80", alignClass[align], className), children })
|
|
1954
|
+
);
|
|
1955
|
+
TableCell.displayName = "TableCell";
|
|
1956
|
+
function TrendIndicator({ trend }) {
|
|
1957
|
+
if (trend.value > 0) {
|
|
1958
|
+
return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-emerald-400", children: [
|
|
1959
|
+
/* @__PURE__ */ jsx("svg", { "aria-hidden": "true", className: "w-3 h-3 shrink-0", fill: "none", viewBox: "0 0 12 12", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 9V3M3 6l3-3 3 3" }) }),
|
|
1960
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1961
|
+
"+",
|
|
1962
|
+
trend.value,
|
|
1963
|
+
trend.label ? ` ${trend.label}` : ""
|
|
1964
|
+
] })
|
|
1965
|
+
] });
|
|
1966
|
+
}
|
|
1967
|
+
if (trend.value < 0) {
|
|
1968
|
+
return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-rose-400", children: [
|
|
1969
|
+
/* @__PURE__ */ jsx("svg", { "aria-hidden": "true", className: "w-3 h-3 shrink-0", fill: "none", viewBox: "0 0 12 12", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 3v6M3 6l3 3 3-3" }) }),
|
|
1970
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1971
|
+
trend.value,
|
|
1972
|
+
trend.label ? ` ${trend.label}` : ""
|
|
1973
|
+
] })
|
|
1974
|
+
] });
|
|
1975
|
+
}
|
|
1976
|
+
return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-white/40", children: [
|
|
1977
|
+
/* @__PURE__ */ jsx("svg", { "aria-hidden": "true", className: "w-3 h-3 shrink-0", fill: "none", viewBox: "0 0 12 12", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M2 6h8" }) }),
|
|
1978
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1979
|
+
trend.value,
|
|
1980
|
+
trend.label ? ` ${trend.label}` : ""
|
|
1981
|
+
] })
|
|
1982
|
+
] });
|
|
1983
|
+
}
|
|
1984
|
+
function StatCard({ value, label, icon, trend, className }) {
|
|
1985
|
+
return /* @__PURE__ */ jsxs(
|
|
1986
|
+
"div",
|
|
1987
|
+
{
|
|
1988
|
+
className: cn(
|
|
1989
|
+
"bg-white/5 ring-1 ring-white/10 rounded-xl p-4 flex items-start gap-3",
|
|
1990
|
+
className
|
|
1991
|
+
),
|
|
1992
|
+
children: [
|
|
1993
|
+
icon != null && /* @__PURE__ */ jsx("div", { className: "w-10 h-10 rounded-lg bg-white/10 flex items-center justify-center text-primary shrink-0", children: icon }),
|
|
1994
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5 min-w-0", children: [
|
|
1995
|
+
/* @__PURE__ */ jsx("span", { className: "text-2xl font-bold text-white tabular-nums leading-none", children: value }),
|
|
1996
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-white/50 truncate", children: label }),
|
|
1997
|
+
trend != null && /* @__PURE__ */ jsx("div", { className: "mt-1", children: /* @__PURE__ */ jsx(TrendIndicator, { trend }) })
|
|
1998
|
+
] })
|
|
1999
|
+
]
|
|
2000
|
+
}
|
|
2001
|
+
);
|
|
2002
|
+
}
|
|
2003
|
+
function getPageRange(page, totalPages, siblingCount) {
|
|
2004
|
+
if (totalPages <= 1) return [1];
|
|
2005
|
+
const siblingStart = Math.max(2, page - siblingCount);
|
|
2006
|
+
const siblingEnd = Math.min(totalPages - 1, page + siblingCount);
|
|
2007
|
+
const showLeftEllipsis = siblingStart > 2;
|
|
2008
|
+
const showRightEllipsis = siblingEnd < totalPages - 1;
|
|
2009
|
+
const items = [1];
|
|
2010
|
+
if (showLeftEllipsis) {
|
|
2011
|
+
items.push("...");
|
|
2012
|
+
} else {
|
|
2013
|
+
for (let i = 2; i < siblingStart; i++) {
|
|
2014
|
+
items.push(i);
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
for (let i = siblingStart; i <= siblingEnd; i++) {
|
|
2018
|
+
items.push(i);
|
|
2019
|
+
}
|
|
2020
|
+
if (showRightEllipsis) {
|
|
2021
|
+
items.push("...");
|
|
2022
|
+
} else {
|
|
2023
|
+
for (let i = siblingEnd + 1; i < totalPages; i++) {
|
|
2024
|
+
items.push(i);
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
if (totalPages > 1) {
|
|
2028
|
+
items.push(totalPages);
|
|
2029
|
+
}
|
|
2030
|
+
return items;
|
|
2031
|
+
}
|
|
2032
|
+
var buttonBase = "inline-flex items-center justify-center rounded-lg px-3 py-2 text-sm font-medium transition-[background-color,opacity] select-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40";
|
|
2033
|
+
var pageButton = cn(buttonBase, "bg-white/5 ring-1 ring-white/10 text-white/80 hover:bg-white/10 hover:text-white");
|
|
2034
|
+
var activeButton = cn(buttonBase, "bg-primary text-white ring-1 ring-primary/20");
|
|
2035
|
+
var navButton = cn(buttonBase, "bg-white/5 ring-1 ring-white/10 text-white/80 hover:bg-white/10 hover:text-white");
|
|
2036
|
+
var ChevronLeft = () => /* @__PURE__ */ jsx(
|
|
2037
|
+
"svg",
|
|
2038
|
+
{
|
|
2039
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2040
|
+
width: "16",
|
|
2041
|
+
height: "16",
|
|
2042
|
+
viewBox: "0 0 24 24",
|
|
2043
|
+
fill: "none",
|
|
2044
|
+
stroke: "currentColor",
|
|
2045
|
+
strokeWidth: "2",
|
|
2046
|
+
strokeLinecap: "round",
|
|
2047
|
+
strokeLinejoin: "round",
|
|
2048
|
+
"aria-hidden": "true",
|
|
2049
|
+
children: /* @__PURE__ */ jsx("polyline", { points: "15 18 9 12 15 6" })
|
|
2050
|
+
}
|
|
2051
|
+
);
|
|
2052
|
+
var ChevronRight = () => /* @__PURE__ */ jsx(
|
|
2053
|
+
"svg",
|
|
2054
|
+
{
|
|
2055
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2056
|
+
width: "16",
|
|
2057
|
+
height: "16",
|
|
2058
|
+
viewBox: "0 0 24 24",
|
|
2059
|
+
fill: "none",
|
|
2060
|
+
stroke: "currentColor",
|
|
2061
|
+
strokeWidth: "2",
|
|
2062
|
+
strokeLinecap: "round",
|
|
2063
|
+
strokeLinejoin: "round",
|
|
2064
|
+
"aria-hidden": "true",
|
|
2065
|
+
children: /* @__PURE__ */ jsx("polyline", { points: "9 18 15 12 9 6" })
|
|
2066
|
+
}
|
|
2067
|
+
);
|
|
2068
|
+
function Pagination({
|
|
2069
|
+
page,
|
|
2070
|
+
totalPages,
|
|
2071
|
+
onPageChange,
|
|
2072
|
+
siblingCount = 1,
|
|
2073
|
+
className
|
|
2074
|
+
}) {
|
|
2075
|
+
if (totalPages <= 1) return null;
|
|
2076
|
+
const items = getPageRange(page, totalPages, siblingCount);
|
|
2077
|
+
const isPrevDisabled = page <= 1;
|
|
2078
|
+
const isNextDisabled = page >= totalPages;
|
|
2079
|
+
return /* @__PURE__ */ jsxs(
|
|
2080
|
+
"nav",
|
|
2081
|
+
{
|
|
2082
|
+
role: "navigation",
|
|
2083
|
+
"aria-label": "Pagination",
|
|
2084
|
+
className: cn("flex items-center gap-1", className),
|
|
2085
|
+
children: [
|
|
2086
|
+
/* @__PURE__ */ jsx(
|
|
2087
|
+
"button",
|
|
2088
|
+
{
|
|
2089
|
+
type: "button",
|
|
2090
|
+
onClick: () => onPageChange(page - 1),
|
|
2091
|
+
disabled: isPrevDisabled,
|
|
2092
|
+
"aria-label": "Previous page",
|
|
2093
|
+
className: cn(navButton, isPrevDisabled && "opacity-40 cursor-not-allowed pointer-events-none"),
|
|
2094
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, {})
|
|
2095
|
+
}
|
|
2096
|
+
),
|
|
2097
|
+
items.map(
|
|
2098
|
+
(item, index) => item === "..." ? /* @__PURE__ */ jsx(
|
|
2099
|
+
"span",
|
|
2100
|
+
{
|
|
2101
|
+
className: "inline-flex items-center justify-center px-3 py-2 text-sm text-white/40 select-none",
|
|
2102
|
+
"aria-hidden": "true",
|
|
2103
|
+
children: "\u2026"
|
|
2104
|
+
},
|
|
2105
|
+
`ellipsis-${index}`
|
|
2106
|
+
) : /* @__PURE__ */ jsx(
|
|
2107
|
+
"button",
|
|
2108
|
+
{
|
|
2109
|
+
type: "button",
|
|
2110
|
+
onClick: () => onPageChange(item),
|
|
2111
|
+
"aria-label": `Page ${item}`,
|
|
2112
|
+
"aria-current": item === page ? "page" : void 0,
|
|
2113
|
+
className: item === page ? activeButton : pageButton,
|
|
2114
|
+
children: item
|
|
2115
|
+
},
|
|
2116
|
+
item
|
|
2117
|
+
)
|
|
2118
|
+
),
|
|
2119
|
+
/* @__PURE__ */ jsx(
|
|
2120
|
+
"button",
|
|
2121
|
+
{
|
|
2122
|
+
type: "button",
|
|
2123
|
+
onClick: () => onPageChange(page + 1),
|
|
2124
|
+
disabled: isNextDisabled,
|
|
2125
|
+
"aria-label": "Next page",
|
|
2126
|
+
className: cn(navButton, isNextDisabled && "opacity-40 cursor-not-allowed pointer-events-none"),
|
|
2127
|
+
children: /* @__PURE__ */ jsx(ChevronRight, {})
|
|
2128
|
+
}
|
|
2129
|
+
)
|
|
2130
|
+
]
|
|
2131
|
+
}
|
|
2132
|
+
);
|
|
2133
|
+
}
|
|
2134
|
+
function Stepper({ steps, activeStep, className }) {
|
|
2135
|
+
return /* @__PURE__ */ jsx("div", { className: cn("flex items-start w-full", className), children: steps.map((step, index) => {
|
|
2136
|
+
const isCompleted = index < activeStep;
|
|
2137
|
+
const isActive = index === activeStep;
|
|
2138
|
+
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
2139
|
+
/* @__PURE__ */ jsxs(
|
|
2140
|
+
"div",
|
|
2141
|
+
{
|
|
2142
|
+
className: "flex flex-col items-center",
|
|
2143
|
+
"aria-current": isActive ? "step" : void 0,
|
|
2144
|
+
children: [
|
|
2145
|
+
/* @__PURE__ */ jsx(
|
|
2146
|
+
"div",
|
|
2147
|
+
{
|
|
2148
|
+
className: cn(
|
|
2149
|
+
"w-8 h-8 rounded-full flex items-center justify-center text-sm font-semibold",
|
|
2150
|
+
isCompleted && "bg-emerald-600 text-white",
|
|
2151
|
+
isActive && "bg-primary text-white ring-2 ring-primary/40",
|
|
2152
|
+
!isCompleted && !isActive && "bg-white/10 text-white/40"
|
|
2153
|
+
),
|
|
2154
|
+
children: isCompleted ? /* @__PURE__ */ jsx(
|
|
2155
|
+
"svg",
|
|
2156
|
+
{
|
|
2157
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2158
|
+
viewBox: "0 0 20 20",
|
|
2159
|
+
fill: "currentColor",
|
|
2160
|
+
className: "w-4 h-4",
|
|
2161
|
+
"aria-hidden": "true",
|
|
2162
|
+
children: /* @__PURE__ */ jsx(
|
|
2163
|
+
"path",
|
|
2164
|
+
{
|
|
2165
|
+
fillRule: "evenodd",
|
|
2166
|
+
d: "M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z",
|
|
2167
|
+
clipRule: "evenodd"
|
|
2168
|
+
}
|
|
2169
|
+
)
|
|
2170
|
+
}
|
|
2171
|
+
) : /* @__PURE__ */ jsx("span", { children: index + 1 })
|
|
2172
|
+
}
|
|
2173
|
+
),
|
|
2174
|
+
/* @__PURE__ */ jsx(
|
|
2175
|
+
"span",
|
|
2176
|
+
{
|
|
2177
|
+
className: cn(
|
|
2178
|
+
"text-xs mt-2 text-center max-w-[6rem]",
|
|
2179
|
+
isActive ? "text-white/80" : "text-white/50"
|
|
2180
|
+
),
|
|
2181
|
+
children: step.label
|
|
2182
|
+
}
|
|
2183
|
+
),
|
|
2184
|
+
step.description && /* @__PURE__ */ jsx("span", { className: "text-xs mt-0.5 text-center text-white/30 max-w-[6rem]", children: step.description })
|
|
2185
|
+
]
|
|
2186
|
+
}
|
|
2187
|
+
),
|
|
2188
|
+
index < steps.length - 1 && /* @__PURE__ */ jsx(
|
|
2189
|
+
"div",
|
|
2190
|
+
{
|
|
2191
|
+
className: cn(
|
|
2192
|
+
"h-0.5 flex-1 mx-2 mt-4 self-start",
|
|
2193
|
+
isCompleted ? "bg-emerald-600" : "bg-white/10"
|
|
2194
|
+
),
|
|
2195
|
+
"aria-hidden": "true"
|
|
2196
|
+
}
|
|
2197
|
+
)
|
|
2198
|
+
] }, index);
|
|
2199
|
+
}) });
|
|
2200
|
+
}
|
|
2201
|
+
var sizeClasses = {
|
|
2202
|
+
sm: "h-1",
|
|
2203
|
+
md: "h-2",
|
|
2204
|
+
lg: "h-3"
|
|
2205
|
+
};
|
|
2206
|
+
var variantClasses = {
|
|
2207
|
+
primary: "bg-primary",
|
|
2208
|
+
success: "bg-emerald-500",
|
|
2209
|
+
warning: "bg-amber-500",
|
|
2210
|
+
danger: "bg-rose-500"
|
|
2211
|
+
};
|
|
2212
|
+
function ProgressBar({
|
|
2213
|
+
value,
|
|
2214
|
+
max = 100,
|
|
2215
|
+
variant = "primary",
|
|
2216
|
+
label,
|
|
2217
|
+
showValue = false,
|
|
2218
|
+
size = "md",
|
|
2219
|
+
className
|
|
2220
|
+
}) {
|
|
2221
|
+
const percentage = Math.min(100, Math.max(0, value / max * 100));
|
|
2222
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("w-full", className), children: [
|
|
2223
|
+
(label || showValue) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-1", children: [
|
|
2224
|
+
label && /* @__PURE__ */ jsx("span", { className: "text-xs text-white/60", children: label }),
|
|
2225
|
+
showValue && /* @__PURE__ */ jsxs("span", { className: "text-xs text-white/60 ml-auto", children: [
|
|
2226
|
+
Math.round(percentage),
|
|
2227
|
+
"%"
|
|
2228
|
+
] })
|
|
2229
|
+
] }),
|
|
2230
|
+
/* @__PURE__ */ jsx(
|
|
2231
|
+
"div",
|
|
2232
|
+
{
|
|
2233
|
+
className: cn("w-full rounded-full bg-white/10", sizeClasses[size]),
|
|
2234
|
+
role: "progressbar",
|
|
2235
|
+
"aria-valuenow": value,
|
|
2236
|
+
"aria-valuemin": 0,
|
|
2237
|
+
"aria-valuemax": max,
|
|
2238
|
+
"aria-label": label,
|
|
2239
|
+
children: /* @__PURE__ */ jsx(
|
|
2240
|
+
"div",
|
|
2241
|
+
{
|
|
2242
|
+
className: cn(
|
|
2243
|
+
"rounded-full transition-[width] duration-300 ease-out",
|
|
2244
|
+
sizeClasses[size],
|
|
2245
|
+
variantClasses[variant]
|
|
2246
|
+
),
|
|
2247
|
+
style: { width: `${percentage}%` }
|
|
2248
|
+
}
|
|
2249
|
+
)
|
|
2250
|
+
}
|
|
2251
|
+
)
|
|
2252
|
+
] });
|
|
2253
|
+
}
|
|
596
2254
|
var maxWidthClass = {
|
|
597
2255
|
sm: "max-w-2xl",
|
|
598
2256
|
md: "max-w-4xl",
|
|
@@ -605,7 +2263,7 @@ function defaultBackground() {
|
|
|
605
2263
|
return /* @__PURE__ */ jsxs("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 overflow-hidden", children: [
|
|
606
2264
|
/* @__PURE__ */ jsx("div", { className: "absolute -top-32 -left-32 h-[600px] w-[600px] rounded-full bg-violet-600/20 blur-[140px]" }),
|
|
607
2265
|
/* @__PURE__ */ jsx("div", { className: "absolute top-20 -right-32 h-[500px] w-[500px] rounded-full bg-purple-500/[0.12] blur-[120px]" }),
|
|
608
|
-
/* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-1/2 h-[400px] w-[500px] -translate-x-1/2 rounded-full bg-
|
|
2266
|
+
/* @__PURE__ */ 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]" })
|
|
609
2267
|
] });
|
|
610
2268
|
}
|
|
611
2269
|
function PageShell({
|
|
@@ -639,11 +2297,8 @@ function PageShell({
|
|
|
639
2297
|
}
|
|
640
2298
|
function Navbar({ logo, children, className, glass = true }) {
|
|
641
2299
|
return /* @__PURE__ */ jsx("header", { className: cn("fixed top-0 w-full z-50", glass && "glass", className), children: /* @__PURE__ */ jsxs("div", { className: "max-w-7xl mx-auto px-6 h-16 flex items-center justify-between", children: [
|
|
642
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: logo
|
|
643
|
-
|
|
644
|
-
/* @__PURE__ */ jsx("span", { className: "text-lg font-bold tracking-tight", children: "MemeLab" })
|
|
645
|
-
] }) }),
|
|
646
|
-
children && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-4", children })
|
|
2300
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: logo ?? /* @__PURE__ */ jsx("div", { className: "w-9 h-9 rounded-xl animated-gradient" }) }),
|
|
2301
|
+
children && /* @__PURE__ */ jsx("nav", { "aria-label": "Main navigation", children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-4", children }) })
|
|
647
2302
|
] }) });
|
|
648
2303
|
}
|
|
649
2304
|
function Sidebar({ children, collapsed = false, onToggle, className }) {
|
|
@@ -681,7 +2336,15 @@ function Sidebar({ children, collapsed = false, onToggle, className }) {
|
|
|
681
2336
|
}
|
|
682
2337
|
);
|
|
683
2338
|
}
|
|
684
|
-
|
|
2339
|
+
var maxWidthClass2 = {
|
|
2340
|
+
sm: "max-w-2xl",
|
|
2341
|
+
md: "max-w-4xl",
|
|
2342
|
+
lg: "max-w-5xl",
|
|
2343
|
+
xl: "max-w-7xl",
|
|
2344
|
+
"2xl": "max-w-[92rem]",
|
|
2345
|
+
full: "max-w-full"
|
|
2346
|
+
};
|
|
2347
|
+
function DashboardLayout({ children, navbar, sidebar, className, mainClassName, maxWidth = "lg" }) {
|
|
685
2348
|
return /* @__PURE__ */ jsxs("div", { className: cn("min-h-screen bg-surface relative overflow-hidden", className), children: [
|
|
686
2349
|
/* @__PURE__ */ jsxs("div", { "aria-hidden": "true", className: "pointer-events-none fixed inset-0", children: [
|
|
687
2350
|
/* @__PURE__ */ jsx("div", { className: "orb orb-purple w-[400px] h-[400px] -top-[150px] -left-[150px] opacity-15" }),
|
|
@@ -690,9 +2353,551 @@ function DashboardLayout({ children, navbar, sidebar, className }) {
|
|
|
690
2353
|
navbar,
|
|
691
2354
|
/* @__PURE__ */ jsxs("div", { className: cn("flex", navbar && "pt-16"), children: [
|
|
692
2355
|
sidebar,
|
|
693
|
-
/* @__PURE__ */ jsx("main", { className: "flex-1 min-w-0 py-8 px-6", children: /* @__PURE__ */ jsx("div", { className: "
|
|
2356
|
+
/* @__PURE__ */ jsx("main", { className: cn("flex-1 min-w-0 py-8 px-6", mainClassName), children: /* @__PURE__ */ jsx("div", { className: cn("mx-auto", maxWidthClass2[maxWidth]), children }) })
|
|
694
2357
|
] })
|
|
695
2358
|
] });
|
|
696
2359
|
}
|
|
2360
|
+
var variantBorderColor = {
|
|
2361
|
+
info: "border-blue-500",
|
|
2362
|
+
success: "border-emerald-500",
|
|
2363
|
+
warning: "border-amber-500",
|
|
2364
|
+
error: "border-rose-500"
|
|
2365
|
+
};
|
|
2366
|
+
var variantIconColor = {
|
|
2367
|
+
info: "text-blue-400",
|
|
2368
|
+
success: "text-emerald-400",
|
|
2369
|
+
warning: "text-amber-400",
|
|
2370
|
+
error: "text-rose-400"
|
|
2371
|
+
};
|
|
2372
|
+
function InfoIcon() {
|
|
2373
|
+
return /* @__PURE__ */ jsxs(
|
|
2374
|
+
"svg",
|
|
2375
|
+
{
|
|
2376
|
+
"aria-hidden": "true",
|
|
2377
|
+
width: "18",
|
|
2378
|
+
height: "18",
|
|
2379
|
+
viewBox: "0 0 18 18",
|
|
2380
|
+
fill: "none",
|
|
2381
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2382
|
+
children: [
|
|
2383
|
+
/* @__PURE__ */ jsx("circle", { cx: "9", cy: "9", r: "7.5", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
2384
|
+
/* @__PURE__ */ jsx("path", { d: "M9 8v5", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round" }),
|
|
2385
|
+
/* @__PURE__ */ jsx("circle", { cx: "9", cy: "5.5", r: "0.875", fill: "currentColor" })
|
|
2386
|
+
]
|
|
2387
|
+
}
|
|
2388
|
+
);
|
|
2389
|
+
}
|
|
2390
|
+
function SuccessIcon() {
|
|
2391
|
+
return /* @__PURE__ */ jsxs(
|
|
2392
|
+
"svg",
|
|
2393
|
+
{
|
|
2394
|
+
"aria-hidden": "true",
|
|
2395
|
+
width: "18",
|
|
2396
|
+
height: "18",
|
|
2397
|
+
viewBox: "0 0 18 18",
|
|
2398
|
+
fill: "none",
|
|
2399
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2400
|
+
children: [
|
|
2401
|
+
/* @__PURE__ */ jsx("circle", { cx: "9", cy: "9", r: "7.5", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
2402
|
+
/* @__PURE__ */ jsx(
|
|
2403
|
+
"path",
|
|
2404
|
+
{
|
|
2405
|
+
d: "M5.5 9.5l2.5 2.5 4.5-5",
|
|
2406
|
+
stroke: "currentColor",
|
|
2407
|
+
strokeWidth: "1.75",
|
|
2408
|
+
strokeLinecap: "round",
|
|
2409
|
+
strokeLinejoin: "round"
|
|
2410
|
+
}
|
|
2411
|
+
)
|
|
2412
|
+
]
|
|
2413
|
+
}
|
|
2414
|
+
);
|
|
2415
|
+
}
|
|
2416
|
+
function WarningIcon() {
|
|
2417
|
+
return /* @__PURE__ */ jsxs(
|
|
2418
|
+
"svg",
|
|
2419
|
+
{
|
|
2420
|
+
"aria-hidden": "true",
|
|
2421
|
+
width: "18",
|
|
2422
|
+
height: "18",
|
|
2423
|
+
viewBox: "0 0 18 18",
|
|
2424
|
+
fill: "none",
|
|
2425
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2426
|
+
children: [
|
|
2427
|
+
/* @__PURE__ */ jsx(
|
|
2428
|
+
"path",
|
|
2429
|
+
{
|
|
2430
|
+
d: "M7.634 2.896a1.6 1.6 0 0 1 2.732 0l5.866 10.167A1.6 1.6 0 0 1 14.866 15.5H3.134a1.6 1.6 0 0 1-1.366-2.437L7.634 2.896Z",
|
|
2431
|
+
stroke: "currentColor",
|
|
2432
|
+
strokeWidth: "1.5",
|
|
2433
|
+
strokeLinejoin: "round"
|
|
2434
|
+
}
|
|
2435
|
+
),
|
|
2436
|
+
/* @__PURE__ */ jsx("path", { d: "M9 7v4", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round" }),
|
|
2437
|
+
/* @__PURE__ */ jsx("circle", { cx: "9", cy: "12.5", r: "0.875", fill: "currentColor" })
|
|
2438
|
+
]
|
|
2439
|
+
}
|
|
2440
|
+
);
|
|
2441
|
+
}
|
|
2442
|
+
function ErrorIcon() {
|
|
2443
|
+
return /* @__PURE__ */ jsxs(
|
|
2444
|
+
"svg",
|
|
2445
|
+
{
|
|
2446
|
+
"aria-hidden": "true",
|
|
2447
|
+
width: "18",
|
|
2448
|
+
height: "18",
|
|
2449
|
+
viewBox: "0 0 18 18",
|
|
2450
|
+
fill: "none",
|
|
2451
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2452
|
+
children: [
|
|
2453
|
+
/* @__PURE__ */ jsx("circle", { cx: "9", cy: "9", r: "7.5", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
2454
|
+
/* @__PURE__ */ jsx(
|
|
2455
|
+
"path",
|
|
2456
|
+
{
|
|
2457
|
+
d: "M6.5 6.5l5 5M11.5 6.5l-5 5",
|
|
2458
|
+
stroke: "currentColor",
|
|
2459
|
+
strokeWidth: "1.75",
|
|
2460
|
+
strokeLinecap: "round"
|
|
2461
|
+
}
|
|
2462
|
+
)
|
|
2463
|
+
]
|
|
2464
|
+
}
|
|
2465
|
+
);
|
|
2466
|
+
}
|
|
2467
|
+
var variantIcon = {
|
|
2468
|
+
info: InfoIcon,
|
|
2469
|
+
success: SuccessIcon,
|
|
2470
|
+
warning: WarningIcon,
|
|
2471
|
+
error: ErrorIcon
|
|
2472
|
+
};
|
|
2473
|
+
function Alert({ variant = "info", title, children, onDismiss, className }) {
|
|
2474
|
+
const Icon = variantIcon[variant];
|
|
2475
|
+
return /* @__PURE__ */ jsxs(
|
|
2476
|
+
"div",
|
|
2477
|
+
{
|
|
2478
|
+
role: "alert",
|
|
2479
|
+
className: cn(
|
|
2480
|
+
"flex items-start gap-3 rounded-xl bg-white/5 ring-1 ring-white/10",
|
|
2481
|
+
"border-l-4 pl-4 pr-3 py-3",
|
|
2482
|
+
variantBorderColor[variant],
|
|
2483
|
+
className
|
|
2484
|
+
),
|
|
2485
|
+
children: [
|
|
2486
|
+
/* @__PURE__ */ jsx("span", { className: cn("mt-0.5 shrink-0", variantIconColor[variant]), children: /* @__PURE__ */ jsx(Icon, {}) }),
|
|
2487
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
2488
|
+
title && /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-white leading-snug mb-0.5", children: title }),
|
|
2489
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm text-white/70 leading-relaxed", children })
|
|
2490
|
+
] }),
|
|
2491
|
+
onDismiss && /* @__PURE__ */ jsx(
|
|
2492
|
+
"button",
|
|
2493
|
+
{
|
|
2494
|
+
type: "button",
|
|
2495
|
+
"aria-label": "Dismiss",
|
|
2496
|
+
onClick: onDismiss,
|
|
2497
|
+
className: cn(
|
|
2498
|
+
"shrink-0 flex items-center justify-center h-6 w-6 rounded-lg mt-0.5",
|
|
2499
|
+
"text-white/40 transition-colors hover:bg-white/10 hover:text-white/80",
|
|
2500
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40"
|
|
2501
|
+
),
|
|
2502
|
+
children: /* @__PURE__ */ jsx(
|
|
2503
|
+
"svg",
|
|
2504
|
+
{
|
|
2505
|
+
"aria-hidden": "true",
|
|
2506
|
+
width: "10",
|
|
2507
|
+
height: "10",
|
|
2508
|
+
viewBox: "0 0 10 10",
|
|
2509
|
+
fill: "none",
|
|
2510
|
+
children: /* @__PURE__ */ jsx("path", { d: "M1 1l8 8M9 1L1 9", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
|
|
2511
|
+
}
|
|
2512
|
+
)
|
|
2513
|
+
}
|
|
2514
|
+
)
|
|
2515
|
+
]
|
|
2516
|
+
}
|
|
2517
|
+
);
|
|
2518
|
+
}
|
|
2519
|
+
function CopyIcon() {
|
|
2520
|
+
return /* @__PURE__ */ jsxs(
|
|
2521
|
+
"svg",
|
|
2522
|
+
{
|
|
2523
|
+
"aria-hidden": "true",
|
|
2524
|
+
width: "16",
|
|
2525
|
+
height: "16",
|
|
2526
|
+
viewBox: "0 0 16 16",
|
|
2527
|
+
fill: "none",
|
|
2528
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2529
|
+
children: [
|
|
2530
|
+
/* @__PURE__ */ jsx("rect", { x: "5", y: "5", width: "9", height: "9", rx: "1.5", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
2531
|
+
/* @__PURE__ */ jsx(
|
|
2532
|
+
"path",
|
|
2533
|
+
{
|
|
2534
|
+
d: "M3.5 11H3a1.5 1.5 0 0 1-1.5-1.5V3A1.5 1.5 0 0 1 3 1.5h6.5A1.5 1.5 0 0 1 11 3v.5",
|
|
2535
|
+
stroke: "currentColor",
|
|
2536
|
+
strokeWidth: "1.5",
|
|
2537
|
+
strokeLinecap: "round"
|
|
2538
|
+
}
|
|
2539
|
+
)
|
|
2540
|
+
]
|
|
2541
|
+
}
|
|
2542
|
+
);
|
|
2543
|
+
}
|
|
2544
|
+
function CheckIcon() {
|
|
2545
|
+
return /* @__PURE__ */ jsx(
|
|
2546
|
+
"svg",
|
|
2547
|
+
{
|
|
2548
|
+
"aria-hidden": "true",
|
|
2549
|
+
width: "16",
|
|
2550
|
+
height: "16",
|
|
2551
|
+
viewBox: "0 0 16 16",
|
|
2552
|
+
fill: "none",
|
|
2553
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2554
|
+
children: /* @__PURE__ */ jsx(
|
|
2555
|
+
"path",
|
|
2556
|
+
{
|
|
2557
|
+
d: "M3 8.5l3.5 3.5 6.5-7",
|
|
2558
|
+
stroke: "currentColor",
|
|
2559
|
+
strokeWidth: "1.75",
|
|
2560
|
+
strokeLinecap: "round",
|
|
2561
|
+
strokeLinejoin: "round"
|
|
2562
|
+
}
|
|
2563
|
+
)
|
|
2564
|
+
}
|
|
2565
|
+
);
|
|
2566
|
+
}
|
|
2567
|
+
function EyeIcon() {
|
|
2568
|
+
return /* @__PURE__ */ jsxs(
|
|
2569
|
+
"svg",
|
|
2570
|
+
{
|
|
2571
|
+
"aria-hidden": "true",
|
|
2572
|
+
width: "16",
|
|
2573
|
+
height: "16",
|
|
2574
|
+
viewBox: "0 0 16 16",
|
|
2575
|
+
fill: "none",
|
|
2576
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2577
|
+
children: [
|
|
2578
|
+
/* @__PURE__ */ jsx(
|
|
2579
|
+
"path",
|
|
2580
|
+
{
|
|
2581
|
+
d: "M1.5 8S3.5 3.5 8 3.5 14.5 8 14.5 8 12.5 12.5 8 12.5 1.5 8 1.5 8Z",
|
|
2582
|
+
stroke: "currentColor",
|
|
2583
|
+
strokeWidth: "1.5",
|
|
2584
|
+
strokeLinejoin: "round"
|
|
2585
|
+
}
|
|
2586
|
+
),
|
|
2587
|
+
/* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "2", stroke: "currentColor", strokeWidth: "1.5" })
|
|
2588
|
+
]
|
|
2589
|
+
}
|
|
2590
|
+
);
|
|
2591
|
+
}
|
|
2592
|
+
function EyeOffIcon() {
|
|
2593
|
+
return /* @__PURE__ */ jsx(
|
|
2594
|
+
"svg",
|
|
2595
|
+
{
|
|
2596
|
+
"aria-hidden": "true",
|
|
2597
|
+
width: "16",
|
|
2598
|
+
height: "16",
|
|
2599
|
+
viewBox: "0 0 16 16",
|
|
2600
|
+
fill: "none",
|
|
2601
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2602
|
+
children: /* @__PURE__ */ jsx(
|
|
2603
|
+
"path",
|
|
2604
|
+
{
|
|
2605
|
+
d: "M2 2l12 12M6.5 6.6A2 2 0 0 0 9.4 9.5M4.3 4.4C2.9 5.4 1.5 8 1.5 8S3.5 12.5 8 12.5c1.4 0 2.6-.4 3.7-1.1M6.7 3.6C7.1 3.5 7.5 3.5 8 3.5c4.5 0 6.5 4.5 6.5 4.5s-.5 1.2-1.5 2.3",
|
|
2606
|
+
stroke: "currentColor",
|
|
2607
|
+
strokeWidth: "1.5",
|
|
2608
|
+
strokeLinecap: "round",
|
|
2609
|
+
strokeLinejoin: "round"
|
|
2610
|
+
}
|
|
2611
|
+
)
|
|
2612
|
+
}
|
|
2613
|
+
);
|
|
2614
|
+
}
|
|
2615
|
+
var iconButtonBase = cn(
|
|
2616
|
+
"flex items-center justify-center h-7 w-7 rounded-lg shrink-0",
|
|
2617
|
+
"text-white/40 transition-colors hover:bg-white/10 hover:text-white/80",
|
|
2618
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40"
|
|
2619
|
+
);
|
|
2620
|
+
function CopyField({ value, label, masked = false, className, id: externalId }) {
|
|
2621
|
+
const generatedId = useId();
|
|
2622
|
+
const fieldId = externalId || generatedId;
|
|
2623
|
+
const { copy, copied } = useClipboard();
|
|
2624
|
+
const [revealed, setRevealed] = useState(false);
|
|
2625
|
+
const displayValue = masked && !revealed ? "\u2022".repeat(Math.min(value.length, 24)) : value;
|
|
2626
|
+
const field = /* @__PURE__ */ jsxs(
|
|
2627
|
+
"div",
|
|
2628
|
+
{
|
|
2629
|
+
className: cn(
|
|
2630
|
+
"flex items-center gap-1 w-full rounded-xl px-3 py-2.5",
|
|
2631
|
+
"bg-white/10 ring-1 ring-white/10",
|
|
2632
|
+
className
|
|
2633
|
+
),
|
|
2634
|
+
children: [
|
|
2635
|
+
/* @__PURE__ */ jsx(
|
|
2636
|
+
"span",
|
|
2637
|
+
{
|
|
2638
|
+
id: fieldId,
|
|
2639
|
+
className: "flex-1 min-w-0 truncate text-sm text-white select-all font-mono",
|
|
2640
|
+
"aria-label": label,
|
|
2641
|
+
children: displayValue
|
|
2642
|
+
}
|
|
2643
|
+
),
|
|
2644
|
+
masked && /* @__PURE__ */ jsx(
|
|
2645
|
+
"button",
|
|
2646
|
+
{
|
|
2647
|
+
type: "button",
|
|
2648
|
+
"aria-label": revealed ? "Hide value" : "Reveal value",
|
|
2649
|
+
onClick: () => setRevealed((v) => !v),
|
|
2650
|
+
className: iconButtonBase,
|
|
2651
|
+
children: revealed ? /* @__PURE__ */ jsx(EyeOffIcon, {}) : /* @__PURE__ */ jsx(EyeIcon, {})
|
|
2652
|
+
}
|
|
2653
|
+
),
|
|
2654
|
+
/* @__PURE__ */ jsx(
|
|
2655
|
+
"button",
|
|
2656
|
+
{
|
|
2657
|
+
type: "button",
|
|
2658
|
+
"aria-label": copied ? "Copied!" : "Copy to clipboard",
|
|
2659
|
+
onClick: () => copy(value),
|
|
2660
|
+
className: cn(iconButtonBase, copied && "text-emerald-400 hover:text-emerald-400"),
|
|
2661
|
+
children: copied ? /* @__PURE__ */ jsx(CheckIcon, {}) : /* @__PURE__ */ jsx(CopyIcon, {})
|
|
2662
|
+
}
|
|
2663
|
+
)
|
|
2664
|
+
]
|
|
2665
|
+
}
|
|
2666
|
+
);
|
|
2667
|
+
if (!label) return field;
|
|
2668
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
2669
|
+
/* @__PURE__ */ jsx("label", { htmlFor: fieldId, className: "block text-sm text-white/70 mb-1.5", children: label }),
|
|
2670
|
+
field
|
|
2671
|
+
] });
|
|
2672
|
+
}
|
|
2673
|
+
var ProgressButton = forwardRef(
|
|
2674
|
+
function ProgressButton2({ isLoading, loadingText, children, disabled, className, ...props }, ref) {
|
|
2675
|
+
return /* @__PURE__ */ jsx(
|
|
2676
|
+
Button,
|
|
2677
|
+
{
|
|
2678
|
+
ref,
|
|
2679
|
+
...props,
|
|
2680
|
+
disabled: disabled || isLoading,
|
|
2681
|
+
"aria-busy": isLoading || void 0,
|
|
2682
|
+
className: cn("relative overflow-hidden", className),
|
|
2683
|
+
children: isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2684
|
+
/* @__PURE__ */ jsx(
|
|
2685
|
+
"span",
|
|
2686
|
+
{
|
|
2687
|
+
"aria-hidden": "true",
|
|
2688
|
+
className: "absolute inset-0 bg-white/20 animate-[ml-shimmer_2s_ease-in-out_infinite] skew-x-[-20deg] pointer-events-none"
|
|
2689
|
+
}
|
|
2690
|
+
),
|
|
2691
|
+
/* @__PURE__ */ jsx(Spinner, { size: "sm" }),
|
|
2692
|
+
loadingText ?? children
|
|
2693
|
+
] }) : children
|
|
2694
|
+
}
|
|
2695
|
+
);
|
|
2696
|
+
}
|
|
2697
|
+
);
|
|
2698
|
+
function reducer(state, action) {
|
|
2699
|
+
switch (action.type) {
|
|
2700
|
+
case "ADD": {
|
|
2701
|
+
const next = [...state, action.toast];
|
|
2702
|
+
return next.length > action.maxToasts ? next.slice(-action.maxToasts) : next;
|
|
2703
|
+
}
|
|
2704
|
+
case "REMOVE":
|
|
2705
|
+
return state.filter((t) => t.id !== action.id);
|
|
2706
|
+
case "REMOVE_ALL":
|
|
2707
|
+
return [];
|
|
2708
|
+
default:
|
|
2709
|
+
return state;
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
var ToastContext = createContext(null);
|
|
2713
|
+
var variantBarColor = {
|
|
2714
|
+
success: "bg-emerald-500",
|
|
2715
|
+
error: "bg-rose-500",
|
|
2716
|
+
warning: "bg-amber-500",
|
|
2717
|
+
info: "bg-primary"
|
|
2718
|
+
};
|
|
2719
|
+
var variantProgressColor = {
|
|
2720
|
+
success: "bg-emerald-500",
|
|
2721
|
+
error: "bg-rose-500",
|
|
2722
|
+
warning: "bg-amber-500",
|
|
2723
|
+
info: "bg-primary"
|
|
2724
|
+
};
|
|
2725
|
+
var variantIconLabel = {
|
|
2726
|
+
success: "\u2713",
|
|
2727
|
+
error: "\u2715",
|
|
2728
|
+
warning: "\u26A0",
|
|
2729
|
+
info: "i"
|
|
2730
|
+
};
|
|
2731
|
+
var variantIconColor2 = {
|
|
2732
|
+
success: "text-emerald-400",
|
|
2733
|
+
error: "text-rose-400",
|
|
2734
|
+
warning: "text-amber-400",
|
|
2735
|
+
info: "text-primary"
|
|
2736
|
+
};
|
|
2737
|
+
var positionClass = {
|
|
2738
|
+
"top-right": "top-4 right-4 items-end",
|
|
2739
|
+
"top-center": "top-4 left-1/2 -translate-x-1/2 items-center",
|
|
2740
|
+
"bottom-right": "bottom-4 right-4 items-end",
|
|
2741
|
+
"bottom-center": "bottom-4 left-1/2 -translate-x-1/2 items-center"
|
|
2742
|
+
};
|
|
2743
|
+
function ToastCard({ toast, onDismiss }) {
|
|
2744
|
+
const duration = toast.duration ?? 5e3;
|
|
2745
|
+
const [visible, setVisible] = useState(false);
|
|
2746
|
+
const [exiting, setExiting] = useState(false);
|
|
2747
|
+
const [started, setStarted] = useState(false);
|
|
2748
|
+
const dismissedRef = useRef(false);
|
|
2749
|
+
const timerRef = useRef(null);
|
|
2750
|
+
const exitTimerRef = useRef(null);
|
|
2751
|
+
useEffect(() => {
|
|
2752
|
+
let innerFrame;
|
|
2753
|
+
const frame = requestAnimationFrame(() => {
|
|
2754
|
+
setVisible(true);
|
|
2755
|
+
innerFrame = requestAnimationFrame(() => setStarted(true));
|
|
2756
|
+
});
|
|
2757
|
+
return () => {
|
|
2758
|
+
cancelAnimationFrame(frame);
|
|
2759
|
+
cancelAnimationFrame(innerFrame);
|
|
2760
|
+
};
|
|
2761
|
+
}, []);
|
|
2762
|
+
useEffect(() => {
|
|
2763
|
+
return () => {
|
|
2764
|
+
if (exitTimerRef.current !== null) clearTimeout(exitTimerRef.current);
|
|
2765
|
+
};
|
|
2766
|
+
}, []);
|
|
2767
|
+
const triggerDismiss = useCallback(() => {
|
|
2768
|
+
if (dismissedRef.current) return;
|
|
2769
|
+
dismissedRef.current = true;
|
|
2770
|
+
if (timerRef.current !== null) clearTimeout(timerRef.current);
|
|
2771
|
+
setExiting(true);
|
|
2772
|
+
exitTimerRef.current = setTimeout(() => onDismiss(toast.id), 280);
|
|
2773
|
+
}, [onDismiss, toast.id]);
|
|
2774
|
+
useEffect(() => {
|
|
2775
|
+
if (duration <= 0) return;
|
|
2776
|
+
timerRef.current = setTimeout(triggerDismiss, duration);
|
|
2777
|
+
return () => {
|
|
2778
|
+
if (timerRef.current !== null) clearTimeout(timerRef.current);
|
|
2779
|
+
};
|
|
2780
|
+
}, [duration, triggerDismiss]);
|
|
2781
|
+
return /* @__PURE__ */ jsxs(
|
|
2782
|
+
"div",
|
|
2783
|
+
{
|
|
2784
|
+
role: "alert",
|
|
2785
|
+
"aria-live": "assertive",
|
|
2786
|
+
"aria-atomic": "true",
|
|
2787
|
+
"data-variant": toast.variant,
|
|
2788
|
+
className: cn(
|
|
2789
|
+
// Base layout
|
|
2790
|
+
"relative flex w-80 max-w-[calc(100vw-2rem)] overflow-hidden rounded-xl shadow-xl",
|
|
2791
|
+
// Glass background
|
|
2792
|
+
"bg-surface-100/95 backdrop-blur-md ring-1 ring-white/10",
|
|
2793
|
+
// Enter / exit transitions
|
|
2794
|
+
"transition-all duration-300",
|
|
2795
|
+
visible && !exiting ? "opacity-100 translate-x-0" : "opacity-0 translate-x-8",
|
|
2796
|
+
exiting && "scale-95"
|
|
2797
|
+
),
|
|
2798
|
+
children: [
|
|
2799
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn("w-[3px] shrink-0 self-stretch", variantBarColor[toast.variant]) }),
|
|
2800
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 items-start gap-3 px-4 py-3 pr-9", children: [
|
|
2801
|
+
/* @__PURE__ */ jsx(
|
|
2802
|
+
"span",
|
|
2803
|
+
{
|
|
2804
|
+
"aria-hidden": "true",
|
|
2805
|
+
className: cn(
|
|
2806
|
+
"mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full text-[11px] font-bold ring-1",
|
|
2807
|
+
variantIconColor2[toast.variant],
|
|
2808
|
+
"ring-current/30"
|
|
2809
|
+
),
|
|
2810
|
+
children: variantIconLabel[toast.variant]
|
|
2811
|
+
}
|
|
2812
|
+
),
|
|
2813
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
2814
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold leading-snug text-white", children: toast.title }),
|
|
2815
|
+
toast.description ? /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs leading-relaxed text-white/60", children: toast.description }) : null
|
|
2816
|
+
] })
|
|
2817
|
+
] }),
|
|
2818
|
+
/* @__PURE__ */ jsx(
|
|
2819
|
+
"button",
|
|
2820
|
+
{
|
|
2821
|
+
type: "button",
|
|
2822
|
+
"aria-label": "Dismiss notification",
|
|
2823
|
+
onClick: triggerDismiss,
|
|
2824
|
+
className: cn(
|
|
2825
|
+
"absolute right-2 top-2 flex h-6 w-6 items-center justify-center rounded-lg",
|
|
2826
|
+
"text-white/40 transition-colors hover:bg-white/10 hover:text-white/80",
|
|
2827
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40"
|
|
2828
|
+
),
|
|
2829
|
+
children: /* @__PURE__ */ jsx("svg", { width: "10", height: "10", viewBox: "0 0 10 10", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M1 1l8 8M9 1L1 9", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) })
|
|
2830
|
+
}
|
|
2831
|
+
),
|
|
2832
|
+
duration > 0 && /* @__PURE__ */ jsx(
|
|
2833
|
+
"span",
|
|
2834
|
+
{
|
|
2835
|
+
"aria-hidden": "true",
|
|
2836
|
+
className: cn(
|
|
2837
|
+
"absolute bottom-0 left-0 h-[2px] ease-linear",
|
|
2838
|
+
variantProgressColor[toast.variant],
|
|
2839
|
+
"opacity-60"
|
|
2840
|
+
),
|
|
2841
|
+
style: {
|
|
2842
|
+
width: started ? "0%" : "100%",
|
|
2843
|
+
transitionProperty: "width",
|
|
2844
|
+
transitionDuration: started ? `${duration}ms` : "0ms"
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
)
|
|
2848
|
+
]
|
|
2849
|
+
}
|
|
2850
|
+
);
|
|
2851
|
+
}
|
|
2852
|
+
function ToastProvider({ children, position = "top-right", maxToasts = 5 }) {
|
|
2853
|
+
const [toasts, dispatch] = useReducer(reducer, []);
|
|
2854
|
+
const counterRef = useRef(0);
|
|
2855
|
+
const maxToastsRef = useRef(maxToasts);
|
|
2856
|
+
maxToastsRef.current = maxToasts;
|
|
2857
|
+
const toast = useCallback(
|
|
2858
|
+
(options) => {
|
|
2859
|
+
const id = `toast-${++counterRef.current}`;
|
|
2860
|
+
const newToast = {
|
|
2861
|
+
id,
|
|
2862
|
+
variant: options.variant,
|
|
2863
|
+
title: options.title,
|
|
2864
|
+
description: options.description,
|
|
2865
|
+
duration: options.duration ?? 5e3
|
|
2866
|
+
};
|
|
2867
|
+
dispatch({ type: "ADD", toast: newToast, maxToasts: maxToastsRef.current });
|
|
2868
|
+
return id;
|
|
2869
|
+
},
|
|
2870
|
+
[]
|
|
2871
|
+
);
|
|
2872
|
+
const dismiss = useCallback((id) => {
|
|
2873
|
+
dispatch({ type: "REMOVE", id });
|
|
2874
|
+
}, []);
|
|
2875
|
+
const dismissAll = useCallback(() => {
|
|
2876
|
+
dispatch({ type: "REMOVE_ALL" });
|
|
2877
|
+
}, []);
|
|
2878
|
+
const value = useMemo(() => ({ toast, dismiss, dismissAll }), [toast, dismiss, dismissAll]);
|
|
2879
|
+
const container = typeof document !== "undefined" ? createPortal(
|
|
2880
|
+
/* @__PURE__ */ jsx(
|
|
2881
|
+
"div",
|
|
2882
|
+
{
|
|
2883
|
+
"aria-label": "Notifications",
|
|
2884
|
+
className: cn("fixed z-[9999] flex flex-col gap-3 pointer-events-none", positionClass[position]),
|
|
2885
|
+
children: toasts.map((t) => /* @__PURE__ */ jsx("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx(ToastCard, { toast: t, onDismiss: dismiss }) }, t.id))
|
|
2886
|
+
}
|
|
2887
|
+
),
|
|
2888
|
+
document.body
|
|
2889
|
+
) : null;
|
|
2890
|
+
return /* @__PURE__ */ jsxs(ToastContext.Provider, { value, children: [
|
|
2891
|
+
children,
|
|
2892
|
+
container
|
|
2893
|
+
] });
|
|
2894
|
+
}
|
|
2895
|
+
function useToast() {
|
|
2896
|
+
const ctx = useContext(ToastContext);
|
|
2897
|
+
if (!ctx) {
|
|
2898
|
+
throw new Error("useToast must be used inside <ToastProvider>");
|
|
2899
|
+
}
|
|
2900
|
+
return ctx;
|
|
2901
|
+
}
|
|
697
2902
|
|
|
698
|
-
export { Badge, Button, Card, CollapsibleSection, ConfirmDialog, DashboardLayout, EmptyState, IconButton, Input, Modal, Navbar, PageShell, Pill, Select, Sidebar, Skeleton, Spinner, Textarea, Toggle, Tooltip, cn, focusSafely, getFocusableElements };
|
|
2903
|
+
export { Alert, Avatar, Badge, Button, Card, Checkbox, CollapsibleSection, ColorInput, ConfirmDialog, CopyField, DashboardLayout, Divider, DropZone, Dropdown, DropdownItem, DropdownMenu, DropdownSeparator, DropdownTrigger, EmptyState, FormField, IconButton, Input, Modal, Navbar, PageShell, Pagination, Pill, ProgressBar, ProgressButton, RadioGroup, RadioItem, SearchInput, Select, Sidebar, Skeleton, Slider, Spinner, StatCard, Stepper, Tab, TabList, TabPanel, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Tabs, TagInput, Textarea, ToastProvider, Toggle, Tooltip, cn, colors, focusSafely, getFocusableElements, useClipboard, useDebounce, useDisclosure, useMediaQuery, useToast };
|