react-ui-suite 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,4055 @@
1
+ // components/Alert/Alert.tsx
2
+ import * as React from "react";
3
+ import { twMerge } from "tailwind-merge";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ var variantStyles = {
6
+ info: {
7
+ container: "bg-sky-50 text-sky-900 ring-1 ring-sky-100 dark:bg-sky-500/10 dark:text-sky-100",
8
+ accent: "bg-sky-400",
9
+ icon: "\u2139\uFE0F"
10
+ },
11
+ success: {
12
+ container: "bg-emerald-50 text-emerald-900 ring-1 ring-emerald-100 dark:bg-emerald-500/10 dark:text-emerald-100",
13
+ accent: "bg-emerald-400",
14
+ icon: "\u2714\uFE0F"
15
+ },
16
+ warning: {
17
+ container: "bg-amber-50 text-amber-900 ring-1 ring-amber-100 dark:bg-amber-400/10 dark:text-amber-100",
18
+ accent: "bg-amber-400",
19
+ icon: "\u26A0\uFE0F"
20
+ },
21
+ danger: {
22
+ container: "bg-rose-50 text-rose-900 ring-1 ring-rose-100 dark:bg-rose-500/10 dark:text-rose-100",
23
+ accent: "bg-rose-400",
24
+ icon: "\u26D4"
25
+ }
26
+ };
27
+ var Alert = React.forwardRef(function Alert2({ title, description, variant = "info", onDismiss, className, ...rest }, ref) {
28
+ const style = variantStyles[variant];
29
+ return /* @__PURE__ */ jsxs(
30
+ "div",
31
+ {
32
+ ...rest,
33
+ ref,
34
+ role: "alert",
35
+ className: twMerge(
36
+ "relative flex items-start gap-3 rounded-2xl px-4 py-3 text-sm shadow-sm",
37
+ style.container,
38
+ className
39
+ ),
40
+ children: [
41
+ /* @__PURE__ */ jsx("span", { className: "mt-[2px] text-base", "aria-hidden": "true", children: style.icon }),
42
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
43
+ /* @__PURE__ */ jsx("p", { className: "font-semibold", children: title }),
44
+ description ? /* @__PURE__ */ jsx("p", { className: "text-sm/relaxed opacity-90", children: description }) : null
45
+ ] }),
46
+ onDismiss ? /* @__PURE__ */ jsx(
47
+ "button",
48
+ {
49
+ type: "button",
50
+ onClick: onDismiss,
51
+ className: "rounded-full p-1 text-xs font-semibold uppercase tracking-wide text-current/70 hover:text-current focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent",
52
+ "aria-label": "Dismiss alert",
53
+ children: "\xD7"
54
+ }
55
+ ) : null,
56
+ /* @__PURE__ */ jsx(
57
+ "span",
58
+ {
59
+ className: twMerge("absolute inset-y-2 left-1 w-1 rounded-full", style.accent),
60
+ "aria-hidden": "true"
61
+ }
62
+ )
63
+ ]
64
+ }
65
+ );
66
+ });
67
+
68
+ // components/Badge/Badge.tsx
69
+ import * as React2 from "react";
70
+ import { twMerge as twMerge2 } from "tailwind-merge";
71
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
72
+ var variantClasses = {
73
+ neutral: "bg-slate-100 text-slate-700 ring-1 ring-slate-200 dark:bg-zinc-900/60 dark:text-zinc-100 dark:ring-zinc-700/60",
74
+ info: "bg-sky-50 text-sky-700 ring-1 ring-sky-200 dark:bg-sky-500/10 dark:text-sky-300",
75
+ success: "bg-emerald-50 text-emerald-700 ring-1 ring-emerald-200 dark:bg-emerald-500/10 dark:text-emerald-300",
76
+ warning: "bg-amber-50 text-amber-700 ring-1 ring-amber-200 dark:bg-amber-500/10 dark:text-amber-200",
77
+ danger: "bg-rose-50 text-rose-700 ring-1 ring-rose-200 dark:bg-rose-500/10 dark:text-rose-300"
78
+ };
79
+ var Badge = React2.forwardRef(function Badge2({ variant = "neutral", icon, className, children, ...rest }, ref) {
80
+ return /* @__PURE__ */ jsxs2(
81
+ "span",
82
+ {
83
+ ...rest,
84
+ ref,
85
+ className: twMerge2(
86
+ "inline-flex items-center gap-1 rounded-full px-3 py-1 text-[0.7rem] font-semibold uppercase tracking-wide",
87
+ variantClasses[variant],
88
+ className
89
+ ),
90
+ children: [
91
+ icon ? /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", children: icon }) : null,
92
+ children
93
+ ]
94
+ }
95
+ );
96
+ });
97
+
98
+ // components/Button/Button.tsx
99
+ import { twMerge as twMerge3 } from "tailwind-merge";
100
+ import { jsx as jsx3 } from "react/jsx-runtime";
101
+ function Button({
102
+ children,
103
+ onClick,
104
+ disabled = false,
105
+ className = "",
106
+ type = "button",
107
+ style,
108
+ ...rest
109
+ }) {
110
+ const hasCustomBackground = typeof className === "string" && /bg-/.test(className);
111
+ const hasCustomText = typeof className === "string" && /text-/.test(className);
112
+ const backgroundClasses = hasCustomBackground ? "" : "bg-white hover:bg-slate-100 dark:bg-zinc-900/70 dark:hover:bg-zinc-800";
113
+ const textClasses = hasCustomText ? "" : "text-slate-900 dark:text-zinc-100";
114
+ return /* @__PURE__ */ jsx3(
115
+ "button",
116
+ {
117
+ type,
118
+ onClick,
119
+ disabled,
120
+ style,
121
+ ...rest,
122
+ className: twMerge3(
123
+ "inline-flex items-center justify-center gap-2 rounded-xl border border-slate-300 px-4 py-2 text-sm font-semibold shadow-sm transition",
124
+ backgroundClasses,
125
+ textClasses,
126
+ "hover:border-slate-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-500",
127
+ "disabled:cursor-not-allowed disabled:opacity-60",
128
+ "dark:border-zinc-700/60 dark:hover:border-zinc-500 dark:shadow-slate-950/20",
129
+ className
130
+ ),
131
+ children
132
+ }
133
+ );
134
+ }
135
+
136
+ // components/Card/Card.tsx
137
+ import * as React3 from "react";
138
+ import { twMerge as twMerge4 } from "tailwind-merge";
139
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
140
+ var Card = React3.forwardRef(function Card2({ eyebrow, title, actions, children, footer, muted, className, ...rest }, ref) {
141
+ const hasHeader = eyebrow || actions;
142
+ return /* @__PURE__ */ jsxs3(
143
+ "div",
144
+ {
145
+ ...rest,
146
+ ref,
147
+ className: twMerge4(
148
+ "flex h-full flex-col rounded-3xl border border-slate-200 bg-white p-5 shadow-lg shadow-slate-200/40 dark:border-zinc-700/60 dark:bg-zinc-900/70 dark:shadow-none",
149
+ muted && "bg-slate-50/70 dark:bg-zinc-900/40",
150
+ className
151
+ ),
152
+ children: [
153
+ hasHeader && /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between gap-4", children: [
154
+ eyebrow ? /* @__PURE__ */ jsx4("p", { className: "text-xs font-semibold uppercase tracking-[0.2em] text-slate-400 dark:text-zinc-400", children: eyebrow }) : /* @__PURE__ */ jsx4("span", {}),
155
+ actions ? /* @__PURE__ */ jsx4("div", { className: "text-sm text-slate-500 dark:text-zinc-400", children: actions }) : null
156
+ ] }),
157
+ title ? /* @__PURE__ */ jsx4(
158
+ "h3",
159
+ {
160
+ className: twMerge4(
161
+ "text-lg font-semibold text-slate-900 dark:text-zinc-100",
162
+ hasHeader ? "mt-3" : "mt-0"
163
+ ),
164
+ children: title
165
+ }
166
+ ) : null,
167
+ children ? /* @__PURE__ */ jsx4("div", { className: "mt-3 flex-1 text-sm text-slate-600 dark:text-zinc-300", children }) : null,
168
+ footer ? /* @__PURE__ */ jsx4("div", { className: "mt-4 border-t border-slate-100 pt-3 text-xs text-slate-500 dark:border-zinc-800 dark:text-zinc-400", children: footer }) : null
169
+ ]
170
+ }
171
+ );
172
+ });
173
+
174
+ // components/Checkbox/Checkbox.tsx
175
+ import * as React4 from "react";
176
+ import { twMerge as twMerge5 } from "tailwind-merge";
177
+
178
+ // components/Combobox/icons.tsx
179
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
180
+ function ChevronDown(props) {
181
+ return /* @__PURE__ */ jsx5("svg", { viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true", ...props, children: /* @__PURE__ */ jsx5(
182
+ "path",
183
+ {
184
+ fillRule: "evenodd",
185
+ d: "M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.172l3.71-3.94a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z",
186
+ clipRule: "evenodd"
187
+ }
188
+ ) });
189
+ }
190
+ function Check(props) {
191
+ return /* @__PURE__ */ jsx5("svg", { viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true", ...props, children: /* @__PURE__ */ jsx5("path", { d: "M16.704 5.29a1 1 0 0 1 0 1.415l-7.2 7.2a1 1 0 0 1-1.415 0l-3.2-3.2a1 1 0 1 1 1.414-1.414l2.493 2.493 6.493-6.493a1 1 0 0 1 1.415 0z" }) });
192
+ }
193
+ function Calendar(props) {
194
+ return /* @__PURE__ */ jsxs4("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "aria-hidden": "true", ...props, children: [
195
+ /* @__PURE__ */ jsx5("rect", { x: "3.5", y: "5", width: "17", height: "15", rx: "2", strokeWidth: "1.5" }),
196
+ /* @__PURE__ */ jsx5("path", { d: "M8 4v3M16 4v3", strokeWidth: "1.5", strokeLinecap: "round" }),
197
+ /* @__PURE__ */ jsx5("path", { d: "M3.5 9h17", strokeWidth: "1.5" }),
198
+ /* @__PURE__ */ jsx5("rect", { x: "7.75", y: "12.25", width: "4.5", height: "4.5", rx: "1", strokeWidth: "1.5" })
199
+ ] });
200
+ }
201
+ function Clock(props) {
202
+ return /* @__PURE__ */ jsxs4("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "aria-hidden": "true", ...props, children: [
203
+ /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "8.5", strokeWidth: "1.5" }),
204
+ /* @__PURE__ */ jsx5(
205
+ "path",
206
+ {
207
+ d: "M12 7.5v4.25l3 1.75",
208
+ strokeWidth: "1.5",
209
+ strokeLinecap: "round",
210
+ strokeLinejoin: "round"
211
+ }
212
+ )
213
+ ] });
214
+ }
215
+
216
+ // utils/ref.ts
217
+ function assignRef(ref, value) {
218
+ if (typeof ref === "function") {
219
+ ref(value);
220
+ } else if (ref) {
221
+ ref.current = value;
222
+ }
223
+ }
224
+
225
+ // components/Checkbox/Checkbox.tsx
226
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
227
+ var Checkbox = React4.forwardRef(function Checkbox2({
228
+ label,
229
+ description,
230
+ checked,
231
+ defaultChecked = false,
232
+ onChange,
233
+ disabled,
234
+ className,
235
+ indeterminate,
236
+ id,
237
+ ...rest
238
+ }, forwardedRef) {
239
+ const internalRef = React4.useRef(null);
240
+ const mergedRef = React4.useCallback(
241
+ (node) => {
242
+ internalRef.current = node;
243
+ assignRef(forwardedRef, node);
244
+ },
245
+ [forwardedRef]
246
+ );
247
+ const isControlled = typeof checked === "boolean";
248
+ const [internalChecked, setInternalChecked] = React4.useState(defaultChecked);
249
+ const resolvedChecked = isControlled ? !!checked : internalChecked;
250
+ const generatedId = React4.useId();
251
+ const checkboxId = id != null ? id : generatedId;
252
+ React4.useEffect(() => {
253
+ if (internalRef.current) {
254
+ internalRef.current.indeterminate = !!indeterminate && !resolvedChecked;
255
+ }
256
+ }, [indeterminate, resolvedChecked]);
257
+ const handleChange = (event) => {
258
+ const next = event.target.checked;
259
+ if (!isControlled) {
260
+ setInternalChecked(next);
261
+ }
262
+ onChange == null ? void 0 : onChange(next);
263
+ };
264
+ return /* @__PURE__ */ jsxs5(
265
+ "label",
266
+ {
267
+ htmlFor: checkboxId,
268
+ className: twMerge5(
269
+ "flex cursor-pointer gap-3 rounded-2xl border border-transparent px-2 py-2 text-left transition hover:bg-slate-50 dark:hover:bg-zinc-900/60",
270
+ "items-center",
271
+ disabled && "cursor-not-allowed opacity-60",
272
+ className
273
+ ),
274
+ children: [
275
+ /* @__PURE__ */ jsxs5("span", { className: "flex h-6 w-6 items-center justify-center", children: [
276
+ /* @__PURE__ */ jsx6(
277
+ "input",
278
+ {
279
+ ...rest,
280
+ ref: mergedRef,
281
+ id: checkboxId,
282
+ type: "checkbox",
283
+ className: "peer sr-only",
284
+ checked: resolvedChecked,
285
+ onChange: handleChange,
286
+ disabled
287
+ }
288
+ ),
289
+ /* @__PURE__ */ jsx6(
290
+ "span",
291
+ {
292
+ className: twMerge5(
293
+ "grid size-5 place-items-center rounded-lg border border-slate-300 bg-white text-[0.65rem] font-semibold text-slate-600 transition drop-shadow-sm peer-focus-visible:outline peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-slate-400 dark:border-zinc-600 dark:bg-zinc-950/70 dark:text-zinc-200",
294
+ resolvedChecked && "border-slate-400 bg-slate-100 text-slate-900 shadow-[0_0_0_1px_rgba(148,163,184,0.45)] dark:border-zinc-500 dark:bg-zinc-500 dark:text-white"
295
+ ),
296
+ "aria-hidden": "true",
297
+ children: resolvedChecked ? /* @__PURE__ */ jsx6(Check, { className: "h-3 w-3" }) : indeterminate ? /* @__PURE__ */ jsx6("span", { className: "h-[2px] w-2 rounded-full bg-current" }) : null
298
+ }
299
+ )
300
+ ] }),
301
+ /* @__PURE__ */ jsxs5("span", { className: "flex flex-1 flex-col", children: [
302
+ /* @__PURE__ */ jsx6("span", { className: "text-sm font-semibold text-slate-900 dark:text-zinc-100", children: label }),
303
+ description ? /* @__PURE__ */ jsx6("span", { className: "text-xs text-slate-500 dark:text-zinc-400", children: description }) : null
304
+ ] })
305
+ ]
306
+ }
307
+ );
308
+ });
309
+
310
+ // components/ColorPicker/ColorPicker.tsx
311
+ import * as React5 from "react";
312
+ import { twMerge as twMerge6 } from "tailwind-merge";
313
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
314
+ var defaultSwatches = ["#0ea5e9", "#22c55e", "#f97316", "#f43f5e", "#6366f1", "#facc15"];
315
+ var formatOrder = ["hex", "rgb", "hsl"];
316
+ function clamp(value, min, max) {
317
+ return Math.min(max, Math.max(min, value));
318
+ }
319
+ function normalizeHex(input) {
320
+ if (!input) return null;
321
+ let hex = input.trim().replace(/^#/, "");
322
+ if (hex.length === 3) {
323
+ hex = hex.split("").map((char) => char + char).join("");
324
+ }
325
+ if (!/^[0-9a-fA-F]{6}$/.test(hex)) return null;
326
+ return `#${hex.toUpperCase()}`;
327
+ }
328
+ function hexToRgb(hex) {
329
+ const normalized = normalizeHex(hex);
330
+ if (!normalized) return null;
331
+ const value = normalized.replace("#", "");
332
+ return {
333
+ r: parseInt(value.slice(0, 2), 16),
334
+ g: parseInt(value.slice(2, 4), 16),
335
+ b: parseInt(value.slice(4, 6), 16)
336
+ };
337
+ }
338
+ function rgbToHex(r, g, b) {
339
+ const parts = [r, g, b].map((component) => clamp(Math.round(component), 0, 255)).map((component) => component.toString(16).padStart(2, "0"));
340
+ return `#${parts.join("").toUpperCase()}`;
341
+ }
342
+ function hexToHsl(hex) {
343
+ const rgb = hexToRgb(hex);
344
+ if (!rgb) return null;
345
+ const r = rgb.r / 255;
346
+ const g = rgb.g / 255;
347
+ const b = rgb.b / 255;
348
+ const max = Math.max(r, g, b);
349
+ const min = Math.min(r, g, b);
350
+ let h = 0;
351
+ let s = 0;
352
+ const l = (max + min) / 2;
353
+ if (max !== min) {
354
+ const d = max - min;
355
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
356
+ switch (max) {
357
+ case r:
358
+ h = (g - b) / d + (g < b ? 6 : 0);
359
+ break;
360
+ case g:
361
+ h = (b - r) / d + 2;
362
+ break;
363
+ case b:
364
+ h = (r - g) / d + 4;
365
+ break;
366
+ }
367
+ h /= 6;
368
+ }
369
+ return {
370
+ h: Math.round(h * 360),
371
+ s: Math.round(s * 100),
372
+ l: Math.round(l * 100)
373
+ };
374
+ }
375
+ function hslToHex(h, s, l) {
376
+ const hue = (h % 360 + 360) % 360;
377
+ const sat = clamp(s / 100, 0, 1);
378
+ const light = clamp(l / 100, 0, 1);
379
+ if (sat === 0) {
380
+ const gray = Math.round(light * 255);
381
+ return rgbToHex(gray, gray, gray);
382
+ }
383
+ const q = light < 0.5 ? light * (1 + sat) : light + sat - light * sat;
384
+ const p = 2 * light - q;
385
+ function hueToRgb(t) {
386
+ if (t < 0) t += 1;
387
+ if (t > 1) t -= 1;
388
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
389
+ if (t < 1 / 2) return q;
390
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
391
+ return p;
392
+ }
393
+ const r = hueToRgb(hue / 360 + 1 / 3);
394
+ const g = hueToRgb(hue / 360);
395
+ const b = hueToRgb(hue / 360 - 1 / 3);
396
+ return rgbToHex(r * 255, g * 255, b * 255);
397
+ }
398
+ var channelLabels = {
399
+ hex: ["HEX"],
400
+ rgb: ["R", "G", "B"],
401
+ hsl: ["H", "S", "L"]
402
+ };
403
+ function hexToHsv(hex) {
404
+ const rgb = hexToRgb(hex);
405
+ if (!rgb) return null;
406
+ const r = rgb.r / 255;
407
+ const g = rgb.g / 255;
408
+ const b = rgb.b / 255;
409
+ const max = Math.max(r, g, b);
410
+ const min = Math.min(r, g, b);
411
+ const delta = max - min;
412
+ let h = 0;
413
+ if (delta !== 0) {
414
+ if (max === r) {
415
+ h = (g - b) / delta % 6;
416
+ } else if (max === g) {
417
+ h = (b - r) / delta + 2;
418
+ } else {
419
+ h = (r - g) / delta + 4;
420
+ }
421
+ h *= 60;
422
+ if (h < 0) h += 360;
423
+ }
424
+ const s = max === 0 ? 0 : delta / max;
425
+ const v = max;
426
+ return {
427
+ h: Math.round(h),
428
+ s: Math.round(s * 100),
429
+ v: Math.round(v * 100)
430
+ };
431
+ }
432
+ function hsvToHex(h, s, v) {
433
+ const hue = (h % 360 + 360) % 360;
434
+ const sat = clamp(s / 100, 0, 1);
435
+ const val = clamp(v / 100, 0, 1);
436
+ const c = val * sat;
437
+ const x = c * (1 - Math.abs(hue / 60 % 2 - 1));
438
+ const m = val - c;
439
+ let rPrime = 0;
440
+ let gPrime = 0;
441
+ let bPrime = 0;
442
+ if (hue < 60) {
443
+ rPrime = c;
444
+ gPrime = x;
445
+ } else if (hue < 120) {
446
+ rPrime = x;
447
+ gPrime = c;
448
+ } else if (hue < 180) {
449
+ gPrime = c;
450
+ bPrime = x;
451
+ } else if (hue < 240) {
452
+ gPrime = x;
453
+ bPrime = c;
454
+ } else if (hue < 300) {
455
+ rPrime = x;
456
+ bPrime = c;
457
+ } else {
458
+ rPrime = c;
459
+ bPrime = x;
460
+ }
461
+ const r = Math.round((rPrime + m) * 255);
462
+ const g = Math.round((gPrime + m) * 255);
463
+ const b = Math.round((bPrime + m) * 255);
464
+ return rgbToHex(r, g, b);
465
+ }
466
+ function getChannelValues(hex, format) {
467
+ var _a;
468
+ const normalized = (_a = normalizeHex(hex)) != null ? _a : "#000000";
469
+ switch (format) {
470
+ case "rgb": {
471
+ const rgb = hexToRgb(normalized);
472
+ if (!rgb) return ["0", "0", "0"];
473
+ return [rgb.r, rgb.g, rgb.b].map((value) => String(value));
474
+ }
475
+ case "hsl": {
476
+ const hsl = hexToHsl(normalized);
477
+ if (!hsl) return ["0", "0", "0"];
478
+ return [hsl.h, hsl.s, hsl.l].map((value) => String(value));
479
+ }
480
+ default: {
481
+ const value = normalized.replace("#", "");
482
+ return [value.toUpperCase()];
483
+ }
484
+ }
485
+ }
486
+ function channelsToHex(values, format) {
487
+ var _a, _b;
488
+ if (values.some((value) => value.trim() === "")) return null;
489
+ switch (format) {
490
+ case "hex": {
491
+ const hex = (_b = (_a = values[0]) == null ? void 0 : _a.trim().replace(/^#/, "")) != null ? _b : "";
492
+ if (!/^[0-9a-fA-F]{3}$/.test(hex) && !/^[0-9a-fA-F]{6}$/.test(hex)) return null;
493
+ return normalizeHex(`#${hex}`);
494
+ }
495
+ case "rgb": {
496
+ const numbers = values.map((value) => Number(value.trim()));
497
+ if (numbers.some((num) => Number.isNaN(num))) return null;
498
+ return rgbToHex(numbers[0], numbers[1], numbers[2]);
499
+ }
500
+ case "hsl": {
501
+ const numbers = values.map((value) => Number(value.trim()));
502
+ if (numbers.some((num) => Number.isNaN(num))) return null;
503
+ return hslToHex(numbers[0], numbers[1], numbers[2]);
504
+ }
505
+ default:
506
+ return null;
507
+ }
508
+ }
509
+ var ColorPicker = React5.forwardRef(
510
+ function ColorPicker2({
511
+ label,
512
+ value,
513
+ defaultValue = "#0ea5e9",
514
+ onChange,
515
+ swatches = defaultSwatches,
516
+ id,
517
+ className,
518
+ ...rest
519
+ }, forwardedRef) {
520
+ var _a;
521
+ const normalizedDefault = React5.useMemo(
522
+ () => {
523
+ var _a2;
524
+ return (_a2 = normalizeHex(defaultValue)) != null ? _a2 : "#0EA5E9";
525
+ },
526
+ [defaultValue]
527
+ );
528
+ const normalizedSwatches = React5.useMemo(
529
+ () => swatches.map((color) => normalizeHex(color)).filter(Boolean),
530
+ [swatches]
531
+ );
532
+ const isControlled = typeof value === "string";
533
+ const [internalValue, setInternalValue] = React5.useState(normalizedDefault);
534
+ const [format, setFormat] = React5.useState("hex");
535
+ const resolvedValue = React5.useMemo(() => {
536
+ var _a2, _b;
537
+ if (isControlled) {
538
+ return (_a2 = normalizeHex(value)) != null ? _a2 : normalizedDefault;
539
+ }
540
+ return (_b = normalizeHex(internalValue)) != null ? _b : normalizedDefault;
541
+ }, [isControlled, value, internalValue, normalizedDefault]);
542
+ const [channelValues, setChannelValues] = React5.useState(
543
+ () => getChannelValues(resolvedValue, format)
544
+ );
545
+ const [swatchList, setSwatchList] = React5.useState(normalizedSwatches);
546
+ const resolvedHsv = React5.useMemo(
547
+ () => {
548
+ var _a2;
549
+ return (_a2 = hexToHsv(resolvedValue)) != null ? _a2 : { h: 199, s: 100, v: 100 };
550
+ },
551
+ [resolvedValue]
552
+ );
553
+ const [hue, setHue] = React5.useState(resolvedHsv.h);
554
+ const [saturation, setSaturation] = React5.useState(resolvedHsv.s);
555
+ const [valueLevel, setValueLevel] = React5.useState(resolvedHsv.v);
556
+ const generatedId = React5.useId();
557
+ const inputId = id != null ? id : generatedId;
558
+ const inputRef = React5.useRef(null);
559
+ const triggerRef = React5.useRef(null);
560
+ const panelRef = React5.useRef(null);
561
+ const gradientRef = React5.useRef(null);
562
+ const hueRef = React5.useRef(null);
563
+ const gradientDragCleanupRef = React5.useRef(null);
564
+ const hueDragCleanupRef = React5.useRef(null);
565
+ const [isOpen, setIsOpen] = React5.useState(false);
566
+ const mergedRef = React5.useCallback(
567
+ (node) => {
568
+ inputRef.current = node;
569
+ assignRef(forwardedRef, node);
570
+ },
571
+ [forwardedRef]
572
+ );
573
+ const shouldLockHue = (s, v) => s <= 1 || v <= 1;
574
+ React5.useEffect(() => {
575
+ setHue((prev) => shouldLockHue(resolvedHsv.s, resolvedHsv.v) ? prev : resolvedHsv.h);
576
+ setSaturation(resolvedHsv.s);
577
+ setValueLevel(resolvedHsv.v);
578
+ }, [resolvedHsv.h, resolvedHsv.s, resolvedHsv.v]);
579
+ React5.useEffect(() => {
580
+ setSwatchList(normalizedSwatches);
581
+ }, [normalizedSwatches]);
582
+ const applyColor = React5.useCallback(
583
+ (next) => {
584
+ const normalized = normalizeHex(next);
585
+ if (!normalized) return;
586
+ if (!isControlled) {
587
+ setInternalValue(normalized);
588
+ }
589
+ onChange == null ? void 0 : onChange(normalized);
590
+ },
591
+ [isControlled, onChange]
592
+ );
593
+ const handleSwatchClick = (color) => {
594
+ const normalized = normalizeHex(color);
595
+ if (!normalized) return;
596
+ const hsv = hexToHsv(normalized);
597
+ if (hsv) {
598
+ setHue((prev) => shouldLockHue(hsv.s, hsv.v) ? prev : hsv.h);
599
+ setSaturation(hsv.s);
600
+ setValueLevel(hsv.v);
601
+ }
602
+ applyColor(normalized);
603
+ };
604
+ const handleAddSwatch = React5.useCallback(() => {
605
+ const normalized = normalizeHex(resolvedValue);
606
+ if (!normalized) return;
607
+ setSwatchList((prev) => {
608
+ if (prev.includes(normalized)) return prev;
609
+ return [...prev, normalized];
610
+ });
611
+ }, [resolvedValue]);
612
+ const handleRemoveSwatch = React5.useCallback((color) => {
613
+ setSwatchList((prev) => prev.filter((value2) => value2.toLowerCase() !== color.toLowerCase()));
614
+ }, []);
615
+ React5.useEffect(() => {
616
+ setChannelValues(getChannelValues(resolvedValue, format));
617
+ }, [resolvedValue, format]);
618
+ const handleChannelChange = React5.useCallback(
619
+ (index, nextValue) => {
620
+ setChannelValues((prev) => {
621
+ const updated = [...prev];
622
+ if (format === "hex") {
623
+ updated[index] = nextValue.replace(/[^0-9a-fA-F]/g, "").toUpperCase();
624
+ } else {
625
+ updated[index] = nextValue;
626
+ }
627
+ const parsed = channelsToHex(updated, format);
628
+ if (parsed) {
629
+ applyColor(parsed);
630
+ }
631
+ return updated;
632
+ });
633
+ },
634
+ [applyColor, format]
635
+ );
636
+ const resetChannels = React5.useCallback(() => {
637
+ setChannelValues(getChannelValues(resolvedValue, format));
638
+ }, [resolvedValue, format]);
639
+ const closePanel = React5.useCallback(() => {
640
+ setIsOpen(false);
641
+ }, []);
642
+ React5.useEffect(() => {
643
+ if (!isOpen) return;
644
+ const handlePointer = (event) => {
645
+ var _a2, _b;
646
+ const target = event.target;
647
+ if (((_a2 = panelRef.current) == null ? void 0 : _a2.contains(target)) || ((_b = triggerRef.current) == null ? void 0 : _b.contains(target))) {
648
+ return;
649
+ }
650
+ closePanel();
651
+ };
652
+ const handleKeydown = (event) => {
653
+ var _a2;
654
+ if (event.key === "Escape") {
655
+ event.preventDefault();
656
+ closePanel();
657
+ (_a2 = triggerRef.current) == null ? void 0 : _a2.focus();
658
+ }
659
+ };
660
+ window.addEventListener("pointerdown", handlePointer);
661
+ window.addEventListener("keydown", handleKeydown);
662
+ return () => {
663
+ window.removeEventListener("pointerdown", handlePointer);
664
+ window.removeEventListener("keydown", handleKeydown);
665
+ };
666
+ }, [closePanel, isOpen]);
667
+ const updateGradientColor = React5.useCallback(
668
+ (clientX, clientY) => {
669
+ var _a2;
670
+ const rect = (_a2 = gradientRef.current) == null ? void 0 : _a2.getBoundingClientRect();
671
+ if (!rect) return;
672
+ const xRatio = clamp((clientX - rect.left) / rect.width, 0, 1);
673
+ const yRatio = clamp((clientY - rect.top) / rect.height, 0, 1);
674
+ const nextS = Math.round(xRatio * 100);
675
+ const nextV = Math.round((1 - yRatio) * 100);
676
+ setSaturation(nextS);
677
+ setValueLevel(nextV);
678
+ applyColor(hsvToHex(hue, nextS, nextV));
679
+ },
680
+ [applyColor, hue]
681
+ );
682
+ const handleGradientPointerDown = React5.useCallback(
683
+ (event) => {
684
+ var _a2;
685
+ event.preventDefault();
686
+ (_a2 = gradientDragCleanupRef.current) == null ? void 0 : _a2.call(gradientDragCleanupRef);
687
+ updateGradientColor(event.clientX, event.clientY);
688
+ const handleMove = (moveEvent) => {
689
+ moveEvent.preventDefault();
690
+ updateGradientColor(moveEvent.clientX, moveEvent.clientY);
691
+ };
692
+ const stop = () => {
693
+ window.removeEventListener("pointermove", handleMove);
694
+ window.removeEventListener("pointerup", stop);
695
+ window.removeEventListener("pointercancel", stop);
696
+ gradientDragCleanupRef.current = null;
697
+ };
698
+ window.addEventListener("pointermove", handleMove);
699
+ window.addEventListener("pointerup", stop);
700
+ window.addEventListener("pointercancel", stop);
701
+ gradientDragCleanupRef.current = stop;
702
+ },
703
+ [updateGradientColor]
704
+ );
705
+ const updateHueFromPointer = React5.useCallback(
706
+ (clientX) => {
707
+ var _a2;
708
+ const rect = (_a2 = hueRef.current) == null ? void 0 : _a2.getBoundingClientRect();
709
+ if (!rect) return;
710
+ const ratio = clamp((clientX - rect.left) / rect.width, 0, 1);
711
+ const nextHue = Math.round(ratio * 360);
712
+ setHue(nextHue);
713
+ applyColor(hsvToHex(nextHue, saturation, valueLevel));
714
+ },
715
+ [applyColor, saturation, valueLevel]
716
+ );
717
+ const handleHuePointerDown = React5.useCallback(
718
+ (event) => {
719
+ var _a2;
720
+ event.preventDefault();
721
+ (_a2 = hueDragCleanupRef.current) == null ? void 0 : _a2.call(hueDragCleanupRef);
722
+ updateHueFromPointer(event.clientX);
723
+ const handleMove = (moveEvent) => {
724
+ moveEvent.preventDefault();
725
+ updateHueFromPointer(moveEvent.clientX);
726
+ };
727
+ const stop = () => {
728
+ window.removeEventListener("pointermove", handleMove);
729
+ window.removeEventListener("pointerup", stop);
730
+ window.removeEventListener("pointercancel", stop);
731
+ hueDragCleanupRef.current = null;
732
+ };
733
+ window.addEventListener("pointermove", handleMove);
734
+ window.addEventListener("pointerup", stop);
735
+ window.addEventListener("pointercancel", stop);
736
+ hueDragCleanupRef.current = stop;
737
+ },
738
+ [updateHueFromPointer]
739
+ );
740
+ React5.useEffect(() => {
741
+ return () => {
742
+ var _a2, _b;
743
+ (_a2 = gradientDragCleanupRef.current) == null ? void 0 : _a2.call(gradientDragCleanupRef);
744
+ (_b = hueDragCleanupRef.current) == null ? void 0 : _b.call(hueDragCleanupRef);
745
+ };
746
+ }, []);
747
+ const activeLabels = channelLabels[format];
748
+ return /* @__PURE__ */ jsxs6("div", { className: "relative inline-flex flex-col items-center gap-2", children: [
749
+ label ? /* @__PURE__ */ jsx7("p", { className: "text-[10px] font-semibold uppercase tracking-[0.3em] text-slate-400 dark:text-zinc-500", children: label }) : null,
750
+ /* @__PURE__ */ jsxs6(
751
+ "button",
752
+ {
753
+ type: "button",
754
+ ref: triggerRef,
755
+ onClick: () => setIsOpen((prev) => !prev),
756
+ "aria-haspopup": "dialog",
757
+ "aria-expanded": isOpen,
758
+ "aria-label": label ? `Adjust ${label}` : "Choose color",
759
+ className: "relative inline-flex h-12 w-12 items-center justify-center rounded-full border border-slate-200 bg-white shadow-sm transition hover:shadow-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-400 dark:border-zinc-700 dark:bg-zinc-900",
760
+ children: [
761
+ /* @__PURE__ */ jsx7(
762
+ "span",
763
+ {
764
+ className: "absolute inset-1 rounded-full border border-white/30 shadow-inner dark:border-zinc-900/60",
765
+ style: { background: resolvedValue },
766
+ "aria-hidden": "true"
767
+ }
768
+ ),
769
+ /* @__PURE__ */ jsx7(
770
+ "span",
771
+ {
772
+ className: "relative text-xl drop-shadow-[0_1px_1px_rgba(15,23,42,0.55)]",
773
+ style: { transform: "translateX(0.5px) translateY(-1.5px)" },
774
+ "aria-hidden": "true",
775
+ children: "\u{1F3A8}"
776
+ }
777
+ )
778
+ ]
779
+ }
780
+ ),
781
+ isOpen ? /* @__PURE__ */ jsx7(
782
+ "div",
783
+ {
784
+ ref: panelRef,
785
+ role: "dialog",
786
+ "aria-label": label ? `${label} color picker` : "Color picker dialog",
787
+ className: "absolute left-1/2 top-full z-20 mt-3 w-[340px] max-w-[90vw] -translate-x-1/2 rounded-2xl border border-slate-200/80 bg-white/95 p-4 shadow-2xl backdrop-blur-sm dark:border-zinc-700/60 dark:bg-zinc-900/95",
788
+ children: /* @__PURE__ */ jsxs6("div", { className: "space-y-4", children: [
789
+ /* @__PURE__ */ jsxs6("div", { className: "space-y-3", children: [
790
+ /* @__PURE__ */ jsx7(
791
+ "div",
792
+ {
793
+ ref: gradientRef,
794
+ onPointerDown: handleGradientPointerDown,
795
+ className: "relative h-40 w-full cursor-crosshair overflow-hidden rounded-xl shadow-[inset_0_1px_2px_rgba(15,23,42,.25)] ring-1 ring-black/5 dark:ring-white/10",
796
+ style: {
797
+ backgroundColor: `hsl(${hue}, 100%, 50%)`,
798
+ backgroundImage: "linear-gradient(to top, rgba(0,0,0,1), rgba(0,0,0,0)), linear-gradient(to right, #fff, rgba(255,255,255,0))"
799
+ },
800
+ "aria-label": "Color area",
801
+ children: /* @__PURE__ */ jsx7(
802
+ "span",
803
+ {
804
+ className: "pointer-events-none absolute h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-[0_1px_2px_rgba(15,23,42,.4)]",
805
+ style: {
806
+ left: `${saturation}%`,
807
+ top: `${100 - valueLevel}%`,
808
+ background: resolvedValue
809
+ }
810
+ }
811
+ )
812
+ }
813
+ ),
814
+ /* @__PURE__ */ jsx7(
815
+ "div",
816
+ {
817
+ ref: hueRef,
818
+ onPointerDown: handleHuePointerDown,
819
+ className: "relative h-3 w-full cursor-pointer rounded-full border border-white/40 shadow-inner dark:border-zinc-800/60",
820
+ style: {
821
+ background: "linear-gradient(90deg, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00)"
822
+ },
823
+ "aria-label": "Hue slider",
824
+ children: /* @__PURE__ */ jsx7(
825
+ "span",
826
+ {
827
+ className: "pointer-events-none absolute top-1/2 h-5 w-2 -translate-x-1/2 -translate-y-1/2 rounded-full border border-slate-900/40 bg-white shadow",
828
+ style: { left: `${hue / 360 * 100}%` }
829
+ }
830
+ )
831
+ }
832
+ )
833
+ ] }),
834
+ /* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap items-center gap-2", children: [
835
+ swatchList.map((color) => /* @__PURE__ */ jsxs6("div", { className: "relative group", children: [
836
+ /* @__PURE__ */ jsx7(
837
+ "button",
838
+ {
839
+ type: "button",
840
+ onClick: () => handleSwatchClick(color),
841
+ className: twMerge6(
842
+ "h-7 w-7 rounded-full border border-white/30 transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-400 dark:border-zinc-900/70",
843
+ color.toLowerCase() === resolvedValue.toLowerCase() && "ring-2 ring-offset-2 ring-slate-500 dark:ring-offset-slate-900"
844
+ ),
845
+ style: { background: color },
846
+ "aria-label": `Select ${color}`
847
+ }
848
+ ),
849
+ /* @__PURE__ */ jsx7(
850
+ "button",
851
+ {
852
+ type: "button",
853
+ onClick: () => handleRemoveSwatch(color),
854
+ className: "absolute -top-1.5 -right-1.5 hidden h-5 w-5 items-center justify-center rounded-full bg-white text-[10px] font-bold text-slate-600 shadow-sm ring-1 ring-slate-200 transition group-hover:flex group-focus-within:flex dark:bg-zinc-900 dark:text-zinc-200 dark:ring-zinc-700",
855
+ "aria-label": `Remove ${color} swatch`,
856
+ children: "\xD7"
857
+ }
858
+ )
859
+ ] }, color)),
860
+ /* @__PURE__ */ jsx7(
861
+ "button",
862
+ {
863
+ type: "button",
864
+ onClick: handleAddSwatch,
865
+ className: "inline-flex h-7 items-center gap-1 rounded-full border border-dashed border-slate-300 px-3 text-xs font-semibold uppercase tracking-wide text-slate-500 transition hover:border-slate-400 hover:text-slate-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-400 dark:border-zinc-600 dark:text-zinc-300 dark:hover:border-zinc-500",
866
+ children: "+ Add swatch"
867
+ }
868
+ )
869
+ ] }),
870
+ /* @__PURE__ */ jsxs6("div", { className: "space-y-2", children: [
871
+ /* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
872
+ /* @__PURE__ */ jsx7("p", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400 dark:text-zinc-500", children: "Channels" }),
873
+ /* @__PURE__ */ jsx7("div", { className: "inline-flex overflow-hidden rounded-xl border border-slate-200 bg-white/80 text-[11px] font-semibold uppercase text-slate-500 dark:border-zinc-700 dark:bg-zinc-900/80 dark:text-zinc-300", children: formatOrder.map((option) => /* @__PURE__ */ jsx7(
874
+ "button",
875
+ {
876
+ type: "button",
877
+ onClick: () => setFormat(option),
878
+ "aria-pressed": format === option,
879
+ className: twMerge6(
880
+ "px-3 py-1.5 transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-400/60",
881
+ format === option ? "bg-white text-slate-900 shadow-[inset_0_0_0_1px_rgba(15,23,42,0.08)] dark:bg-white/15 dark:text-white" : "text-slate-500 dark:text-zinc-400"
882
+ ),
883
+ children: option.toUpperCase()
884
+ },
885
+ option
886
+ )) })
887
+ ] }),
888
+ /* @__PURE__ */ jsx7("div", { className: "flex flex-wrap gap-2", children: format === "hex" ? /* @__PURE__ */ jsxs6("label", { className: "flex min-w-[180px] flex-1 items-center gap-2 rounded-xl border border-slate-200 bg-white/60 px-3 py-1.5 font-mono text-sm text-slate-900 focus-within:border-slate-400 dark:border-zinc-700 dark:bg-zinc-900/70 dark:text-zinc-100", children: [
889
+ /* @__PURE__ */ jsx7("span", { className: "text-base font-semibold text-slate-400 dark:text-zinc-500", children: "#" }),
890
+ /* @__PURE__ */ jsx7(
891
+ "input",
892
+ {
893
+ type: "text",
894
+ value: (_a = channelValues[0]) != null ? _a : "",
895
+ onChange: (event) => handleChannelChange(0, event.target.value),
896
+ onBlur: resetChannels,
897
+ inputMode: "text",
898
+ maxLength: 6,
899
+ className: "h-6 min-w-0 flex-1 border-none bg-transparent p-0 text-left text-sm uppercase tracking-[0.2em] text-slate-900 placeholder:text-slate-400 focus:outline-none dark:text-zinc-50"
900
+ }
901
+ )
902
+ ] }) : channelValues.map((value2, index) => /* @__PURE__ */ jsxs6(
903
+ "label",
904
+ {
905
+ className: "flex min-w-[90px] flex-1 items-center gap-2 rounded-xl border border-slate-200 bg-white/60 px-3 py-1.5 font-mono text-sm text-slate-900 focus-within:border-slate-400 dark:border-zinc-700 dark:bg-zinc-900/70 dark:text-zinc-100",
906
+ children: [
907
+ /* @__PURE__ */ jsx7("span", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400 dark:text-zinc-500", children: activeLabels[index] }),
908
+ /* @__PURE__ */ jsx7(
909
+ "input",
910
+ {
911
+ type: "text",
912
+ value: value2,
913
+ onChange: (event) => handleChannelChange(index, event.target.value),
914
+ onBlur: resetChannels,
915
+ inputMode: "numeric",
916
+ className: "h-6 min-w-0 flex-1 border-none bg-transparent p-0 text-right text-sm text-slate-900 placeholder:text-slate-400 focus:outline-none dark:text-zinc-50"
917
+ }
918
+ )
919
+ ]
920
+ },
921
+ activeLabels[index]
922
+ )) })
923
+ ] })
924
+ ] })
925
+ }
926
+ ) : null,
927
+ /* @__PURE__ */ jsx7(
928
+ "input",
929
+ {
930
+ ...rest,
931
+ ref: mergedRef,
932
+ id: inputId,
933
+ type: "hidden",
934
+ className,
935
+ value: resolvedValue,
936
+ readOnly: true
937
+ }
938
+ )
939
+ ] });
940
+ }
941
+ );
942
+
943
+ // components/Combobox/Combobox.tsx
944
+ import * as React9 from "react";
945
+ import { twMerge as twMerge10 } from "tailwind-merge";
946
+
947
+ // components/Dropdown/Dropdown.tsx
948
+ import * as React6 from "react";
949
+ import { twMerge as twMerge7 } from "tailwind-merge";
950
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
951
+ var Dropdown = React6.forwardRef(
952
+ ({
953
+ isOpen,
954
+ disabled,
955
+ placeholder,
956
+ displayValue,
957
+ query,
958
+ className,
959
+ inputClassName,
960
+ highlightClass,
961
+ ariaControls,
962
+ ariaActiveDescendant,
963
+ ariaLabel,
964
+ shellClassName,
965
+ leadingContent,
966
+ inlineContent,
967
+ inputProps,
968
+ inputRef,
969
+ chevronRef,
970
+ showChevron = true,
971
+ onShellFocusCapture,
972
+ onShellBlurCapture,
973
+ onKeyDownCapture,
974
+ onShellMouseDown,
975
+ onInputFocus,
976
+ onInputMouseDown,
977
+ onInputChange,
978
+ onChevronClick,
979
+ children
980
+ }, ref) => {
981
+ const focusClasses = !isOpen ? "focus-within:border-slate-400 focus-within:shadow-[0_0_0_1px_rgba(148,163,184,0.45)] dark:focus-within:border-slate-500" : "";
982
+ const buttonClasses = "inline-flex h-9 w-10 items-center justify-center rounded-xl bg-transparent text-sm text-slate-600 transition focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 dark:text-zinc-200";
983
+ const inputClasses = twMerge7(
984
+ "w-full bg-transparent text-sm text-slate-900 placeholder:text-slate-400 focus:outline-none dark:text-zinc-100 dark:placeholder:text-zinc-500",
985
+ inputClassName,
986
+ inputProps == null ? void 0 : inputProps.className
987
+ );
988
+ const mergedPlaceholder = placeholder != null ? placeholder : inputProps == null ? void 0 : inputProps.placeholder;
989
+ const mergedAriaLabel = ariaLabel != null ? ariaLabel : inputProps == null ? void 0 : inputProps["aria-label"];
990
+ return /* @__PURE__ */ jsxs7("div", { ref, className: twMerge7("relative w-full", className), children: [
991
+ /* @__PURE__ */ jsxs7(
992
+ "div",
993
+ {
994
+ onKeyDownCapture,
995
+ onMouseDown: onShellMouseDown,
996
+ onFocusCapture: onShellFocusCapture,
997
+ onBlurCapture: onShellBlurCapture,
998
+ className: twMerge7(
999
+ "flex h-11 items-center gap-1 rounded-2xl border border-slate-300 bg-white pl-3 pr-1 text-slate-900 shadow-sm transition dark:border-zinc-700/60 dark:bg-zinc-900/70 dark:text-zinc-100",
1000
+ focusClasses,
1001
+ isOpen && twMerge7("rounded-b-none border-b-0 dark:border-b-0", highlightClass),
1002
+ disabled && "opacity-60",
1003
+ shellClassName
1004
+ ),
1005
+ children: [
1006
+ leadingContent ? /* @__PURE__ */ jsx8("div", { className: "flex items-center", children: leadingContent }) : null,
1007
+ /* @__PURE__ */ jsx8(
1008
+ "input",
1009
+ {
1010
+ ...inputProps,
1011
+ ref: inputRef,
1012
+ role: "combobox",
1013
+ "aria-expanded": isOpen,
1014
+ "aria-autocomplete": "list",
1015
+ "aria-controls": ariaControls,
1016
+ "aria-activedescendant": ariaActiveDescendant,
1017
+ "aria-label": mergedAriaLabel,
1018
+ disabled: !!disabled || (inputProps == null ? void 0 : inputProps.disabled),
1019
+ readOnly: !isOpen,
1020
+ onMouseDown: onInputMouseDown,
1021
+ value: isOpen ? query : displayValue,
1022
+ onFocus: onInputFocus,
1023
+ onChange: onInputChange,
1024
+ placeholder: mergedPlaceholder,
1025
+ className: inputClasses
1026
+ }
1027
+ ),
1028
+ inlineContent ? /* @__PURE__ */ jsx8("div", { className: "flex items-center gap-2", children: inlineContent }) : null,
1029
+ showChevron ? /* @__PURE__ */ jsx8(
1030
+ "button",
1031
+ {
1032
+ ref: chevronRef,
1033
+ type: "button",
1034
+ "aria-label": isOpen ? "Close" : "Open",
1035
+ onClick: onChevronClick,
1036
+ className: buttonClasses,
1037
+ disabled: !!disabled,
1038
+ children: /* @__PURE__ */ jsx8(
1039
+ ChevronDown,
1040
+ {
1041
+ className: twMerge7("size-4 transition-transform", isOpen && "rotate-180")
1042
+ }
1043
+ )
1044
+ }
1045
+ ) : null
1046
+ ]
1047
+ }
1048
+ ),
1049
+ children
1050
+ ] });
1051
+ }
1052
+ );
1053
+ Dropdown.displayName = "Dropdown";
1054
+
1055
+ // components/Popover/Popover.tsx
1056
+ import * as React7 from "react";
1057
+ import { twMerge as twMerge8 } from "tailwind-merge";
1058
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1059
+ var MIN_THUMB_SIZE = 28;
1060
+ var TRACK_PADDING = 6;
1061
+ function Scrollbar({ scrollRef }) {
1062
+ const [{ visible, size, offset }, setThumbState] = React7.useState({
1063
+ visible: false,
1064
+ size: MIN_THUMB_SIZE,
1065
+ offset: 0
1066
+ });
1067
+ const dragState = React7.useRef(null);
1068
+ React7.useLayoutEffect(() => {
1069
+ const target = scrollRef.current;
1070
+ if (!target) return;
1071
+ let raf = 0;
1072
+ const update = () => {
1073
+ const el = scrollRef.current;
1074
+ if (!el) return;
1075
+ const { scrollTop, scrollHeight, clientHeight } = el;
1076
+ const scrollRange = scrollHeight - clientHeight;
1077
+ const trackHeight = Math.max(0, clientHeight - TRACK_PADDING * 2);
1078
+ if (scrollRange <= 0 || trackHeight <= 0) {
1079
+ setThumbState((prev) => prev.visible ? { ...prev, visible: false } : prev);
1080
+ return;
1081
+ }
1082
+ const ratio = clientHeight / scrollHeight;
1083
+ const thumbSize = Math.max(trackHeight * ratio, MIN_THUMB_SIZE);
1084
+ const maxOffset = Math.max(0, trackHeight - thumbSize);
1085
+ const thumbOffset = maxOffset > 0 ? scrollTop / scrollRange * maxOffset : 0;
1086
+ setThumbState({ visible: true, size: thumbSize, offset: thumbOffset });
1087
+ };
1088
+ const handleScroll = () => {
1089
+ cancelAnimationFrame(raf);
1090
+ raf = window.requestAnimationFrame(update);
1091
+ };
1092
+ target.addEventListener("scroll", handleScroll, { passive: true });
1093
+ update();
1094
+ const resizeObserver = typeof window !== "undefined" && "ResizeObserver" in window ? new ResizeObserver(() => update()) : null;
1095
+ resizeObserver == null ? void 0 : resizeObserver.observe(target);
1096
+ return () => {
1097
+ target.removeEventListener("scroll", handleScroll);
1098
+ resizeObserver == null ? void 0 : resizeObserver.disconnect();
1099
+ cancelAnimationFrame(raf);
1100
+ };
1101
+ }, [scrollRef]);
1102
+ React7.useEffect(() => {
1103
+ const handlePointerMove = (event) => {
1104
+ const drag = dragState.current;
1105
+ if (!drag) return;
1106
+ const el = scrollRef.current;
1107
+ if (!el) return;
1108
+ const trackHeight = Math.max(0, el.clientHeight - TRACK_PADDING * 2);
1109
+ const scrollRange = el.scrollHeight - el.clientHeight;
1110
+ if (scrollRange <= 0 || trackHeight <= drag.thumbSize) return;
1111
+ event.preventDefault();
1112
+ const delta = event.clientY - drag.startY;
1113
+ const ratio = scrollRange / (trackHeight - drag.thumbSize);
1114
+ const nextScroll = drag.startScrollTop + delta * ratio;
1115
+ el.scrollTop = Math.min(scrollRange, Math.max(0, nextScroll));
1116
+ };
1117
+ const handlePointerUp = () => {
1118
+ dragState.current = null;
1119
+ };
1120
+ window.addEventListener("pointermove", handlePointerMove);
1121
+ window.addEventListener("pointerup", handlePointerUp);
1122
+ return () => {
1123
+ window.removeEventListener("pointermove", handlePointerMove);
1124
+ window.removeEventListener("pointerup", handlePointerUp);
1125
+ };
1126
+ }, [scrollRef]);
1127
+ if (!visible) return null;
1128
+ const handleThumbPointerDown = (event) => {
1129
+ var _a, _b;
1130
+ const el = scrollRef.current;
1131
+ if (!el) return;
1132
+ event.preventDefault();
1133
+ event.stopPropagation();
1134
+ dragState.current = {
1135
+ startY: event.clientY,
1136
+ startScrollTop: el.scrollTop,
1137
+ thumbSize: size
1138
+ };
1139
+ (_b = (_a = event.currentTarget).setPointerCapture) == null ? void 0 : _b.call(_a, event.pointerId);
1140
+ };
1141
+ const handleTrackPointerDown = (event) => {
1142
+ var _a, _b;
1143
+ const el = scrollRef.current;
1144
+ if (!el) return;
1145
+ event.preventDefault();
1146
+ event.stopPropagation();
1147
+ const trackRect = event.currentTarget.getBoundingClientRect();
1148
+ const clickOffset = event.clientY - trackRect.top - TRACK_PADDING;
1149
+ const trackLength = trackRect.height - TRACK_PADDING * 2;
1150
+ const scrollRange = el.scrollHeight - el.clientHeight;
1151
+ if (scrollRange <= 0 || trackLength <= 0) return;
1152
+ const effective = Math.max(1, trackLength - size);
1153
+ const ratio = Math.max(0, Math.min(effective, clickOffset - size / 2)) / effective;
1154
+ el.scrollTop = ratio * scrollRange;
1155
+ dragState.current = {
1156
+ startY: event.clientY,
1157
+ startScrollTop: el.scrollTop,
1158
+ thumbSize: size
1159
+ };
1160
+ (_b = (_a = event.currentTarget).setPointerCapture) == null ? void 0 : _b.call(_a, event.pointerId);
1161
+ };
1162
+ return /* @__PURE__ */ jsx9(
1163
+ "div",
1164
+ {
1165
+ "aria-hidden": "true",
1166
+ className: "pointer-events-none absolute inset-y-[6px] right-[2px] flex w-3 justify-center",
1167
+ children: /* @__PURE__ */ jsx9(
1168
+ "div",
1169
+ {
1170
+ className: "pointer-events-auto relative h-full w-1 rounded-full bg-slate-900/5 shadow-inner dark:bg-white/10",
1171
+ onPointerDown: handleTrackPointerDown,
1172
+ children: /* @__PURE__ */ jsx9(
1173
+ "div",
1174
+ {
1175
+ className: "pointer-events-auto absolute left-1/2 w-1.5 -translate-x-1/2 rounded-full bg-slate-400/80 shadow-sm transition-colors dark:bg-zinc-200/70",
1176
+ style: { height: `${size}px`, top: `${offset}px` },
1177
+ onPointerDown: handleThumbPointerDown
1178
+ }
1179
+ )
1180
+ }
1181
+ )
1182
+ }
1183
+ );
1184
+ }
1185
+ function Popover({ className, children }) {
1186
+ const scrollRef = React7.useRef(null);
1187
+ return /* @__PURE__ */ jsx9(
1188
+ "div",
1189
+ {
1190
+ className: twMerge8(
1191
+ "absolute left-0 right-0 top-full z-[999] -mt-px rounded-b-2xl rounded-t-none border border-slate-300 bg-white/95 py-1 shadow-xl shadow-slate-200/60 backdrop-blur dark:border-zinc-600 dark:bg-zinc-900/95 dark:shadow-zinc-900/40",
1192
+ className
1193
+ ),
1194
+ children: /* @__PURE__ */ jsxs8("div", { className: "relative", children: [
1195
+ children({ scrollRef }),
1196
+ /* @__PURE__ */ jsx9(Scrollbar, { scrollRef })
1197
+ ] })
1198
+ }
1199
+ );
1200
+ }
1201
+
1202
+ // components/Combobox/hooks.ts
1203
+ import { useCallback as useCallback3, useEffect as useEffect4, useState as useState4 } from "react";
1204
+ function useControlledState(controlled, defaultValue) {
1205
+ const [internal, setInternal] = useState4(defaultValue);
1206
+ const isControlled = controlled !== void 0;
1207
+ const value = isControlled ? controlled : internal;
1208
+ const setValue = useCallback3(
1209
+ (next) => {
1210
+ if (!isControlled) {
1211
+ setInternal(next);
1212
+ }
1213
+ },
1214
+ [isControlled]
1215
+ );
1216
+ return [value, setValue];
1217
+ }
1218
+ function useOutsideClick(refs, handler) {
1219
+ useEffect4(() => {
1220
+ function onDoc(e) {
1221
+ const target = e.target;
1222
+ const inside = refs.some((r) => r.current && r.current.contains(target));
1223
+ if (!inside) handler();
1224
+ }
1225
+ document.addEventListener("mousedown", onDoc);
1226
+ return () => document.removeEventListener("mousedown", onDoc);
1227
+ }, [refs, handler]);
1228
+ }
1229
+
1230
+ // components/Combobox/Listbox.tsx
1231
+ import * as React8 from "react";
1232
+ import { twMerge as twMerge9 } from "tailwind-merge";
1233
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1234
+ function Listbox({
1235
+ id,
1236
+ options,
1237
+ activeIndex,
1238
+ selectedId,
1239
+ onHoverIndex,
1240
+ onSelectIndex,
1241
+ listRef,
1242
+ emptyState,
1243
+ renderOption
1244
+ }) {
1245
+ React8.useEffect(() => {
1246
+ var _a;
1247
+ if (activeIndex < 0) return;
1248
+ const el = (_a = listRef.current) == null ? void 0 : _a.querySelector(`[data-index="${activeIndex}"]`);
1249
+ el == null ? void 0 : el.scrollIntoView({ block: "nearest" });
1250
+ }, [activeIndex, listRef]);
1251
+ return /* @__PURE__ */ jsxs9(
1252
+ "ul",
1253
+ {
1254
+ ref: listRef,
1255
+ id,
1256
+ role: "listbox",
1257
+ className: twMerge9("combobox-scrollbar max-h-64 overflow-auto px-1 pr-4"),
1258
+ children: [
1259
+ options.length === 0 && /* @__PURE__ */ jsx10("li", { "aria-disabled": true, className: "select-none", children: /* @__PURE__ */ jsx10("div", { className: "px-3 py-2 text-sm text-slate-500 dark:text-zinc-500", children: emptyState != null ? emptyState : "No results" }) }),
1260
+ options.map((opt, i) => {
1261
+ const selected = opt.id === selectedId;
1262
+ const active = i === activeIndex;
1263
+ const optionState = { active, selected };
1264
+ return /* @__PURE__ */ jsx10(
1265
+ "li",
1266
+ {
1267
+ id: `${id}-option-${i}`,
1268
+ role: "option",
1269
+ "aria-selected": selected,
1270
+ "aria-disabled": opt.disabled,
1271
+ "data-index": i,
1272
+ className: "list-none",
1273
+ onMouseEnter: () => !opt.disabled && onHoverIndex(i),
1274
+ onMouseDown: (e) => e.preventDefault(),
1275
+ onClick: () => !opt.disabled && onSelectIndex(i),
1276
+ children: renderOption ? renderOption(opt, optionState) : /* @__PURE__ */ jsxs9(
1277
+ "div",
1278
+ {
1279
+ className: twMerge9(
1280
+ "flex cursor-pointer items-center gap-3 rounded-xl px-3 py-2 text-sm",
1281
+ active ? "bg-slate-100 text-slate-900 dark:bg-zinc-800/70 dark:text-zinc-100" : "text-slate-700 hover:bg-slate-100 dark:text-zinc-200 dark:hover:bg-zinc-800/70",
1282
+ opt.disabled && "cursor-not-allowed opacity-50"
1283
+ ),
1284
+ children: [
1285
+ /* @__PURE__ */ jsx10("span", { className: "truncate", children: opt.label }),
1286
+ selected ? /* @__PURE__ */ jsx10(Check, { className: "ml-auto h-3 w-3 text-slate-600 dark:text-zinc-300" }) : /* @__PURE__ */ jsx10("span", { className: "ml-auto inline-flex h-3 w-3" })
1287
+ ]
1288
+ }
1289
+ )
1290
+ },
1291
+ opt.id
1292
+ );
1293
+ })
1294
+ ]
1295
+ }
1296
+ );
1297
+ }
1298
+
1299
+ // components/Combobox/Combobox.tsx
1300
+ import { jsx as jsx11 } from "react/jsx-runtime";
1301
+ function InnerCombobox({
1302
+ options,
1303
+ value: valueProp,
1304
+ defaultValue,
1305
+ onChange,
1306
+ placeholder = "Select.",
1307
+ disabled,
1308
+ className,
1309
+ listClassName,
1310
+ inputClassName,
1311
+ emptyState,
1312
+ renderOption,
1313
+ filter,
1314
+ openOnFocus = true,
1315
+ ariaLabel
1316
+ }, forwardedRef) {
1317
+ var _a, _b;
1318
+ const inputRef = React9.useRef(null);
1319
+ const containerRef = React9.useRef(null);
1320
+ const chevronRef = React9.useRef(null);
1321
+ const outsideClickRefs = React9.useMemo(
1322
+ () => [containerRef],
1323
+ [containerRef]
1324
+ );
1325
+ const mergedInputRef = (node) => {
1326
+ inputRef.current = node;
1327
+ assignRef(forwardedRef, node);
1328
+ };
1329
+ const [open, setOpen] = React9.useState(false);
1330
+ const closeOnOutsideClick = React9.useCallback(() => setOpen(false), []);
1331
+ const [query, setQuery] = React9.useState("");
1332
+ const [activeIndex, setActiveIndex] = React9.useState(-1);
1333
+ const suppressNextOpenRef = React9.useRef(false);
1334
+ const suppressToggleRef = React9.useRef(false);
1335
+ const prevOpenRef = React9.useRef(open);
1336
+ const [selected, setSelected] = useControlledState(
1337
+ valueProp,
1338
+ defaultValue != null ? defaultValue : null
1339
+ );
1340
+ const isEffectivelyOpen = open && !suppressNextOpenRef.current;
1341
+ const id = React9.useId();
1342
+ const listboxId = `${id}-listbox`;
1343
+ const normalizedFilter = React9.useMemo(() => {
1344
+ if (filter) return filter;
1345
+ return (opt, q) => opt.label.toLowerCase().includes(q.trim().toLowerCase());
1346
+ }, [filter]);
1347
+ const visibleOptions = React9.useMemo(() => {
1348
+ const base = options != null ? options : [];
1349
+ return query ? base.filter((o) => normalizedFilter(o, query)) : base;
1350
+ }, [options, query, normalizedFilter]);
1351
+ const selectedOptionIndex = React9.useMemo(() => {
1352
+ if (!selected) return -1;
1353
+ const indexFromOptions = (options != null ? options : []).findIndex((option) => option.id === selected.id);
1354
+ if (indexFromOptions !== -1) return indexFromOptions;
1355
+ return visibleOptions.findIndex((option) => option.id === selected.id);
1356
+ }, [options, visibleOptions, selected]);
1357
+ const firstEnabledOptionIndex = React9.useMemo(() => {
1358
+ if (!options) return -1;
1359
+ return options.findIndex((option) => !option.disabled);
1360
+ }, [options]);
1361
+ const lastEnabledOptionIndex = React9.useMemo(() => {
1362
+ if (!options) return -1;
1363
+ for (let i = options.length - 1; i >= 0; i -= 1) {
1364
+ if (!options[i].disabled) {
1365
+ return i;
1366
+ }
1367
+ }
1368
+ return -1;
1369
+ }, [options]);
1370
+ const cycleEnabledOptionIndex = React9.useCallback(
1371
+ (startIndex, direction) => {
1372
+ if (!options || options.length === 0) return -1;
1373
+ let next = startIndex;
1374
+ for (let i = 0; i < options.length; i += 1) {
1375
+ next = (next + direction + options.length) % options.length;
1376
+ if (!options[next].disabled) return next;
1377
+ }
1378
+ return -1;
1379
+ },
1380
+ [options]
1381
+ );
1382
+ const selectedIndex = React9.useMemo(() => {
1383
+ if (!selected) return -1;
1384
+ return visibleOptions.findIndex((option) => option.id === selected.id);
1385
+ }, [visibleOptions, selected]);
1386
+ const firstEnabledIndex = React9.useMemo(() => {
1387
+ return visibleOptions.findIndex((option) => !option.disabled);
1388
+ }, [visibleOptions]);
1389
+ const lastEnabledIndex = React9.useMemo(() => {
1390
+ for (let i = visibleOptions.length - 1; i >= 0; i -= 1) {
1391
+ if (!visibleOptions[i].disabled) {
1392
+ return i;
1393
+ }
1394
+ }
1395
+ return -1;
1396
+ }, [visibleOptions]);
1397
+ const cycleEnabledIndex = React9.useCallback(
1398
+ (startIndex, direction) => {
1399
+ if (visibleOptions.length === 0) return -1;
1400
+ let next = startIndex;
1401
+ for (let i = 0; i < visibleOptions.length; i += 1) {
1402
+ next = (next + direction + visibleOptions.length) % visibleOptions.length;
1403
+ if (!visibleOptions[next].disabled) return next;
1404
+ }
1405
+ return -1;
1406
+ },
1407
+ [visibleOptions]
1408
+ );
1409
+ React9.useEffect(() => {
1410
+ if (visibleOptions.length === 0) {
1411
+ setActiveIndex(-1);
1412
+ return;
1413
+ }
1414
+ if (activeIndex >= visibleOptions.length) setActiveIndex(visibleOptions.length - 1);
1415
+ }, [visibleOptions, activeIndex]);
1416
+ React9.useEffect(() => {
1417
+ const wasOpen = prevOpenRef.current;
1418
+ prevOpenRef.current = open;
1419
+ if (open && !wasOpen) {
1420
+ const initialIndex = selectedIndex !== -1 ? selectedIndex : firstEnabledIndex;
1421
+ setActiveIndex(initialIndex);
1422
+ }
1423
+ if (!open && wasOpen) {
1424
+ setActiveIndex(-1);
1425
+ }
1426
+ }, [open, selectedIndex, firstEnabledIndex]);
1427
+ React9.useLayoutEffect(() => {
1428
+ if (open && suppressNextOpenRef.current) {
1429
+ suppressNextOpenRef.current = false;
1430
+ setOpen(false);
1431
+ }
1432
+ }, [open]);
1433
+ useOutsideClick(outsideClickRefs, closeOnOutsideClick);
1434
+ function commitSelection(opt, opts = {}) {
1435
+ const { refocus = true } = opts;
1436
+ setSelected(opt);
1437
+ onChange == null ? void 0 : onChange(opt);
1438
+ if (opt) setQuery("");
1439
+ setOpen(false);
1440
+ if (refocus) {
1441
+ requestAnimationFrame(() => {
1442
+ var _a2;
1443
+ return (_a2 = inputRef.current) == null ? void 0 : _a2.focus();
1444
+ });
1445
+ }
1446
+ }
1447
+ function onKeyDown(e) {
1448
+ const isArrowDown = e.key === "ArrowDown" || e.code === "ArrowDown" || e.key === "Down";
1449
+ const isArrowUp = e.key === "ArrowUp" || e.code === "ArrowUp" || e.key === "Up";
1450
+ if (isArrowDown || isArrowUp) {
1451
+ e.preventDefault();
1452
+ const direction = isArrowDown ? 1 : -1;
1453
+ if (!open) {
1454
+ suppressNextOpenRef.current = true;
1455
+ const source = options != null ? options : [];
1456
+ if (source.length === 0) return;
1457
+ if (selectedOptionIndex === -1) {
1458
+ const fallbackIndex = direction === 1 ? firstEnabledOptionIndex : lastEnabledOptionIndex;
1459
+ if (fallbackIndex !== -1) {
1460
+ commitSelection(source[fallbackIndex], { refocus: false });
1461
+ }
1462
+ } else {
1463
+ const nextIndex = cycleEnabledOptionIndex(selectedOptionIndex, direction);
1464
+ if (nextIndex !== -1 && nextIndex !== selectedOptionIndex) {
1465
+ commitSelection(source[nextIndex], { refocus: false });
1466
+ }
1467
+ }
1468
+ return;
1469
+ }
1470
+ const baseIndex = activeIndex === -1 ? direction === 1 ? firstEnabledIndex : lastEnabledIndex : activeIndex;
1471
+ if (baseIndex === -1) return;
1472
+ const next = cycleEnabledIndex(baseIndex, direction);
1473
+ if (next !== -1) {
1474
+ setActiveIndex(next);
1475
+ }
1476
+ return;
1477
+ }
1478
+ if (!open && (e.key === "Enter" || e.key === " ")) {
1479
+ e.preventDefault();
1480
+ suppressNextOpenRef.current = false;
1481
+ setOpen(true);
1482
+ return;
1483
+ }
1484
+ if (!open) return;
1485
+ switch (e.key) {
1486
+ case "Home": {
1487
+ if (firstEnabledIndex !== -1) {
1488
+ e.preventDefault();
1489
+ setActiveIndex(firstEnabledIndex);
1490
+ }
1491
+ break;
1492
+ }
1493
+ case "End": {
1494
+ if (lastEnabledIndex !== -1) {
1495
+ e.preventDefault();
1496
+ setActiveIndex(lastEnabledIndex);
1497
+ }
1498
+ break;
1499
+ }
1500
+ case "Enter": {
1501
+ e.preventDefault();
1502
+ if (activeIndex >= 0 && !visibleOptions[activeIndex].disabled) {
1503
+ commitSelection(visibleOptions[activeIndex]);
1504
+ }
1505
+ break;
1506
+ }
1507
+ case "Escape": {
1508
+ e.preventDefault();
1509
+ setOpen(false);
1510
+ break;
1511
+ }
1512
+ }
1513
+ }
1514
+ const selectedLabel = (_a = selected == null ? void 0 : selected.label) != null ? _a : "";
1515
+ const selectedId = (_b = selected == null ? void 0 : selected.id) != null ? _b : null;
1516
+ const highlightBorder = "border-slate-400 dark:border-slate-500";
1517
+ const listboxHighlight = isEffectivelyOpen ? highlightBorder : "";
1518
+ function openList() {
1519
+ if (disabled) return;
1520
+ suppressNextOpenRef.current = false;
1521
+ setOpen(true);
1522
+ setQuery("");
1523
+ const initialIndex = selectedIndex !== -1 ? selectedIndex : firstEnabledIndex;
1524
+ setActiveIndex(initialIndex);
1525
+ requestAnimationFrame(() => {
1526
+ var _a2;
1527
+ return (_a2 = inputRef.current) == null ? void 0 : _a2.focus();
1528
+ });
1529
+ }
1530
+ return /* @__PURE__ */ jsx11(
1531
+ Dropdown,
1532
+ {
1533
+ ref: containerRef,
1534
+ isOpen: isEffectivelyOpen,
1535
+ disabled,
1536
+ placeholder,
1537
+ displayValue: selectedLabel,
1538
+ query,
1539
+ className,
1540
+ inputClassName,
1541
+ highlightClass: highlightBorder,
1542
+ ariaControls: listboxId,
1543
+ ariaActiveDescendant: activeIndex >= 0 ? `${listboxId}-option-${activeIndex}` : void 0,
1544
+ ariaLabel,
1545
+ inputRef: mergedInputRef,
1546
+ chevronRef,
1547
+ onKeyDownCapture: onKeyDown,
1548
+ onShellMouseDown: (e) => {
1549
+ var _a2;
1550
+ if (disabled) return;
1551
+ if ((_a2 = chevronRef.current) == null ? void 0 : _a2.contains(e.target)) return;
1552
+ if (!isEffectivelyOpen) {
1553
+ e.preventDefault();
1554
+ openList();
1555
+ suppressToggleRef.current = true;
1556
+ }
1557
+ },
1558
+ onInputMouseDown: (e) => {
1559
+ if (!open && !disabled) {
1560
+ e.preventDefault();
1561
+ openList();
1562
+ }
1563
+ },
1564
+ onInputFocus: () => {
1565
+ if (suppressNextOpenRef.current) {
1566
+ suppressNextOpenRef.current = false;
1567
+ return;
1568
+ }
1569
+ if (openOnFocus && !disabled) {
1570
+ setOpen(true);
1571
+ setQuery("");
1572
+ const initialIndex = selectedIndex !== -1 ? selectedIndex : firstEnabledIndex;
1573
+ setActiveIndex(initialIndex);
1574
+ }
1575
+ },
1576
+ onInputChange: (e) => {
1577
+ setQuery(e.target.value);
1578
+ if (!open) {
1579
+ suppressNextOpenRef.current = false;
1580
+ setOpen(true);
1581
+ }
1582
+ },
1583
+ onChevronClick: () => {
1584
+ if (disabled) return;
1585
+ if (suppressToggleRef.current) {
1586
+ suppressToggleRef.current = false;
1587
+ setOpen((o) => !o);
1588
+ return;
1589
+ }
1590
+ suppressNextOpenRef.current = false;
1591
+ setOpen((o) => !o);
1592
+ },
1593
+ children: isEffectivelyOpen && /* @__PURE__ */ jsx11(Popover, { className: twMerge10(listboxHighlight, listClassName), children: ({ scrollRef }) => /* @__PURE__ */ jsx11(
1594
+ Listbox,
1595
+ {
1596
+ id: listboxId,
1597
+ options: visibleOptions,
1598
+ activeIndex,
1599
+ selectedId,
1600
+ onHoverIndex: setActiveIndex,
1601
+ onSelectIndex: (i) => commitSelection(visibleOptions[i]),
1602
+ listRef: scrollRef,
1603
+ emptyState,
1604
+ renderOption
1605
+ }
1606
+ ) })
1607
+ }
1608
+ );
1609
+ }
1610
+ var Combobox = React9.forwardRef(InnerCombobox);
1611
+
1612
+ // components/DatalistInput/DatalistInput.tsx
1613
+ import * as React10 from "react";
1614
+ import { twMerge as twMerge11 } from "tailwind-merge";
1615
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1616
+ var DatalistInput = React10.forwardRef(
1617
+ function DatalistInput2({ label, description, options, className, id, disabled, ...rest }, ref) {
1618
+ var _a, _b, _c;
1619
+ const generatedId = React10.useId();
1620
+ const inputId = id != null ? id : generatedId;
1621
+ const descriptionId = React10.useId();
1622
+ const dropdownRef = React10.useRef(null);
1623
+ const listboxRef = React10.useRef(null);
1624
+ const inputRef = React10.useRef(null);
1625
+ const mergedRef = React10.useCallback(
1626
+ (node) => {
1627
+ inputRef.current = node;
1628
+ assignRef(ref, node);
1629
+ },
1630
+ [ref]
1631
+ );
1632
+ const [query, setQuery] = React10.useState((_b = (_a = rest.defaultValue) == null ? void 0 : _a.toString()) != null ? _b : "");
1633
+ const [open, setOpen] = React10.useState(false);
1634
+ const [activeIndex, setActiveIndex] = React10.useState(-1);
1635
+ const listboxId = `${inputId}-listbox`;
1636
+ const activeDescendant = activeIndex >= 0 ? `${listboxId}-option-${activeIndex}` : void 0;
1637
+ const controlledValue = (_c = rest.value) == null ? void 0 : _c.toString();
1638
+ const displayValue = controlledValue != null ? controlledValue : query;
1639
+ useOutsideClick([dropdownRef], () => setOpen(false));
1640
+ const filtered = React10.useMemo(() => {
1641
+ if (!displayValue.trim()) return options;
1642
+ return options.filter((opt) => opt.toLowerCase().includes(displayValue.toLowerCase()));
1643
+ }, [options, displayValue]);
1644
+ React10.useEffect(() => {
1645
+ if (controlledValue !== void 0) {
1646
+ setQuery(controlledValue);
1647
+ }
1648
+ }, [controlledValue]);
1649
+ const emitChange = (val) => {
1650
+ var _a2;
1651
+ setQuery(val);
1652
+ (_a2 = rest.onChange) == null ? void 0 : _a2.call(rest, {
1653
+ target: { value: val }
1654
+ });
1655
+ };
1656
+ const handleKeyDown = (e) => {
1657
+ if (!open && (e.key === "ArrowDown" || e.key === "ArrowUp")) {
1658
+ setOpen(true);
1659
+ setActiveIndex(0);
1660
+ }
1661
+ if (!open) return;
1662
+ switch (e.key) {
1663
+ case "ArrowDown":
1664
+ e.preventDefault();
1665
+ setActiveIndex((prev) => Math.min(filtered.length - 1, prev + 1));
1666
+ break;
1667
+ case "ArrowUp":
1668
+ e.preventDefault();
1669
+ setActiveIndex((prev) => Math.max(0, prev - 1));
1670
+ break;
1671
+ case "Enter":
1672
+ if (activeIndex >= 0 && filtered[activeIndex]) {
1673
+ emitChange(filtered[activeIndex]);
1674
+ setOpen(false);
1675
+ }
1676
+ break;
1677
+ case "Escape":
1678
+ setOpen(false);
1679
+ break;
1680
+ }
1681
+ };
1682
+ const handleSelect = (val) => {
1683
+ var _a2;
1684
+ emitChange(val);
1685
+ setOpen(false);
1686
+ (_a2 = inputRef.current) == null ? void 0 : _a2.focus();
1687
+ };
1688
+ const handleInputChange = (e) => {
1689
+ var _a2;
1690
+ setQuery(e.target.value);
1691
+ setOpen(true);
1692
+ setActiveIndex(0);
1693
+ (_a2 = rest.onChange) == null ? void 0 : _a2.call(rest, e);
1694
+ };
1695
+ const setListRef = (node, scrollRef) => {
1696
+ scrollRef.current = node;
1697
+ listboxRef.current = node;
1698
+ };
1699
+ return /* @__PURE__ */ jsxs10("div", { className: "space-y-1.5", children: [
1700
+ label ? /* @__PURE__ */ jsx12(
1701
+ "label",
1702
+ {
1703
+ htmlFor: inputId,
1704
+ className: "text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-zinc-400",
1705
+ children: label
1706
+ }
1707
+ ) : null,
1708
+ /* @__PURE__ */ jsx12(
1709
+ Dropdown,
1710
+ {
1711
+ ref: dropdownRef,
1712
+ isOpen: open,
1713
+ disabled,
1714
+ placeholder: rest.placeholder,
1715
+ displayValue,
1716
+ query: displayValue,
1717
+ inputRef: mergedRef,
1718
+ showChevron: false,
1719
+ inlineContent: /* @__PURE__ */ jsx12("span", { className: "text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400 dark:text-zinc-500", children: "CMD" }),
1720
+ onKeyDownCapture: handleKeyDown,
1721
+ onShellMouseDown: () => setOpen(true),
1722
+ onInputFocus: () => setOpen(true),
1723
+ onInputMouseDown: () => setOpen(true),
1724
+ onInputChange: handleInputChange,
1725
+ ariaControls: listboxId,
1726
+ ariaActiveDescendant: activeDescendant,
1727
+ ariaLabel: rest["aria-label"],
1728
+ inputClassName: className,
1729
+ inputProps: {
1730
+ ...rest,
1731
+ id: inputId,
1732
+ "aria-describedby": description ? descriptionId : rest["aria-describedby"]
1733
+ },
1734
+ children: open && filtered.length ? /* @__PURE__ */ jsx12(Popover, { children: ({ scrollRef }) => /* @__PURE__ */ jsx12(
1735
+ "ul",
1736
+ {
1737
+ ref: (node) => setListRef(node, scrollRef),
1738
+ id: listboxId,
1739
+ role: "listbox",
1740
+ className: "combobox-scrollbar max-h-56 overflow-auto py-1 text-sm text-slate-800 dark:text-zinc-100",
1741
+ children: filtered.map((opt, index) => /* @__PURE__ */ jsx12(
1742
+ "li",
1743
+ {
1744
+ id: `${listboxId}-option-${index}`,
1745
+ role: "option",
1746
+ "aria-selected": index === activeIndex,
1747
+ children: /* @__PURE__ */ jsxs10(
1748
+ "button",
1749
+ {
1750
+ type: "button",
1751
+ onMouseEnter: () => setActiveIndex(index),
1752
+ onClick: () => handleSelect(opt),
1753
+ className: twMerge11(
1754
+ "flex w-full items-center gap-3 px-3 py-2 text-left transition hover:bg-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-400/70 dark:hover:bg-zinc-800/60",
1755
+ index === activeIndex && "bg-slate-50 dark:bg-zinc-800/60"
1756
+ ),
1757
+ children: [
1758
+ /* @__PURE__ */ jsx12("span", { className: "text-[10px] uppercase tracking-[0.2em] text-slate-400 dark:text-zinc-500", children: "cmd" }),
1759
+ /* @__PURE__ */ jsx12("span", { className: "font-semibold", children: opt })
1760
+ ]
1761
+ }
1762
+ )
1763
+ },
1764
+ opt
1765
+ ))
1766
+ }
1767
+ ) }) : null
1768
+ }
1769
+ ),
1770
+ description ? /* @__PURE__ */ jsx12("p", { id: descriptionId, className: "text-xs text-slate-500 dark:text-zinc-400", children: description }) : null
1771
+ ] });
1772
+ }
1773
+ );
1774
+
1775
+ // components/DatePicker/DatePicker.tsx
1776
+ import * as React12 from "react";
1777
+ import { twMerge as twMerge13 } from "tailwind-merge";
1778
+
1779
+ // components/Select/Select.tsx
1780
+ import * as React11 from "react";
1781
+ import { twMerge as twMerge12 } from "tailwind-merge";
1782
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1783
+ function Select({
1784
+ label,
1785
+ description,
1786
+ error,
1787
+ options,
1788
+ value,
1789
+ defaultValue,
1790
+ onChange,
1791
+ placeholder = "Select an option",
1792
+ className,
1793
+ disabled,
1794
+ leadingContent,
1795
+ inlineContent
1796
+ }) {
1797
+ var _a, _b;
1798
+ const containerRef = React11.useRef(null);
1799
+ const inputRef = React11.useRef(null);
1800
+ const chevronRef = React11.useRef(null);
1801
+ const popoverListRef = React11.useRef(null);
1802
+ const suppressToggleRef = React11.useRef(false);
1803
+ const id = React11.useId();
1804
+ const listboxId = `${id}-listbox`;
1805
+ const [open, setOpen] = React11.useState(false);
1806
+ const [activeIndex, setActiveIndex] = React11.useState(-1);
1807
+ const [selected, setSelected] = useControlledState(value, defaultValue != null ? defaultValue : null);
1808
+ useOutsideClick(
1809
+ [containerRef],
1810
+ () => setOpen(false)
1811
+ );
1812
+ const selectedOption = (_a = options.find((opt) => opt.value === selected)) != null ? _a : null;
1813
+ const selectedIndex = React11.useMemo(
1814
+ () => options.findIndex((opt) => opt.value === selected),
1815
+ [options, selected]
1816
+ );
1817
+ const firstEnabled = React11.useMemo(() => options.findIndex((o) => !o.disabled), [options]);
1818
+ const lastEnabled = React11.useMemo(() => {
1819
+ for (let i = options.length - 1; i >= 0; i -= 1) {
1820
+ if (!options[i].disabled) return i;
1821
+ }
1822
+ return -1;
1823
+ }, [options]);
1824
+ const cycleEnabled = React11.useCallback(
1825
+ (start, dir) => {
1826
+ if (!options.length) return -1;
1827
+ let next = start;
1828
+ for (let i = 0; i < options.length; i += 1) {
1829
+ next = (next + dir + options.length) % options.length;
1830
+ if (!options[next].disabled) return next;
1831
+ }
1832
+ return -1;
1833
+ },
1834
+ [options]
1835
+ );
1836
+ const commitSelection = (index) => {
1837
+ const opt = options[index];
1838
+ if (!opt || opt.disabled) return;
1839
+ setSelected(opt.value);
1840
+ onChange == null ? void 0 : onChange(opt.value);
1841
+ setOpen(false);
1842
+ requestAnimationFrame(() => {
1843
+ var _a2;
1844
+ return (_a2 = inputRef.current) == null ? void 0 : _a2.focus();
1845
+ });
1846
+ };
1847
+ const openList = () => {
1848
+ if (disabled) return;
1849
+ setOpen(true);
1850
+ setActiveIndex(selectedIndex !== -1 ? selectedIndex : firstEnabled);
1851
+ requestAnimationFrame(() => {
1852
+ var _a2;
1853
+ return (_a2 = inputRef.current) == null ? void 0 : _a2.focus();
1854
+ });
1855
+ };
1856
+ const handleKeyDown = (e) => {
1857
+ const isArrowDown = e.key === "ArrowDown" || e.code === "ArrowDown" || e.key === "Down";
1858
+ const isArrowUp = e.key === "ArrowUp" || e.code === "ArrowUp" || e.key === "Up";
1859
+ if (!open && (isArrowDown || isArrowUp)) {
1860
+ e.preventDefault();
1861
+ const direction = isArrowDown ? 1 : -1;
1862
+ const source = options;
1863
+ if (!source.length) return;
1864
+ const startIndex = selectedIndex === -1 ? direction === 1 ? firstEnabled : lastEnabled : selectedIndex;
1865
+ const nextIndex = startIndex === -1 ? -1 : cycleEnabled(startIndex, direction);
1866
+ if (nextIndex !== -1 && nextIndex !== selectedIndex) {
1867
+ commitSelection(nextIndex);
1868
+ }
1869
+ return;
1870
+ }
1871
+ if (!open && (e.key === "Enter" || e.key === " ")) {
1872
+ e.preventDefault();
1873
+ openList();
1874
+ return;
1875
+ }
1876
+ if (!open) return;
1877
+ switch (e.key) {
1878
+ case "ArrowDown":
1879
+ e.preventDefault();
1880
+ setActiveIndex((prev) => {
1881
+ const start = prev === -1 ? firstEnabled : prev;
1882
+ return cycleEnabled(start, 1);
1883
+ });
1884
+ break;
1885
+ case "ArrowUp":
1886
+ e.preventDefault();
1887
+ setActiveIndex((prev) => {
1888
+ const start = prev === -1 ? lastEnabled : prev;
1889
+ return cycleEnabled(start, -1);
1890
+ });
1891
+ break;
1892
+ case "Home":
1893
+ e.preventDefault();
1894
+ setActiveIndex(firstEnabled);
1895
+ break;
1896
+ case "End":
1897
+ e.preventDefault();
1898
+ setActiveIndex(lastEnabled);
1899
+ break;
1900
+ case "Enter":
1901
+ case " ":
1902
+ e.preventDefault();
1903
+ if (activeIndex >= 0) commitSelection(activeIndex);
1904
+ break;
1905
+ case "Escape":
1906
+ e.preventDefault();
1907
+ setOpen(false);
1908
+ break;
1909
+ }
1910
+ };
1911
+ React11.useEffect(() => {
1912
+ var _a2;
1913
+ if (!open) return;
1914
+ const refObj = popoverListRef.current;
1915
+ const el = (_a2 = refObj == null ? void 0 : refObj.current) == null ? void 0 : _a2.querySelector(`[data-index="${activeIndex}"]`);
1916
+ el == null ? void 0 : el.scrollIntoView({ block: "nearest" });
1917
+ }, [open, activeIndex]);
1918
+ const displayValue = (_b = selectedOption == null ? void 0 : selectedOption.label) != null ? _b : "";
1919
+ const highlightBorder = "border-slate-400 dark:border-slate-500";
1920
+ const listboxHighlight = open ? highlightBorder : "";
1921
+ return /* @__PURE__ */ jsxs11("div", { className: "space-y-1.5", children: [
1922
+ label ? /* @__PURE__ */ jsx13("p", { className: "text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-zinc-400", children: label }) : null,
1923
+ /* @__PURE__ */ jsx13(
1924
+ Dropdown,
1925
+ {
1926
+ ref: containerRef,
1927
+ isOpen: open,
1928
+ disabled,
1929
+ placeholder,
1930
+ displayValue,
1931
+ query: displayValue,
1932
+ className: twMerge12(
1933
+ "w-full",
1934
+ error && "border-rose-300 focus-within:border-rose-400 focus-within:shadow-[0_0_0_1px_rgba(248,113,113,0.35)] dark:border-rose-500/60",
1935
+ className
1936
+ ),
1937
+ inputClassName: "font-semibold",
1938
+ highlightClass: highlightBorder,
1939
+ ariaControls: listboxId,
1940
+ ariaLabel: label,
1941
+ inputRef,
1942
+ chevronRef,
1943
+ leadingContent,
1944
+ inlineContent,
1945
+ onKeyDownCapture: handleKeyDown,
1946
+ onShellMouseDown: (e) => {
1947
+ var _a2;
1948
+ if (disabled) return;
1949
+ if ((_a2 = chevronRef.current) == null ? void 0 : _a2.contains(e.target)) return;
1950
+ if (!open) {
1951
+ e.preventDefault();
1952
+ openList();
1953
+ suppressToggleRef.current = true;
1954
+ }
1955
+ },
1956
+ onInputMouseDown: (e) => {
1957
+ if (!open && !disabled) {
1958
+ e.preventDefault();
1959
+ openList();
1960
+ }
1961
+ },
1962
+ onInputFocus: () => {
1963
+ if (suppressToggleRef.current) {
1964
+ suppressToggleRef.current = false;
1965
+ }
1966
+ },
1967
+ onInputChange: () => {
1968
+ },
1969
+ onChevronClick: () => {
1970
+ if (disabled) return;
1971
+ if (suppressToggleRef.current) {
1972
+ suppressToggleRef.current = false;
1973
+ setOpen((o) => !o);
1974
+ return;
1975
+ }
1976
+ setOpen((o) => !o);
1977
+ },
1978
+ children: open && /* @__PURE__ */ jsx13(Popover, { className: listboxHighlight, children: ({ scrollRef }) => {
1979
+ popoverListRef.current = scrollRef;
1980
+ return /* @__PURE__ */ jsx13(
1981
+ "ul",
1982
+ {
1983
+ ref: scrollRef,
1984
+ id: listboxId,
1985
+ role: "listbox",
1986
+ className: "combobox-scrollbar max-h-64 overflow-auto px-1 pr-4",
1987
+ children: options.map((opt, index) => {
1988
+ const isSelected = selected === opt.value;
1989
+ const isActive = activeIndex === index;
1990
+ return /* @__PURE__ */ jsx13("li", { className: "list-none", "data-index": index, children: /* @__PURE__ */ jsxs11(
1991
+ "button",
1992
+ {
1993
+ type: "button",
1994
+ role: "option",
1995
+ "aria-selected": isSelected,
1996
+ disabled: opt.disabled,
1997
+ onMouseEnter: () => !opt.disabled && setActiveIndex(index),
1998
+ onClick: () => commitSelection(index),
1999
+ className: twMerge12(
2000
+ "flex w-full items-center gap-3 rounded-xl px-3 py-2 text-left text-sm transition",
2001
+ isActive ? "bg-slate-100 text-slate-900 dark:bg-zinc-800/70 dark:text-zinc-100" : "text-slate-700 hover:bg-slate-100 dark:text-zinc-200 dark:hover:bg-zinc-800/70",
2002
+ isSelected && "font-semibold",
2003
+ opt.disabled && "cursor-not-allowed opacity-50"
2004
+ ),
2005
+ children: [
2006
+ /* @__PURE__ */ jsxs11("span", { className: "flex-1 text-left", children: [
2007
+ /* @__PURE__ */ jsx13("span", { className: "block text-slate-900 dark:text-zinc-100", children: opt.label }),
2008
+ opt.description ? /* @__PURE__ */ jsx13("span", { className: "block text-xs text-slate-500 dark:text-zinc-400", children: opt.description }) : null
2009
+ ] }),
2010
+ isSelected ? /* @__PURE__ */ jsx13(Check, { className: "ml-auto h-3 w-3 text-slate-600 dark:text-zinc-300" }) : /* @__PURE__ */ jsx13("span", { className: "ml-auto inline-flex h-3 w-3" })
2011
+ ]
2012
+ }
2013
+ ) }, opt.value);
2014
+ })
2015
+ }
2016
+ );
2017
+ } })
2018
+ }
2019
+ ),
2020
+ description ? /* @__PURE__ */ jsx13("p", { className: "text-xs text-slate-500 dark:text-zinc-400", children: description }) : null,
2021
+ error ? /* @__PURE__ */ jsx13("p", { className: "text-xs font-medium text-rose-500 dark:text-rose-400", children: error }) : null
2022
+ ] });
2023
+ }
2024
+
2025
+ // components/DatePicker/DatePicker.tsx
2026
+ import { Fragment, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
2027
+ function formatLocalDateString(year, month, day) {
2028
+ const y = String(year).padStart(4, "0");
2029
+ const m = String(month + 1).padStart(2, "0");
2030
+ const d = String(day).padStart(2, "0");
2031
+ return `${y}-${m}-${d}`;
2032
+ }
2033
+ function parseLocalDateString(value) {
2034
+ if (!value) return null;
2035
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value.trim());
2036
+ if (!match) return null;
2037
+ const [, y, m, d] = match;
2038
+ const year = Number(y);
2039
+ const month = Number(m) - 1;
2040
+ const day = Number(d);
2041
+ const date = new Date(year, month, day);
2042
+ if (Number.isNaN(date.getTime())) return null;
2043
+ return { date, year, month, day };
2044
+ }
2045
+ function getMonthDays(year, month) {
2046
+ const first = new Date(year, month, 1);
2047
+ const start = first.getDay();
2048
+ const days = new Date(year, month + 1, 0).getDate();
2049
+ const grid = [];
2050
+ for (let i = 0; i < start; i += 1) grid.push({ day: null });
2051
+ for (let d = 1; d <= days; d += 1) {
2052
+ const date = new Date(year, month, d);
2053
+ grid.push({ day: d, date: date.toISOString().slice(0, 10) });
2054
+ }
2055
+ return grid;
2056
+ }
2057
+ var DatePicker = React12.forwardRef(function DatePicker2({
2058
+ label,
2059
+ description,
2060
+ error,
2061
+ className,
2062
+ disabled,
2063
+ value,
2064
+ defaultValue,
2065
+ onChange,
2066
+ type = "date",
2067
+ timeIntervalMinutes = 30,
2068
+ use24HourClock = true
2069
+ }, ref) {
2070
+ const isDate = type === "date";
2071
+ const clampInterval = Math.max(1, Math.min(60, Math.floor(timeIntervalMinutes)));
2072
+ const normalizeTimeString = (input) => {
2073
+ if (!input) return null;
2074
+ const match = /(\d{1,2}):(\d{2})/.exec(input);
2075
+ if (!match) return null;
2076
+ const h = Math.min(23, Math.max(0, Number(match[1])));
2077
+ const m = Math.min(59, Math.max(0, Number(match[2])));
2078
+ return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`;
2079
+ };
2080
+ const formatTimeLabel = React12.useCallback(
2081
+ (time) => {
2082
+ if (use24HourClock) return time;
2083
+ const [hStr, m] = time.split(":");
2084
+ let hNum = Number(hStr);
2085
+ const suffix = hNum >= 12 ? "PM" : "AM";
2086
+ hNum = hNum % 12 || 12;
2087
+ return `${String(hNum).padStart(2, "0")}:${m} ${suffix}`;
2088
+ },
2089
+ [use24HourClock]
2090
+ );
2091
+ const initial = (() => {
2092
+ var _a, _b, _c;
2093
+ if (!isDate) {
2094
+ return (_b = (_a = normalizeTimeString(value)) != null ? _a : normalizeTimeString(defaultValue)) != null ? _b : (() => {
2095
+ const now = /* @__PURE__ */ new Date();
2096
+ const minutes = now.getMinutes();
2097
+ const rounded = Math.round(minutes / clampInterval) * clampInterval;
2098
+ const h = rounded >= 60 ? now.getHours() + 1 : now.getHours();
2099
+ const m = rounded % 60;
2100
+ const safeH = (h + 24) % 24;
2101
+ return `${String(safeH).padStart(2, "0")}:${String(m).padStart(2, "0")}`;
2102
+ })();
2103
+ }
2104
+ const normalized = (_c = parseLocalDateString(value != null ? value : defaultValue)) == null ? void 0 : _c.date;
2105
+ if (normalized)
2106
+ return formatLocalDateString(
2107
+ normalized.getFullYear(),
2108
+ normalized.getMonth(),
2109
+ normalized.getDate()
2110
+ );
2111
+ const today = /* @__PURE__ */ new Date();
2112
+ return formatLocalDateString(today.getFullYear(), today.getMonth(), today.getDate());
2113
+ })();
2114
+ const [open, setOpen] = React12.useState(false);
2115
+ const [current, setCurrent] = React12.useState(initial);
2116
+ const [month, setMonth] = React12.useState(() => {
2117
+ var _a, _b;
2118
+ const base = (_b = (_a = parseLocalDateString(initial)) == null ? void 0 : _a.date) != null ? _b : /* @__PURE__ */ new Date();
2119
+ return { year: base.getFullYear(), month: base.getMonth() };
2120
+ });
2121
+ const [viewMode, setViewMode] = React12.useState("day");
2122
+ const dropdownRef = React12.useRef(null);
2123
+ const toggleRef = React12.useRef(null);
2124
+ const inputInnerRef = React12.useRef(null);
2125
+ const suppressToggleRef = React12.useRef(false);
2126
+ const canvasRef = React12.useRef(null);
2127
+ const [useShortLabel, setUseShortLabel] = React12.useState(false);
2128
+ const id = React12.useId();
2129
+ const popoverId = `${id}-popover`;
2130
+ const setInputRef = React12.useCallback(
2131
+ (node) => {
2132
+ inputInnerRef.current = node;
2133
+ assignRef(ref, node);
2134
+ },
2135
+ [ref]
2136
+ );
2137
+ useOutsideClick(
2138
+ [dropdownRef],
2139
+ () => setOpen(false)
2140
+ );
2141
+ const commit = (next) => {
2142
+ setCurrent(next);
2143
+ onChange == null ? void 0 : onChange(next);
2144
+ setOpen(false);
2145
+ setViewMode("day");
2146
+ };
2147
+ const parsedDate = React12.useMemo(() => {
2148
+ var _a;
2149
+ const parsed = parseLocalDateString(current);
2150
+ return (_a = parsed == null ? void 0 : parsed.date) != null ? _a : null;
2151
+ }, [current]);
2152
+ const longDisplayLabel = React12.useMemo(() => {
2153
+ if (!parsedDate) return current;
2154
+ return parsedDate.toLocaleDateString(void 0, {
2155
+ weekday: "short",
2156
+ month: "short",
2157
+ day: "numeric",
2158
+ year: "numeric"
2159
+ });
2160
+ }, [parsedDate, current]);
2161
+ const shortDisplayLabel = React12.useMemo(() => {
2162
+ if (!parsedDate) return current;
2163
+ return parsedDate.toLocaleDateString(void 0, {
2164
+ month: "2-digit",
2165
+ day: "2-digit",
2166
+ year: "2-digit"
2167
+ });
2168
+ }, [parsedDate, current]);
2169
+ const displayLabel = useShortLabel ? shortDisplayLabel : longDisplayLabel;
2170
+ const timeOptions = React12.useMemo(() => {
2171
+ const opts = [];
2172
+ for (let h = 0; h < 24; h += 1) {
2173
+ for (let m = 0; m < 60; m += clampInterval) {
2174
+ const hh = String(h).padStart(2, "0");
2175
+ const mm = String(m).padStart(2, "0");
2176
+ const valueStr = `${hh}:${mm}`;
2177
+ opts.push({ value: valueStr, label: formatTimeLabel(valueStr) });
2178
+ }
2179
+ }
2180
+ return opts;
2181
+ }, [clampInterval, formatTimeLabel]);
2182
+ const highlightBorder = "border-slate-400 shadow-[0_0_0_1px_rgba(148,163,184,0.45)] dark:border-slate-500";
2183
+ const monthNames = React12.useMemo(
2184
+ () => ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
2185
+ []
2186
+ );
2187
+ const decadeStart = Math.floor(month.year / 10) * 10;
2188
+ const goPrev = () => {
2189
+ if (viewMode === "day") {
2190
+ setMonth((prev) => ({
2191
+ year: prev.month === 0 ? prev.year - 1 : prev.year,
2192
+ month: prev.month === 0 ? 11 : prev.month - 1
2193
+ }));
2194
+ } else if (viewMode === "month") {
2195
+ setMonth((prev) => ({ ...prev, year: prev.year - 1 }));
2196
+ } else {
2197
+ setMonth((prev) => ({ ...prev, year: prev.year - 10 }));
2198
+ }
2199
+ };
2200
+ const goNext = () => {
2201
+ if (viewMode === "day") {
2202
+ setMonth((prev) => ({
2203
+ year: prev.month === 11 ? prev.year + 1 : prev.year,
2204
+ month: prev.month === 11 ? 0 : prev.month + 1
2205
+ }));
2206
+ } else if (viewMode === "month") {
2207
+ setMonth((prev) => ({ ...prev, year: prev.year + 1 }));
2208
+ } else {
2209
+ setMonth((prev) => ({ ...prev, year: prev.year + 10 }));
2210
+ }
2211
+ };
2212
+ const rangeStart = decadeStart - 1;
2213
+ const rangeEnd = decadeStart + 10;
2214
+ const headerLabel = viewMode === "year" ? `${rangeStart} - ${rangeEnd}` : viewMode === "month" ? `${month.year}` : new Date(month.year, month.month).toLocaleString("default", {
2215
+ month: "long",
2216
+ year: "numeric"
2217
+ });
2218
+ function openPicker() {
2219
+ if (disabled) return;
2220
+ const base = parsedDate != null ? parsedDate : /* @__PURE__ */ new Date();
2221
+ setMonth({ year: base.getFullYear(), month: base.getMonth() });
2222
+ setViewMode("day");
2223
+ setOpen(true);
2224
+ }
2225
+ const evaluateLabelFit = React12.useCallback(() => {
2226
+ var _a;
2227
+ if (!parsedDate) {
2228
+ setUseShortLabel(false);
2229
+ return;
2230
+ }
2231
+ const inputEl = inputInnerRef.current;
2232
+ if (!inputEl) return;
2233
+ const inputStyles = getComputedStyle(inputEl);
2234
+ const padding = parseFloat(inputStyles.paddingLeft || "0") + parseFloat(inputStyles.paddingRight || "0");
2235
+ const available = inputEl.clientWidth - padding;
2236
+ if (available <= 0) return;
2237
+ const font = inputStyles.font || `${inputStyles.fontWeight} ${inputStyles.fontSize} ${inputStyles.fontFamily}`;
2238
+ const canvas = (_a = canvasRef.current) != null ? _a : document.createElement("canvas");
2239
+ canvasRef.current = canvas;
2240
+ const ctx = canvas.getContext("2d");
2241
+ if (!ctx) return;
2242
+ ctx.font = font;
2243
+ const fullWidth = ctx.measureText(longDisplayLabel).width;
2244
+ const fitsLong = fullWidth <= available;
2245
+ setUseShortLabel(!fitsLong);
2246
+ }, [parsedDate, longDisplayLabel]);
2247
+ React12.useLayoutEffect(() => {
2248
+ evaluateLabelFit();
2249
+ const ro = new ResizeObserver(() => evaluateLabelFit());
2250
+ if (dropdownRef.current) ro.observe(dropdownRef.current);
2251
+ return () => ro.disconnect();
2252
+ }, [evaluateLabelFit]);
2253
+ React12.useEffect(() => {
2254
+ const id2 = requestAnimationFrame(() => evaluateLabelFit());
2255
+ return () => cancelAnimationFrame(id2);
2256
+ }, [displayLabel, evaluateLabelFit]);
2257
+ if (!isDate) {
2258
+ return /* @__PURE__ */ jsx14(
2259
+ Select,
2260
+ {
2261
+ label,
2262
+ description,
2263
+ error,
2264
+ options: timeOptions,
2265
+ value: current,
2266
+ onChange: (val) => commit(val != null ? val : current),
2267
+ placeholder: "Select time",
2268
+ disabled,
2269
+ className,
2270
+ leadingContent: /* @__PURE__ */ jsx14("span", { className: "pointer-events-none rounded-xl bg-slate-100 px-2 py-1 shadow-inner dark:bg-zinc-800/70", children: /* @__PURE__ */ jsx14(Clock, { className: "h-4 w-4 text-slate-500 dark:text-zinc-300" }) })
2271
+ }
2272
+ );
2273
+ }
2274
+ return /* @__PURE__ */ jsxs12("div", { className: "space-y-1.5", children: [
2275
+ label ? /* @__PURE__ */ jsx14("p", { className: "text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-zinc-400", children: label }) : null,
2276
+ /* @__PURE__ */ jsx14("div", { className: "relative", children: /* @__PURE__ */ jsx14(
2277
+ Dropdown,
2278
+ {
2279
+ ref: dropdownRef,
2280
+ isOpen: open,
2281
+ disabled,
2282
+ placeholder: "",
2283
+ displayValue: displayLabel,
2284
+ query: displayLabel,
2285
+ className: "w-full",
2286
+ inputClassName: twMerge13("min-w-0 font-semibold", className),
2287
+ shellClassName: twMerge13(
2288
+ error && "border-rose-300 focus-within:border-rose-400 focus-within:shadow-[0_0_0_1px_rgba(248,113,113,0.35)] dark:border-rose-500/60"
2289
+ ),
2290
+ leadingContent: /* @__PURE__ */ jsx14("span", { className: "pointer-events-none rounded-xl bg-slate-100 px-2 py-1 shadow-inner dark:bg-zinc-800/70", children: /* @__PURE__ */ jsx14(Calendar, { className: "h-4 w-4 text-slate-500 dark:text-zinc-300" }) }),
2291
+ highlightClass: highlightBorder,
2292
+ ariaControls: popoverId,
2293
+ ariaLabel: label,
2294
+ inputRef: setInputRef,
2295
+ chevronRef: toggleRef,
2296
+ onKeyDownCapture: () => {
2297
+ },
2298
+ onShellMouseDown: (e) => {
2299
+ var _a;
2300
+ if (disabled) return;
2301
+ if ((_a = toggleRef.current) == null ? void 0 : _a.contains(e.target)) return;
2302
+ if (!open) {
2303
+ e.preventDefault();
2304
+ suppressToggleRef.current = true;
2305
+ openPicker();
2306
+ requestAnimationFrame(() => {
2307
+ var _a2;
2308
+ return (_a2 = toggleRef.current) == null ? void 0 : _a2.focus();
2309
+ });
2310
+ }
2311
+ },
2312
+ onInputMouseDown: (e) => {
2313
+ if (!open && !disabled) {
2314
+ e.preventDefault();
2315
+ suppressToggleRef.current = true;
2316
+ openPicker();
2317
+ requestAnimationFrame(() => {
2318
+ var _a;
2319
+ return (_a = toggleRef.current) == null ? void 0 : _a.focus();
2320
+ });
2321
+ }
2322
+ },
2323
+ onInputFocus: () => {
2324
+ if (suppressToggleRef.current) {
2325
+ suppressToggleRef.current = false;
2326
+ return;
2327
+ }
2328
+ openPicker();
2329
+ },
2330
+ onInputChange: () => {
2331
+ },
2332
+ onChevronClick: () => {
2333
+ if (disabled) return;
2334
+ if (suppressToggleRef.current) {
2335
+ suppressToggleRef.current = false;
2336
+ setOpen((o) => !o);
2337
+ return;
2338
+ }
2339
+ setOpen((o) => !o);
2340
+ },
2341
+ children: open && /* @__PURE__ */ jsx14(Popover, { className: twMerge13("p-3", highlightBorder), children: () => /* @__PURE__ */ jsxs12("div", { className: "space-y-3", id: popoverId, children: [
2342
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center justify-between text-sm text-slate-600 dark:text-zinc-300", children: [
2343
+ /* @__PURE__ */ jsx14(
2344
+ Button,
2345
+ {
2346
+ type: "button",
2347
+ onClick: goPrev,
2348
+ className: "h-8 w-8 min-w-0 rounded-xl border border-slate-200 bg-white p-0 text-sm font-semibold text-slate-700 shadow-sm dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100",
2349
+ children: /* @__PURE__ */ jsx14("span", { style: { transform: "translateY(-1.5px)" }, children: "<" })
2350
+ }
2351
+ ),
2352
+ viewMode === "year" ? /* @__PURE__ */ jsx14("span", { className: "font-semibold text-slate-900 dark:text-zinc-100", children: headerLabel }) : /* @__PURE__ */ jsx14(
2353
+ "button",
2354
+ {
2355
+ type: "button",
2356
+ onClick: () => setViewMode((prev) => prev === "day" ? "month" : "year"),
2357
+ className: "font-semibold text-slate-900 transition hover:underline dark:text-zinc-100",
2358
+ children: headerLabel
2359
+ }
2360
+ ),
2361
+ /* @__PURE__ */ jsx14(
2362
+ Button,
2363
+ {
2364
+ type: "button",
2365
+ onClick: goNext,
2366
+ className: "h-8 w-8 min-w-0 rounded-xl border border-slate-200 bg-white p-0 text-sm font-semibold text-slate-700 shadow-sm dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100",
2367
+ children: /* @__PURE__ */ jsx14("span", { style: { transform: "translateY(-1.5px)" }, children: ">" })
2368
+ }
2369
+ )
2370
+ ] }),
2371
+ viewMode === "day" ? /* @__PURE__ */ jsxs12(Fragment, { children: [
2372
+ /* @__PURE__ */ jsx14("div", { className: "grid grid-cols-7 gap-1 text-center text-[11px] uppercase tracking-[0.2em] text-slate-400 dark:text-zinc-500", children: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map((d) => /* @__PURE__ */ jsx14("span", { children: d }, d)) }),
2373
+ /* @__PURE__ */ jsx14("div", { className: "grid grid-cols-7 gap-1", children: getMonthDays(month.year, month.month).map((cell, idx) => {
2374
+ var _a, _b;
2375
+ const isSelected = cell.date === current;
2376
+ return /* @__PURE__ */ jsx14(
2377
+ "button",
2378
+ {
2379
+ type: "button",
2380
+ disabled: !cell.date,
2381
+ onClick: () => {
2382
+ if (!cell.date) return;
2383
+ commit(cell.date);
2384
+ },
2385
+ className: twMerge13(
2386
+ "h-9 rounded-xl text-sm font-semibold text-slate-700 transition hover:bg-slate-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-400/70 dark:text-zinc-200 dark:hover:bg-zinc-800",
2387
+ isSelected && "bg-slate-200 text-slate-900 ring-1 ring-slate-400/70 shadow-sm dark:bg-zinc-800 dark:hover:bg-zinc-700/80 dark:text-zinc-100 dark:ring-zinc-500/70",
2388
+ !cell.date && "opacity-30"
2389
+ ),
2390
+ children: (_b = cell.day) != null ? _b : ""
2391
+ },
2392
+ `${(_a = cell.date) != null ? _a : "empty"}-${idx}`
2393
+ );
2394
+ }) })
2395
+ ] }) : viewMode === "month" ? /* @__PURE__ */ jsx14("div", { className: "grid grid-cols-3 gap-2", children: monthNames.map((name, idx) => {
2396
+ const isSelected = (parsedDate == null ? void 0 : parsedDate.getFullYear()) === month.year && (parsedDate == null ? void 0 : parsedDate.getMonth()) === idx;
2397
+ return /* @__PURE__ */ jsx14(
2398
+ "button",
2399
+ {
2400
+ type: "button",
2401
+ onClick: () => {
2402
+ setMonth((prev) => ({ ...prev, month: idx }));
2403
+ setViewMode("day");
2404
+ },
2405
+ className: twMerge13(
2406
+ "h-10 rounded-xl border border-slate-200 bg-white text-sm font-semibold text-slate-700 shadow-sm transition hover:-translate-y-[1px] hover:shadow-md dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100",
2407
+ isSelected && "border-slate-400 bg-slate-100 dark:border-slate-500 dark:bg-zinc-800"
2408
+ ),
2409
+ children: name
2410
+ },
2411
+ name
2412
+ );
2413
+ }) }) : /* @__PURE__ */ jsx14("div", { className: "grid grid-cols-3 gap-2", children: Array.from({ length: 12 }, (_, i) => rangeStart + i).map((yr) => {
2414
+ const isSelected = (parsedDate == null ? void 0 : parsedDate.getFullYear()) === yr;
2415
+ return /* @__PURE__ */ jsx14(
2416
+ "button",
2417
+ {
2418
+ type: "button",
2419
+ onClick: () => {
2420
+ setMonth((prev) => ({ ...prev, year: yr }));
2421
+ setViewMode("month");
2422
+ },
2423
+ className: twMerge13(
2424
+ "h-10 rounded-xl border border-slate-200 bg-white text-sm font-semibold text-slate-700 shadow-sm transition hover:-translate-y-[1px] hover:shadow-md dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100",
2425
+ isSelected && "border-slate-400 bg-slate-100 dark:border-slate-500 dark:bg-zinc-800"
2426
+ ),
2427
+ children: yr
2428
+ },
2429
+ yr
2430
+ );
2431
+ }) })
2432
+ ] }) })
2433
+ }
2434
+ ) }),
2435
+ description ? /* @__PURE__ */ jsx14("p", { className: "text-xs text-slate-500 dark:text-zinc-400", children: description }) : null,
2436
+ error ? /* @__PURE__ */ jsx14("p", { className: "text-xs font-medium text-rose-500 dark:text-rose-400", children: error }) : null
2437
+ ] });
2438
+ });
2439
+
2440
+ // components/Dialog/Dialog.tsx
2441
+ import * as React13 from "react";
2442
+ import { createPortal } from "react-dom";
2443
+ import { twMerge as twMerge14 } from "tailwind-merge";
2444
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
2445
+ function Dialog({
2446
+ open,
2447
+ onClose,
2448
+ title,
2449
+ description,
2450
+ children,
2451
+ footer,
2452
+ modal = true,
2453
+ draggable = true
2454
+ }) {
2455
+ const overlayRef = React13.useRef(null);
2456
+ const dialogRef = React13.useRef(null);
2457
+ const dragState = React13.useRef(null);
2458
+ const [dragOffset, setDragOffset] = React13.useState({ x: 0, y: 0 });
2459
+ React13.useEffect(() => {
2460
+ if (!open) return;
2461
+ const handleKey = (event) => {
2462
+ if (event.key === "Escape") {
2463
+ event.preventDefault();
2464
+ onClose();
2465
+ }
2466
+ };
2467
+ window.addEventListener("keydown", handleKey);
2468
+ return () => window.removeEventListener("keydown", handleKey);
2469
+ }, [open, onClose]);
2470
+ React13.useEffect(() => {
2471
+ var _a;
2472
+ if (!open) return;
2473
+ const previouslyFocused = document.activeElement;
2474
+ (_a = dialogRef.current) == null ? void 0 : _a.focus();
2475
+ return () => previouslyFocused == null ? void 0 : previouslyFocused.focus();
2476
+ }, [open]);
2477
+ React13.useEffect(() => {
2478
+ if (!open || modal) return;
2479
+ const handlePointer = (event) => {
2480
+ const target = event.target;
2481
+ if (dialogRef.current && !dialogRef.current.contains(target)) {
2482
+ onClose();
2483
+ }
2484
+ };
2485
+ document.addEventListener("pointerdown", handlePointer);
2486
+ return () => document.removeEventListener("pointerdown", handlePointer);
2487
+ }, [open, modal, onClose]);
2488
+ React13.useEffect(() => {
2489
+ if (!open) {
2490
+ setDragOffset({ x: 0, y: 0 });
2491
+ }
2492
+ }, [open]);
2493
+ React13.useEffect(() => {
2494
+ return () => {
2495
+ dragState.current = null;
2496
+ };
2497
+ }, []);
2498
+ const clampOffset = React13.useCallback(
2499
+ (x, y, size) => {
2500
+ if (!modal) return { x, y };
2501
+ const maxX = Math.max(0, (window.innerWidth - size.width) / 2);
2502
+ const maxY = Math.max(0, (window.innerHeight - size.height) / 2);
2503
+ return {
2504
+ x: Math.min(maxX, Math.max(-maxX, x)),
2505
+ y: Math.min(maxY, Math.max(-maxY, y))
2506
+ };
2507
+ },
2508
+ [modal]
2509
+ );
2510
+ const isInteractiveTarget = (node) => {
2511
+ if (!node) return false;
2512
+ if (node.closest("[data-dialog-draggable-stop]")) return true;
2513
+ const tag = node.tagName.toLowerCase();
2514
+ const interactive = ["button", "input", "select", "textarea", "option", "a", "label"];
2515
+ if (interactive.includes(tag)) return true;
2516
+ if (node.getAttribute("role") === "button" || node.getAttribute("role") === "link") return true;
2517
+ if (node.isContentEditable) return true;
2518
+ return false;
2519
+ };
2520
+ const onDragPointerDown = (event) => {
2521
+ var _a;
2522
+ if (!draggable) return;
2523
+ if (isInteractiveTarget(event.target)) return;
2524
+ event.preventDefault();
2525
+ const rect = (_a = dialogRef.current) == null ? void 0 : _a.getBoundingClientRect();
2526
+ if (!rect) return;
2527
+ dragState.current = {
2528
+ startX: event.clientX,
2529
+ startY: event.clientY,
2530
+ originX: dragOffset.x,
2531
+ originY: dragOffset.y,
2532
+ width: rect.width,
2533
+ height: rect.height
2534
+ };
2535
+ const handleMove = (e) => {
2536
+ const current = dragState.current;
2537
+ if (!current) return;
2538
+ const deltaX = e.clientX - current.startX;
2539
+ const deltaY = e.clientY - current.startY;
2540
+ const next = {
2541
+ x: current.originX + deltaX,
2542
+ y: current.originY + deltaY
2543
+ };
2544
+ const clamped = clampOffset(next.x, next.y, { width: current.width, height: current.height });
2545
+ setDragOffset(clamped);
2546
+ };
2547
+ const handleUp = () => {
2548
+ dragState.current = null;
2549
+ window.removeEventListener("pointermove", handleMove);
2550
+ window.removeEventListener("pointerup", handleUp);
2551
+ window.removeEventListener("pointercancel", handleUp);
2552
+ };
2553
+ window.addEventListener("pointermove", handleMove);
2554
+ window.addEventListener("pointerup", handleUp);
2555
+ window.addEventListener("pointercancel", handleUp);
2556
+ };
2557
+ if (!open) return null;
2558
+ const dialogCard = /* @__PURE__ */ jsxs13(
2559
+ "div",
2560
+ {
2561
+ ref: dialogRef,
2562
+ role: "dialog",
2563
+ "aria-modal": modal || void 0,
2564
+ tabIndex: -1,
2565
+ "aria-label": title,
2566
+ className: twMerge14(
2567
+ "w-[min(640px,92vw)] rounded-3xl border border-slate-200 bg-white/95 p-6 shadow-2xl ring-1 ring-white/80 dark:border-zinc-700/60 dark:bg-zinc-900/95 dark:ring-zinc-800/80",
2568
+ modal ? "" : "pointer-events-auto",
2569
+ draggable && "cursor-grab active:cursor-grabbing"
2570
+ ),
2571
+ style: { transform: `translate(${dragOffset.x}px, ${dragOffset.y}px)` },
2572
+ onPointerDown: onDragPointerDown,
2573
+ children: [
2574
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-start justify-between gap-3", children: [
2575
+ /* @__PURE__ */ jsxs13("div", { children: [
2576
+ /* @__PURE__ */ jsx15("p", { className: "text-xs font-semibold uppercase tracking-[0.3em] text-slate-500 dark:text-zinc-400", children: "Dialog" }),
2577
+ /* @__PURE__ */ jsx15("h2", { className: "text-xl font-semibold text-slate-900 dark:text-zinc-100", children: title }),
2578
+ description ? /* @__PURE__ */ jsx15("p", { className: "mt-1 text-sm text-slate-600 dark:text-zinc-400", children: description }) : null
2579
+ ] }),
2580
+ /* @__PURE__ */ jsx15(
2581
+ Button,
2582
+ {
2583
+ onClick: onClose,
2584
+ "aria-label": "Close dialog",
2585
+ className: "size-8 !px-0 !py-0 text-sm font-semibold",
2586
+ children: "X"
2587
+ }
2588
+ )
2589
+ ] }),
2590
+ /* @__PURE__ */ jsx15("div", { className: "mt-4 space-y-3 text-sm text-slate-700 dark:text-zinc-300", children }),
2591
+ footer ? /* @__PURE__ */ jsx15("div", { className: "mt-6 flex flex-wrap justify-end gap-2", children: footer }) : null
2592
+ ]
2593
+ }
2594
+ );
2595
+ return createPortal(
2596
+ modal ? /* @__PURE__ */ jsx15(
2597
+ "div",
2598
+ {
2599
+ ref: overlayRef,
2600
+ className: "fixed inset-0 z-50 flex items-center justify-center bg-zinc-950/60 backdrop-blur-sm",
2601
+ onMouseDown: (e) => {
2602
+ if (e.target === overlayRef.current) onClose();
2603
+ },
2604
+ children: dialogCard
2605
+ }
2606
+ ) : /* @__PURE__ */ jsx15("div", { className: "pointer-events-none fixed bottom-6 right-6 z-50 flex justify-end", children: dialogCard }),
2607
+ document.body
2608
+ );
2609
+ }
2610
+
2611
+ // components/Disclosure/Disclosure.tsx
2612
+ import * as React14 from "react";
2613
+ import { twMerge as twMerge15 } from "tailwind-merge";
2614
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
2615
+ var Disclosure = React14.forwardRef(function Disclosure2({ title, children, className, subtle, ...rest }, ref) {
2616
+ const summaryId = React14.useId();
2617
+ return /* @__PURE__ */ jsxs14(
2618
+ "details",
2619
+ {
2620
+ ...rest,
2621
+ ref,
2622
+ className: twMerge15(
2623
+ "group rounded-2xl border border-slate-200 bg-white/90 p-3 shadow-sm transition dark:border-zinc-700/60 dark:bg-zinc-900/70",
2624
+ subtle && "border-transparent bg-white/60 shadow-none dark:bg-zinc-900/40",
2625
+ className
2626
+ ),
2627
+ children: [
2628
+ /* @__PURE__ */ jsxs14(
2629
+ "summary",
2630
+ {
2631
+ id: summaryId,
2632
+ className: "flex cursor-pointer list-none items-center justify-between gap-3 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-400/70",
2633
+ children: [
2634
+ /* @__PURE__ */ jsx16("span", { className: "flex flex-1 items-center gap-2", children: /* @__PURE__ */ jsx16("span", { className: "rounded-lg bg-slate-100 px-2 py-1 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500 shadow-inner dark:bg-zinc-800/70 dark:text-zinc-300", children: title }) }),
2635
+ /* @__PURE__ */ jsx16(
2636
+ "span",
2637
+ {
2638
+ className: "text-slate-400 transition-transform duration-200 group-open:rotate-180 dark:text-zinc-500",
2639
+ "aria-hidden": "true",
2640
+ children: "\u25BE"
2641
+ }
2642
+ )
2643
+ ]
2644
+ }
2645
+ ),
2646
+ /* @__PURE__ */ jsx16("div", { className: "mt-3 space-y-2 text-sm text-slate-600 dark:text-zinc-300", children })
2647
+ ]
2648
+ }
2649
+ );
2650
+ });
2651
+
2652
+ // components/InputField/InputField.tsx
2653
+ import * as React15 from "react";
2654
+ import { twMerge as twMerge16 } from "tailwind-merge";
2655
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
2656
+ var InputField = React15.forwardRef(function InputField2({ label, description, error, leadingIcon, trailingLabel, className, id, disabled, ...rest }, ref) {
2657
+ const generatedId = React15.useId();
2658
+ const inputId = id != null ? id : generatedId;
2659
+ const descriptionId = React15.useId();
2660
+ const errorId = React15.useId();
2661
+ const hintIds = [description ? descriptionId : null, error ? errorId : null].filter(Boolean);
2662
+ const resolvedAriaDescribedBy = hintIds.length ? hintIds.join(" ") : void 0;
2663
+ const containerClasses = twMerge16(
2664
+ "flex items-center gap-3 rounded-2xl border border-slate-300 bg-white/70 px-3 py-2 transition focus-within:border-slate-400 focus-within:shadow-[0_0_0_1px_rgba(148,163,184,0.45)] dark:border-zinc-700 dark:bg-zinc-900/70 dark:focus-within:border-slate-500",
2665
+ disabled && "opacity-60",
2666
+ error && "border-rose-300 focus-within:border-rose-400 focus-within:shadow-[0_0_0_1px_rgba(248,113,113,0.35)] dark:border-rose-500/60"
2667
+ );
2668
+ const inputClasses = twMerge16(
2669
+ "flex-1 border-none bg-transparent text-sm text-slate-900 placeholder:text-slate-400 focus:outline-none disabled:cursor-not-allowed dark:text-zinc-100 dark:placeholder:text-zinc-500",
2670
+ className
2671
+ );
2672
+ const leadingElm = leadingIcon ? /* @__PURE__ */ jsx17("span", { className: "text-slate-400 dark:text-zinc-500", "aria-hidden": "true", children: leadingIcon }) : null;
2673
+ const trailingElm = trailingLabel ? /* @__PURE__ */ jsx17("span", { className: "text-xs font-semibold uppercase tracking-wide text-slate-400 dark:text-zinc-500", children: trailingLabel }) : null;
2674
+ return /* @__PURE__ */ jsxs15("div", { className: "space-y-1.5", children: [
2675
+ label ? /* @__PURE__ */ jsx17(
2676
+ "label",
2677
+ {
2678
+ htmlFor: inputId,
2679
+ className: "text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-zinc-400",
2680
+ children: label
2681
+ }
2682
+ ) : null,
2683
+ /* @__PURE__ */ jsxs15("div", { className: containerClasses, children: [
2684
+ leadingElm,
2685
+ /* @__PURE__ */ jsx17(
2686
+ "input",
2687
+ {
2688
+ ...rest,
2689
+ ref,
2690
+ id: inputId,
2691
+ className: inputClasses,
2692
+ "aria-invalid": error ? true : void 0,
2693
+ "aria-describedby": resolvedAriaDescribedBy,
2694
+ disabled
2695
+ }
2696
+ ),
2697
+ trailingElm
2698
+ ] }),
2699
+ description ? /* @__PURE__ */ jsx17("p", { id: descriptionId, className: "text-xs text-slate-500 dark:text-zinc-400", children: description }) : null,
2700
+ error ? /* @__PURE__ */ jsx17("p", { id: errorId, className: "text-xs font-medium text-rose-500 dark:text-rose-400", children: error }) : null
2701
+ ] });
2702
+ });
2703
+
2704
+ // components/NumberInput/NumberInput.tsx
2705
+ import * as React16 from "react";
2706
+ import { twMerge as twMerge17 } from "tailwind-merge";
2707
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
2708
+ var NumberInput = React16.forwardRef(
2709
+ function NumberInput2({
2710
+ label,
2711
+ description,
2712
+ error,
2713
+ className,
2714
+ disabled,
2715
+ value,
2716
+ defaultValue = 0,
2717
+ onChange,
2718
+ step = 1,
2719
+ suffix,
2720
+ min,
2721
+ max
2722
+ }, ref) {
2723
+ const [internal, setInternal] = React16.useState(defaultValue);
2724
+ const isControlled = typeof value === "number";
2725
+ const resolved = isControlled ? value != null ? value : 0 : internal;
2726
+ const update = (next) => {
2727
+ const clamped = Math.max(min != null ? min : -Infinity, Math.min(max != null ? max : Infinity, next));
2728
+ if (!isControlled) setInternal(clamped);
2729
+ onChange == null ? void 0 : onChange(clamped);
2730
+ };
2731
+ return /* @__PURE__ */ jsxs16("div", { className: "space-y-1.5", children: [
2732
+ label ? /* @__PURE__ */ jsx18("p", { className: "text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-zinc-400", children: label }) : null,
2733
+ /* @__PURE__ */ jsxs16(
2734
+ "div",
2735
+ {
2736
+ className: twMerge17(
2737
+ "flex items-center gap-3 rounded-2xl border border-slate-300 bg-white/80 px-3 py-2 shadow-sm transition focus-within:border-slate-400 focus-within:shadow-[0_0_0_1px_rgba(148,163,184,0.45)] dark:border-zinc-700 dark:bg-zinc-900/70 dark:focus-within:border-slate-500",
2738
+ disabled && "opacity-60",
2739
+ error && "border-rose-300 focus-within:border-rose-400 focus-within:shadow-[0_0_0_1px_rgba(248,113,113,0.35)] dark:border-rose-500/60"
2740
+ ),
2741
+ children: [
2742
+ /* @__PURE__ */ jsxs16("div", { className: "flex flex-col gap-1", children: [
2743
+ /* @__PURE__ */ jsx18(
2744
+ "button",
2745
+ {
2746
+ type: "button",
2747
+ "aria-label": "Increase",
2748
+ onClick: () => update(resolved + step),
2749
+ className: "grid size-7 place-items-center rounded-xl border border-slate-200 bg-white text-xs font-semibold text-slate-700 shadow-sm transition hover:-translate-y-[1px] hover:shadow-md disabled:opacity-50 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100",
2750
+ disabled,
2751
+ children: "+"
2752
+ }
2753
+ ),
2754
+ /* @__PURE__ */ jsx18(
2755
+ "button",
2756
+ {
2757
+ type: "button",
2758
+ "aria-label": "Decrease",
2759
+ onClick: () => update(resolved - step),
2760
+ className: "grid size-7 place-items-center rounded-xl border border-slate-200 bg-white text-xs font-semibold text-slate-700 shadow-sm transition hover:-translate-y-[1px] hover:shadow-md disabled:opacity-50 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100",
2761
+ disabled,
2762
+ children: "-"
2763
+ }
2764
+ )
2765
+ ] }),
2766
+ /* @__PURE__ */ jsx18(
2767
+ "input",
2768
+ {
2769
+ ref,
2770
+ type: "number",
2771
+ value: resolved,
2772
+ onChange: (e) => update(Number(e.target.value)),
2773
+ min,
2774
+ max,
2775
+ step,
2776
+ disabled,
2777
+ className: twMerge17(
2778
+ "flex-1 border-none bg-transparent text-2xl font-semibold tabular-nums text-slate-900 placeholder:text-slate-400 focus:outline-none dark:text-zinc-100 dark:placeholder:text-zinc-500 appearance-none [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none",
2779
+ className
2780
+ )
2781
+ }
2782
+ ),
2783
+ suffix ? /* @__PURE__ */ jsx18("span", { className: "text-sm font-semibold uppercase tracking-wide text-slate-500 dark:text-zinc-300", children: suffix }) : null
2784
+ ]
2785
+ }
2786
+ ),
2787
+ description ? /* @__PURE__ */ jsx18("p", { className: "text-xs text-slate-500 dark:text-zinc-400", children: description }) : null,
2788
+ error ? /* @__PURE__ */ jsx18("p", { className: "text-xs font-medium text-rose-500 dark:text-rose-400", children: error }) : null
2789
+ ] });
2790
+ }
2791
+ );
2792
+
2793
+ // components/OutputChip/OutputChip.tsx
2794
+ import * as React17 from "react";
2795
+ import { twMerge as twMerge18 } from "tailwind-merge";
2796
+ import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
2797
+ var toneStyles = {
2798
+ neutral: { bg: "bg-slate-900", text: "text-white", ring: "ring-slate-300" },
2799
+ success: { bg: "bg-emerald-500", text: "text-white", ring: "ring-emerald-200" },
2800
+ warning: { bg: "bg-amber-500", text: "text-white", ring: "ring-amber-200" },
2801
+ danger: { bg: "bg-rose-500", text: "text-white", ring: "ring-rose-200" }
2802
+ };
2803
+ var OutputChip = React17.forwardRef(function OutputChip2({ children, className, tone = "neutral", label, ...rest }, ref) {
2804
+ const styles = toneStyles[tone];
2805
+ return /* @__PURE__ */ jsxs17(
2806
+ "output",
2807
+ {
2808
+ ...rest,
2809
+ ref,
2810
+ className: twMerge18(
2811
+ "inline-flex items-center justify-center gap-2 rounded-full px-3 py-1 text-xs font-semibold uppercase tracking-[0.25em] shadow-sm ring-1 text-center",
2812
+ styles.bg,
2813
+ styles.text,
2814
+ styles.ring,
2815
+ className
2816
+ ),
2817
+ children: [
2818
+ label ? /* @__PURE__ */ jsx19("span", { className: "opacity-70", children: label }) : null,
2819
+ /* @__PURE__ */ jsx19("span", { children })
2820
+ ]
2821
+ }
2822
+ );
2823
+ });
2824
+
2825
+ // components/ProgressMeter/ProgressMeter.tsx
2826
+ import { twMerge as twMerge19 } from "tailwind-merge";
2827
+ import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
2828
+ function Progress({
2829
+ label,
2830
+ description,
2831
+ value,
2832
+ max = 100,
2833
+ showValue = true,
2834
+ className
2835
+ }) {
2836
+ const clamped = Math.min(max, Math.max(0, value));
2837
+ const percent = max === 0 ? 0 : Math.round(clamped / max * 100);
2838
+ return /* @__PURE__ */ jsxs18("div", { className: twMerge19("space-y-1.5", className), children: [
2839
+ label ? /* @__PURE__ */ jsxs18("div", { className: "flex items-center justify-between gap-3", children: [
2840
+ /* @__PURE__ */ jsx20("p", { className: "text-xs font-semibold uppercase tracking-[0.24em] text-slate-500 dark:text-zinc-400", children: label }),
2841
+ showValue ? /* @__PURE__ */ jsxs18("span", { className: "text-xs font-semibold text-slate-600 dark:text-zinc-300", children: [
2842
+ percent,
2843
+ "%"
2844
+ ] }) : null
2845
+ ] }) : null,
2846
+ description ? /* @__PURE__ */ jsx20("p", { className: "text-[11px] text-slate-500 dark:text-zinc-400", children: description }) : null,
2847
+ /* @__PURE__ */ jsxs18("div", { className: "relative h-3 w-full overflow-hidden rounded-full border border-slate-200 bg-white shadow-inner ring-1 ring-slate-100/80 dark:border-zinc-700 dark:bg-zinc-900/80 dark:ring-zinc-800/70", children: [
2848
+ /* @__PURE__ */ jsx20(
2849
+ "progress",
2850
+ {
2851
+ value: clamped,
2852
+ max,
2853
+ className: "absolute inset-0 h-full w-full appearance-none",
2854
+ "aria-label": label
2855
+ }
2856
+ ),
2857
+ /* @__PURE__ */ jsx20(
2858
+ "div",
2859
+ {
2860
+ className: "absolute inset-y-0 left-0 rounded-full bg-gradient-to-r from-slate-900 via-slate-700 to-slate-900 shadow-[0_0_0_1px_rgba(15,23,42,0.2)] transition-[width]",
2861
+ style: { width: `${percent}%` }
2862
+ }
2863
+ )
2864
+ ] })
2865
+ ] });
2866
+ }
2867
+ function Meter({
2868
+ label,
2869
+ description,
2870
+ value,
2871
+ min = 0,
2872
+ max = 100,
2873
+ thresholds,
2874
+ className
2875
+ }) {
2876
+ var _a, _b;
2877
+ const clamped = Math.min(max, Math.max(min, value));
2878
+ const percent = max === min ? 0 : (clamped - min) / (max - min) * 100;
2879
+ const activeColor = thresholds ? thresholds.reduce(
2880
+ (acc, t) => clamped >= t.value ? t.color : acc,
2881
+ (_b = (_a = thresholds[0]) == null ? void 0 : _a.color) != null ? _b : "linear-gradient(90deg, #22c55e, #0ea5e9)"
2882
+ ) : "linear-gradient(90deg, #22c55e, #0ea5e9)";
2883
+ return /* @__PURE__ */ jsxs18("div", { className: twMerge19("space-y-1.5", className), children: [
2884
+ label ? /* @__PURE__ */ jsxs18("div", { className: "flex items-center justify-between gap-3", children: [
2885
+ /* @__PURE__ */ jsx20("p", { className: "text-xs font-semibold uppercase tracking-[0.24em] text-slate-500 dark:text-zinc-400", children: label }),
2886
+ /* @__PURE__ */ jsx20("span", { className: "text-xs font-semibold text-slate-600 dark:text-zinc-300", children: clamped })
2887
+ ] }) : null,
2888
+ description ? /* @__PURE__ */ jsx20("p", { className: "text-[11px] text-slate-500 dark:text-zinc-400", children: description }) : null,
2889
+ /* @__PURE__ */ jsxs18("div", { className: "relative h-3 w-full overflow-hidden rounded-full border border-slate-200 bg-white shadow-inner ring-1 ring-slate-100/80 dark:border-zinc-700 dark:bg-zinc-900/80 dark:ring-zinc-800/70", children: [
2890
+ /* @__PURE__ */ jsx20(
2891
+ "meter",
2892
+ {
2893
+ value: clamped,
2894
+ min,
2895
+ max,
2896
+ className: "absolute inset-0 h-full w-full appearance-none",
2897
+ "aria-label": label
2898
+ }
2899
+ ),
2900
+ /* @__PURE__ */ jsx20(
2901
+ "div",
2902
+ {
2903
+ className: "absolute inset-y-0 left-0 rounded-full shadow-[0_0_0_1px_rgba(15,23,42,0.2)] transition-[width]",
2904
+ style: { width: `${percent}%`, background: activeColor }
2905
+ }
2906
+ )
2907
+ ] })
2908
+ ] });
2909
+ }
2910
+
2911
+ // components/Radio/Radio.tsx
2912
+ import * as React18 from "react";
2913
+ import { twMerge as twMerge20 } from "tailwind-merge";
2914
+ import { jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
2915
+ var Radio = React18.forwardRef(function Radio2({
2916
+ label,
2917
+ description,
2918
+ extra,
2919
+ checked,
2920
+ defaultChecked = false,
2921
+ onChange,
2922
+ disabled,
2923
+ className,
2924
+ id,
2925
+ ...rest
2926
+ }, forwardedRef) {
2927
+ const generatedId = React18.useId();
2928
+ const radioId = id != null ? id : generatedId;
2929
+ const descriptionId = React18.useId();
2930
+ const isControlled = typeof checked === "boolean";
2931
+ const [internalChecked, setInternalChecked] = React18.useState(defaultChecked);
2932
+ const resolvedChecked = isControlled ? !!checked : internalChecked;
2933
+ const handleChange = (event) => {
2934
+ const next = event.target.checked;
2935
+ if (!isControlled) {
2936
+ setInternalChecked(next);
2937
+ }
2938
+ onChange == null ? void 0 : onChange(next);
2939
+ };
2940
+ return /* @__PURE__ */ jsxs19(
2941
+ "label",
2942
+ {
2943
+ htmlFor: radioId,
2944
+ className: twMerge20(
2945
+ "flex items-center justify-between gap-3 rounded-2xl border border-transparent px-3 py-2 text-left transition hover:bg-slate-50 focus-within:bg-slate-50 dark:hover:bg-zinc-900/60 dark:focus-within:bg-zinc-900/60",
2946
+ disabled && "cursor-not-allowed opacity-60",
2947
+ className
2948
+ ),
2949
+ children: [
2950
+ /* @__PURE__ */ jsxs19("span", { className: "flex flex-1 items-center gap-3", children: [
2951
+ /* @__PURE__ */ jsxs19("span", { className: "flex h-6 w-6 items-center justify-center", children: [
2952
+ /* @__PURE__ */ jsx21(
2953
+ "input",
2954
+ {
2955
+ ...rest,
2956
+ ref: forwardedRef,
2957
+ id: radioId,
2958
+ type: "radio",
2959
+ className: "peer sr-only",
2960
+ checked: resolvedChecked,
2961
+ onChange: handleChange,
2962
+ "aria-describedby": description ? descriptionId : rest["aria-describedby"],
2963
+ disabled
2964
+ }
2965
+ ),
2966
+ /* @__PURE__ */ jsx21(
2967
+ "span",
2968
+ {
2969
+ className: twMerge20(
2970
+ "grid size-5 place-items-center rounded-full border border-slate-300 bg-white text-slate-600 shadow-sm transition duration-150 peer-focus-visible:outline peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-slate-400 dark:border-zinc-600 dark:bg-zinc-950/70 dark:text-zinc-200",
2971
+ resolvedChecked && "border-slate-400 bg-slate-100 shadow-[0_0_0_1px_rgba(148,163,184,0.45)] dark:border-zinc-500 dark:bg-zinc-700/70"
2972
+ ),
2973
+ "aria-hidden": "true",
2974
+ children: /* @__PURE__ */ jsx21(
2975
+ "span",
2976
+ {
2977
+ className: twMerge20(
2978
+ "block h-2.5 w-2.5 rounded-full bg-slate-900 opacity-0 transition duration-200 dark:bg-zinc-100",
2979
+ resolvedChecked && "opacity-100"
2980
+ )
2981
+ }
2982
+ )
2983
+ }
2984
+ )
2985
+ ] }),
2986
+ /* @__PURE__ */ jsxs19("span", { className: "flex flex-1 flex-col", children: [
2987
+ /* @__PURE__ */ jsx21("span", { className: "text-sm font-semibold text-slate-900 dark:text-zinc-100", children: label }),
2988
+ description ? /* @__PURE__ */ jsx21("span", { id: descriptionId, className: "text-xs text-slate-500 dark:text-zinc-400", children: description }) : null
2989
+ ] })
2990
+ ] }),
2991
+ extra ? /* @__PURE__ */ jsx21("span", { className: "self-center rounded-xl border border-slate-200 bg-white/80 px-3 py-1 text-xs font-semibold text-slate-600 dark:border-zinc-700 dark:bg-zinc-900/60 dark:text-zinc-200", children: extra }) : null
2992
+ ]
2993
+ }
2994
+ );
2995
+ });
2996
+
2997
+ // components/Slider/Slider.tsx
2998
+ import * as React19 from "react";
2999
+ import { twMerge as twMerge21 } from "tailwind-merge";
3000
+ import { jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
3001
+ function clampValue(value, min, max) {
3002
+ return Math.min(max, Math.max(min, value));
3003
+ }
3004
+ function percentFromValue(value, min, max) {
3005
+ if (max === min) return 0;
3006
+ return clampValue((value - min) / (max - min) * 100, 0, 100);
3007
+ }
3008
+ function snapToStep(value, min, max, step) {
3009
+ const safeStep = step > 0 ? step : 1;
3010
+ const limited = clampValue(value, min, max);
3011
+ const stepped = Math.round((limited - min) / safeStep) * safeStep + min;
3012
+ return clampValue(Number(stepped.toFixed(6)), min, max);
3013
+ }
3014
+ var Slider = React19.forwardRef(function Slider2({
3015
+ label,
3016
+ value,
3017
+ defaultValue,
3018
+ onChange,
3019
+ formatValue = (val) => `${val}`,
3020
+ renderTrack,
3021
+ renderThumb,
3022
+ trackClassName,
3023
+ thumbClassName,
3024
+ className,
3025
+ min = 0,
3026
+ max = 100,
3027
+ step = 1,
3028
+ thumbSize = 24,
3029
+ edgeOverlap = 0,
3030
+ fillMode = "stretch",
3031
+ disabled,
3032
+ orientation = "horizontal",
3033
+ reversed = false,
3034
+ id,
3035
+ ...rest
3036
+ }, forwardedRef) {
3037
+ const isControlled = typeof value === "number";
3038
+ const initialValue = typeof defaultValue === "number" ? defaultValue : min;
3039
+ const [internalValue, setInternalValue] = React19.useState(
3040
+ () => snapToStep(initialValue, min, max, step)
3041
+ );
3042
+ const [focused, setFocused] = React19.useState(false);
3043
+ const [dragging, setDragging] = React19.useState(false);
3044
+ const [trackMetrics, setTrackMetrics] = React19.useState({
3045
+ length: 0,
3046
+ mainOffset: 0,
3047
+ crossOffset: 0,
3048
+ thickness: 0
3049
+ });
3050
+ const generatedId = React19.useId();
3051
+ const inputId = id != null ? id : generatedId;
3052
+ const inputRef = React19.useRef(null);
3053
+ const interactionRef = React19.useRef(null);
3054
+ const trackContainerRef = React19.useRef(null);
3055
+ const trackRef = React19.useRef(null);
3056
+ const mergedRef = React19.useCallback(
3057
+ (node) => {
3058
+ inputRef.current = node;
3059
+ assignRef(forwardedRef, node);
3060
+ },
3061
+ [forwardedRef]
3062
+ );
3063
+ React19.useEffect(() => {
3064
+ setInternalValue((prev) => snapToStep(prev, min, max, step));
3065
+ }, [min, max, step]);
3066
+ const resolvedValue = snapToStep(
3067
+ isControlled ? typeof value === "number" ? value : min : internalValue,
3068
+ min,
3069
+ max,
3070
+ step
3071
+ );
3072
+ const percentage = percentFromValue(resolvedValue, min, max);
3073
+ const isVertical = orientation === "vertical";
3074
+ const thumbDiameter = Math.max(thumbSize, 2);
3075
+ const thumbRadius = thumbDiameter / 2;
3076
+ React19.useLayoutEffect(() => {
3077
+ const track = trackRef.current;
3078
+ const container = trackContainerRef.current;
3079
+ if (!track || !container) return;
3080
+ const update = () => {
3081
+ const trackRect = track.getBoundingClientRect();
3082
+ const containerRect = container.getBoundingClientRect();
3083
+ const isVert = orientation === "vertical";
3084
+ const length = isVert ? trackRect.height : trackRect.width;
3085
+ const thickness = isVert ? trackRect.width : trackRect.height;
3086
+ const mainOffset = isVert ? trackRect.top - containerRect.top : trackRect.left - containerRect.left;
3087
+ const crossOffset = isVert ? trackRect.left - containerRect.left : trackRect.top - containerRect.top;
3088
+ setTrackMetrics({ length, mainOffset, crossOffset, thickness });
3089
+ };
3090
+ update();
3091
+ const observer = new ResizeObserver(update);
3092
+ observer.observe(track);
3093
+ observer.observe(container);
3094
+ return () => observer.disconnect();
3095
+ }, [orientation]);
3096
+ const trackLength = trackMetrics.length;
3097
+ const trackOffset = trackMetrics.mainOffset;
3098
+ const trackCrossOffset = trackMetrics.crossOffset;
3099
+ const trackThickness = trackMetrics.thickness;
3100
+ const overlap = Math.max(edgeOverlap, 0);
3101
+ const minCenter = Math.max(thumbRadius - overlap, 0);
3102
+ const maxCenter = Math.max(trackLength - thumbRadius + overlap, minCenter);
3103
+ const usableLength = Math.max(maxCenter - minCenter, 0);
3104
+ const logicalRatio = clampValue((resolvedValue - min) / (max - min || 1), 0, 1);
3105
+ const positionRatio = (() => {
3106
+ if (isVertical) {
3107
+ return reversed ? logicalRatio : 1 - logicalRatio;
3108
+ }
3109
+ return reversed ? 1 - logicalRatio : logicalRatio;
3110
+ })();
3111
+ const positionPx = minCenter + usableLength * positionRatio;
3112
+ const gradientSize = React19.useMemo(() => {
3113
+ if (fillMode === "stretch" || trackLength === 0) return "100% 100%";
3114
+ return isVertical ? `100% ${trackLength}px` : `${trackLength}px 100%`;
3115
+ }, [fillMode, isVertical, trackLength]);
3116
+ const gradientPosition = React19.useMemo(() => {
3117
+ if (fillMode === "stretch") return void 0;
3118
+ if (!isVertical && reversed) return "right center";
3119
+ if (isVertical && !reversed) return "center bottom";
3120
+ return "left top";
3121
+ }, [fillMode, isVertical, reversed]);
3122
+ const progressStyle = React19.useMemo(() => {
3123
+ if (trackLength === 0) return {};
3124
+ const half = thumbRadius;
3125
+ if (!isVertical && !reversed) {
3126
+ const width = Math.min(positionPx + half, trackLength);
3127
+ return { left: 0, width, top: 0, bottom: 0 };
3128
+ }
3129
+ if (!isVertical && reversed) {
3130
+ const start2 = Math.max(positionPx - half, 0);
3131
+ const width = Math.max(trackLength - start2, 0);
3132
+ return { right: 0, width, top: 0, bottom: 0 };
3133
+ }
3134
+ if (isVertical && reversed) {
3135
+ const height2 = Math.min(positionPx + half, trackLength);
3136
+ return { top: 0, height: height2, left: 0, right: 0 };
3137
+ }
3138
+ const start = Math.max(positionPx - half, 0);
3139
+ const height = Math.max(Math.min(trackLength - start, trackLength), 0);
3140
+ return { bottom: 0, height, left: 0, right: 0 };
3141
+ }, [isVertical, reversed, positionPx, trackLength, thumbRadius]);
3142
+ const trackGradient = React19.useMemo(() => {
3143
+ if (!isVertical && !reversed) return "linear-gradient(90deg, #e2e8f0, #fff, #e2e8f0)";
3144
+ if (!isVertical && reversed) return "linear-gradient(270deg, #e2e8f0, #fff, #e2e8f0)";
3145
+ if (isVertical && reversed) return "linear-gradient(180deg, #e2e8f0, #fff, #e2e8f0)";
3146
+ return "linear-gradient(0deg, #e2e8f0, #fff, #e2e8f0)";
3147
+ }, [isVertical, reversed]);
3148
+ const fillGradient = React19.useMemo(() => {
3149
+ if (!isVertical && !reversed) return "linear-gradient(90deg, #38bdf8, #6366f1)";
3150
+ if (!isVertical && reversed) return "linear-gradient(270deg, #38bdf8, #6366f1)";
3151
+ if (isVertical && reversed) return "linear-gradient(180deg, #38bdf8, #6366f1)";
3152
+ return "linear-gradient(0deg, #38bdf8, #6366f1)";
3153
+ }, [isVertical, reversed]);
3154
+ const thumbStyle = React19.useMemo(() => {
3155
+ if (isVertical) {
3156
+ return { top: trackOffset + positionPx, left: trackCrossOffset + trackThickness / 2 };
3157
+ }
3158
+ return { left: trackOffset + positionPx, top: trackCrossOffset + trackThickness / 2 };
3159
+ }, [isVertical, positionPx, trackOffset, trackCrossOffset, trackThickness]);
3160
+ const renderProps = React19.useMemo(
3161
+ () => ({
3162
+ value: resolvedValue,
3163
+ min,
3164
+ max,
3165
+ percentage,
3166
+ disabled,
3167
+ focused,
3168
+ orientation,
3169
+ reversed,
3170
+ position: positionPx,
3171
+ trackLength,
3172
+ thumbSize: thumbDiameter,
3173
+ trackOffset,
3174
+ trackCrossOffset,
3175
+ trackThickness
3176
+ }),
3177
+ [
3178
+ resolvedValue,
3179
+ min,
3180
+ max,
3181
+ percentage,
3182
+ disabled,
3183
+ focused,
3184
+ orientation,
3185
+ reversed,
3186
+ positionPx,
3187
+ trackLength,
3188
+ thumbDiameter,
3189
+ trackOffset,
3190
+ trackCrossOffset,
3191
+ trackThickness
3192
+ ]
3193
+ );
3194
+ const commitValue = React19.useCallback(
3195
+ (next) => {
3196
+ const snapped = snapToStep(next, min, max, step);
3197
+ if (!isControlled) {
3198
+ setInternalValue(snapped);
3199
+ }
3200
+ onChange == null ? void 0 : onChange(snapped);
3201
+ },
3202
+ [isControlled, max, min, onChange, step]
3203
+ );
3204
+ const handleInputChange = (event) => {
3205
+ const next = Number(event.target.value);
3206
+ if (Number.isNaN(next)) return;
3207
+ commitValue(next);
3208
+ };
3209
+ const formattedValue = formatValue(resolvedValue);
3210
+ const setValueFromPointer = React19.useCallback(
3211
+ (clientX, clientY) => {
3212
+ var _a;
3213
+ const rect = (_a = trackRef.current) == null ? void 0 : _a.getBoundingClientRect();
3214
+ if (!rect) return;
3215
+ const overlapComp = Math.max(thumbRadius - overlap, 0);
3216
+ const length = isVertical ? rect.height : rect.width;
3217
+ const usable = Math.max(length - overlapComp * 2, 1);
3218
+ if (length <= 0) return;
3219
+ const clampToTrack = (pos) => clampValue(pos, overlapComp, length - overlapComp);
3220
+ if (isVertical) {
3221
+ const position2 = clampToTrack(clientY - rect.top);
3222
+ const positionRatio3 = (position2 - overlapComp) / usable;
3223
+ const logicalRatio3 = reversed ? positionRatio3 : 1 - positionRatio3;
3224
+ const next2 = min + logicalRatio3 * (max - min);
3225
+ commitValue(next2);
3226
+ return;
3227
+ }
3228
+ const position = clampToTrack(clientX - rect.left);
3229
+ const positionRatio2 = (position - overlapComp) / usable;
3230
+ const logicalRatio2 = reversed ? 1 - positionRatio2 : positionRatio2;
3231
+ const next = min + logicalRatio2 * (max - min);
3232
+ commitValue(next);
3233
+ },
3234
+ [commitValue, isVertical, max, min, overlap, reversed, thumbRadius]
3235
+ );
3236
+ const handlePointerDown = React19.useCallback(
3237
+ (event) => {
3238
+ var _a, _b;
3239
+ event.preventDefault();
3240
+ (_a = interactionRef.current) == null ? void 0 : _a.setPointerCapture(event.pointerId);
3241
+ (_b = inputRef.current) == null ? void 0 : _b.focus({ preventScroll: true });
3242
+ setDragging(true);
3243
+ setValueFromPointer(event.clientX, event.clientY);
3244
+ const move = (moveEvent) => {
3245
+ moveEvent.preventDefault();
3246
+ setValueFromPointer(moveEvent.clientX, moveEvent.clientY);
3247
+ };
3248
+ const stop = (upEvent) => {
3249
+ var _a2;
3250
+ upEvent.preventDefault();
3251
+ (_a2 = interactionRef.current) == null ? void 0 : _a2.releasePointerCapture(event.pointerId);
3252
+ setDragging(false);
3253
+ window.removeEventListener("pointermove", move);
3254
+ window.removeEventListener("pointerup", stop);
3255
+ window.removeEventListener("pointercancel", stop);
3256
+ };
3257
+ window.addEventListener("pointermove", move, { passive: false });
3258
+ window.addEventListener("pointerup", stop, { passive: false });
3259
+ window.addEventListener("pointercancel", stop, { passive: false });
3260
+ },
3261
+ [setValueFromPointer]
3262
+ );
3263
+ const defaultTrack = /* @__PURE__ */ jsxs20(
3264
+ "div",
3265
+ {
3266
+ ref: trackRef,
3267
+ className: twMerge21(
3268
+ "relative overflow-hidden rounded-full shadow-inner ring-1 ring-slate-200/80 dark:ring-white/10",
3269
+ "bg-[length:100%_100%]",
3270
+ isVertical ? "mx-auto h-full w-3" : "h-3 w-full",
3271
+ disabled && "opacity-60",
3272
+ trackClassName
3273
+ ),
3274
+ style: { backgroundImage: trackGradient },
3275
+ "aria-hidden": "true",
3276
+ children: [
3277
+ /* @__PURE__ */ jsx22(
3278
+ "div",
3279
+ {
3280
+ className: twMerge21(
3281
+ "absolute rounded-full shadow-[0_0_0_1px_rgba(59,130,246,0.18)]",
3282
+ dragging ? "transition-none" : "transition-[width,height] duration-150 ease-out",
3283
+ isVertical ? "left-0 right-0" : "inset-y-0"
3284
+ ),
3285
+ style: {
3286
+ ...progressStyle,
3287
+ backgroundImage: fillGradient,
3288
+ backgroundSize: gradientSize,
3289
+ backgroundPosition: gradientPosition
3290
+ }
3291
+ }
3292
+ ),
3293
+ /* @__PURE__ */ jsx22("div", { className: "pointer-events-none absolute inset-0 rounded-full ring-1 ring-black/5 dark:ring-white/10" })
3294
+ ]
3295
+ }
3296
+ );
3297
+ const defaultThumb = /* @__PURE__ */ jsx22(
3298
+ "span",
3299
+ {
3300
+ className: twMerge21(
3301
+ "absolute top-1/2 -translate-y-1/2 -translate-x-1/2 rounded-full border border-white bg-white shadow-md shadow-slate-900/20 ring-1 ring-slate-200 transition-transform duration-150 dark:border-zinc-700/60 dark:bg-zinc-800 dark:shadow-black/30 dark:ring-zinc-700",
3302
+ focused && "scale-[1.04] ring-slate-300 dark:ring-slate-500",
3303
+ disabled && "opacity-60 shadow-none dark:shadow-none",
3304
+ thumbClassName
3305
+ ),
3306
+ style: { ...thumbStyle, width: thumbDiameter, height: thumbDiameter },
3307
+ "aria-hidden": "true",
3308
+ children: /* @__PURE__ */ jsxs20("span", { className: "relative block h-full w-full", children: [
3309
+ /* @__PURE__ */ jsx22("span", { className: "absolute inset-[5px] rounded-full bg-gradient-to-br from-slate-50 to-slate-200 dark:from-slate-600 dark:to-slate-700" }),
3310
+ /* @__PURE__ */ jsx22("span", { className: "absolute inset-0 rounded-full bg-slate-950/5 backdrop-blur-[1px] dark:bg-white/5" })
3311
+ ] })
3312
+ }
3313
+ );
3314
+ return /* @__PURE__ */ jsxs20("div", { className: twMerge21("w-full space-y-2", isVertical && "max-w-[120px]", className), children: [
3315
+ label ? /* @__PURE__ */ jsxs20("div", { className: "flex items-center justify-between gap-3", children: [
3316
+ /* @__PURE__ */ jsx22(
3317
+ "label",
3318
+ {
3319
+ htmlFor: inputId,
3320
+ className: "text-[11px] font-semibold uppercase tracking-[0.25em] text-slate-500 dark:text-zinc-400",
3321
+ children: label
3322
+ }
3323
+ ),
3324
+ /* @__PURE__ */ jsx22("span", { className: "rounded-full bg-white/90 px-2 py-0.5 text-xs font-semibold text-slate-700 shadow-sm ring-1 ring-slate-200 dark:bg-zinc-900/80 dark:text-zinc-200 dark:ring-zinc-700", children: formattedValue })
3325
+ ] }) : null,
3326
+ /* @__PURE__ */ jsxs20(
3327
+ "div",
3328
+ {
3329
+ ref: trackContainerRef,
3330
+ className: twMerge21(
3331
+ "relative w-full pt-2 pb-3",
3332
+ isVertical && "flex h-48 items-center justify-center px-4 pb-4 pt-4"
3333
+ ),
3334
+ "data-orientation": orientation,
3335
+ children: [
3336
+ renderTrack ? renderTrack({ ...renderProps, children: defaultTrack }) : defaultTrack,
3337
+ renderThumb ? renderThumb(renderProps) : defaultThumb,
3338
+ /* @__PURE__ */ jsx22(
3339
+ "input",
3340
+ {
3341
+ ...rest,
3342
+ ref: mergedRef,
3343
+ id: inputId,
3344
+ type: "range",
3345
+ min,
3346
+ max,
3347
+ step,
3348
+ value: resolvedValue,
3349
+ onChange: handleInputChange,
3350
+ onInput: handleInputChange,
3351
+ onFocus: () => setFocused(true),
3352
+ onBlur: () => setFocused(false),
3353
+ disabled,
3354
+ "aria-valuetext": formattedValue,
3355
+ "aria-orientation": orientation,
3356
+ "data-orientation": orientation,
3357
+ className: "absolute inset-0 h-10 w-full appearance-none opacity-0 pointer-events-none"
3358
+ }
3359
+ ),
3360
+ /* @__PURE__ */ jsx22(
3361
+ "div",
3362
+ {
3363
+ ref: interactionRef,
3364
+ "aria-hidden": "true",
3365
+ onPointerDown: handlePointerDown,
3366
+ className: twMerge21(
3367
+ "absolute inset-0 cursor-pointer bg-transparent",
3368
+ isVertical ? "left-1/2 w-8 -translate-x-1/2" : "top-1/2 h-8 -translate-y-1/2"
3369
+ )
3370
+ }
3371
+ )
3372
+ ]
3373
+ }
3374
+ )
3375
+ ] });
3376
+ });
3377
+
3378
+ // components/StackedList/StackedList.tsx
3379
+ import { twMerge as twMerge22 } from "tailwind-merge";
3380
+ import { jsx as jsx23, jsxs as jsxs21 } from "react/jsx-runtime";
3381
+ function StackedList({ items, dense, className, ...rest }) {
3382
+ return /* @__PURE__ */ jsx23(
3383
+ "div",
3384
+ {
3385
+ ...rest,
3386
+ className: twMerge22(
3387
+ "rounded-3xl border border-slate-200 bg-white/80 shadow-xl shadow-slate-200/40 dark:border-zinc-800 dark:bg-zinc-900/80 dark:shadow-none",
3388
+ className
3389
+ ),
3390
+ children: /* @__PURE__ */ jsx23("ul", { role: "list", className: "divide-y divide-slate-100 dark:divide-zinc-800", children: items.map((item) => /* @__PURE__ */ jsxs21(
3391
+ "li",
3392
+ {
3393
+ className: twMerge22("flex items-start gap-4 px-5 py-4", dense && "py-3"),
3394
+ children: [
3395
+ item.icon ? /* @__PURE__ */ jsx23(
3396
+ "div",
3397
+ {
3398
+ className: "mt-0.5 rounded-2xl bg-slate-100 p-2 text-slate-600 dark:bg-zinc-900/50 dark:text-zinc-200",
3399
+ "aria-hidden": "true",
3400
+ children: item.icon
3401
+ }
3402
+ ) : null,
3403
+ /* @__PURE__ */ jsxs21("div", { className: "flex-1", children: [
3404
+ /* @__PURE__ */ jsx23("p", { className: "text-sm font-semibold text-slate-900 dark:text-zinc-100", children: item.title }),
3405
+ item.description ? /* @__PURE__ */ jsx23("p", { className: "text-sm text-slate-500 dark:text-zinc-400", children: item.description }) : null
3406
+ ] }),
3407
+ /* @__PURE__ */ jsxs21("div", { className: "flex flex-col items-end gap-1 text-right", children: [
3408
+ item.meta ? /* @__PURE__ */ jsx23("span", { className: "text-xs uppercase tracking-wide text-slate-400 dark:text-zinc-500", children: item.meta }) : null,
3409
+ item.action
3410
+ ] })
3411
+ ]
3412
+ },
3413
+ item.id
3414
+ )) })
3415
+ }
3416
+ );
3417
+ }
3418
+
3419
+ // components/Table/Table.tsx
3420
+ import * as React20 from "react";
3421
+ import { twMerge as twMerge23 } from "tailwind-merge";
3422
+ import { jsx as jsx24, jsxs as jsxs22 } from "react/jsx-runtime";
3423
+ var MIN_THUMB = 24;
3424
+ var TRACK_PADDING2 = 6;
3425
+ var TRACK_THICKNESS = 6;
3426
+ var TRACK_INSET = 8;
3427
+ var V_TRACK_INSET = 10;
3428
+ function useScrollbarMetrics(ref, axis, extraSpace) {
3429
+ const [state, setState] = React20.useState({
3430
+ visible: false,
3431
+ size: MIN_THUMB,
3432
+ offset: 0
3433
+ });
3434
+ React20.useLayoutEffect(() => {
3435
+ const el = ref.current;
3436
+ if (!el) return;
3437
+ let raf = 0;
3438
+ const update = () => {
3439
+ const target = ref.current;
3440
+ if (!target) return;
3441
+ const scrollRange = axis === "vertical" ? target.scrollHeight - target.clientHeight : target.scrollWidth - target.clientWidth;
3442
+ const trackLength = axis === "vertical" ? Math.max(0, target.clientHeight + extraSpace - TRACK_PADDING2 * 2 - V_TRACK_INSET * 2) : Math.max(0, target.clientWidth + extraSpace - 2 * (TRACK_PADDING2 + TRACK_INSET));
3443
+ if (scrollRange <= 0 || trackLength <= 0) {
3444
+ setState((prev) => prev.visible ? { ...prev, visible: false } : prev);
3445
+ return;
3446
+ }
3447
+ const ratio = axis === "vertical" ? target.clientHeight / target.scrollHeight : target.clientWidth / target.scrollWidth;
3448
+ const thumbSize = Math.max(trackLength * ratio, MIN_THUMB);
3449
+ const maxOffset = Math.max(0, trackLength - thumbSize);
3450
+ const currentOffset = maxOffset > 0 ? (axis === "vertical" ? target.scrollTop : target.scrollLeft) / scrollRange * maxOffset : 0;
3451
+ setState({ visible: true, size: thumbSize, offset: currentOffset });
3452
+ };
3453
+ const handleScroll = () => {
3454
+ cancelAnimationFrame(raf);
3455
+ raf = window.requestAnimationFrame(update);
3456
+ };
3457
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(update) : null;
3458
+ resizeObserver == null ? void 0 : resizeObserver.observe(el);
3459
+ el.addEventListener("scroll", handleScroll, { passive: true });
3460
+ update();
3461
+ return () => {
3462
+ resizeObserver == null ? void 0 : resizeObserver.disconnect();
3463
+ el.removeEventListener("scroll", handleScroll);
3464
+ cancelAnimationFrame(raf);
3465
+ };
3466
+ }, [ref, axis, extraSpace]);
3467
+ return state;
3468
+ }
3469
+ function useThumbDrag(ref, axis, thumbState, extraSpace) {
3470
+ const startDrag = React20.useCallback(
3471
+ (event, startScrollOverride) => {
3472
+ var _a, _b;
3473
+ const el = ref.current;
3474
+ if (!el) return;
3475
+ event.preventDefault();
3476
+ const startPos = axis === "vertical" ? event.clientY : event.clientX;
3477
+ const startScroll = startScrollOverride != null ? startScrollOverride : axis === "vertical" ? el.scrollTop : el.scrollLeft;
3478
+ const thumbSize = thumbState.size;
3479
+ const handleMove = (moveEvent) => {
3480
+ const delta = (axis === "vertical" ? moveEvent.clientY : moveEvent.clientX) - startPos;
3481
+ const scrollRange = axis === "vertical" ? el.scrollHeight - el.clientHeight : el.scrollWidth - el.clientWidth;
3482
+ const trackLength = axis === "vertical" ? el.clientHeight + extraSpace - TRACK_PADDING2 * 2 - V_TRACK_INSET * 2 : el.clientWidth + extraSpace - 2 * (TRACK_PADDING2 + TRACK_INSET);
3483
+ if (scrollRange <= 0 || trackLength <= thumbSize) return;
3484
+ const ratio = scrollRange / (trackLength - thumbSize);
3485
+ const next = startScroll + delta * ratio;
3486
+ if (axis === "vertical") el.scrollTop = Math.min(scrollRange, Math.max(0, next));
3487
+ else el.scrollLeft = Math.min(scrollRange, Math.max(0, next));
3488
+ };
3489
+ const handleUp = () => {
3490
+ window.removeEventListener("pointermove", handleMove);
3491
+ window.removeEventListener("pointerup", handleUp);
3492
+ window.removeEventListener("pointercancel", handleUp);
3493
+ };
3494
+ (_b = (_a = event.currentTarget).setPointerCapture) == null ? void 0 : _b.call(_a, event.pointerId);
3495
+ window.addEventListener("pointermove", handleMove);
3496
+ window.addEventListener("pointerup", handleUp);
3497
+ window.addEventListener("pointercancel", handleUp);
3498
+ },
3499
+ [axis, ref, thumbState.size, extraSpace]
3500
+ );
3501
+ const onThumbPointerDown = React20.useCallback(
3502
+ (event) => {
3503
+ event.stopPropagation();
3504
+ startDrag(event);
3505
+ },
3506
+ [startDrag]
3507
+ );
3508
+ return { onThumbPointerDown, startDrag };
3509
+ }
3510
+ function Table({
3511
+ columns,
3512
+ data,
3513
+ caption,
3514
+ className,
3515
+ scrollAreaStyle,
3516
+ style
3517
+ }) {
3518
+ const scrollRef = React20.useRef(null);
3519
+ const [padRight, setPadRight] = React20.useState(0);
3520
+ const [padBottom, setPadBottom] = React20.useState(0);
3521
+ const vThumb = useScrollbarMetrics(scrollRef, "vertical", padBottom);
3522
+ const hThumb = useScrollbarMetrics(scrollRef, "horizontal", padRight);
3523
+ React20.useEffect(() => {
3524
+ setPadRight(vThumb.visible ? TRACK_PADDING2 + 10 : 0);
3525
+ }, [vThumb.visible]);
3526
+ React20.useEffect(() => {
3527
+ setPadBottom(hThumb.visible ? TRACK_PADDING2 + 10 : 0);
3528
+ }, [hThumb.visible]);
3529
+ const { onThumbPointerDown: handleVThumbDown, startDrag: startVDrag } = useThumbDrag(
3530
+ scrollRef,
3531
+ "vertical",
3532
+ vThumb,
3533
+ padBottom
3534
+ );
3535
+ const { onThumbPointerDown: handleHThumbDown, startDrag: startHDrag } = useThumbDrag(
3536
+ scrollRef,
3537
+ "horizontal",
3538
+ hThumb,
3539
+ padRight
3540
+ );
3541
+ const handleTrackPointerDown = React20.useCallback(
3542
+ (axis, thumb, startDrag, event) => {
3543
+ const el = scrollRef.current;
3544
+ if (!el) return;
3545
+ event.preventDefault();
3546
+ event.stopPropagation();
3547
+ const rect = event.currentTarget.getBoundingClientRect();
3548
+ const clickOffset = axis === "vertical" ? event.clientY - rect.top : event.clientX - rect.left;
3549
+ const trackLength = axis === "vertical" ? rect.height : rect.width;
3550
+ const scrollRange = axis === "vertical" ? el.scrollHeight - el.clientHeight : el.scrollWidth - el.clientWidth;
3551
+ if (scrollRange <= 0 || trackLength <= 0) return;
3552
+ const effective = Math.max(1, trackLength - thumb.size);
3553
+ const ratio = Math.max(0, Math.min(effective, clickOffset - thumb.size / 2)) / effective;
3554
+ const target = ratio * scrollRange;
3555
+ if (axis === "vertical") el.scrollTop = target;
3556
+ else el.scrollLeft = target;
3557
+ startDrag(event, axis === "vertical" ? el.scrollTop : el.scrollLeft);
3558
+ },
3559
+ []
3560
+ );
3561
+ const vSlot = padRight || TRACK_PADDING2;
3562
+ const hSlot = padBottom || TRACK_PADDING2;
3563
+ const vOffset = Math.max(TRACK_PADDING2, (vSlot - TRACK_THICKNESS) / 2);
3564
+ const hOffset = Math.max(TRACK_PADDING2, (hSlot - TRACK_THICKNESS) / 2);
3565
+ const hBottom = Math.max(TRACK_PADDING2 / 2, (hSlot - TRACK_THICKNESS) / 2);
3566
+ return /* @__PURE__ */ jsx24(
3567
+ "div",
3568
+ {
3569
+ className: twMerge23(
3570
+ "overflow-hidden rounded-3xl border border-slate-200 shadow-sm dark:border-zinc-800",
3571
+ className
3572
+ ),
3573
+ style,
3574
+ children: /* @__PURE__ */ jsxs22(
3575
+ "div",
3576
+ {
3577
+ className: "relative overflow-hidden bg-white/90 dark:bg-zinc-950/80",
3578
+ style: { paddingRight: padRight, paddingBottom: padBottom },
3579
+ children: [
3580
+ /* @__PURE__ */ jsx24(
3581
+ "div",
3582
+ {
3583
+ ref: scrollRef,
3584
+ className: "table-scrollbar overflow-auto",
3585
+ style: { ...scrollAreaStyle },
3586
+ children: /* @__PURE__ */ jsxs22("table", { className: "w-full border-collapse text-sm text-slate-700 dark:text-zinc-200", children: [
3587
+ caption ? /* @__PURE__ */ jsx24("caption", { className: "bg-slate-50 px-4 py-2 text-left text-xs font-semibold uppercase tracking-[0.25em] text-slate-500 dark:bg-zinc-900/70 dark:text-zinc-400", children: caption }) : null,
3588
+ /* @__PURE__ */ jsx24("thead", { className: "bg-white/90 text-xs uppercase tracking-[0.16em] text-slate-500 dark:bg-zinc-900/80 dark:text-zinc-400", children: /* @__PURE__ */ jsx24("tr", { children: columns.map((col) => /* @__PURE__ */ jsx24(
3589
+ "th",
3590
+ {
3591
+ scope: "col",
3592
+ className: twMerge23(
3593
+ "px-4 py-3 text-left font-semibold",
3594
+ col.align === "right" && "text-right",
3595
+ col.align === "center" && "text-center"
3596
+ ),
3597
+ children: col.header
3598
+ },
3599
+ String(col.key)
3600
+ )) }) }),
3601
+ /* @__PURE__ */ jsx24("tbody", { children: data.map((row, rowIndex) => /* @__PURE__ */ jsx24(
3602
+ "tr",
3603
+ {
3604
+ className: twMerge23(
3605
+ "border-t border-slate-100 transition dark:border-zinc-800",
3606
+ rowIndex % 2 === 0 ? "bg-slate-100/70 hover:bg-slate-200 dark:bg-zinc-950/70 dark:hover:bg-zinc-800" : "bg-white/80 hover:bg-slate-200 dark:bg-zinc-900/60 dark:hover:bg-zinc-800"
3607
+ ),
3608
+ children: columns.map((col) => /* @__PURE__ */ jsx24(
3609
+ "td",
3610
+ {
3611
+ className: twMerge23(
3612
+ "px-4 py-3 align-middle",
3613
+ col.align === "right" && "text-right",
3614
+ col.align === "center" && "text-center"
3615
+ ),
3616
+ children: col.render ? col.render(row[col.key], row) : row[col.key]
3617
+ },
3618
+ String(col.key)
3619
+ ))
3620
+ },
3621
+ rowIndex
3622
+ )) })
3623
+ ] })
3624
+ }
3625
+ ),
3626
+ vThumb.visible ? /* @__PURE__ */ jsx24(
3627
+ "div",
3628
+ {
3629
+ className: "absolute z-20 pointer-events-auto rounded-full bg-white/80 shadow-inner dark:bg-zinc-900/70",
3630
+ style: {
3631
+ right: vOffset,
3632
+ top: TRACK_PADDING2 + V_TRACK_INSET,
3633
+ bottom: TRACK_PADDING2 + V_TRACK_INSET,
3634
+ width: TRACK_THICKNESS
3635
+ },
3636
+ onPointerDown: (e) => handleTrackPointerDown("vertical", vThumb, startVDrag, e),
3637
+ children: /* @__PURE__ */ jsx24("div", { className: "relative h-full w-full rounded-full bg-slate-900/5 shadow-inner dark:bg-white/10", children: /* @__PURE__ */ jsx24(
3638
+ "div",
3639
+ {
3640
+ className: "absolute left-1/2 w-full -translate-x-1/2 rounded-full bg-slate-400/80 shadow-sm transition-colors dark:bg-zinc-500/70",
3641
+ style: { height: `${vThumb.size}px`, top: `${vThumb.offset}px` },
3642
+ onPointerDown: handleVThumbDown
3643
+ }
3644
+ ) })
3645
+ }
3646
+ ) : null,
3647
+ hThumb.visible ? /* @__PURE__ */ jsx24(
3648
+ "div",
3649
+ {
3650
+ className: "absolute z-20 pointer-events-auto rounded-full bg-white/80 shadow-inner dark:bg-zinc-900/70",
3651
+ style: {
3652
+ left: hOffset + TRACK_INSET,
3653
+ right: hOffset + TRACK_INSET,
3654
+ bottom: hBottom,
3655
+ height: TRACK_THICKNESS
3656
+ },
3657
+ onPointerDown: (e) => handleTrackPointerDown("horizontal", hThumb, startHDrag, e),
3658
+ children: /* @__PURE__ */ jsx24("div", { className: "relative h-full w-full rounded-full bg-slate-900/5 shadow-inner dark:bg-white/10", children: /* @__PURE__ */ jsx24(
3659
+ "div",
3660
+ {
3661
+ className: "absolute top-1/2 h-full -translate-y-1/2 rounded-full bg-slate-400/80 shadow-sm transition-colors dark:bg-zinc-500/70",
3662
+ style: { width: `${hThumb.size}px`, left: `${hThumb.offset}px` },
3663
+ onPointerDown: handleHThumbDown
3664
+ }
3665
+ ) })
3666
+ }
3667
+ ) : null
3668
+ ]
3669
+ }
3670
+ )
3671
+ }
3672
+ );
3673
+ }
3674
+
3675
+ // components/Textarea/Textarea.tsx
3676
+ import * as React21 from "react";
3677
+ import { twMerge as twMerge24 } from "tailwind-merge";
3678
+ import { jsx as jsx25, jsxs as jsxs23 } from "react/jsx-runtime";
3679
+ var MIN_THUMB2 = 24;
3680
+ var TRACK_THICKNESS2 = 6;
3681
+ var TRACK_TOP = 8;
3682
+ var TRACK_BOTTOM = 22;
3683
+ var TRACK_REDUCTION = 20;
3684
+ var TRACK_REDUCTION_HALF = TRACK_REDUCTION / 2;
3685
+ var Textarea = React21.forwardRef(function Textarea2({
3686
+ label,
3687
+ description,
3688
+ error,
3689
+ className,
3690
+ id,
3691
+ disabled,
3692
+ maxLength,
3693
+ showCount = true,
3694
+ resizeDirection = "vertical",
3695
+ ...rest
3696
+ }, ref) {
3697
+ var _a, _b;
3698
+ const generatedId = React21.useId();
3699
+ const textareaId = id != null ? id : generatedId;
3700
+ const descriptionId = React21.useId();
3701
+ const errorId = React21.useId();
3702
+ const textareaRef = React21.useRef(null);
3703
+ const shellRef = React21.useRef(null);
3704
+ const resizeListenersRef = React21.useRef({});
3705
+ const [thumb, setThumb] = React21.useState({
3706
+ visible: false,
3707
+ size: MIN_THUMB2,
3708
+ offset: 0
3709
+ });
3710
+ const hintIds = [description ? descriptionId : null, error ? errorId : null].filter(Boolean);
3711
+ const resolvedAriaDescribedBy = hintIds.length ? hintIds.join(" ") : void 0;
3712
+ const [value, setValue] = React21.useState((_b = (_a = rest.defaultValue) == null ? void 0 : _a.toString()) != null ? _b : "");
3713
+ const [height, setHeight] = React21.useState(void 0);
3714
+ const [width, setWidth] = React21.useState(void 0);
3715
+ const setRefs = React21.useCallback(
3716
+ (node) => {
3717
+ textareaRef.current = node;
3718
+ if (typeof ref === "function") {
3719
+ ref(node);
3720
+ } else if (ref) {
3721
+ ref.current = node;
3722
+ }
3723
+ },
3724
+ [ref]
3725
+ );
3726
+ React21.useEffect(() => {
3727
+ if (typeof rest.value === "string") {
3728
+ setValue(rest.value);
3729
+ }
3730
+ }, [rest.value]);
3731
+ React21.useLayoutEffect(() => {
3732
+ if (textareaRef.current && height === void 0) {
3733
+ setHeight(textareaRef.current.offsetHeight);
3734
+ }
3735
+ }, [height]);
3736
+ React21.useLayoutEffect(() => {
3737
+ const el = textareaRef.current;
3738
+ if (!el) return;
3739
+ let raf = 0;
3740
+ const update = () => {
3741
+ var _a2, _b2;
3742
+ const target = textareaRef.current;
3743
+ if (!target) return;
3744
+ const scrollRange = target.scrollHeight - target.clientHeight;
3745
+ const hostHeight = (_b2 = (_a2 = shellRef.current) == null ? void 0 : _a2.clientHeight) != null ? _b2 : target.clientHeight;
3746
+ const trackLength = Math.max(0, hostHeight - TRACK_TOP - TRACK_BOTTOM - TRACK_REDUCTION);
3747
+ if (scrollRange <= 0 || trackLength <= 0) {
3748
+ setThumb((prev) => prev.visible ? { ...prev, visible: false } : prev);
3749
+ return;
3750
+ }
3751
+ const ratio = target.clientHeight / target.scrollHeight;
3752
+ const size = Math.max(trackLength * ratio, MIN_THUMB2);
3753
+ const maxOffset = Math.max(0, trackLength - size);
3754
+ const offset = maxOffset > 0 ? Math.min(maxOffset, target.scrollTop / scrollRange * maxOffset) : 0;
3755
+ setThumb({ visible: true, size, offset });
3756
+ };
3757
+ const handleScroll = () => {
3758
+ cancelAnimationFrame(raf);
3759
+ raf = window.requestAnimationFrame(update);
3760
+ };
3761
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(update) : null;
3762
+ ro == null ? void 0 : ro.observe(el);
3763
+ el.addEventListener("scroll", handleScroll, { passive: true });
3764
+ update();
3765
+ return () => {
3766
+ ro == null ? void 0 : ro.disconnect();
3767
+ el.removeEventListener("scroll", handleScroll);
3768
+ cancelAnimationFrame(raf);
3769
+ };
3770
+ }, [height]);
3771
+ React21.useLayoutEffect(() => {
3772
+ if (!shellRef.current || width !== void 0) return;
3773
+ setWidth(shellRef.current.offsetWidth);
3774
+ }, [width]);
3775
+ React21.useEffect(() => {
3776
+ return () => {
3777
+ if (resizeListenersRef.current.move) {
3778
+ window.removeEventListener("mousemove", resizeListenersRef.current.move);
3779
+ }
3780
+ if (resizeListenersRef.current.up) {
3781
+ window.removeEventListener("mouseup", resizeListenersRef.current.up);
3782
+ }
3783
+ };
3784
+ }, []);
3785
+ const handleResizeStart = (event) => {
3786
+ var _a2, _b2, _c, _d, _e;
3787
+ event.preventDefault();
3788
+ if (!textareaRef.current) return;
3789
+ const allowY = resizeDirection === "vertical" || resizeDirection === "both";
3790
+ const allowX = resizeDirection === "horizontal" || resizeDirection === "both";
3791
+ const startY = event.clientY;
3792
+ const startX = event.clientX;
3793
+ const startHeight = textareaRef.current.offsetHeight;
3794
+ const startWidth = (_b2 = (_a2 = shellRef.current) == null ? void 0 : _a2.offsetWidth) != null ? _b2 : textareaRef.current.offsetWidth;
3795
+ const minHeight = 120;
3796
+ const minWidth = 240;
3797
+ const parentWidth = (_e = (_d = (_c = shellRef.current) == null ? void 0 : _c.parentElement) == null ? void 0 : _d.clientWidth) != null ? _e : startWidth;
3798
+ const onMove = (moveEvent) => {
3799
+ if (allowY) {
3800
+ const nextHeight = Math.max(minHeight, startHeight + (moveEvent.clientY - startY));
3801
+ setHeight(nextHeight);
3802
+ }
3803
+ if (allowX) {
3804
+ const proposed = startWidth + (moveEvent.clientX - startX);
3805
+ const nextWidth = Math.min(parentWidth, Math.max(minWidth, proposed));
3806
+ setWidth(nextWidth);
3807
+ }
3808
+ };
3809
+ const onUp = () => {
3810
+ window.removeEventListener("mousemove", onMove);
3811
+ window.removeEventListener("mouseup", onUp);
3812
+ resizeListenersRef.current = {};
3813
+ };
3814
+ resizeListenersRef.current = { move: onMove, up: onUp };
3815
+ window.addEventListener("mousemove", onMove);
3816
+ window.addEventListener("mouseup", onUp);
3817
+ };
3818
+ const handleThumbDrag = React21.useCallback(
3819
+ (event, startScrollOverride) => {
3820
+ var _a2, _b2;
3821
+ const el = textareaRef.current;
3822
+ if (!el) return;
3823
+ event.preventDefault();
3824
+ const startY = event.clientY;
3825
+ const startScroll = startScrollOverride != null ? startScrollOverride : el.scrollTop;
3826
+ const handleMove = (moveEvent) => {
3827
+ var _a3, _b3;
3828
+ const delta = moveEvent.clientY - startY;
3829
+ const scrollRange = el.scrollHeight - el.clientHeight;
3830
+ const hostHeight = (_b3 = (_a3 = shellRef.current) == null ? void 0 : _a3.clientHeight) != null ? _b3 : el.clientHeight;
3831
+ const trackLength = Math.max(0, hostHeight - TRACK_TOP - TRACK_BOTTOM - TRACK_REDUCTION);
3832
+ if (scrollRange <= 0 || trackLength <= thumb.size) return;
3833
+ const ratio = scrollRange / (trackLength - thumb.size);
3834
+ const next = startScroll + delta * ratio;
3835
+ el.scrollTop = Math.min(scrollRange, Math.max(0, next));
3836
+ };
3837
+ const handleUp = () => {
3838
+ window.removeEventListener("pointermove", handleMove);
3839
+ window.removeEventListener("pointerup", handleUp);
3840
+ window.removeEventListener("pointercancel", handleUp);
3841
+ };
3842
+ (_b2 = (_a2 = event.currentTarget).setPointerCapture) == null ? void 0 : _b2.call(_a2, event.pointerId);
3843
+ window.addEventListener("pointermove", handleMove);
3844
+ window.addEventListener("pointerup", handleUp);
3845
+ window.addEventListener("pointercancel", handleUp);
3846
+ },
3847
+ [thumb.size]
3848
+ );
3849
+ const shellClasses = twMerge24(
3850
+ "relative rounded-2xl border border-slate-300 bg-white/80 px-3 py-2 shadow-sm transition focus-within:border-slate-400 focus-within:shadow-[0_0_0_1px_rgba(148,163,184,0.45)] dark:border-zinc-700 dark:bg-zinc-900/70 dark:focus-within:border-slate-500",
3851
+ disabled && "opacity-60",
3852
+ error && "border-rose-300 focus-within:border-rose-400 focus-within:shadow-[0_0_0_1px_rgba(248,113,113,0.35)] dark:border-rose-500/60"
3853
+ );
3854
+ const textareaClasses = twMerge24(
3855
+ "textarea-scrollbar min-h-[120px] w-full resize-none border-none bg-transparent pb-4 pr-5 text-sm text-slate-900 placeholder:text-slate-400 focus:outline-none dark:text-zinc-100 dark:placeholder:text-zinc-500",
3856
+ className
3857
+ );
3858
+ const count = value.length;
3859
+ const limit = maxLength != null ? maxLength : void 0;
3860
+ const { style, ...restProps } = rest;
3861
+ const textareaStyle = {
3862
+ ...style,
3863
+ ...height !== void 0 ? { height } : null,
3864
+ ...width !== void 0 ? { width } : null,
3865
+ maxWidth: "100%"
3866
+ };
3867
+ const shellStyle = width !== void 0 ? { width, maxWidth: "100%" } : { maxWidth: "100%" };
3868
+ return /* @__PURE__ */ jsxs23("div", { className: "space-y-1.5", children: [
3869
+ label ? /* @__PURE__ */ jsx25(
3870
+ "label",
3871
+ {
3872
+ htmlFor: textareaId,
3873
+ className: "text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-zinc-400",
3874
+ children: label
3875
+ }
3876
+ ) : null,
3877
+ /* @__PURE__ */ jsxs23("div", { className: shellClasses, ref: shellRef, style: shellStyle, children: [
3878
+ /* @__PURE__ */ jsx25(
3879
+ "textarea",
3880
+ {
3881
+ ...restProps,
3882
+ ref: setRefs,
3883
+ id: textareaId,
3884
+ className: textareaClasses,
3885
+ "aria-invalid": error ? true : void 0,
3886
+ "aria-describedby": resolvedAriaDescribedBy,
3887
+ disabled,
3888
+ maxLength,
3889
+ style: textareaStyle,
3890
+ onChange: (e) => {
3891
+ var _a2;
3892
+ setValue(e.target.value);
3893
+ (_a2 = rest.onChange) == null ? void 0 : _a2.call(rest, e);
3894
+ }
3895
+ }
3896
+ ),
3897
+ thumb.visible ? /* @__PURE__ */ jsx25(
3898
+ "div",
3899
+ {
3900
+ className: "pointer-events-auto absolute z-20 rounded-full bg-white/60 shadow-inner backdrop-blur-sm transition-colors dark:bg-zinc-800/70",
3901
+ style: {
3902
+ right: 4,
3903
+ top: TRACK_TOP + TRACK_REDUCTION_HALF,
3904
+ bottom: TRACK_BOTTOM + TRACK_REDUCTION_HALF,
3905
+ width: TRACK_THICKNESS2
3906
+ },
3907
+ onPointerDown: (event) => {
3908
+ event.preventDefault();
3909
+ event.stopPropagation();
3910
+ const el = textareaRef.current;
3911
+ if (!el) return;
3912
+ const rect = event.currentTarget.getBoundingClientRect();
3913
+ const clickOffset = event.clientY - rect.top;
3914
+ const trackLength = rect.height;
3915
+ const scrollRange = el.scrollHeight - el.clientHeight;
3916
+ if (scrollRange <= 0 || trackLength <= 0) return;
3917
+ const effective = Math.max(1, trackLength - thumb.size);
3918
+ const ratio = Math.max(0, Math.min(effective, clickOffset - thumb.size / 2)) / effective;
3919
+ const target = ratio * scrollRange;
3920
+ el.scrollTop = target;
3921
+ handleThumbDrag(event, target);
3922
+ },
3923
+ children: /* @__PURE__ */ jsx25("div", { className: "relative h-full w-full rounded-full bg-slate-900/5 shadow-inner dark:bg-white/10", children: /* @__PURE__ */ jsx25(
3924
+ "div",
3925
+ {
3926
+ className: "absolute left-1/2 w-full -translate-x-1/2 rounded-full bg-slate-400/80 shadow-sm transition-colors dark:bg-zinc-500/70",
3927
+ style: { height: `${thumb.size}px`, top: `${thumb.offset}px` },
3928
+ onPointerDown: (event) => {
3929
+ event.stopPropagation();
3930
+ handleThumbDrag(event);
3931
+ }
3932
+ }
3933
+ ) })
3934
+ }
3935
+ ) : null,
3936
+ resizeDirection !== "none" ? /* @__PURE__ */ jsxs23("div", { className: "pointer-events-none absolute bottom-2 right-2 flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400 dark:text-zinc-500", children: [
3937
+ showCount && limit ? /* @__PURE__ */ jsxs23("div", { className: "pointer-events-auto", children: [
3938
+ count,
3939
+ "/",
3940
+ limit
3941
+ ] }) : null,
3942
+ /* @__PURE__ */ jsx25(
3943
+ "button",
3944
+ {
3945
+ type: "button",
3946
+ "aria-label": "Resize textarea",
3947
+ onMouseDown: handleResizeStart,
3948
+ className: "pointer-events-auto inline-flex h-[14px] w-[14px] items-center justify-center rounded-[3px] bg-transparent text-slate-400 outline-none transition hover:text-slate-500 focus-visible:ring-2 focus-visible:ring-slate-300 focus-visible:ring-offset-1 focus-visible:ring-offset-white active:cursor-grab dark:bg-transparent dark:text-zinc-400 dark:hover:text-zinc-200 dark:focus-visible:ring-slate-500 dark:focus-visible:ring-offset-zinc-900",
3949
+ style: {
3950
+ border: "none",
3951
+ boxShadow: "none",
3952
+ appearance: "none",
3953
+ background: "transparent"
3954
+ },
3955
+ children: /* @__PURE__ */ jsx25(
3956
+ "svg",
3957
+ {
3958
+ viewBox: "0 0 12 12",
3959
+ "aria-hidden": "true",
3960
+ className: "h-3 w-3 text-slate-400 dark:text-zinc-400",
3961
+ children: /* @__PURE__ */ jsx25(
3962
+ "path",
3963
+ {
3964
+ d: "M2 10.5 10.5 2M4.5 10.5 10.5 4.5M7 10.5 10.5 7",
3965
+ stroke: "currentColor",
3966
+ strokeWidth: "1.1"
3967
+ }
3968
+ )
3969
+ }
3970
+ )
3971
+ }
3972
+ )
3973
+ ] }) : null
3974
+ ] }),
3975
+ description ? /* @__PURE__ */ jsx25("p", { id: descriptionId, className: "text-xs text-slate-500 dark:text-zinc-400", children: description }) : null,
3976
+ error ? /* @__PURE__ */ jsx25("p", { id: errorId, className: "text-xs font-medium text-rose-500 dark:text-rose-400", children: error }) : null
3977
+ ] });
3978
+ });
3979
+
3980
+ // components/Toggle/Toggle.tsx
3981
+ import * as React22 from "react";
3982
+ import { twMerge as twMerge25 } from "tailwind-merge";
3983
+ import { jsx as jsx26 } from "react/jsx-runtime";
3984
+ var Toggle = React22.forwardRef(function Toggle2({ checked, defaultChecked = false, onChange, disabled, className, onClick, ...rest }, ref) {
3985
+ const isControlled = typeof checked === "boolean";
3986
+ const [internalChecked, setInternalChecked] = React22.useState(defaultChecked);
3987
+ const resolvedChecked = isControlled ? !!checked : internalChecked;
3988
+ const handleClick = (event) => {
3989
+ if (disabled) {
3990
+ event.preventDefault();
3991
+ return;
3992
+ }
3993
+ const next = !resolvedChecked;
3994
+ if (!isControlled) {
3995
+ setInternalChecked(next);
3996
+ }
3997
+ onChange == null ? void 0 : onChange(next);
3998
+ onClick == null ? void 0 : onClick(event);
3999
+ };
4000
+ const buttonClasses = twMerge25(
4001
+ "relative inline-flex h-7 w-12 shrink-0 cursor-pointer items-center rounded-full border border-slate-300 bg-slate-200 transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-500 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-100",
4002
+ "dark:border-zinc-700/70 dark:bg-zinc-900/70 dark:focus-visible:ring-offset-slate-950",
4003
+ resolvedChecked && "border-slate-400 bg-slate-500/40 shadow-[0_0_0_1px_rgba(148,163,184,0.45)] dark:border-zinc-500 dark:bg-zinc-500",
4004
+ disabled && "cursor-not-allowed opacity-60",
4005
+ className
4006
+ );
4007
+ const thumbClasses = twMerge25(
4008
+ "pointer-events-none absolute left-1 top-[3px] size-5 rounded-full bg-white text-zinc-900 shadow-lg shadow-black/30 transition-transform duration-200",
4009
+ resolvedChecked ? "translate-x-[19px]" : "translate-x-0"
4010
+ );
4011
+ return /* @__PURE__ */ jsx26(
4012
+ "button",
4013
+ {
4014
+ ...rest,
4015
+ ref,
4016
+ type: "button",
4017
+ role: "switch",
4018
+ "aria-checked": resolvedChecked,
4019
+ disabled,
4020
+ "data-state": resolvedChecked ? "on" : "off",
4021
+ className: buttonClasses,
4022
+ onClick: handleClick,
4023
+ children: /* @__PURE__ */ jsx26("span", { "aria-hidden": "true", className: thumbClasses })
4024
+ }
4025
+ );
4026
+ });
4027
+ var Toggle_default = Toggle;
4028
+ export {
4029
+ Alert,
4030
+ Badge,
4031
+ Button,
4032
+ Card,
4033
+ Checkbox,
4034
+ ColorPicker,
4035
+ Combobox,
4036
+ DatalistInput,
4037
+ DatePicker,
4038
+ Dialog,
4039
+ Disclosure,
4040
+ Dropdown,
4041
+ InputField,
4042
+ Meter,
4043
+ NumberInput,
4044
+ OutputChip,
4045
+ Popover,
4046
+ Progress,
4047
+ Radio,
4048
+ Select,
4049
+ Slider,
4050
+ StackedList,
4051
+ Table,
4052
+ Textarea,
4053
+ Toggle_default as Toggle
4054
+ };
4055
+ //# sourceMappingURL=index.js.map