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