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