@kjaniec-dev/ui 0.8.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/index.cjs +3825 -180
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +666 -3
- package/dist/index.d.ts +666 -3
- package/dist/index.js +3765 -180
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { clsx } from 'clsx';
|
|
2
3
|
import { twMerge } from 'tailwind-merge';
|
|
3
|
-
import * as
|
|
4
|
+
import * as React52 from 'react';
|
|
4
5
|
import { cva } from 'class-variance-authority';
|
|
5
6
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
6
7
|
|
|
@@ -38,7 +39,7 @@ var buttonVariants = cva(
|
|
|
38
39
|
defaultVariants: { variant: "primary", size: "md" }
|
|
39
40
|
}
|
|
40
41
|
);
|
|
41
|
-
var Button =
|
|
42
|
+
var Button = React52.forwardRef(
|
|
42
43
|
({ className, variant, size, loading, leadingIcon, trailingIcon, children, disabled, ...props }, ref) => {
|
|
43
44
|
return /* @__PURE__ */ jsxs(
|
|
44
45
|
"button",
|
|
@@ -85,7 +86,7 @@ var badgeVariants = cva(
|
|
|
85
86
|
defaultVariants: { variant: "neutral" }
|
|
86
87
|
}
|
|
87
88
|
);
|
|
88
|
-
var Badge =
|
|
89
|
+
var Badge = React52.forwardRef(
|
|
89
90
|
({ className, variant, dot, children, ...props }, ref) => /* @__PURE__ */ jsxs("span", { ref, className: cn(badgeVariants({ variant }), className), ...props, children: [
|
|
90
91
|
dot && /* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-current", "aria-hidden": true }),
|
|
91
92
|
children
|
|
@@ -112,7 +113,7 @@ var iconColor = {
|
|
|
112
113
|
warning: "text-warning",
|
|
113
114
|
danger: "text-danger"
|
|
114
115
|
};
|
|
115
|
-
var Alert =
|
|
116
|
+
var Alert = React52.forwardRef(
|
|
116
117
|
({ className, variant = "info", icon, title, children, ...props }, ref) => /* @__PURE__ */ jsxs("div", { ref, role: "alert", className: cn(alertVariants({ variant }), className), ...props, children: [
|
|
117
118
|
icon && /* @__PURE__ */ jsx("span", { className: cn("shrink-0 mt-0.5 [&_svg]:h-[1.15rem] [&_svg]:w-[1.15rem]", iconColor[variant ?? "info"]), children: icon }),
|
|
118
119
|
/* @__PURE__ */ jsxs("div", { className: "text-foreground", children: [
|
|
@@ -122,7 +123,7 @@ var Alert = React18.forwardRef(
|
|
|
122
123
|
] })
|
|
123
124
|
);
|
|
124
125
|
Alert.displayName = "Alert";
|
|
125
|
-
var Spinner =
|
|
126
|
+
var Spinner = React52.forwardRef(
|
|
126
127
|
({ className, size = 24, style, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
127
128
|
"span",
|
|
128
129
|
{
|
|
@@ -139,7 +140,7 @@ var Spinner = React18.forwardRef(
|
|
|
139
140
|
)
|
|
140
141
|
);
|
|
141
142
|
Spinner.displayName = "Spinner";
|
|
142
|
-
var Progress =
|
|
143
|
+
var Progress = React52.forwardRef(
|
|
143
144
|
({ className, value = 0, tone = "primary", barClassName, ...props }, ref) => {
|
|
144
145
|
const pct = Math.max(0, Math.min(100, value));
|
|
145
146
|
return /* @__PURE__ */ jsx(
|
|
@@ -169,14 +170,14 @@ var Progress = React18.forwardRef(
|
|
|
169
170
|
}
|
|
170
171
|
);
|
|
171
172
|
Progress.displayName = "Progress";
|
|
172
|
-
var Label =
|
|
173
|
+
var Label = React52.forwardRef(
|
|
173
174
|
({ className, required, children, ...props }, ref) => /* @__PURE__ */ jsxs("label", { ref, className: cn("text-[0.8rem] font-semibold text-foreground", className), ...props, children: [
|
|
174
175
|
children,
|
|
175
176
|
required && /* @__PURE__ */ jsx("span", { className: "text-danger", children: " *" })
|
|
176
177
|
] })
|
|
177
178
|
);
|
|
178
179
|
Label.displayName = "Label";
|
|
179
|
-
var Hint =
|
|
180
|
+
var Hint = React52.forwardRef(
|
|
180
181
|
({ className, error, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
181
182
|
"span",
|
|
182
183
|
{
|
|
@@ -187,17 +188,17 @@ var Hint = React18.forwardRef(
|
|
|
187
188
|
)
|
|
188
189
|
);
|
|
189
190
|
Hint.displayName = "Hint";
|
|
190
|
-
var Field =
|
|
191
|
+
var Field = React52.forwardRef(
|
|
191
192
|
({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("flex flex-col gap-1.5", className), ...props })
|
|
192
193
|
);
|
|
193
194
|
Field.displayName = "Field";
|
|
194
|
-
var FormField =
|
|
195
|
+
var FormField = React52.forwardRef(
|
|
195
196
|
({ className, label, hint, error, required, children, ...props }, ref) => {
|
|
196
|
-
const id =
|
|
197
|
+
const id = React52.useId();
|
|
197
198
|
const hintId = `${id}-hint`;
|
|
198
199
|
const errorId = `${id}-error`;
|
|
199
|
-
const child =
|
|
200
|
-
const clonedChild =
|
|
200
|
+
const child = React52.Children.only(children);
|
|
201
|
+
const clonedChild = React52.cloneElement(child, {
|
|
201
202
|
id: child.props.id ?? id,
|
|
202
203
|
"aria-describedby": cn(
|
|
203
204
|
hint && hintId,
|
|
@@ -216,9 +217,136 @@ var FormField = React18.forwardRef(
|
|
|
216
217
|
}
|
|
217
218
|
);
|
|
218
219
|
FormField.displayName = "FormField";
|
|
220
|
+
var sizeDimensions = {
|
|
221
|
+
sm: 36,
|
|
222
|
+
md: 64,
|
|
223
|
+
lg: 96,
|
|
224
|
+
xl: 128
|
|
225
|
+
};
|
|
226
|
+
var defaultStrokeWidths = {
|
|
227
|
+
sm: 3.5,
|
|
228
|
+
md: 6,
|
|
229
|
+
lg: 8,
|
|
230
|
+
xl: 10
|
|
231
|
+
};
|
|
232
|
+
var textSizeClasses = {
|
|
233
|
+
sm: "text-[0.65rem] font-semibold leading-none",
|
|
234
|
+
md: "text-xs font-semibold leading-none",
|
|
235
|
+
lg: "text-base font-bold leading-none",
|
|
236
|
+
xl: "text-xl font-bold leading-none"
|
|
237
|
+
};
|
|
238
|
+
var toneClasses = {
|
|
239
|
+
primary: "text-primary stroke-current",
|
|
240
|
+
secondary: "text-secondary stroke-current",
|
|
241
|
+
success: "text-success stroke-current",
|
|
242
|
+
warning: "text-warning stroke-current",
|
|
243
|
+
danger: "text-danger stroke-current",
|
|
244
|
+
info: "text-info stroke-current"
|
|
245
|
+
};
|
|
246
|
+
var ProgressRing = React52.forwardRef(
|
|
247
|
+
({
|
|
248
|
+
className,
|
|
249
|
+
style,
|
|
250
|
+
value = 0,
|
|
251
|
+
min = 0,
|
|
252
|
+
max = 100,
|
|
253
|
+
size = "md",
|
|
254
|
+
strokeWidth,
|
|
255
|
+
tone = "primary",
|
|
256
|
+
showValue = false,
|
|
257
|
+
formatValue,
|
|
258
|
+
trackClassName,
|
|
259
|
+
indicatorClassName,
|
|
260
|
+
children,
|
|
261
|
+
"aria-label": ariaLabel,
|
|
262
|
+
...props
|
|
263
|
+
}, ref) => {
|
|
264
|
+
const range2 = max > min ? max - min : 100;
|
|
265
|
+
const clampedValue = Math.max(min, Math.min(max, value));
|
|
266
|
+
const percentage = Math.max(0, Math.min(100, (clampedValue - min) / range2 * 100));
|
|
267
|
+
const dimension = sizeDimensions[size];
|
|
268
|
+
const strokeWidthVal = strokeWidth ?? defaultStrokeWidths[size];
|
|
269
|
+
const radius = Math.max(0, (dimension - strokeWidthVal) / 2);
|
|
270
|
+
const circumference = 2 * Math.PI * radius;
|
|
271
|
+
const strokeDashoffset = circumference - percentage / 100 * circumference;
|
|
272
|
+
const computedAriaLabel = ariaLabel ?? (props["aria-labelledby"] ? void 0 : "Progress");
|
|
273
|
+
return /* @__PURE__ */ jsxs(
|
|
274
|
+
"div",
|
|
275
|
+
{
|
|
276
|
+
ref,
|
|
277
|
+
role: "progressbar",
|
|
278
|
+
"aria-label": computedAriaLabel,
|
|
279
|
+
"aria-valuenow": clampedValue,
|
|
280
|
+
"aria-valuemin": min,
|
|
281
|
+
"aria-valuemax": max,
|
|
282
|
+
className: cn("relative inline-flex items-center justify-center shrink-0", className),
|
|
283
|
+
style: { width: dimension, height: dimension, ...style },
|
|
284
|
+
...props,
|
|
285
|
+
children: [
|
|
286
|
+
/* @__PURE__ */ jsxs(
|
|
287
|
+
"svg",
|
|
288
|
+
{
|
|
289
|
+
width: dimension,
|
|
290
|
+
height: dimension,
|
|
291
|
+
viewBox: `0 0 ${dimension} ${dimension}`,
|
|
292
|
+
className: "-rotate-90 transform",
|
|
293
|
+
children: [
|
|
294
|
+
/* @__PURE__ */ jsx(
|
|
295
|
+
"circle",
|
|
296
|
+
{
|
|
297
|
+
cx: dimension / 2,
|
|
298
|
+
cy: dimension / 2,
|
|
299
|
+
r: radius,
|
|
300
|
+
strokeWidth: strokeWidthVal,
|
|
301
|
+
className: cn("stroke-muted/30 fill-none", trackClassName)
|
|
302
|
+
}
|
|
303
|
+
),
|
|
304
|
+
/* @__PURE__ */ jsx(
|
|
305
|
+
"circle",
|
|
306
|
+
{
|
|
307
|
+
cx: dimension / 2,
|
|
308
|
+
cy: dimension / 2,
|
|
309
|
+
r: radius,
|
|
310
|
+
strokeWidth: strokeWidthVal,
|
|
311
|
+
strokeDasharray: circumference,
|
|
312
|
+
strokeDashoffset,
|
|
313
|
+
strokeLinecap: percentage === 0 ? "butt" : "round",
|
|
314
|
+
className: cn(
|
|
315
|
+
"fill-none transition-[stroke-dashoffset,opacity] duration-500 ease-in-out",
|
|
316
|
+
percentage === 0 && "opacity-0",
|
|
317
|
+
toneClasses[tone],
|
|
318
|
+
indicatorClassName
|
|
319
|
+
)
|
|
320
|
+
}
|
|
321
|
+
)
|
|
322
|
+
]
|
|
323
|
+
}
|
|
324
|
+
),
|
|
325
|
+
(children || showValue) && /* @__PURE__ */ jsx(
|
|
326
|
+
"div",
|
|
327
|
+
{
|
|
328
|
+
className: cn(
|
|
329
|
+
"absolute inset-0 flex flex-col items-center justify-center text-center text-foreground p-1",
|
|
330
|
+
textSizeClasses[size]
|
|
331
|
+
),
|
|
332
|
+
children: children ?? (formatValue ? formatValue(clampedValue, Math.round(percentage)) : `${Math.round(percentage)}%`)
|
|
333
|
+
}
|
|
334
|
+
)
|
|
335
|
+
]
|
|
336
|
+
}
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
);
|
|
340
|
+
ProgressRing.displayName = "ProgressRing";
|
|
341
|
+
var ProgressRingField = React52.forwardRef(
|
|
342
|
+
({ label, hint, error, required, fieldClassName, ...ringProps }, ref) => {
|
|
343
|
+
return /* @__PURE__ */ jsx(FormField, { label, hint, error, required, className: fieldClassName, children: /* @__PURE__ */ jsx(ProgressRing, { ref, "aria-label": ringProps["aria-label"] ?? label, ...ringProps }) });
|
|
344
|
+
}
|
|
345
|
+
);
|
|
346
|
+
ProgressRingField.displayName = "ProgressRingField";
|
|
219
347
|
var baseField = "w-full font-sans text-sm text-foreground bg-surface border rounded-kj-md px-[0.85rem] transition-[border-color,box-shadow] duration-150 placeholder:text-muted-foreground focus:outline-none focus:border-ring focus:ring-[3px] focus:ring-ring/30 disabled:bg-muted disabled:text-muted-foreground disabled:cursor-not-allowed";
|
|
220
|
-
var Input =
|
|
221
|
-
({ className, error, leadingIcon,
|
|
348
|
+
var Input = React52.forwardRef(
|
|
349
|
+
({ className, error, leadingIcon, ...props }, ref) => {
|
|
222
350
|
const field = /* @__PURE__ */ jsx(
|
|
223
351
|
"input",
|
|
224
352
|
{
|
|
@@ -242,7 +370,7 @@ var Input = React18.forwardRef(
|
|
|
242
370
|
}
|
|
243
371
|
);
|
|
244
372
|
Input.displayName = "Input";
|
|
245
|
-
var Textarea =
|
|
373
|
+
var Textarea = React52.forwardRef(
|
|
246
374
|
({ className, error, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
247
375
|
"textarea",
|
|
248
376
|
{
|
|
@@ -259,14 +387,14 @@ var Textarea = React18.forwardRef(
|
|
|
259
387
|
)
|
|
260
388
|
);
|
|
261
389
|
Textarea.displayName = "Textarea";
|
|
262
|
-
var TextField =
|
|
390
|
+
var TextField = React52.forwardRef(
|
|
263
391
|
({ label, hint, error, required, className, ...props }, ref) => {
|
|
264
392
|
return /* @__PURE__ */ jsx(FormField, { label, hint, error, required, className, children: /* @__PURE__ */ jsx(Input, { ref, error: !!error, ...props }) });
|
|
265
393
|
}
|
|
266
394
|
);
|
|
267
395
|
TextField.displayName = "TextField";
|
|
268
396
|
var chevron = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2371717a' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E";
|
|
269
|
-
var Select =
|
|
397
|
+
var Select = React52.forwardRef(
|
|
270
398
|
({ className, error, style, children, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
271
399
|
"select",
|
|
272
400
|
{
|
|
@@ -294,15 +422,15 @@ var Select = React18.forwardRef(
|
|
|
294
422
|
)
|
|
295
423
|
);
|
|
296
424
|
Select.displayName = "Select";
|
|
297
|
-
var SelectField =
|
|
425
|
+
var SelectField = React52.forwardRef(
|
|
298
426
|
({ label, hint, error, required, className, children, ...props }, ref) => {
|
|
299
427
|
return /* @__PURE__ */ jsx(FormField, { label, hint, error, required, className, children: /* @__PURE__ */ jsx(Select, { ref, error: !!error, ...props, children }) });
|
|
300
428
|
}
|
|
301
429
|
);
|
|
302
430
|
SelectField.displayName = "SelectField";
|
|
303
|
-
var Checkbox =
|
|
431
|
+
var Checkbox = React52.forwardRef(
|
|
304
432
|
({ className, label, id, ...props }, ref) => {
|
|
305
|
-
const autoId =
|
|
433
|
+
const autoId = React52.useId();
|
|
306
434
|
const inputId = id ?? autoId;
|
|
307
435
|
return /* @__PURE__ */ jsxs("label", { htmlFor: inputId, className: cn("inline-flex items-center gap-2.5 cursor-pointer text-sm select-none", className), children: [
|
|
308
436
|
/* @__PURE__ */ jsx("input", { ref, id: inputId, type: "checkbox", className: "peer sr-only", ...props }),
|
|
@@ -312,9 +440,9 @@ var Checkbox = React18.forwardRef(
|
|
|
312
440
|
}
|
|
313
441
|
);
|
|
314
442
|
Checkbox.displayName = "Checkbox";
|
|
315
|
-
var Radio =
|
|
443
|
+
var Radio = React52.forwardRef(
|
|
316
444
|
({ className, label, id, ...props }, ref) => {
|
|
317
|
-
const autoId =
|
|
445
|
+
const autoId = React52.useId();
|
|
318
446
|
const inputId = id ?? autoId;
|
|
319
447
|
return /* @__PURE__ */ jsxs("label", { htmlFor: inputId, className: cn("inline-flex items-center gap-2.5 cursor-pointer text-sm select-none", className), children: [
|
|
320
448
|
/* @__PURE__ */ jsx("input", { ref, id: inputId, type: "radio", className: "peer sr-only", ...props }),
|
|
@@ -324,9 +452,9 @@ var Radio = React18.forwardRef(
|
|
|
324
452
|
}
|
|
325
453
|
);
|
|
326
454
|
Radio.displayName = "Radio";
|
|
327
|
-
var CheckboxField =
|
|
455
|
+
var CheckboxField = React52.forwardRef(
|
|
328
456
|
({ label, hint, error, className, ...props }, ref) => {
|
|
329
|
-
const id =
|
|
457
|
+
const id = React52.useId();
|
|
330
458
|
const hintId = `${id}-hint`;
|
|
331
459
|
const errorId = `${id}-error`;
|
|
332
460
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1 w-full", className), children: [
|
|
@@ -347,9 +475,9 @@ var CheckboxField = React18.forwardRef(
|
|
|
347
475
|
}
|
|
348
476
|
);
|
|
349
477
|
CheckboxField.displayName = "CheckboxField";
|
|
350
|
-
var Switch =
|
|
478
|
+
var Switch = React52.forwardRef(
|
|
351
479
|
({ className, label, id, ...props }, ref) => {
|
|
352
|
-
const autoId =
|
|
480
|
+
const autoId = React52.useId();
|
|
353
481
|
const inputId = id ?? autoId;
|
|
354
482
|
return /* @__PURE__ */ jsxs("label", { htmlFor: inputId, className: cn("inline-flex items-center gap-2.5 cursor-pointer text-sm select-none", className), children: [
|
|
355
483
|
/* @__PURE__ */ jsx("input", { ref, id: inputId, type: "checkbox", role: "switch", className: "peer sr-only", ...props }),
|
|
@@ -359,19 +487,47 @@ var Switch = React18.forwardRef(
|
|
|
359
487
|
}
|
|
360
488
|
);
|
|
361
489
|
Switch.displayName = "Switch";
|
|
362
|
-
var Slider =
|
|
363
|
-
({ className, ...props }, ref) =>
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
490
|
+
var Slider = React52.forwardRef(
|
|
491
|
+
({ className, style, value, defaultValue, min = 0, max = 100, onChange, ...props }, ref) => {
|
|
492
|
+
const [localValue, setLocalValue] = React52.useState(
|
|
493
|
+
Number(defaultValue ?? value ?? 50)
|
|
494
|
+
);
|
|
495
|
+
const isControlled = value !== void 0;
|
|
496
|
+
const currentVal = isControlled ? Number(value) : localValue;
|
|
497
|
+
const numMin = Number(min);
|
|
498
|
+
const numMax = Number(max);
|
|
499
|
+
const percentage = Math.min(
|
|
500
|
+
100,
|
|
501
|
+
Math.max(0, (currentVal - numMin) / (numMax - numMin) * 100)
|
|
502
|
+
);
|
|
503
|
+
const handleChange = (e) => {
|
|
504
|
+
if (!isControlled) {
|
|
505
|
+
setLocalValue(Number(e.target.value));
|
|
506
|
+
}
|
|
507
|
+
onChange?.(e);
|
|
508
|
+
};
|
|
509
|
+
return /* @__PURE__ */ jsx(
|
|
510
|
+
"input",
|
|
511
|
+
{
|
|
512
|
+
ref,
|
|
513
|
+
type: "range",
|
|
514
|
+
value,
|
|
515
|
+
defaultValue,
|
|
516
|
+
min,
|
|
517
|
+
max,
|
|
518
|
+
onChange: handleChange,
|
|
519
|
+
style: {
|
|
520
|
+
background: `linear-gradient(to right, var(--kj-primary) ${percentage}%, var(--kj-border, rgba(120, 120, 128, 0.2)) ${percentage}%)`,
|
|
521
|
+
...style
|
|
522
|
+
},
|
|
523
|
+
className: cn(
|
|
524
|
+
"kj-slider w-full h-1.5 rounded-full appearance-none cursor-pointer accent-primary",
|
|
525
|
+
className
|
|
526
|
+
),
|
|
527
|
+
...props
|
|
528
|
+
}
|
|
529
|
+
);
|
|
530
|
+
}
|
|
375
531
|
);
|
|
376
532
|
Slider.displayName = "Slider";
|
|
377
533
|
var sliderThumbCSS = `
|
|
@@ -412,7 +568,47 @@ function Segmented({
|
|
|
412
568
|
}
|
|
413
569
|
);
|
|
414
570
|
}
|
|
415
|
-
|
|
571
|
+
function ToggleGroup({
|
|
572
|
+
options,
|
|
573
|
+
value,
|
|
574
|
+
onChange,
|
|
575
|
+
disabled,
|
|
576
|
+
className,
|
|
577
|
+
...props
|
|
578
|
+
}) {
|
|
579
|
+
const toggle = (optionValue) => {
|
|
580
|
+
if (disabled) return;
|
|
581
|
+
const next = value.includes(optionValue) ? value.filter((v) => v !== optionValue) : [...value, optionValue];
|
|
582
|
+
onChange(next);
|
|
583
|
+
};
|
|
584
|
+
return /* @__PURE__ */ jsx(
|
|
585
|
+
"div",
|
|
586
|
+
{
|
|
587
|
+
role: "group",
|
|
588
|
+
className: cn("inline-flex gap-0.5 p-[3px] rounded-kj-md bg-muted", className),
|
|
589
|
+
...props,
|
|
590
|
+
children: options.map((opt) => {
|
|
591
|
+
const active = value.includes(opt.value);
|
|
592
|
+
return /* @__PURE__ */ jsx(
|
|
593
|
+
"button",
|
|
594
|
+
{
|
|
595
|
+
type: "button",
|
|
596
|
+
"aria-pressed": active,
|
|
597
|
+
disabled,
|
|
598
|
+
onClick: () => toggle(opt.value),
|
|
599
|
+
className: cn(
|
|
600
|
+
"px-3.5 py-1.5 text-[0.82rem] font-semibold rounded-[calc(var(--kj-radius-md)-3px)] cursor-pointer transition-all duration-150 disabled:cursor-not-allowed disabled:opacity-50",
|
|
601
|
+
active ? "bg-surface text-foreground shadow-kj-xs" : "bg-transparent text-muted-foreground hover:text-foreground"
|
|
602
|
+
),
|
|
603
|
+
children: opt.label
|
|
604
|
+
},
|
|
605
|
+
opt.value
|
|
606
|
+
);
|
|
607
|
+
})
|
|
608
|
+
}
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
var Card = React52.forwardRef(
|
|
416
612
|
({ as: Tag = "div", className, elevated, interactive, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
417
613
|
Tag,
|
|
418
614
|
{
|
|
@@ -428,23 +624,23 @@ var Card = React18.forwardRef(
|
|
|
428
624
|
)
|
|
429
625
|
);
|
|
430
626
|
Card.displayName = "Card";
|
|
431
|
-
var CardHeader =
|
|
627
|
+
var CardHeader = React52.forwardRef(
|
|
432
628
|
({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-[1.35rem] flex flex-col gap-1", className), ...props })
|
|
433
629
|
);
|
|
434
630
|
CardHeader.displayName = "CardHeader";
|
|
435
|
-
var CardTitle =
|
|
436
|
-
({ className, ...props }, ref) => /* @__PURE__ */ jsx("h3", { ref, className: cn("m-0 text-base font-bold tracking-[-0.01em]", className), ...props })
|
|
631
|
+
var CardTitle = React52.forwardRef(
|
|
632
|
+
({ className, children, ...props }, ref) => /* @__PURE__ */ jsx("h3", { ref, className: cn("m-0 text-base font-bold tracking-[-0.01em]", className), ...props, children })
|
|
437
633
|
);
|
|
438
634
|
CardTitle.displayName = "CardTitle";
|
|
439
|
-
var CardDescription =
|
|
635
|
+
var CardDescription = React52.forwardRef(
|
|
440
636
|
({ className, ...props }, ref) => /* @__PURE__ */ jsx("p", { ref, className: cn("m-0 text-[0.85rem] text-muted-foreground", className), ...props })
|
|
441
637
|
);
|
|
442
638
|
CardDescription.displayName = "CardDescription";
|
|
443
|
-
var CardContent =
|
|
639
|
+
var CardContent = React52.forwardRef(
|
|
444
640
|
({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("px-[1.35rem] pb-[1.35rem]", className), ...props })
|
|
445
641
|
);
|
|
446
642
|
CardContent.displayName = "CardContent";
|
|
447
|
-
var CardFooter =
|
|
643
|
+
var CardFooter = React52.forwardRef(
|
|
448
644
|
({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
449
645
|
"div",
|
|
450
646
|
{
|
|
@@ -455,7 +651,7 @@ var CardFooter = React18.forwardRef(
|
|
|
455
651
|
)
|
|
456
652
|
);
|
|
457
653
|
CardFooter.displayName = "CardFooter";
|
|
458
|
-
var Stat =
|
|
654
|
+
var Stat = React52.forwardRef(
|
|
459
655
|
({ className, label, value, delta, trend, ...props }, ref) => /* @__PURE__ */ jsxs(Card, { ref, className: cn("p-[1.35rem] flex flex-col gap-2", className), ...props, children: [
|
|
460
656
|
/* @__PURE__ */ jsx("span", { className: "text-[0.78rem] font-semibold uppercase tracking-[0.05em] text-muted-foreground", children: label }),
|
|
461
657
|
/* @__PURE__ */ jsx("span", { className: "text-[2rem] font-bold tracking-[-0.03em] tabular-nums leading-none", children: value }),
|
|
@@ -495,7 +691,7 @@ var avatarVariants = cva(
|
|
|
495
691
|
defaultVariants: { size: "md", tone: "secondary" }
|
|
496
692
|
}
|
|
497
693
|
);
|
|
498
|
-
var Avatar =
|
|
694
|
+
var Avatar = React52.forwardRef(
|
|
499
695
|
({ className, size, tone, src, alt, status, children, ...props }, ref) => {
|
|
500
696
|
const node = /* @__PURE__ */ jsx("span", { ref, className: cn(avatarVariants({ size, tone }), "overflow-hidden", className), ...props, children: src ? /* @__PURE__ */ jsx("img", { src, alt, className: "h-full w-full object-cover" }) : children });
|
|
501
697
|
if (!status) return node;
|
|
@@ -506,21 +702,21 @@ var Avatar = React18.forwardRef(
|
|
|
506
702
|
}
|
|
507
703
|
);
|
|
508
704
|
Avatar.displayName = "Avatar";
|
|
509
|
-
var AvatarGroup =
|
|
705
|
+
var AvatarGroup = React52.forwardRef(
|
|
510
706
|
({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("flex [&>*:not(:first-child)]:-ml-2.5", className), ...props })
|
|
511
707
|
);
|
|
512
708
|
AvatarGroup.displayName = "AvatarGroup";
|
|
513
|
-
var TabsContext =
|
|
709
|
+
var TabsContext = React52.createContext(null);
|
|
514
710
|
function useTabs() {
|
|
515
|
-
const ctx =
|
|
711
|
+
const ctx = React52.useContext(TabsContext);
|
|
516
712
|
if (!ctx) throw new Error("Tabs.* must be used within <Tabs>");
|
|
517
713
|
return ctx;
|
|
518
714
|
}
|
|
519
715
|
function Tabs({ value, defaultValue, onValueChange, className, children, ...props }) {
|
|
520
|
-
const [internal, setInternal] =
|
|
521
|
-
const uid =
|
|
716
|
+
const [internal, setInternal] = React52.useState(defaultValue ?? "");
|
|
717
|
+
const uid = React52.useId();
|
|
522
718
|
const current = value ?? internal;
|
|
523
|
-
const setValue =
|
|
719
|
+
const setValue = React52.useCallback(
|
|
524
720
|
(v) => {
|
|
525
721
|
if (value === void 0) setInternal(v);
|
|
526
722
|
onValueChange?.(v);
|
|
@@ -529,7 +725,7 @@ function Tabs({ value, defaultValue, onValueChange, className, children, ...prop
|
|
|
529
725
|
);
|
|
530
726
|
return /* @__PURE__ */ jsx(TabsContext.Provider, { value: { value: current, setValue, uid }, children: /* @__PURE__ */ jsx("div", { className, ...props, children }) });
|
|
531
727
|
}
|
|
532
|
-
var TabsList =
|
|
728
|
+
var TabsList = React52.forwardRef(
|
|
533
729
|
({ className, onKeyDown, ...props }, ref) => {
|
|
534
730
|
const handleKeyDown = (e) => {
|
|
535
731
|
const container = e.currentTarget;
|
|
@@ -576,7 +772,7 @@ var TabsList = React18.forwardRef(
|
|
|
576
772
|
}
|
|
577
773
|
);
|
|
578
774
|
TabsList.displayName = "TabsList";
|
|
579
|
-
var TabsTrigger =
|
|
775
|
+
var TabsTrigger = React52.forwardRef(
|
|
580
776
|
({ className, value, children, ...props }, ref) => {
|
|
581
777
|
const { value: current, setValue, uid } = useTabs();
|
|
582
778
|
const active = current === value;
|
|
@@ -605,7 +801,7 @@ var TabsTrigger = React18.forwardRef(
|
|
|
605
801
|
}
|
|
606
802
|
);
|
|
607
803
|
TabsTrigger.displayName = "TabsTrigger";
|
|
608
|
-
var TabsContent =
|
|
804
|
+
var TabsContent = React52.forwardRef(
|
|
609
805
|
({ className, value, ...props }, ref) => {
|
|
610
806
|
const { value: current, uid } = useTabs();
|
|
611
807
|
const active = current === value;
|
|
@@ -625,10 +821,10 @@ var TabsContent = React18.forwardRef(
|
|
|
625
821
|
}
|
|
626
822
|
);
|
|
627
823
|
TabsContent.displayName = "TabsContent";
|
|
628
|
-
var AccordionContext =
|
|
824
|
+
var AccordionContext = React52.createContext(null);
|
|
629
825
|
function Accordion({ type = "single", defaultValue = [], className, children, ...props }) {
|
|
630
|
-
const [open, setOpen] =
|
|
631
|
-
const toggle =
|
|
826
|
+
const [open, setOpen] = React52.useState(defaultValue);
|
|
827
|
+
const toggle = React52.useCallback(
|
|
632
828
|
(v) => {
|
|
633
829
|
setOpen((prev) => {
|
|
634
830
|
const isOpen = prev.includes(v);
|
|
@@ -640,15 +836,15 @@ function Accordion({ type = "single", defaultValue = [], className, children, ..
|
|
|
640
836
|
);
|
|
641
837
|
return /* @__PURE__ */ jsx(AccordionContext.Provider, { value: { open, toggle }, children: /* @__PURE__ */ jsx("div", { className: cn("border border-border rounded-kj-lg overflow-hidden", className), ...props, children }) });
|
|
642
838
|
}
|
|
643
|
-
var ItemContext =
|
|
644
|
-
var AccordionItem =
|
|
839
|
+
var ItemContext = React52.createContext("");
|
|
840
|
+
var AccordionItem = React52.forwardRef(
|
|
645
841
|
({ className, value, ...props }, ref) => /* @__PURE__ */ jsx(ItemContext.Provider, { value, children: /* @__PURE__ */ jsx("div", { ref, className: cn("border-t border-border first:border-t-0", className), ...props }) })
|
|
646
842
|
);
|
|
647
843
|
AccordionItem.displayName = "AccordionItem";
|
|
648
|
-
var AccordionTrigger =
|
|
844
|
+
var AccordionTrigger = React52.forwardRef(
|
|
649
845
|
({ className, children, ...props }, ref) => {
|
|
650
|
-
const ctx =
|
|
651
|
-
const value =
|
|
846
|
+
const ctx = React52.useContext(AccordionContext);
|
|
847
|
+
const value = React52.useContext(ItemContext);
|
|
652
848
|
const isOpen = ctx.open.includes(value);
|
|
653
849
|
return /* @__PURE__ */ jsxs(
|
|
654
850
|
"button",
|
|
@@ -685,10 +881,10 @@ var AccordionTrigger = React18.forwardRef(
|
|
|
685
881
|
}
|
|
686
882
|
);
|
|
687
883
|
AccordionTrigger.displayName = "AccordionTrigger";
|
|
688
|
-
var AccordionContent =
|
|
884
|
+
var AccordionContent = React52.forwardRef(
|
|
689
885
|
({ className, children, ...props }, ref) => {
|
|
690
|
-
const ctx =
|
|
691
|
-
const value =
|
|
886
|
+
const ctx = React52.useContext(AccordionContext);
|
|
887
|
+
const value = React52.useContext(ItemContext);
|
|
692
888
|
const isOpen = ctx.open.includes(value);
|
|
693
889
|
return /* @__PURE__ */ jsx(
|
|
694
890
|
"div",
|
|
@@ -702,11 +898,11 @@ var AccordionContent = React18.forwardRef(
|
|
|
702
898
|
}
|
|
703
899
|
);
|
|
704
900
|
AccordionContent.displayName = "AccordionContent";
|
|
705
|
-
var MenuContext =
|
|
901
|
+
var MenuContext = React52.createContext(null);
|
|
706
902
|
function DropdownMenu({ children, className }) {
|
|
707
|
-
const [open, setOpen] =
|
|
708
|
-
const ref =
|
|
709
|
-
|
|
903
|
+
const [open, setOpen] = React52.useState(false);
|
|
904
|
+
const ref = React52.useRef(null);
|
|
905
|
+
React52.useEffect(() => {
|
|
710
906
|
if (!open) return;
|
|
711
907
|
const onDoc = (e) => {
|
|
712
908
|
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
@@ -721,11 +917,11 @@ function DropdownMenu({ children, className }) {
|
|
|
721
917
|
}, [open]);
|
|
722
918
|
return /* @__PURE__ */ jsx(MenuContext.Provider, { value: { open, setOpen }, children: /* @__PURE__ */ jsx("div", { ref, className: cn("relative inline-block", className), children }) });
|
|
723
919
|
}
|
|
724
|
-
var DropdownMenuTrigger =
|
|
725
|
-
const ctx =
|
|
920
|
+
var DropdownMenuTrigger = React52.forwardRef(({ onClick, asChild, ...props }, ref) => {
|
|
921
|
+
const ctx = React52.useContext(MenuContext);
|
|
726
922
|
if (asChild) {
|
|
727
|
-
const child =
|
|
728
|
-
return
|
|
923
|
+
const child = React52.Children.only(props.children);
|
|
924
|
+
return React52.cloneElement(child, {
|
|
729
925
|
ref,
|
|
730
926
|
...props,
|
|
731
927
|
...child.props,
|
|
@@ -755,9 +951,9 @@ var DropdownMenuTrigger = React18.forwardRef(({ onClick, asChild, ...props }, re
|
|
|
755
951
|
});
|
|
756
952
|
DropdownMenuTrigger.displayName = "DropdownMenuTrigger";
|
|
757
953
|
function DropdownMenuContent({ className, align = "start", children, ...props }) {
|
|
758
|
-
const ctx =
|
|
759
|
-
const ref =
|
|
760
|
-
|
|
954
|
+
const ctx = React52.useContext(MenuContext);
|
|
955
|
+
const ref = React52.useRef(null);
|
|
956
|
+
React52.useEffect(() => {
|
|
761
957
|
if (!ctx.open) return;
|
|
762
958
|
const container = ref.current;
|
|
763
959
|
if (container) {
|
|
@@ -804,9 +1000,9 @@ function DropdownMenuContent({ className, align = "start", children, ...props })
|
|
|
804
1000
|
}
|
|
805
1001
|
);
|
|
806
1002
|
}
|
|
807
|
-
var DropdownMenuItem =
|
|
1003
|
+
var DropdownMenuItem = React52.forwardRef(
|
|
808
1004
|
({ className, danger, icon, children, onClick, ...props }, ref) => {
|
|
809
|
-
const ctx =
|
|
1005
|
+
const ctx = React52.useContext(MenuContext);
|
|
810
1006
|
return /* @__PURE__ */ jsxs(
|
|
811
1007
|
"button",
|
|
812
1008
|
{
|
|
@@ -836,11 +1032,11 @@ DropdownMenuItem.displayName = "DropdownMenuItem";
|
|
|
836
1032
|
function DropdownMenuSeparator({ className }) {
|
|
837
1033
|
return /* @__PURE__ */ jsx("div", { className: cn("h-px bg-border my-1.5", className), role: "separator" });
|
|
838
1034
|
}
|
|
839
|
-
var Breadcrumb =
|
|
1035
|
+
var Breadcrumb = React52.forwardRef(
|
|
840
1036
|
({ className, children, ...props }, ref) => /* @__PURE__ */ jsx("nav", { ref, "aria-label": "Breadcrumb", className: cn("flex items-center gap-1.5 text-[0.85rem] text-muted-foreground", className), ...props, children })
|
|
841
1037
|
);
|
|
842
1038
|
Breadcrumb.displayName = "Breadcrumb";
|
|
843
|
-
var BreadcrumbItem =
|
|
1039
|
+
var BreadcrumbItem = React52.forwardRef(
|
|
844
1040
|
({ className, current, children, ...props }, ref) => {
|
|
845
1041
|
if (current) {
|
|
846
1042
|
return /* @__PURE__ */ jsx("span", { "aria-current": "page", className: cn("text-foreground font-semibold", className), children });
|
|
@@ -887,7 +1083,7 @@ function Pagination({ page, pageCount, onPageChange, className }) {
|
|
|
887
1083
|
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Next", disabled: page >= pageCount, onClick: () => onPageChange(page + 1), children: /* @__PURE__ */ jsx(Chevron, { dir: "right" }) })
|
|
888
1084
|
] });
|
|
889
1085
|
}
|
|
890
|
-
var BottomNavigation =
|
|
1086
|
+
var BottomNavigation = React52.forwardRef(
|
|
891
1087
|
({ className, items, showLabels = "always", fixed = true, ...props }, ref) => {
|
|
892
1088
|
return /* @__PURE__ */ jsx(
|
|
893
1089
|
"nav",
|
|
@@ -959,27 +1155,27 @@ var BottomNavigation = React18.forwardRef(
|
|
|
959
1155
|
}
|
|
960
1156
|
);
|
|
961
1157
|
BottomNavigation.displayName = "BottomNavigation";
|
|
962
|
-
var TableWrap =
|
|
1158
|
+
var TableWrap = React52.forwardRef(
|
|
963
1159
|
({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("border border-border rounded-kj-lg overflow-x-auto w-full", className), ...props })
|
|
964
1160
|
);
|
|
965
1161
|
TableWrap.displayName = "TableWrap";
|
|
966
|
-
var Table =
|
|
1162
|
+
var Table = React52.forwardRef(
|
|
967
1163
|
({ className, ...props }, ref) => /* @__PURE__ */ jsx("table", { ref, className: cn("w-full border-collapse text-[0.85rem]", className), ...props })
|
|
968
1164
|
);
|
|
969
1165
|
Table.displayName = "Table";
|
|
970
|
-
var TableHeader =
|
|
1166
|
+
var TableHeader = React52.forwardRef(
|
|
971
1167
|
({ className, ...props }, ref) => /* @__PURE__ */ jsx("thead", { ref, className, ...props })
|
|
972
1168
|
);
|
|
973
1169
|
TableHeader.displayName = "TableHeader";
|
|
974
|
-
var TableBody =
|
|
1170
|
+
var TableBody = React52.forwardRef(
|
|
975
1171
|
({ className, ...props }, ref) => /* @__PURE__ */ jsx("tbody", { ref, className, ...props })
|
|
976
1172
|
);
|
|
977
1173
|
TableBody.displayName = "TableBody";
|
|
978
|
-
var TableRow =
|
|
1174
|
+
var TableRow = React52.forwardRef(
|
|
979
1175
|
({ className, ...props }, ref) => /* @__PURE__ */ jsx("tr", { ref, className: cn("transition-colors hover:bg-muted/50", className), ...props })
|
|
980
1176
|
);
|
|
981
1177
|
TableRow.displayName = "TableRow";
|
|
982
|
-
var TableHead =
|
|
1178
|
+
var TableHead = React52.forwardRef(
|
|
983
1179
|
({ className, numeric, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
984
1180
|
"th",
|
|
985
1181
|
{
|
|
@@ -994,7 +1190,7 @@ var TableHead = React18.forwardRef(
|
|
|
994
1190
|
)
|
|
995
1191
|
);
|
|
996
1192
|
TableHead.displayName = "TableHead";
|
|
997
|
-
var TableCell =
|
|
1193
|
+
var TableCell = React52.forwardRef(
|
|
998
1194
|
({ className, numeric, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
999
1195
|
"td",
|
|
1000
1196
|
{
|
|
@@ -1010,30 +1206,33 @@ var TableCell = React18.forwardRef(
|
|
|
1010
1206
|
);
|
|
1011
1207
|
TableCell.displayName = "TableCell";
|
|
1012
1208
|
function Tooltip({ content, children, className }) {
|
|
1013
|
-
return
|
|
1014
|
-
|
|
1015
|
-
/* @__PURE__ */
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1209
|
+
return (
|
|
1210
|
+
/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */
|
|
1211
|
+
/* @__PURE__ */ jsxs("span", { className: cn("relative inline-flex group", className), tabIndex: 0, children: [
|
|
1212
|
+
children,
|
|
1213
|
+
/* @__PURE__ */ jsx(
|
|
1214
|
+
"span",
|
|
1215
|
+
{
|
|
1216
|
+
role: "tooltip",
|
|
1217
|
+
className: cn(
|
|
1218
|
+
"pointer-events-none absolute bottom-[calc(100%+8px)] left-1/2 -translate-x-1/2 translate-y-1 whitespace-nowrap",
|
|
1219
|
+
"rounded-kj-sm bg-foreground text-background text-xs font-medium px-2.5 py-1.5 shadow-kj-md",
|
|
1220
|
+
"opacity-0 transition-all duration-150 group-hover:opacity-100 group-hover:translate-y-0 group-focus-visible:opacity-100 group-focus-visible:translate-y-0",
|
|
1221
|
+
"after:content-[''] after:absolute after:top-full after:left-1/2 after:-translate-x-1/2 after:border-[5px] after:border-transparent after:border-t-foreground"
|
|
1222
|
+
),
|
|
1223
|
+
children: content
|
|
1224
|
+
}
|
|
1225
|
+
)
|
|
1226
|
+
] })
|
|
1227
|
+
);
|
|
1029
1228
|
}
|
|
1030
|
-
var ModalContext =
|
|
1229
|
+
var ModalContext = React52.createContext(null);
|
|
1031
1230
|
function Modal({ open, onClose, children, width = 440, className, showClose = true }) {
|
|
1032
|
-
const titleId =
|
|
1033
|
-
const descId =
|
|
1034
|
-
const containerRef =
|
|
1035
|
-
const lastActiveElement =
|
|
1036
|
-
|
|
1231
|
+
const titleId = React52.useId();
|
|
1232
|
+
const descId = React52.useId();
|
|
1233
|
+
const containerRef = React52.useRef(null);
|
|
1234
|
+
const lastActiveElement = React52.useRef(null);
|
|
1235
|
+
React52.useEffect(() => {
|
|
1037
1236
|
if (!open) {
|
|
1038
1237
|
if (lastActiveElement.current) {
|
|
1039
1238
|
lastActiveElement.current.focus();
|
|
@@ -1134,20 +1333,20 @@ function Modal({ open, onClose, children, width = 440, className, showClose = tr
|
|
|
1134
1333
|
}
|
|
1135
1334
|
);
|
|
1136
1335
|
}
|
|
1137
|
-
function ModalTitle({ className, id, ...props }) {
|
|
1138
|
-
const ctx =
|
|
1139
|
-
return /* @__PURE__ */ jsx("h3", { id: id ?? ctx?.titleId, className: cn("m-0 mb-2 text-[1.15rem] font-bold tracking-[-0.01em]", className), ...props });
|
|
1336
|
+
function ModalTitle({ className, id, children, ...props }) {
|
|
1337
|
+
const ctx = React52.useContext(ModalContext);
|
|
1338
|
+
return /* @__PURE__ */ jsx("h3", { id: id ?? ctx?.titleId, className: cn("m-0 mb-2 text-[1.15rem] font-bold tracking-[-0.01em]", className), ...props, children });
|
|
1140
1339
|
}
|
|
1141
1340
|
function ModalDescription({ className, id, ...props }) {
|
|
1142
|
-
const ctx =
|
|
1341
|
+
const ctx = React52.useContext(ModalContext);
|
|
1143
1342
|
return /* @__PURE__ */ jsx("p", { id: id ?? ctx?.descId, className: cn("m-0 text-[0.9rem] text-muted-foreground", className), ...props });
|
|
1144
1343
|
}
|
|
1145
1344
|
function ModalActions({ className, ...props }) {
|
|
1146
1345
|
return /* @__PURE__ */ jsx("div", { className: cn("flex gap-2.5 justify-end mt-6", className), ...props });
|
|
1147
1346
|
}
|
|
1148
|
-
var ToastContext =
|
|
1347
|
+
var ToastContext = React52.createContext(null);
|
|
1149
1348
|
function useToast() {
|
|
1150
|
-
const ctx =
|
|
1349
|
+
const ctx = React52.useContext(ToastContext);
|
|
1151
1350
|
if (!ctx) throw new Error("useToast must be used within <ToastProvider>");
|
|
1152
1351
|
return ctx;
|
|
1153
1352
|
}
|
|
@@ -1168,13 +1367,13 @@ var toneBorder = {
|
|
|
1168
1367
|
danger: "border-l-danger"
|
|
1169
1368
|
};
|
|
1170
1369
|
function ToastProvider({ children }) {
|
|
1171
|
-
const [items, setItems] =
|
|
1172
|
-
const idRef =
|
|
1173
|
-
const remove =
|
|
1370
|
+
const [items, setItems] = React52.useState([]);
|
|
1371
|
+
const idRef = React52.useRef(0);
|
|
1372
|
+
const remove = React52.useCallback((id) => {
|
|
1174
1373
|
setItems((prev) => prev.map((t) => t.id === id ? { ...t, leaving: true } : t));
|
|
1175
1374
|
setTimeout(() => setItems((prev) => prev.filter((t) => t.id !== id)), 250);
|
|
1176
1375
|
}, []);
|
|
1177
|
-
const toast =
|
|
1376
|
+
const toast = React52.useCallback(
|
|
1178
1377
|
({ message, tone = "default", duration = 3200 }) => {
|
|
1179
1378
|
const id = ++idRef.current;
|
|
1180
1379
|
setItems((prev) => [...prev, { id, message, tone, duration }]);
|
|
@@ -1204,7 +1403,7 @@ function ToastProvider({ children }) {
|
|
|
1204
1403
|
)) })
|
|
1205
1404
|
] });
|
|
1206
1405
|
}
|
|
1207
|
-
var PageHeader =
|
|
1406
|
+
var PageHeader = React52.forwardRef(
|
|
1208
1407
|
({ eyebrow, title, description, actions }, ref) => /* @__PURE__ */ jsx("header", { ref, className: "py-12 md:py-16", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6 md:flex-row md:items-end md:justify-between", children: [
|
|
1209
1408
|
/* @__PURE__ */ jsxs("div", { className: "space-y-3 max-w-2xl", children: [
|
|
1210
1409
|
eyebrow && /* @__PURE__ */ jsx("p", { className: "font-mono text-xs font-bold uppercase tracking-[0.2em] text-primary", children: eyebrow }),
|
|
@@ -1215,7 +1414,111 @@ var PageHeader = React18.forwardRef(
|
|
|
1215
1414
|
] }) })
|
|
1216
1415
|
);
|
|
1217
1416
|
PageHeader.displayName = "PageHeader";
|
|
1218
|
-
var
|
|
1417
|
+
var SectionHeaderKicker = React52.forwardRef(
|
|
1418
|
+
({ className, children, ...props }, ref) => {
|
|
1419
|
+
if (!children) return null;
|
|
1420
|
+
return /* @__PURE__ */ jsx(
|
|
1421
|
+
"p",
|
|
1422
|
+
{
|
|
1423
|
+
ref,
|
|
1424
|
+
className: cn(
|
|
1425
|
+
"font-mono text-xs font-bold uppercase tracking-[0.2em] text-primary",
|
|
1426
|
+
className
|
|
1427
|
+
),
|
|
1428
|
+
...props,
|
|
1429
|
+
children
|
|
1430
|
+
}
|
|
1431
|
+
);
|
|
1432
|
+
}
|
|
1433
|
+
);
|
|
1434
|
+
SectionHeaderKicker.displayName = "SectionHeaderKicker";
|
|
1435
|
+
var SectionHeaderTitle = React52.forwardRef(
|
|
1436
|
+
({ className, as: Component = "h2", children, ...props }, ref) => {
|
|
1437
|
+
if (!children) return null;
|
|
1438
|
+
return /* @__PURE__ */ jsx(
|
|
1439
|
+
Component,
|
|
1440
|
+
{
|
|
1441
|
+
ref,
|
|
1442
|
+
className: cn("text-2xl font-bold tracking-tight text-foreground sm:text-3xl", className),
|
|
1443
|
+
...props,
|
|
1444
|
+
children
|
|
1445
|
+
}
|
|
1446
|
+
);
|
|
1447
|
+
}
|
|
1448
|
+
);
|
|
1449
|
+
SectionHeaderTitle.displayName = "SectionHeaderTitle";
|
|
1450
|
+
var SectionHeaderDescription = React52.forwardRef(({ className, children, ...props }, ref) => {
|
|
1451
|
+
if (!children) return null;
|
|
1452
|
+
return /* @__PURE__ */ jsx(
|
|
1453
|
+
"p",
|
|
1454
|
+
{
|
|
1455
|
+
ref,
|
|
1456
|
+
className: cn("text-base leading-relaxed text-muted-foreground max-w-2xl", className),
|
|
1457
|
+
...props,
|
|
1458
|
+
children
|
|
1459
|
+
}
|
|
1460
|
+
);
|
|
1461
|
+
});
|
|
1462
|
+
SectionHeaderDescription.displayName = "SectionHeaderDescription";
|
|
1463
|
+
var SectionHeaderActions = React52.forwardRef(
|
|
1464
|
+
({ className, children, ...props }, ref) => {
|
|
1465
|
+
if (!children) return null;
|
|
1466
|
+
return /* @__PURE__ */ jsx("div", { ref, className: cn("flex flex-wrap items-center gap-3", className), ...props, children });
|
|
1467
|
+
}
|
|
1468
|
+
);
|
|
1469
|
+
SectionHeaderActions.displayName = "SectionHeaderActions";
|
|
1470
|
+
var SectionHeader = React52.forwardRef(
|
|
1471
|
+
({
|
|
1472
|
+
kicker,
|
|
1473
|
+
title,
|
|
1474
|
+
description,
|
|
1475
|
+
actions,
|
|
1476
|
+
align = "left",
|
|
1477
|
+
headingLevel = "h2",
|
|
1478
|
+
divider = false,
|
|
1479
|
+
className,
|
|
1480
|
+
children,
|
|
1481
|
+
...props
|
|
1482
|
+
}, ref) => {
|
|
1483
|
+
const isCentered = align === "center";
|
|
1484
|
+
return /* @__PURE__ */ jsx(
|
|
1485
|
+
"div",
|
|
1486
|
+
{
|
|
1487
|
+
ref,
|
|
1488
|
+
className: cn(
|
|
1489
|
+
"space-y-4",
|
|
1490
|
+
isCentered ? "text-center items-center mx-auto max-w-3xl" : "md:flex md:items-end md:justify-between md:space-y-0",
|
|
1491
|
+
divider && "border-b border-border pb-6",
|
|
1492
|
+
className
|
|
1493
|
+
),
|
|
1494
|
+
...props,
|
|
1495
|
+
children: children ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1496
|
+
/* @__PURE__ */ jsxs(
|
|
1497
|
+
"div",
|
|
1498
|
+
{
|
|
1499
|
+
className: cn(
|
|
1500
|
+
"space-y-2",
|
|
1501
|
+
isCentered && "flex flex-col items-center"
|
|
1502
|
+
),
|
|
1503
|
+
children: [
|
|
1504
|
+
kicker && /* @__PURE__ */ jsx(SectionHeaderKicker, { children: kicker }),
|
|
1505
|
+
title && /* @__PURE__ */ jsx(SectionHeaderTitle, { as: headingLevel, children: title }),
|
|
1506
|
+
description && /* @__PURE__ */ jsx(SectionHeaderDescription, { children: description })
|
|
1507
|
+
]
|
|
1508
|
+
}
|
|
1509
|
+
),
|
|
1510
|
+
actions && /* @__PURE__ */ jsx(SectionHeaderActions, { className: cn(isCentered && "justify-center mt-4"), children: actions })
|
|
1511
|
+
] })
|
|
1512
|
+
}
|
|
1513
|
+
);
|
|
1514
|
+
}
|
|
1515
|
+
);
|
|
1516
|
+
SectionHeader.displayName = "SectionHeader";
|
|
1517
|
+
SectionHeader.Kicker = SectionHeaderKicker;
|
|
1518
|
+
SectionHeader.Title = SectionHeaderTitle;
|
|
1519
|
+
SectionHeader.Description = SectionHeaderDescription;
|
|
1520
|
+
SectionHeader.Actions = SectionHeaderActions;
|
|
1521
|
+
var EmptyState = React52.forwardRef(
|
|
1219
1522
|
({ className, icon, title, description, action, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
1220
1523
|
"div",
|
|
1221
1524
|
{
|
|
@@ -1235,7 +1538,7 @@ var EmptyState = React18.forwardRef(
|
|
|
1235
1538
|
)
|
|
1236
1539
|
);
|
|
1237
1540
|
EmptyState.displayName = "EmptyState";
|
|
1238
|
-
var MetricCard =
|
|
1541
|
+
var MetricCard = React52.forwardRef(
|
|
1239
1542
|
({ className, title, value, trend, trendDirection = "neutral", description, icon, ...props }, ref) => {
|
|
1240
1543
|
const trendColor = {
|
|
1241
1544
|
up: "text-success bg-success-surface border-success/20",
|
|
@@ -1276,22 +1579,22 @@ function DataTable({
|
|
|
1276
1579
|
onSort,
|
|
1277
1580
|
...props
|
|
1278
1581
|
}) {
|
|
1279
|
-
const getRowId =
|
|
1582
|
+
const getRowId = React52.useCallback(
|
|
1280
1583
|
(row, index) => getRowKey ? getRowKey(row, index) : index,
|
|
1281
1584
|
[getRowKey]
|
|
1282
1585
|
);
|
|
1283
1586
|
const isSelectionActive = selectedRows !== void 0 && onSelectionChange !== void 0;
|
|
1284
|
-
const allSelected =
|
|
1587
|
+
const allSelected = React52.useMemo(() => {
|
|
1285
1588
|
if (!isSelectionActive || data.length === 0) return false;
|
|
1286
1589
|
return data.every((row, idx) => selectedRows.has(getRowId(row, idx)));
|
|
1287
1590
|
}, [isSelectionActive, data, selectedRows, getRowId]);
|
|
1288
|
-
const someSelected =
|
|
1591
|
+
const someSelected = React52.useMemo(() => {
|
|
1289
1592
|
if (!isSelectionActive || data.length === 0) return false;
|
|
1290
1593
|
const selectedCount = data.filter((row, idx) => selectedRows.has(getRowId(row, idx))).length;
|
|
1291
1594
|
return selectedCount > 0 && selectedCount < data.length;
|
|
1292
1595
|
}, [isSelectionActive, data, selectedRows, getRowId]);
|
|
1293
|
-
const headerCheckboxRef =
|
|
1294
|
-
|
|
1596
|
+
const headerCheckboxRef = React52.useRef(null);
|
|
1597
|
+
React52.useEffect(() => {
|
|
1295
1598
|
if (headerCheckboxRef.current) {
|
|
1296
1599
|
headerCheckboxRef.current.indeterminate = someSelected;
|
|
1297
1600
|
}
|
|
@@ -1445,7 +1748,7 @@ function DataTable({
|
|
|
1445
1748
|
] });
|
|
1446
1749
|
}
|
|
1447
1750
|
DataTable.displayName = "DataTable";
|
|
1448
|
-
var ErrorState =
|
|
1751
|
+
var ErrorState = React52.forwardRef(
|
|
1449
1752
|
({ className, title = "Wyst\u0105pi\u0142 b\u0142\u0105d", message, onRetry, retryLabel = "Spr\xF3buj ponownie", ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
1450
1753
|
"div",
|
|
1451
1754
|
{
|
|
@@ -1465,7 +1768,7 @@ var ErrorState = React18.forwardRef(
|
|
|
1465
1768
|
)
|
|
1466
1769
|
);
|
|
1467
1770
|
ErrorState.displayName = "ErrorState";
|
|
1468
|
-
var Skeleton =
|
|
1771
|
+
var Skeleton = React52.forwardRef(
|
|
1469
1772
|
({ className, variant = "rectangular", width, height, style, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1470
1773
|
"div",
|
|
1471
1774
|
{
|
|
@@ -1487,7 +1790,7 @@ var Skeleton = React18.forwardRef(
|
|
|
1487
1790
|
)
|
|
1488
1791
|
);
|
|
1489
1792
|
Skeleton.displayName = "Skeleton";
|
|
1490
|
-
var DashboardShell =
|
|
1793
|
+
var DashboardShell = React52.forwardRef(
|
|
1491
1794
|
({
|
|
1492
1795
|
className,
|
|
1493
1796
|
sidebar,
|
|
@@ -1499,7 +1802,7 @@ var DashboardShell = React18.forwardRef(
|
|
|
1499
1802
|
mobileSidebar,
|
|
1500
1803
|
...props
|
|
1501
1804
|
}, ref) => {
|
|
1502
|
-
const [mobileOpen, setMobileOpen] =
|
|
1805
|
+
const [mobileOpen, setMobileOpen] = React52.useState(false);
|
|
1503
1806
|
const contentWidthClass = {
|
|
1504
1807
|
default: "max-w-7xl w-full mx-auto",
|
|
1505
1808
|
wide: "max-w-screen-2xl w-full mx-auto",
|
|
@@ -1590,7 +1893,7 @@ var DashboardShell = React18.forwardRef(
|
|
|
1590
1893
|
}
|
|
1591
1894
|
);
|
|
1592
1895
|
DashboardShell.displayName = "DashboardShell";
|
|
1593
|
-
var SettingsLayout =
|
|
1896
|
+
var SettingsLayout = React52.forwardRef(
|
|
1594
1897
|
({ className, sidebar, children, title, description, ...props }, ref) => {
|
|
1595
1898
|
return /* @__PURE__ */ jsxs("div", { ref, className: cn("space-y-6 w-full", className), ...props, children: [
|
|
1596
1899
|
(title || description) && /* @__PURE__ */ jsxs("div", { className: "border-b border-border pb-4", children: [
|
|
@@ -1605,7 +1908,7 @@ var SettingsLayout = React18.forwardRef(
|
|
|
1605
1908
|
}
|
|
1606
1909
|
);
|
|
1607
1910
|
SettingsLayout.displayName = "SettingsLayout";
|
|
1608
|
-
var DetailPageLayout =
|
|
1911
|
+
var DetailPageLayout = React52.forwardRef(
|
|
1609
1912
|
({ className, title, description, backHref, backLabel = "Back", onBackClick, actions, aside, children, ...props }, ref) => {
|
|
1610
1913
|
return /* @__PURE__ */ jsxs("div", { ref, className: cn("space-y-6 w-full", className), ...props, children: [
|
|
1611
1914
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
@@ -1651,7 +1954,7 @@ var DetailPageLayout = React18.forwardRef(
|
|
|
1651
1954
|
}
|
|
1652
1955
|
);
|
|
1653
1956
|
DetailPageLayout.displayName = "DetailPageLayout";
|
|
1654
|
-
var TableToolbar =
|
|
1957
|
+
var TableToolbar = React52.forwardRef(
|
|
1655
1958
|
({ className, searchQuery, onSearchChange, searchPlaceholder = "Search...", actions, filters, ...props }, ref) => {
|
|
1656
1959
|
return /* @__PURE__ */ jsxs(
|
|
1657
1960
|
"div",
|
|
@@ -1720,9 +2023,9 @@ function Drawer({
|
|
|
1720
2023
|
width = "max-w-md",
|
|
1721
2024
|
side = "right"
|
|
1722
2025
|
}) {
|
|
1723
|
-
const containerRef =
|
|
1724
|
-
const lastActiveElement =
|
|
1725
|
-
|
|
2026
|
+
const containerRef = React52.useRef(null);
|
|
2027
|
+
const lastActiveElement = React52.useRef(null);
|
|
2028
|
+
React52.useEffect(() => {
|
|
1726
2029
|
if (!open) {
|
|
1727
2030
|
if (lastActiveElement.current) {
|
|
1728
2031
|
lastActiveElement.current.focus();
|
|
@@ -1841,11 +2144,11 @@ function Drawer({
|
|
|
1841
2144
|
}
|
|
1842
2145
|
Drawer.displayName = "Drawer";
|
|
1843
2146
|
function CommandPalette({ open, onClose, items, placeholder = "Type a command or search..." }) {
|
|
1844
|
-
const [search, setSearch] =
|
|
1845
|
-
const [activeIndex, setActiveIndex] =
|
|
1846
|
-
const containerRef =
|
|
1847
|
-
const listRef =
|
|
1848
|
-
|
|
2147
|
+
const [search, setSearch] = React52.useState("");
|
|
2148
|
+
const [activeIndex, setActiveIndex] = React52.useState(0);
|
|
2149
|
+
const containerRef = React52.useRef(null);
|
|
2150
|
+
const listRef = React52.useRef(null);
|
|
2151
|
+
React52.useEffect(() => {
|
|
1849
2152
|
if (open) {
|
|
1850
2153
|
setSearch("");
|
|
1851
2154
|
setActiveIndex(0);
|
|
@@ -1854,17 +2157,17 @@ function CommandPalette({ open, onClose, items, placeholder = "Type a command or
|
|
|
1854
2157
|
document.body.style.overflow = "";
|
|
1855
2158
|
}
|
|
1856
2159
|
}, [open]);
|
|
1857
|
-
const filteredItems =
|
|
2160
|
+
const filteredItems = React52.useMemo(() => {
|
|
1858
2161
|
if (!search) return items;
|
|
1859
2162
|
const cleanSearch = search.toLowerCase();
|
|
1860
2163
|
return items.filter(
|
|
1861
2164
|
(item) => item.title.toLowerCase().includes(cleanSearch) || item.subtitle?.toLowerCase().includes(cleanSearch) || item.category?.toLowerCase().includes(cleanSearch)
|
|
1862
2165
|
);
|
|
1863
2166
|
}, [search, items]);
|
|
1864
|
-
|
|
2167
|
+
React52.useEffect(() => {
|
|
1865
2168
|
setActiveIndex(0);
|
|
1866
2169
|
}, [filteredItems]);
|
|
1867
|
-
|
|
2170
|
+
React52.useEffect(() => {
|
|
1868
2171
|
if (!open) return;
|
|
1869
2172
|
const handleKeyDown = (e) => {
|
|
1870
2173
|
if (e.key === "Escape") {
|
|
@@ -1888,7 +2191,7 @@ function CommandPalette({ open, onClose, items, placeholder = "Type a command or
|
|
|
1888
2191
|
document.addEventListener("keydown", handleKeyDown);
|
|
1889
2192
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
1890
2193
|
}, [open, filteredItems, activeIndex, onClose]);
|
|
1891
|
-
|
|
2194
|
+
React52.useEffect(() => {
|
|
1892
2195
|
if (listRef.current) {
|
|
1893
2196
|
const activeEl = listRef.current.querySelector('[aria-selected="true"]');
|
|
1894
2197
|
if (activeEl) {
|
|
@@ -2091,7 +2394,7 @@ var fabVariants = cva(
|
|
|
2091
2394
|
defaultVariants: { variant: "primary", size: "md", position: "bottom-right" }
|
|
2092
2395
|
}
|
|
2093
2396
|
);
|
|
2094
|
-
var Fab =
|
|
2397
|
+
var Fab = React52.forwardRef(
|
|
2095
2398
|
({ className, variant, size, position, mobileOnly, icon, label, loading, disabled, ...props }, ref) => {
|
|
2096
2399
|
return /* @__PURE__ */ jsx(
|
|
2097
2400
|
"button",
|
|
@@ -2118,7 +2421,7 @@ var Fab = React18.forwardRef(
|
|
|
2118
2421
|
}
|
|
2119
2422
|
);
|
|
2120
2423
|
Fab.displayName = "Fab";
|
|
2121
|
-
var BottomSheetContext =
|
|
2424
|
+
var BottomSheetContext = React52.createContext(null);
|
|
2122
2425
|
function BottomSheet({
|
|
2123
2426
|
open,
|
|
2124
2427
|
onClose,
|
|
@@ -2127,12 +2430,12 @@ function BottomSheet({
|
|
|
2127
2430
|
className,
|
|
2128
2431
|
showClose = true
|
|
2129
2432
|
}) {
|
|
2130
|
-
const titleId =
|
|
2131
|
-
const descId =
|
|
2132
|
-
const containerRef =
|
|
2133
|
-
const lastActiveElement =
|
|
2134
|
-
const [mounted, setMounted] =
|
|
2135
|
-
|
|
2433
|
+
const titleId = React52.useId();
|
|
2434
|
+
const descId = React52.useId();
|
|
2435
|
+
const containerRef = React52.useRef(null);
|
|
2436
|
+
const lastActiveElement = React52.useRef(null);
|
|
2437
|
+
const [mounted, setMounted] = React52.useState(open);
|
|
2438
|
+
React52.useEffect(() => {
|
|
2136
2439
|
if (open) {
|
|
2137
2440
|
setMounted(true);
|
|
2138
2441
|
} else {
|
|
@@ -2140,7 +2443,7 @@ function BottomSheet({
|
|
|
2140
2443
|
return () => clearTimeout(timer);
|
|
2141
2444
|
}
|
|
2142
2445
|
}, [open]);
|
|
2143
|
-
|
|
2446
|
+
React52.useEffect(() => {
|
|
2144
2447
|
if (!open) return;
|
|
2145
2448
|
lastActiveElement.current = document.activeElement;
|
|
2146
2449
|
const container = containerRef.current;
|
|
@@ -2262,12 +2565,12 @@ function BottomSheet({
|
|
|
2262
2565
|
function BottomSheetHeader({ className, ...props }) {
|
|
2263
2566
|
return /* @__PURE__ */ jsx("div", { className: cn("p-6 flex flex-col gap-1.5 border-b border-border", className), ...props });
|
|
2264
2567
|
}
|
|
2265
|
-
function BottomSheetTitle({ className, id, ...props }) {
|
|
2266
|
-
const ctx =
|
|
2267
|
-
return /* @__PURE__ */ jsx("h2", { id: id ?? ctx?.titleId, className: cn("m-0 text-[1.15rem] font-bold tracking-[-0.01em]", className), ...props });
|
|
2568
|
+
function BottomSheetTitle({ className, id, children, ...props }) {
|
|
2569
|
+
const ctx = React52.useContext(BottomSheetContext);
|
|
2570
|
+
return /* @__PURE__ */ jsx("h2", { id: id ?? ctx?.titleId, className: cn("m-0 text-[1.15rem] font-bold tracking-[-0.01em]", className), ...props, children });
|
|
2268
2571
|
}
|
|
2269
2572
|
function BottomSheetDescription({ className, id, ...props }) {
|
|
2270
|
-
const ctx =
|
|
2573
|
+
const ctx = React52.useContext(BottomSheetContext);
|
|
2271
2574
|
return /* @__PURE__ */ jsx("p", { id: id ?? ctx?.descId, className: cn("m-0 text-[0.9rem] text-muted-foreground", className), ...props });
|
|
2272
2575
|
}
|
|
2273
2576
|
function BottomSheetContent({ className, ...props }) {
|
|
@@ -2288,7 +2591,7 @@ var kbdVariants = cva(
|
|
|
2288
2591
|
defaultVariants: { size: "sm" }
|
|
2289
2592
|
}
|
|
2290
2593
|
);
|
|
2291
|
-
var Kbd =
|
|
2594
|
+
var Kbd = React52.forwardRef(
|
|
2292
2595
|
({ className, size, keys, children, ...props }, ref) => {
|
|
2293
2596
|
if (keys && keys.length > 0) {
|
|
2294
2597
|
return /* @__PURE__ */ jsx(
|
|
@@ -2305,9 +2608,9 @@ var Kbd = React18.forwardRef(
|
|
|
2305
2608
|
}
|
|
2306
2609
|
);
|
|
2307
2610
|
Kbd.displayName = "Kbd";
|
|
2308
|
-
var CodeBlock =
|
|
2611
|
+
var CodeBlock = React52.forwardRef(
|
|
2309
2612
|
({ className, code, language, filename, copyable = true, maxHeight, ...props }, ref) => {
|
|
2310
|
-
const [copied, setCopied] =
|
|
2613
|
+
const [copied, setCopied] = React52.useState(false);
|
|
2311
2614
|
const canCopy = copyable && typeof navigator !== "undefined" && !!navigator.clipboard;
|
|
2312
2615
|
const onCopy = () => {
|
|
2313
2616
|
navigator.clipboard.writeText(code).then(
|
|
@@ -2353,26 +2656,40 @@ var CodeBlock = React18.forwardRef(
|
|
|
2353
2656
|
}
|
|
2354
2657
|
);
|
|
2355
2658
|
CodeBlock.displayName = "CodeBlock";
|
|
2356
|
-
|
|
2659
|
+
function Separator({ orientation = "horizontal", decorative = true, className }) {
|
|
2660
|
+
const semanticProps = decorative ? { role: "none", "aria-hidden": true } : { role: "separator", "aria-orientation": orientation };
|
|
2661
|
+
return /* @__PURE__ */ jsx(
|
|
2662
|
+
"div",
|
|
2663
|
+
{
|
|
2664
|
+
className: cn(
|
|
2665
|
+
"shrink-0 bg-border",
|
|
2666
|
+
orientation === "horizontal" ? "h-px w-full" : "w-px h-full",
|
|
2667
|
+
className
|
|
2668
|
+
),
|
|
2669
|
+
...semanticProps
|
|
2670
|
+
}
|
|
2671
|
+
);
|
|
2672
|
+
}
|
|
2673
|
+
var PopoverContext = React52.createContext(null);
|
|
2357
2674
|
function usePopoverContext(part) {
|
|
2358
|
-
const ctx =
|
|
2675
|
+
const ctx = React52.useContext(PopoverContext);
|
|
2359
2676
|
if (!ctx) throw new Error(`${part} must be used inside <Popover>`);
|
|
2360
2677
|
return ctx;
|
|
2361
2678
|
}
|
|
2362
2679
|
function Popover({ children, className, open: openProp, onOpenChange }) {
|
|
2363
|
-
const [openState, setOpenState] =
|
|
2680
|
+
const [openState, setOpenState] = React52.useState(false);
|
|
2364
2681
|
const controlled = openProp !== void 0;
|
|
2365
2682
|
const open = controlled ? openProp : openState;
|
|
2366
|
-
const triggerRef =
|
|
2367
|
-
const ref =
|
|
2368
|
-
const setOpen =
|
|
2683
|
+
const triggerRef = React52.useRef(null);
|
|
2684
|
+
const ref = React52.useRef(null);
|
|
2685
|
+
const setOpen = React52.useCallback(
|
|
2369
2686
|
(v) => {
|
|
2370
2687
|
if (!controlled) setOpenState(v);
|
|
2371
2688
|
onOpenChange?.(v);
|
|
2372
2689
|
},
|
|
2373
2690
|
[controlled, onOpenChange]
|
|
2374
2691
|
);
|
|
2375
|
-
|
|
2692
|
+
React52.useEffect(() => {
|
|
2376
2693
|
if (!open) return;
|
|
2377
2694
|
const onDoc = (e) => {
|
|
2378
2695
|
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
@@ -2392,7 +2709,7 @@ function Popover({ children, className, open: openProp, onOpenChange }) {
|
|
|
2392
2709
|
}, [open, setOpen]);
|
|
2393
2710
|
return /* @__PURE__ */ jsx(PopoverContext.Provider, { value: { open, setOpen, triggerRef }, children: /* @__PURE__ */ jsx("div", { ref, className: cn("relative inline-block", className), children }) });
|
|
2394
2711
|
}
|
|
2395
|
-
var PopoverTrigger =
|
|
2712
|
+
var PopoverTrigger = React52.forwardRef(({ onClick, asChild, ...props }, ref) => {
|
|
2396
2713
|
const ctx = usePopoverContext("PopoverTrigger");
|
|
2397
2714
|
const setRefs = (node) => {
|
|
2398
2715
|
ctx.triggerRef.current = node;
|
|
@@ -2400,8 +2717,8 @@ var PopoverTrigger = React18.forwardRef(({ onClick, asChild, ...props }, ref) =>
|
|
|
2400
2717
|
else if (ref) ref.current = node;
|
|
2401
2718
|
};
|
|
2402
2719
|
if (asChild) {
|
|
2403
|
-
const child =
|
|
2404
|
-
return
|
|
2720
|
+
const child = React52.Children.only(props.children);
|
|
2721
|
+
return React52.cloneElement(child, {
|
|
2405
2722
|
ref: setRefs,
|
|
2406
2723
|
...props,
|
|
2407
2724
|
...child.props,
|
|
@@ -2452,8 +2769,8 @@ function PopoverContent({
|
|
|
2452
2769
|
...props
|
|
2453
2770
|
}) {
|
|
2454
2771
|
const ctx = usePopoverContext("PopoverContent");
|
|
2455
|
-
const ref =
|
|
2456
|
-
|
|
2772
|
+
const ref = React52.useRef(null);
|
|
2773
|
+
React52.useEffect(() => {
|
|
2457
2774
|
if (ctx.open) ref.current?.focus();
|
|
2458
2775
|
}, [ctx.open]);
|
|
2459
2776
|
if (!ctx.open) return null;
|
|
@@ -2474,7 +2791,3275 @@ function PopoverContent({
|
|
|
2474
2791
|
}
|
|
2475
2792
|
);
|
|
2476
2793
|
}
|
|
2794
|
+
function toArray(v) {
|
|
2795
|
+
if (v == null) return [];
|
|
2796
|
+
return Array.isArray(v) ? v : [v];
|
|
2797
|
+
}
|
|
2798
|
+
var Combobox = React52.forwardRef(
|
|
2799
|
+
function Combobox2(props, ref) {
|
|
2800
|
+
const {
|
|
2801
|
+
options,
|
|
2802
|
+
placeholder = "Select\u2026",
|
|
2803
|
+
searchPlaceholder = "Search\u2026",
|
|
2804
|
+
emptyMessage = "No results",
|
|
2805
|
+
disabled = false,
|
|
2806
|
+
error = false,
|
|
2807
|
+
required = false,
|
|
2808
|
+
className,
|
|
2809
|
+
id,
|
|
2810
|
+
name,
|
|
2811
|
+
"aria-describedby": describedBy
|
|
2812
|
+
} = props;
|
|
2813
|
+
const multiple = props.multiple ?? false;
|
|
2814
|
+
const reactId = React52.useId();
|
|
2815
|
+
const baseId = id ?? reactId;
|
|
2816
|
+
const listId = `${baseId}-listbox`;
|
|
2817
|
+
const optionId = (i) => `${baseId}-opt-${i}`;
|
|
2818
|
+
const controlled = props.value !== void 0;
|
|
2819
|
+
const [internal, setInternal] = React52.useState(() => toArray(props.defaultValue));
|
|
2820
|
+
const selected = controlled ? toArray(props.value) : internal;
|
|
2821
|
+
const [open, setOpen] = React52.useState(false);
|
|
2822
|
+
const [search, setSearch] = React52.useState("");
|
|
2823
|
+
const [activeIndex, setActiveIndex] = React52.useState(0);
|
|
2824
|
+
const containerRef = React52.useRef(null);
|
|
2825
|
+
const triggerRef = React52.useRef(null);
|
|
2826
|
+
const inputRef = React52.useRef(null);
|
|
2827
|
+
const listRef = React52.useRef(null);
|
|
2828
|
+
const setTriggerRef = (node) => {
|
|
2829
|
+
triggerRef.current = node;
|
|
2830
|
+
if (typeof ref === "function") ref(node);
|
|
2831
|
+
else if (ref) ref.current = node;
|
|
2832
|
+
};
|
|
2833
|
+
const filtered = React52.useMemo(() => {
|
|
2834
|
+
const q = search.trim().toLowerCase();
|
|
2835
|
+
return q ? options.filter((o) => o.label.toLowerCase().includes(q)) : options;
|
|
2836
|
+
}, [search, options]);
|
|
2837
|
+
const firstEnabled = React52.useCallback(() => {
|
|
2838
|
+
const i = filtered.findIndex((o) => !o.disabled);
|
|
2839
|
+
return i === -1 ? 0 : i;
|
|
2840
|
+
}, [filtered]);
|
|
2841
|
+
const labelFor = (v) => options.find((o) => o.value === v)?.label ?? v;
|
|
2842
|
+
const emit = (next) => {
|
|
2843
|
+
if (!controlled) setInternal(next);
|
|
2844
|
+
if (props.multiple) {
|
|
2845
|
+
props.onChange?.(next);
|
|
2846
|
+
} else {
|
|
2847
|
+
props.onChange?.(next[0] ?? "");
|
|
2848
|
+
}
|
|
2849
|
+
};
|
|
2850
|
+
const closePopup = React52.useCallback(() => {
|
|
2851
|
+
setOpen(false);
|
|
2852
|
+
setSearch("");
|
|
2853
|
+
}, []);
|
|
2854
|
+
const openPopup = () => {
|
|
2855
|
+
if (!disabled) setOpen(true);
|
|
2856
|
+
};
|
|
2857
|
+
const handleSelect = (opt) => {
|
|
2858
|
+
if (opt.disabled) return;
|
|
2859
|
+
if (multiple) {
|
|
2860
|
+
const next = selected.includes(opt.value) ? selected.filter((v) => v !== opt.value) : [...selected, opt.value];
|
|
2861
|
+
emit(next);
|
|
2862
|
+
inputRef.current?.focus();
|
|
2863
|
+
} else {
|
|
2864
|
+
emit([opt.value]);
|
|
2865
|
+
closePopup();
|
|
2866
|
+
triggerRef.current?.focus();
|
|
2867
|
+
}
|
|
2868
|
+
};
|
|
2869
|
+
const removeValue = (value) => {
|
|
2870
|
+
emit(selected.filter((v) => v !== value));
|
|
2871
|
+
};
|
|
2872
|
+
React52.useEffect(() => {
|
|
2873
|
+
if (open) setActiveIndex(firstEnabled());
|
|
2874
|
+
}, [open, firstEnabled]);
|
|
2875
|
+
React52.useEffect(() => {
|
|
2876
|
+
if (open) inputRef.current?.focus();
|
|
2877
|
+
}, [open]);
|
|
2878
|
+
React52.useEffect(() => {
|
|
2879
|
+
if (!open) return;
|
|
2880
|
+
const onDown = (e) => {
|
|
2881
|
+
if (containerRef.current && !containerRef.current.contains(e.target)) closePopup();
|
|
2882
|
+
};
|
|
2883
|
+
document.addEventListener("mousedown", onDown);
|
|
2884
|
+
return () => document.removeEventListener("mousedown", onDown);
|
|
2885
|
+
}, [open, closePopup]);
|
|
2886
|
+
React52.useEffect(() => {
|
|
2887
|
+
const el = listRef.current?.querySelector('[data-active="true"]');
|
|
2888
|
+
el?.scrollIntoView({ block: "nearest" });
|
|
2889
|
+
}, [activeIndex]);
|
|
2890
|
+
const moveActive = (dir) => {
|
|
2891
|
+
if (filtered.length === 0) return;
|
|
2892
|
+
setActiveIndex((prev) => {
|
|
2893
|
+
let i = prev;
|
|
2894
|
+
for (let step = 0; step < filtered.length; step++) {
|
|
2895
|
+
i = (i + dir + filtered.length) % filtered.length;
|
|
2896
|
+
if (!filtered[i]?.disabled) return i;
|
|
2897
|
+
}
|
|
2898
|
+
return prev;
|
|
2899
|
+
});
|
|
2900
|
+
};
|
|
2901
|
+
const onInputKeyDown = (e) => {
|
|
2902
|
+
switch (e.key) {
|
|
2903
|
+
case "ArrowDown":
|
|
2904
|
+
e.preventDefault();
|
|
2905
|
+
moveActive(1);
|
|
2906
|
+
break;
|
|
2907
|
+
case "ArrowUp":
|
|
2908
|
+
e.preventDefault();
|
|
2909
|
+
moveActive(-1);
|
|
2910
|
+
break;
|
|
2911
|
+
case "Home":
|
|
2912
|
+
e.preventDefault();
|
|
2913
|
+
setActiveIndex(firstEnabled());
|
|
2914
|
+
break;
|
|
2915
|
+
case "End":
|
|
2916
|
+
e.preventDefault();
|
|
2917
|
+
for (let i = filtered.length - 1; i >= 0; i--) {
|
|
2918
|
+
if (!filtered[i].disabled) {
|
|
2919
|
+
setActiveIndex(i);
|
|
2920
|
+
break;
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
break;
|
|
2924
|
+
case "Enter":
|
|
2925
|
+
e.preventDefault();
|
|
2926
|
+
if (filtered[activeIndex]) handleSelect(filtered[activeIndex]);
|
|
2927
|
+
break;
|
|
2928
|
+
case "Escape":
|
|
2929
|
+
e.preventDefault();
|
|
2930
|
+
closePopup();
|
|
2931
|
+
triggerRef.current?.focus();
|
|
2932
|
+
break;
|
|
2933
|
+
case "Backspace":
|
|
2934
|
+
if (multiple && search === "" && selected.length > 0) {
|
|
2935
|
+
removeValue(selected[selected.length - 1]);
|
|
2936
|
+
}
|
|
2937
|
+
break;
|
|
2938
|
+
}
|
|
2939
|
+
};
|
|
2940
|
+
const onTriggerKeyDown = (e) => {
|
|
2941
|
+
if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
|
|
2942
|
+
e.preventDefault();
|
|
2943
|
+
openPopup();
|
|
2944
|
+
}
|
|
2945
|
+
};
|
|
2946
|
+
const showPlaceholder = multiple ? selected.length === 0 : !selected[0];
|
|
2947
|
+
return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: cn("relative w-full", className), children: [
|
|
2948
|
+
/* @__PURE__ */ jsxs(
|
|
2949
|
+
"div",
|
|
2950
|
+
{
|
|
2951
|
+
className: cn(
|
|
2952
|
+
"relative flex w-full items-center gap-1.5 flex-wrap border rounded-kj-md bg-surface",
|
|
2953
|
+
"pl-[0.85rem] pr-9 py-[calc(0.45rem*var(--kj-density,1))] min-h-[calc(2.5rem*var(--kj-density,1))]",
|
|
2954
|
+
"transition-[border-color,box-shadow] duration-150",
|
|
2955
|
+
"focus-within:outline-none focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/30",
|
|
2956
|
+
disabled && "bg-muted cursor-not-allowed",
|
|
2957
|
+
error ? "border-danger" : "border-input"
|
|
2958
|
+
),
|
|
2959
|
+
children: [
|
|
2960
|
+
multiple && selected.map((value) => /* @__PURE__ */ jsxs(
|
|
2961
|
+
"span",
|
|
2962
|
+
{
|
|
2963
|
+
className: "inline-flex items-center gap-1 rounded-kj-sm bg-primary/10 text-primary text-xs font-medium pl-2 pr-1 py-0.5",
|
|
2964
|
+
children: [
|
|
2965
|
+
labelFor(value),
|
|
2966
|
+
/* @__PURE__ */ jsx(
|
|
2967
|
+
"button",
|
|
2968
|
+
{
|
|
2969
|
+
type: "button",
|
|
2970
|
+
"aria-label": `Remove ${labelFor(value)}`,
|
|
2971
|
+
disabled,
|
|
2972
|
+
onClick: (e) => {
|
|
2973
|
+
e.stopPropagation();
|
|
2974
|
+
removeValue(value);
|
|
2975
|
+
},
|
|
2976
|
+
className: "rounded-full p-0.5 leading-none hover:bg-primary/20",
|
|
2977
|
+
children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "h-3 w-3", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", children: /* @__PURE__ */ jsx("path", { d: "M6 6l12 12M18 6L6 18" }) })
|
|
2978
|
+
}
|
|
2979
|
+
)
|
|
2980
|
+
]
|
|
2981
|
+
},
|
|
2982
|
+
value
|
|
2983
|
+
)),
|
|
2984
|
+
/* @__PURE__ */ jsx(
|
|
2985
|
+
"button",
|
|
2986
|
+
{
|
|
2987
|
+
ref: setTriggerRef,
|
|
2988
|
+
type: "button",
|
|
2989
|
+
id,
|
|
2990
|
+
disabled,
|
|
2991
|
+
"aria-haspopup": "listbox",
|
|
2992
|
+
"aria-expanded": open,
|
|
2993
|
+
"aria-controls": open && filtered.length > 0 ? listId : void 0,
|
|
2994
|
+
"aria-invalid": error || void 0,
|
|
2995
|
+
"aria-required": required || void 0,
|
|
2996
|
+
"aria-describedby": describedBy,
|
|
2997
|
+
onClick: () => open ? closePopup() : openPopup(),
|
|
2998
|
+
onKeyDown: onTriggerKeyDown,
|
|
2999
|
+
className: cn(
|
|
3000
|
+
"flex-1 min-w-[60px] text-left bg-transparent border-none outline-none cursor-pointer text-sm disabled:cursor-not-allowed",
|
|
3001
|
+
showPlaceholder && "text-muted-foreground"
|
|
3002
|
+
),
|
|
3003
|
+
children: multiple ? selected.length === 0 ? placeholder : "" : (selected[0] ? labelFor(selected[0]) : "") || placeholder
|
|
3004
|
+
}
|
|
3005
|
+
),
|
|
3006
|
+
name && multiple && selected.map((v) => /* @__PURE__ */ jsx("input", { type: "hidden", name, value: v }, v)),
|
|
3007
|
+
name && !multiple && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: selected[0] ?? "" }),
|
|
3008
|
+
/* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" }) }) })
|
|
3009
|
+
]
|
|
3010
|
+
}
|
|
3011
|
+
),
|
|
3012
|
+
open && /* @__PURE__ */ jsxs("div", { className: "absolute z-40 top-[calc(100%+6px)] left-0 w-full min-w-[12rem] bg-surface border border-border rounded-kj-md shadow-kj-lg overflow-hidden animate-[kjpop_.12s_ease]", children: [
|
|
3013
|
+
/* @__PURE__ */ jsx("div", { className: "p-2 border-b border-border", children: /* @__PURE__ */ jsx(
|
|
3014
|
+
"input",
|
|
3015
|
+
{
|
|
3016
|
+
ref: inputRef,
|
|
3017
|
+
type: "text",
|
|
3018
|
+
role: "combobox",
|
|
3019
|
+
"aria-autocomplete": "list",
|
|
3020
|
+
"aria-expanded": open,
|
|
3021
|
+
"aria-controls": filtered.length > 0 ? listId : void 0,
|
|
3022
|
+
"aria-activedescendant": filtered[activeIndex] ? optionId(activeIndex) : void 0,
|
|
3023
|
+
"aria-label": searchPlaceholder,
|
|
3024
|
+
placeholder: searchPlaceholder,
|
|
3025
|
+
value: search,
|
|
3026
|
+
onChange: (e) => setSearch(e.target.value),
|
|
3027
|
+
onKeyDown: onInputKeyDown,
|
|
3028
|
+
className: "w-full bg-transparent text-sm text-foreground placeholder:text-muted-foreground outline-none px-2 py-1"
|
|
3029
|
+
}
|
|
3030
|
+
) }),
|
|
3031
|
+
filtered.length === 0 ? /* @__PURE__ */ jsx("div", { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }) : /* @__PURE__ */ jsx(
|
|
3032
|
+
"div",
|
|
3033
|
+
{
|
|
3034
|
+
ref: listRef,
|
|
3035
|
+
id: listId,
|
|
3036
|
+
role: "listbox",
|
|
3037
|
+
"aria-multiselectable": multiple || void 0,
|
|
3038
|
+
"aria-label": placeholder,
|
|
3039
|
+
className: "max-h-60 overflow-y-auto p-1",
|
|
3040
|
+
children: filtered.map((opt, i) => {
|
|
3041
|
+
const isSelected = selected.includes(opt.value);
|
|
3042
|
+
const isActive = i === activeIndex;
|
|
3043
|
+
return /* @__PURE__ */ jsxs(
|
|
3044
|
+
"div",
|
|
3045
|
+
{
|
|
3046
|
+
id: optionId(i),
|
|
3047
|
+
role: "option",
|
|
3048
|
+
"aria-selected": isSelected,
|
|
3049
|
+
"aria-disabled": opt.disabled || void 0,
|
|
3050
|
+
"data-active": isActive || void 0,
|
|
3051
|
+
onMouseEnter: () => !opt.disabled && setActiveIndex(i),
|
|
3052
|
+
onClick: () => handleSelect(opt),
|
|
3053
|
+
className: cn(
|
|
3054
|
+
"flex items-center justify-between gap-2 px-3 py-2 rounded-kj-sm text-sm cursor-pointer select-none",
|
|
3055
|
+
opt.disabled && "text-muted-foreground opacity-50 cursor-not-allowed",
|
|
3056
|
+
!opt.disabled && isActive && "bg-primary/10 text-primary",
|
|
3057
|
+
!opt.disabled && !isActive && "text-foreground hover:bg-muted"
|
|
3058
|
+
),
|
|
3059
|
+
children: [
|
|
3060
|
+
/* @__PURE__ */ jsx("span", { children: opt.label }),
|
|
3061
|
+
isSelected && /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4 text-primary shrink-0", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m5 12 5 5L20 7" }) })
|
|
3062
|
+
]
|
|
3063
|
+
},
|
|
3064
|
+
opt.value
|
|
3065
|
+
);
|
|
3066
|
+
})
|
|
3067
|
+
}
|
|
3068
|
+
)
|
|
3069
|
+
] })
|
|
3070
|
+
] });
|
|
3071
|
+
}
|
|
3072
|
+
);
|
|
3073
|
+
Combobox.displayName = "Combobox";
|
|
3074
|
+
var ComboboxField = React52.forwardRef(
|
|
3075
|
+
function ComboboxField2({ label, hint, error, required, className, ...props }, ref) {
|
|
3076
|
+
return /* @__PURE__ */ jsx(FormField, { label, hint, error, required, className, children: /* @__PURE__ */ jsx(Combobox, { ref, error: !!error, ...props }) });
|
|
3077
|
+
}
|
|
3078
|
+
);
|
|
3079
|
+
ComboboxField.displayName = "ComboboxField";
|
|
3080
|
+
|
|
3081
|
+
// src/components/calendar-internals.ts
|
|
3082
|
+
function startOfDay(d) {
|
|
3083
|
+
return new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
3084
|
+
}
|
|
3085
|
+
function isSameDay(a, b) {
|
|
3086
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
3087
|
+
}
|
|
3088
|
+
function addDays(d, n) {
|
|
3089
|
+
const r = new Date(d);
|
|
3090
|
+
r.setDate(r.getDate() + n);
|
|
3091
|
+
return r;
|
|
3092
|
+
}
|
|
3093
|
+
function addMonths(d, n) {
|
|
3094
|
+
const r = new Date(d);
|
|
3095
|
+
r.setMonth(r.getMonth() + n);
|
|
3096
|
+
return r;
|
|
3097
|
+
}
|
|
3098
|
+
function clampToMonth(year, month, day) {
|
|
3099
|
+
const daysInTarget = new Date(year, month + 1, 0).getDate();
|
|
3100
|
+
return new Date(year, month, Math.min(day, daysInTarget));
|
|
3101
|
+
}
|
|
3102
|
+
function addMonthsClamped(d, n) {
|
|
3103
|
+
return clampToMonth(d.getFullYear(), d.getMonth() + n, d.getDate());
|
|
3104
|
+
}
|
|
3105
|
+
function addYearsClamped(d, n) {
|
|
3106
|
+
return clampToMonth(d.getFullYear() + n, d.getMonth(), d.getDate());
|
|
3107
|
+
}
|
|
3108
|
+
function startOfMonth(d) {
|
|
3109
|
+
return new Date(d.getFullYear(), d.getMonth(), 1);
|
|
3110
|
+
}
|
|
3111
|
+
function startOfWeek(d) {
|
|
3112
|
+
const r = startOfDay(d);
|
|
3113
|
+
r.setDate(r.getDate() - r.getDay());
|
|
3114
|
+
return r;
|
|
3115
|
+
}
|
|
3116
|
+
function buildGridDays(viewMonth) {
|
|
3117
|
+
const gridStart = startOfWeek(startOfMonth(viewMonth));
|
|
3118
|
+
return Array.from({ length: 42 }, (_, i) => addDays(gridStart, i));
|
|
3119
|
+
}
|
|
3120
|
+
function dateKey(d) {
|
|
3121
|
+
return `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`;
|
|
3122
|
+
}
|
|
3123
|
+
function toISODateString(d) {
|
|
3124
|
+
const y = d.getFullYear();
|
|
3125
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
3126
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
3127
|
+
return `${y}-${m}-${day}`;
|
|
3128
|
+
}
|
|
3129
|
+
var monthLabelFormat = new Intl.DateTimeFormat(void 0, { month: "long", year: "numeric" });
|
|
3130
|
+
var fullDateFormat = new Intl.DateTimeFormat(void 0, { dateStyle: "full" });
|
|
3131
|
+
var weekdayShortFormat = new Intl.DateTimeFormat(void 0, { weekday: "short" });
|
|
3132
|
+
var weekdayLongFormat = new Intl.DateTimeFormat(void 0, { weekday: "long" });
|
|
3133
|
+
var weekdayLabels = Array.from({ length: 7 }, (_, i) => {
|
|
3134
|
+
const d = addDays(startOfWeek(/* @__PURE__ */ new Date()), i);
|
|
3135
|
+
return { key: i, short: weekdayShortFormat.format(d), long: weekdayLongFormat.format(d) };
|
|
3136
|
+
});
|
|
3137
|
+
var CalendarGrid = React52.forwardRef(function CalendarGrid2({ viewMonth, isDayDisabled, cellState, onSelectDay, onFocusDay, initialFocusDate, className }, ref) {
|
|
3138
|
+
const gridDays = React52.useMemo(() => buildGridDays(viewMonth), [viewMonth]);
|
|
3139
|
+
const inViewMonth = (d) => d.getMonth() === viewMonth.getMonth() && d.getFullYear() === viewMonth.getFullYear();
|
|
3140
|
+
const [focusedDate, setFocusedDate] = React52.useState(() => startOfDay(initialFocusDate));
|
|
3141
|
+
const pendingFocusRef = React52.useRef(false);
|
|
3142
|
+
const gridRef = React52.useRef(null);
|
|
3143
|
+
const rovingTarget = inViewMonth(focusedDate) && !isDayDisabled(focusedDate) ? focusedDate : gridDays.find((d) => inViewMonth(d) && !isDayDisabled(d)) ?? gridDays.find((d) => inViewMonth(d)) ?? gridDays[0];
|
|
3144
|
+
const moveFocusTo = (next, viaKeyboard) => {
|
|
3145
|
+
pendingFocusRef.current = viaKeyboard;
|
|
3146
|
+
setFocusedDate(next);
|
|
3147
|
+
onFocusDay?.(next);
|
|
3148
|
+
};
|
|
3149
|
+
React52.useEffect(() => {
|
|
3150
|
+
if (!pendingFocusRef.current) return;
|
|
3151
|
+
pendingFocusRef.current = false;
|
|
3152
|
+
const el = gridRef.current?.querySelector(`[data-date="${dateKey(rovingTarget)}"]`);
|
|
3153
|
+
el?.focus();
|
|
3154
|
+
}, [rovingTarget]);
|
|
3155
|
+
const step = (from, delta) => {
|
|
3156
|
+
let next = addDays(from, delta);
|
|
3157
|
+
let guard = 0;
|
|
3158
|
+
while (isDayDisabled(next) && guard < 1e3) {
|
|
3159
|
+
next = addDays(next, delta);
|
|
3160
|
+
guard++;
|
|
3161
|
+
}
|
|
3162
|
+
return isDayDisabled(next) ? from : next;
|
|
3163
|
+
};
|
|
3164
|
+
const selectDay = (d) => {
|
|
3165
|
+
if (isDayDisabled(d)) return;
|
|
3166
|
+
moveFocusTo(d, false);
|
|
3167
|
+
onSelectDay(d);
|
|
3168
|
+
};
|
|
3169
|
+
const onGridKeyDown = (e) => {
|
|
3170
|
+
switch (e.key) {
|
|
3171
|
+
case "ArrowRight":
|
|
3172
|
+
e.preventDefault();
|
|
3173
|
+
moveFocusTo(step(rovingTarget, 1), true);
|
|
3174
|
+
break;
|
|
3175
|
+
case "ArrowLeft":
|
|
3176
|
+
e.preventDefault();
|
|
3177
|
+
moveFocusTo(step(rovingTarget, -1), true);
|
|
3178
|
+
break;
|
|
3179
|
+
case "ArrowDown":
|
|
3180
|
+
e.preventDefault();
|
|
3181
|
+
moveFocusTo(step(rovingTarget, 7), true);
|
|
3182
|
+
break;
|
|
3183
|
+
case "ArrowUp":
|
|
3184
|
+
e.preventDefault();
|
|
3185
|
+
moveFocusTo(step(rovingTarget, -7), true);
|
|
3186
|
+
break;
|
|
3187
|
+
case "Home": {
|
|
3188
|
+
e.preventDefault();
|
|
3189
|
+
const target = startOfWeek(rovingTarget);
|
|
3190
|
+
moveFocusTo(isDayDisabled(target) ? step(target, 1) : target, true);
|
|
3191
|
+
break;
|
|
3192
|
+
}
|
|
3193
|
+
case "End": {
|
|
3194
|
+
e.preventDefault();
|
|
3195
|
+
const target = addDays(startOfWeek(rovingTarget), 6);
|
|
3196
|
+
moveFocusTo(isDayDisabled(target) ? step(target, -1) : target, true);
|
|
3197
|
+
break;
|
|
3198
|
+
}
|
|
3199
|
+
case "PageUp": {
|
|
3200
|
+
e.preventDefault();
|
|
3201
|
+
const target = e.shiftKey ? addYearsClamped(rovingTarget, -1) : addMonthsClamped(rovingTarget, -1);
|
|
3202
|
+
moveFocusTo(isDayDisabled(target) ? step(target, -1) : target, true);
|
|
3203
|
+
break;
|
|
3204
|
+
}
|
|
3205
|
+
case "PageDown": {
|
|
3206
|
+
e.preventDefault();
|
|
3207
|
+
const target = e.shiftKey ? addYearsClamped(rovingTarget, 1) : addMonthsClamped(rovingTarget, 1);
|
|
3208
|
+
moveFocusTo(isDayDisabled(target) ? step(target, 1) : target, true);
|
|
3209
|
+
break;
|
|
3210
|
+
}
|
|
3211
|
+
case "Enter":
|
|
3212
|
+
case " ":
|
|
3213
|
+
e.preventDefault();
|
|
3214
|
+
selectDay(rovingTarget);
|
|
3215
|
+
break;
|
|
3216
|
+
}
|
|
3217
|
+
};
|
|
3218
|
+
return /* @__PURE__ */ jsx("div", { ref, className: cn(className), children: /* @__PURE__ */ jsxs("div", { ref: gridRef, role: "grid", "aria-label": monthLabelFormat.format(viewMonth), onKeyDown: onGridKeyDown, children: [
|
|
3219
|
+
/* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7", children: weekdayLabels.map((w) => /* @__PURE__ */ jsx(
|
|
3220
|
+
"div",
|
|
3221
|
+
{
|
|
3222
|
+
role: "columnheader",
|
|
3223
|
+
"aria-label": w.long,
|
|
3224
|
+
className: "h-8 flex items-center justify-center text-xs font-medium text-muted-foreground",
|
|
3225
|
+
children: w.short
|
|
3226
|
+
},
|
|
3227
|
+
w.key
|
|
3228
|
+
)) }),
|
|
3229
|
+
Array.from({ length: 6 }, (_, week) => /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7", children: gridDays.slice(week * 7, week * 7 + 7).map((day) => {
|
|
3230
|
+
if (!inViewMonth(day)) {
|
|
3231
|
+
return /* @__PURE__ */ jsx("div", { role: "gridcell", "aria-hidden": "true", className: "h-9" }, dateKey(day));
|
|
3232
|
+
}
|
|
3233
|
+
const disabled = isDayDisabled(day);
|
|
3234
|
+
const state = cellState(day);
|
|
3235
|
+
const isRoving = isSameDay(day, rovingTarget);
|
|
3236
|
+
const isEndpoint = !!(state.selected || state.rangeStart || state.rangeEnd);
|
|
3237
|
+
return /* @__PURE__ */ jsx(
|
|
3238
|
+
"button",
|
|
3239
|
+
{
|
|
3240
|
+
type: "button",
|
|
3241
|
+
role: "gridcell",
|
|
3242
|
+
"data-date": dateKey(day),
|
|
3243
|
+
tabIndex: isRoving ? 0 : -1,
|
|
3244
|
+
"aria-selected": isEndpoint,
|
|
3245
|
+
"aria-disabled": disabled || void 0,
|
|
3246
|
+
"aria-label": fullDateFormat.format(day),
|
|
3247
|
+
onMouseEnter: () => onFocusDay?.(day),
|
|
3248
|
+
onClick: () => selectDay(day),
|
|
3249
|
+
className: cn(
|
|
3250
|
+
"h-9 w-9 mx-auto flex items-center justify-center text-sm",
|
|
3251
|
+
state.inRange && !isEndpoint ? "rounded-none" : "rounded-full",
|
|
3252
|
+
"focus:outline-none focus:ring-[3px] focus:ring-ring/30",
|
|
3253
|
+
disabled && "text-muted-foreground opacity-40 cursor-not-allowed",
|
|
3254
|
+
!disabled && isEndpoint && "bg-primary text-primary-foreground",
|
|
3255
|
+
!disabled && state.inRange && !isEndpoint && "bg-primary/15 text-foreground",
|
|
3256
|
+
!disabled && !isEndpoint && !state.inRange && state.today && "ring-1 ring-primary text-primary font-medium",
|
|
3257
|
+
!disabled && !isEndpoint && !state.inRange && !state.today && "text-foreground hover:bg-muted"
|
|
3258
|
+
),
|
|
3259
|
+
children: day.getDate()
|
|
3260
|
+
},
|
|
3261
|
+
dateKey(day)
|
|
3262
|
+
);
|
|
3263
|
+
}) }, week))
|
|
3264
|
+
] }) });
|
|
3265
|
+
});
|
|
3266
|
+
CalendarGrid.displayName = "CalendarGrid";
|
|
3267
|
+
var Calendar = React52.forwardRef(
|
|
3268
|
+
function Calendar2({ value, defaultValue, onChange, month, defaultMonth, onMonthChange, min, max, disabledDates, className }, ref) {
|
|
3269
|
+
const controlled = value !== void 0;
|
|
3270
|
+
const [internalValue, setInternalValue] = React52.useState(defaultValue);
|
|
3271
|
+
const selected = controlled ? value : internalValue;
|
|
3272
|
+
const monthControlled = month !== void 0;
|
|
3273
|
+
const [internalMonth, setInternalMonth] = React52.useState(
|
|
3274
|
+
() => startOfMonth(month ?? defaultMonth ?? selected ?? /* @__PURE__ */ new Date())
|
|
3275
|
+
);
|
|
3276
|
+
const viewMonth = monthControlled ? startOfMonth(month) : internalMonth;
|
|
3277
|
+
const setViewMonth = (next) => {
|
|
3278
|
+
const normalized = startOfMonth(next);
|
|
3279
|
+
if (!monthControlled) setInternalMonth(normalized);
|
|
3280
|
+
onMonthChange?.(normalized);
|
|
3281
|
+
};
|
|
3282
|
+
const isDayDisabled = React52.useCallback(
|
|
3283
|
+
(d) => {
|
|
3284
|
+
if (min && d < startOfDay(min)) return true;
|
|
3285
|
+
if (max && d > startOfDay(max)) return true;
|
|
3286
|
+
return disabledDates?.(d) ?? false;
|
|
3287
|
+
},
|
|
3288
|
+
[min, max, disabledDates]
|
|
3289
|
+
);
|
|
3290
|
+
const commitValue = (d) => {
|
|
3291
|
+
if (!controlled) setInternalValue(d);
|
|
3292
|
+
onChange?.(d);
|
|
3293
|
+
};
|
|
3294
|
+
const today = /* @__PURE__ */ new Date();
|
|
3295
|
+
const cellState = (d) => ({
|
|
3296
|
+
selected: selected ? isSameDay(d, selected) : false,
|
|
3297
|
+
today: isSameDay(d, today)
|
|
3298
|
+
});
|
|
3299
|
+
const inViewMonth = (d) => d.getMonth() === viewMonth.getMonth() && d.getFullYear() === viewMonth.getFullYear();
|
|
3300
|
+
const handleFocusDay = (d) => {
|
|
3301
|
+
if (!inViewMonth(d)) setViewMonth(d);
|
|
3302
|
+
};
|
|
3303
|
+
return /* @__PURE__ */ jsxs("div", { ref, className: cn("w-[280px] select-none", className), children: [
|
|
3304
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
3305
|
+
/* @__PURE__ */ jsx(
|
|
3306
|
+
"button",
|
|
3307
|
+
{
|
|
3308
|
+
type: "button",
|
|
3309
|
+
"aria-label": "Previous month",
|
|
3310
|
+
onClick: () => setViewMonth(addMonths(viewMonth, -1)),
|
|
3311
|
+
className: "p-1.5 rounded-kj-sm text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
3312
|
+
children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" }) })
|
|
3313
|
+
}
|
|
3314
|
+
),
|
|
3315
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: monthLabelFormat.format(viewMonth) }),
|
|
3316
|
+
/* @__PURE__ */ jsx(
|
|
3317
|
+
"button",
|
|
3318
|
+
{
|
|
3319
|
+
type: "button",
|
|
3320
|
+
"aria-label": "Next month",
|
|
3321
|
+
onClick: () => setViewMonth(addMonths(viewMonth, 1)),
|
|
3322
|
+
className: "p-1.5 rounded-kj-sm text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
3323
|
+
children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }) })
|
|
3324
|
+
}
|
|
3325
|
+
)
|
|
3326
|
+
] }),
|
|
3327
|
+
/* @__PURE__ */ jsx(
|
|
3328
|
+
CalendarGrid,
|
|
3329
|
+
{
|
|
3330
|
+
viewMonth,
|
|
3331
|
+
isDayDisabled,
|
|
3332
|
+
cellState,
|
|
3333
|
+
onSelectDay: commitValue,
|
|
3334
|
+
onFocusDay: handleFocusDay,
|
|
3335
|
+
initialFocusDate: selected ?? /* @__PURE__ */ new Date()
|
|
3336
|
+
}
|
|
3337
|
+
)
|
|
3338
|
+
] });
|
|
3339
|
+
}
|
|
3340
|
+
);
|
|
3341
|
+
Calendar.displayName = "Calendar";
|
|
3342
|
+
var displayFormat = new Intl.DateTimeFormat(void 0, { dateStyle: "medium" });
|
|
3343
|
+
var DatePicker = React52.forwardRef(
|
|
3344
|
+
function DatePicker2({
|
|
3345
|
+
value,
|
|
3346
|
+
defaultValue,
|
|
3347
|
+
onChange,
|
|
3348
|
+
min,
|
|
3349
|
+
max,
|
|
3350
|
+
disabledDates,
|
|
3351
|
+
placeholder = "Pick a date\u2026",
|
|
3352
|
+
disabled = false,
|
|
3353
|
+
error = false,
|
|
3354
|
+
required = false,
|
|
3355
|
+
className,
|
|
3356
|
+
id,
|
|
3357
|
+
name,
|
|
3358
|
+
"aria-describedby": describedBy
|
|
3359
|
+
}, ref) {
|
|
3360
|
+
const controlled = value !== void 0;
|
|
3361
|
+
const [internalValue, setInternalValue] = React52.useState(defaultValue);
|
|
3362
|
+
const selected = controlled ? value : internalValue;
|
|
3363
|
+
const [open, setOpen] = React52.useState(false);
|
|
3364
|
+
const containerRef = React52.useRef(null);
|
|
3365
|
+
const triggerRef = React52.useRef(null);
|
|
3366
|
+
const setTriggerRef = (node) => {
|
|
3367
|
+
triggerRef.current = node;
|
|
3368
|
+
if (typeof ref === "function") ref(node);
|
|
3369
|
+
else if (ref) ref.current = node;
|
|
3370
|
+
};
|
|
3371
|
+
const commitValue = (d) => {
|
|
3372
|
+
if (!controlled) setInternalValue(d);
|
|
3373
|
+
onChange?.(d);
|
|
3374
|
+
};
|
|
3375
|
+
const closePopup = React52.useCallback(() => setOpen(false), []);
|
|
3376
|
+
const openPopup = () => {
|
|
3377
|
+
if (!disabled) setOpen(true);
|
|
3378
|
+
};
|
|
3379
|
+
const handleCalendarChange = (d) => {
|
|
3380
|
+
commitValue(d);
|
|
3381
|
+
closePopup();
|
|
3382
|
+
triggerRef.current?.focus();
|
|
3383
|
+
};
|
|
3384
|
+
React52.useEffect(() => {
|
|
3385
|
+
if (!open) return;
|
|
3386
|
+
const onDown = (e) => {
|
|
3387
|
+
if (containerRef.current && !containerRef.current.contains(e.target)) closePopup();
|
|
3388
|
+
};
|
|
3389
|
+
document.addEventListener("mousedown", onDown);
|
|
3390
|
+
return () => document.removeEventListener("mousedown", onDown);
|
|
3391
|
+
}, [open, closePopup]);
|
|
3392
|
+
const onTriggerKeyDown = (e) => {
|
|
3393
|
+
if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
|
|
3394
|
+
e.preventDefault();
|
|
3395
|
+
openPopup();
|
|
3396
|
+
} else if (e.key === "Escape" && open) {
|
|
3397
|
+
e.preventDefault();
|
|
3398
|
+
closePopup();
|
|
3399
|
+
}
|
|
3400
|
+
};
|
|
3401
|
+
const onPanelKeyDown = (e) => {
|
|
3402
|
+
if (e.key === "Escape") {
|
|
3403
|
+
e.preventDefault();
|
|
3404
|
+
closePopup();
|
|
3405
|
+
triggerRef.current?.focus();
|
|
3406
|
+
}
|
|
3407
|
+
};
|
|
3408
|
+
return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: cn("relative inline-block w-full", className), children: [
|
|
3409
|
+
/* @__PURE__ */ jsxs(
|
|
3410
|
+
"button",
|
|
3411
|
+
{
|
|
3412
|
+
ref: setTriggerRef,
|
|
3413
|
+
type: "button",
|
|
3414
|
+
id,
|
|
3415
|
+
disabled,
|
|
3416
|
+
"aria-haspopup": "grid",
|
|
3417
|
+
"aria-expanded": open,
|
|
3418
|
+
"aria-invalid": error || void 0,
|
|
3419
|
+
"aria-required": required || void 0,
|
|
3420
|
+
"aria-describedby": describedBy,
|
|
3421
|
+
onClick: () => open ? closePopup() : openPopup(),
|
|
3422
|
+
onKeyDown: onTriggerKeyDown,
|
|
3423
|
+
className: cn(
|
|
3424
|
+
"w-full flex items-center justify-between gap-2 border rounded-kj-md bg-surface text-left text-sm",
|
|
3425
|
+
"pl-[0.85rem] pr-3 py-[calc(0.6rem*var(--kj-density,1))]",
|
|
3426
|
+
"transition-[border-color,box-shadow] duration-150",
|
|
3427
|
+
"focus:outline-none focus:border-ring focus:ring-[3px] focus:ring-ring/30",
|
|
3428
|
+
disabled && "bg-muted text-muted-foreground cursor-not-allowed",
|
|
3429
|
+
error ? "border-danger" : "border-input",
|
|
3430
|
+
!selected && "text-muted-foreground"
|
|
3431
|
+
),
|
|
3432
|
+
children: [
|
|
3433
|
+
/* @__PURE__ */ jsx("span", { children: selected ? displayFormat.format(selected) : placeholder }),
|
|
3434
|
+
/* @__PURE__ */ jsxs(
|
|
3435
|
+
"svg",
|
|
3436
|
+
{
|
|
3437
|
+
viewBox: "0 0 24 24",
|
|
3438
|
+
className: "h-4 w-4 text-muted-foreground shrink-0",
|
|
3439
|
+
fill: "none",
|
|
3440
|
+
stroke: "currentColor",
|
|
3441
|
+
strokeWidth: "2",
|
|
3442
|
+
strokeLinecap: "round",
|
|
3443
|
+
strokeLinejoin: "round",
|
|
3444
|
+
children: [
|
|
3445
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
|
|
3446
|
+
/* @__PURE__ */ jsx("path", { d: "M3 10h18M8 2v4M16 2v4" })
|
|
3447
|
+
]
|
|
3448
|
+
}
|
|
3449
|
+
)
|
|
3450
|
+
]
|
|
3451
|
+
}
|
|
3452
|
+
),
|
|
3453
|
+
name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: selected ? toISODateString(selected) : "" }),
|
|
3454
|
+
open && /* @__PURE__ */ jsx(
|
|
3455
|
+
"div",
|
|
3456
|
+
{
|
|
3457
|
+
onKeyDown: onPanelKeyDown,
|
|
3458
|
+
className: "absolute z-40 top-[calc(100%+6px)] left-0 bg-surface border border-border rounded-kj-md shadow-kj-lg p-3 animate-[kjpop_.12s_ease]",
|
|
3459
|
+
children: /* @__PURE__ */ jsx(Calendar, { value: selected, onChange: handleCalendarChange, min, max, disabledDates })
|
|
3460
|
+
}
|
|
3461
|
+
)
|
|
3462
|
+
] });
|
|
3463
|
+
}
|
|
3464
|
+
);
|
|
3465
|
+
DatePicker.displayName = "DatePicker";
|
|
3466
|
+
var DatePickerField = React52.forwardRef(
|
|
3467
|
+
function DatePickerField2({ label, hint, error, required, className, ...props }, ref) {
|
|
3468
|
+
return /* @__PURE__ */ jsx(FormField, { label, hint, error, required, className, children: /* @__PURE__ */ jsx(DatePicker, { ref, error: !!error, ...props }) });
|
|
3469
|
+
}
|
|
3470
|
+
);
|
|
3471
|
+
DatePickerField.displayName = "DatePickerField";
|
|
3472
|
+
function isBetweenExclusive(d, lo, hi) {
|
|
3473
|
+
const t = startOfDay(d).getTime();
|
|
3474
|
+
return t > startOfDay(lo).getTime() && t < startOfDay(hi).getTime();
|
|
3475
|
+
}
|
|
3476
|
+
var RangeCalendar = React52.forwardRef(
|
|
3477
|
+
function RangeCalendar2({ value, defaultValue, onChange, month, defaultMonth, onMonthChange, min, max, disabledDates, className }, ref) {
|
|
3478
|
+
const controlled = value !== void 0;
|
|
3479
|
+
const [internalValue, setInternalValue] = React52.useState(defaultValue ?? {});
|
|
3480
|
+
const range2 = controlled ? value : internalValue;
|
|
3481
|
+
const monthControlled = month !== void 0;
|
|
3482
|
+
const [internalMonth, setInternalMonth] = React52.useState(
|
|
3483
|
+
() => startOfMonth(month ?? defaultMonth ?? range2.start ?? /* @__PURE__ */ new Date())
|
|
3484
|
+
);
|
|
3485
|
+
const leftMonth = monthControlled ? startOfMonth(month) : internalMonth;
|
|
3486
|
+
const rightMonth = addMonths(leftMonth, 1);
|
|
3487
|
+
const setViewMonth = (next) => {
|
|
3488
|
+
const normalized = startOfMonth(next);
|
|
3489
|
+
if (!monthControlled) setInternalMonth(normalized);
|
|
3490
|
+
onMonthChange?.(normalized);
|
|
3491
|
+
};
|
|
3492
|
+
const isDayDisabled = React52.useCallback(
|
|
3493
|
+
(d) => {
|
|
3494
|
+
if (min && d < startOfDay(min)) return true;
|
|
3495
|
+
if (max && d > startOfDay(max)) return true;
|
|
3496
|
+
return disabledDates?.(d) ?? false;
|
|
3497
|
+
},
|
|
3498
|
+
[min, max, disabledDates]
|
|
3499
|
+
);
|
|
3500
|
+
const [pendingStart, setPendingStart] = React52.useState(void 0);
|
|
3501
|
+
const [hoverDate, setHoverDate] = React52.useState(void 0);
|
|
3502
|
+
const commitRange = (next) => {
|
|
3503
|
+
if (!controlled) setInternalValue(next);
|
|
3504
|
+
onChange?.(next);
|
|
3505
|
+
};
|
|
3506
|
+
const handleSelectDay = (d) => {
|
|
3507
|
+
const day = startOfDay(d);
|
|
3508
|
+
if (pendingStart === void 0) {
|
|
3509
|
+
setPendingStart(day);
|
|
3510
|
+
setHoverDate(void 0);
|
|
3511
|
+
return;
|
|
3512
|
+
}
|
|
3513
|
+
if (day < startOfDay(pendingStart)) {
|
|
3514
|
+
setPendingStart(day);
|
|
3515
|
+
setHoverDate(void 0);
|
|
3516
|
+
return;
|
|
3517
|
+
}
|
|
3518
|
+
commitRange({ start: pendingStart, end: day });
|
|
3519
|
+
setPendingStart(void 0);
|
|
3520
|
+
setHoverDate(void 0);
|
|
3521
|
+
};
|
|
3522
|
+
const today = /* @__PURE__ */ new Date();
|
|
3523
|
+
const previewEnd = pendingStart !== void 0 ? hoverDate : void 0;
|
|
3524
|
+
const cellState = (d) => {
|
|
3525
|
+
const isToday = isSameDay(d, today);
|
|
3526
|
+
if (pendingStart !== void 0) {
|
|
3527
|
+
if (previewEnd === void 0) {
|
|
3528
|
+
return { rangeStart: isSameDay(d, pendingStart), today: isToday };
|
|
3529
|
+
}
|
|
3530
|
+
if (startOfDay(previewEnd) < startOfDay(pendingStart)) {
|
|
3531
|
+
return { rangeStart: isSameDay(d, previewEnd), today: isToday };
|
|
3532
|
+
}
|
|
3533
|
+
return {
|
|
3534
|
+
rangeStart: isSameDay(d, pendingStart),
|
|
3535
|
+
rangeEnd: isSameDay(d, previewEnd),
|
|
3536
|
+
inRange: isBetweenExclusive(d, pendingStart, previewEnd),
|
|
3537
|
+
today: isToday
|
|
3538
|
+
};
|
|
3539
|
+
}
|
|
3540
|
+
if (range2.start && range2.end) {
|
|
3541
|
+
return {
|
|
3542
|
+
rangeStart: isSameDay(d, range2.start),
|
|
3543
|
+
rangeEnd: isSameDay(d, range2.end),
|
|
3544
|
+
inRange: isBetweenExclusive(d, range2.start, range2.end),
|
|
3545
|
+
today: isToday
|
|
3546
|
+
};
|
|
3547
|
+
}
|
|
3548
|
+
return { rangeStart: range2.start ? isSameDay(d, range2.start) : false, today: isToday };
|
|
3549
|
+
};
|
|
3550
|
+
const inLockedRange = (d) => d.getMonth() === leftMonth.getMonth() && d.getFullYear() === leftMonth.getFullYear() || d.getMonth() === rightMonth.getMonth() && d.getFullYear() === rightMonth.getFullYear();
|
|
3551
|
+
const handleFocusDay = (d) => {
|
|
3552
|
+
if (pendingStart !== void 0) setHoverDate(d);
|
|
3553
|
+
if (!inLockedRange(d)) setViewMonth(d);
|
|
3554
|
+
};
|
|
3555
|
+
const navButton = (direction) => /* @__PURE__ */ jsx(
|
|
3556
|
+
"button",
|
|
3557
|
+
{
|
|
3558
|
+
type: "button",
|
|
3559
|
+
"aria-label": direction === "prev" ? "Previous month" : "Next month",
|
|
3560
|
+
onClick: () => setViewMonth(addMonths(leftMonth, direction === "prev" ? -1 : 1)),
|
|
3561
|
+
className: "p-1.5 rounded-kj-sm text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
3562
|
+
children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: direction === "prev" ? /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" }) : /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }) })
|
|
3563
|
+
}
|
|
3564
|
+
);
|
|
3565
|
+
return /* @__PURE__ */ jsxs("div", { ref, className: cn("flex gap-6 select-none", className), children: [
|
|
3566
|
+
/* @__PURE__ */ jsxs("div", { className: "w-[280px]", children: [
|
|
3567
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
3568
|
+
navButton("prev"),
|
|
3569
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: monthLabelFormat.format(leftMonth) }),
|
|
3570
|
+
/* @__PURE__ */ jsx("span", { className: "w-[26px]", "aria-hidden": "true" })
|
|
3571
|
+
] }),
|
|
3572
|
+
/* @__PURE__ */ jsx(
|
|
3573
|
+
CalendarGrid,
|
|
3574
|
+
{
|
|
3575
|
+
viewMonth: leftMonth,
|
|
3576
|
+
isDayDisabled,
|
|
3577
|
+
cellState,
|
|
3578
|
+
onSelectDay: handleSelectDay,
|
|
3579
|
+
onFocusDay: handleFocusDay,
|
|
3580
|
+
initialFocusDate: pendingStart ?? range2.start ?? /* @__PURE__ */ new Date()
|
|
3581
|
+
}
|
|
3582
|
+
)
|
|
3583
|
+
] }),
|
|
3584
|
+
/* @__PURE__ */ jsxs("div", { className: "w-[280px]", children: [
|
|
3585
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
3586
|
+
/* @__PURE__ */ jsx("span", { className: "w-[26px]", "aria-hidden": "true" }),
|
|
3587
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: monthLabelFormat.format(rightMonth) }),
|
|
3588
|
+
navButton("next")
|
|
3589
|
+
] }),
|
|
3590
|
+
/* @__PURE__ */ jsx(
|
|
3591
|
+
CalendarGrid,
|
|
3592
|
+
{
|
|
3593
|
+
viewMonth: rightMonth,
|
|
3594
|
+
isDayDisabled,
|
|
3595
|
+
cellState,
|
|
3596
|
+
onSelectDay: handleSelectDay,
|
|
3597
|
+
onFocusDay: handleFocusDay,
|
|
3598
|
+
initialFocusDate: pendingStart ?? range2.start ?? /* @__PURE__ */ new Date()
|
|
3599
|
+
}
|
|
3600
|
+
)
|
|
3601
|
+
] })
|
|
3602
|
+
] });
|
|
3603
|
+
}
|
|
3604
|
+
);
|
|
3605
|
+
RangeCalendar.displayName = "RangeCalendar";
|
|
3606
|
+
var displayFormat2 = new Intl.DateTimeFormat(void 0, { dateStyle: "medium" });
|
|
3607
|
+
function formatRangeLabel(range2) {
|
|
3608
|
+
if (!range2.start || !range2.end) return void 0;
|
|
3609
|
+
return `${displayFormat2.format(range2.start)} \u2013 ${displayFormat2.format(range2.end)}`;
|
|
3610
|
+
}
|
|
3611
|
+
var DateRangePicker = React52.forwardRef(
|
|
3612
|
+
function DateRangePicker2({
|
|
3613
|
+
value,
|
|
3614
|
+
defaultValue,
|
|
3615
|
+
onChange,
|
|
3616
|
+
min,
|
|
3617
|
+
max,
|
|
3618
|
+
disabledDates,
|
|
3619
|
+
placeholder = "Pick a date range\u2026",
|
|
3620
|
+
disabled = false,
|
|
3621
|
+
error = false,
|
|
3622
|
+
required = false,
|
|
3623
|
+
className,
|
|
3624
|
+
id,
|
|
3625
|
+
name,
|
|
3626
|
+
"aria-describedby": describedBy
|
|
3627
|
+
}, ref) {
|
|
3628
|
+
const controlled = value !== void 0;
|
|
3629
|
+
const [internalValue, setInternalValue] = React52.useState(defaultValue ?? {});
|
|
3630
|
+
const selected = controlled ? value : internalValue;
|
|
3631
|
+
const [open, setOpen] = React52.useState(false);
|
|
3632
|
+
const containerRef = React52.useRef(null);
|
|
3633
|
+
const triggerRef = React52.useRef(null);
|
|
3634
|
+
const setTriggerRef = (node) => {
|
|
3635
|
+
triggerRef.current = node;
|
|
3636
|
+
if (typeof ref === "function") ref(node);
|
|
3637
|
+
else if (ref) ref.current = node;
|
|
3638
|
+
};
|
|
3639
|
+
const commitValue = (r) => {
|
|
3640
|
+
if (!controlled) setInternalValue(r);
|
|
3641
|
+
onChange?.(r);
|
|
3642
|
+
};
|
|
3643
|
+
const closePopup = React52.useCallback(() => setOpen(false), []);
|
|
3644
|
+
const openPopup = () => {
|
|
3645
|
+
if (!disabled) setOpen(true);
|
|
3646
|
+
};
|
|
3647
|
+
const handleRangeChange = (r) => {
|
|
3648
|
+
commitValue(r);
|
|
3649
|
+
closePopup();
|
|
3650
|
+
triggerRef.current?.focus();
|
|
3651
|
+
};
|
|
3652
|
+
React52.useEffect(() => {
|
|
3653
|
+
if (!open) return;
|
|
3654
|
+
const onDown = (e) => {
|
|
3655
|
+
if (containerRef.current && !containerRef.current.contains(e.target)) closePopup();
|
|
3656
|
+
};
|
|
3657
|
+
document.addEventListener("mousedown", onDown);
|
|
3658
|
+
return () => document.removeEventListener("mousedown", onDown);
|
|
3659
|
+
}, [open, closePopup]);
|
|
3660
|
+
const onTriggerKeyDown = (e) => {
|
|
3661
|
+
if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
|
|
3662
|
+
e.preventDefault();
|
|
3663
|
+
openPopup();
|
|
3664
|
+
} else if (e.key === "Escape" && open) {
|
|
3665
|
+
e.preventDefault();
|
|
3666
|
+
closePopup();
|
|
3667
|
+
triggerRef.current?.focus();
|
|
3668
|
+
}
|
|
3669
|
+
};
|
|
3670
|
+
const onPanelKeyDown = (e) => {
|
|
3671
|
+
if (e.key === "Escape") {
|
|
3672
|
+
e.preventDefault();
|
|
3673
|
+
closePopup();
|
|
3674
|
+
triggerRef.current?.focus();
|
|
3675
|
+
}
|
|
3676
|
+
};
|
|
3677
|
+
const label = formatRangeLabel(selected);
|
|
3678
|
+
return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: cn("relative inline-block w-full", className), children: [
|
|
3679
|
+
/* @__PURE__ */ jsxs(
|
|
3680
|
+
"button",
|
|
3681
|
+
{
|
|
3682
|
+
ref: setTriggerRef,
|
|
3683
|
+
type: "button",
|
|
3684
|
+
id,
|
|
3685
|
+
disabled,
|
|
3686
|
+
"aria-haspopup": "dialog",
|
|
3687
|
+
"aria-expanded": open,
|
|
3688
|
+
"aria-invalid": error || void 0,
|
|
3689
|
+
"aria-required": required || void 0,
|
|
3690
|
+
"aria-describedby": describedBy,
|
|
3691
|
+
onClick: () => open ? closePopup() : openPopup(),
|
|
3692
|
+
onKeyDown: onTriggerKeyDown,
|
|
3693
|
+
className: cn(
|
|
3694
|
+
"w-full flex items-center justify-between gap-2 border rounded-kj-md bg-surface text-left text-sm",
|
|
3695
|
+
"pl-[0.85rem] pr-3 py-[calc(0.6rem*var(--kj-density,1))]",
|
|
3696
|
+
"transition-[border-color,box-shadow] duration-150",
|
|
3697
|
+
"focus:outline-none focus:border-ring focus:ring-[3px] focus:ring-ring/30",
|
|
3698
|
+
disabled && "bg-muted text-muted-foreground cursor-not-allowed",
|
|
3699
|
+
error ? "border-danger" : "border-input",
|
|
3700
|
+
!label && "text-muted-foreground"
|
|
3701
|
+
),
|
|
3702
|
+
children: [
|
|
3703
|
+
/* @__PURE__ */ jsx("span", { children: label ?? placeholder }),
|
|
3704
|
+
/* @__PURE__ */ jsxs(
|
|
3705
|
+
"svg",
|
|
3706
|
+
{
|
|
3707
|
+
viewBox: "0 0 24 24",
|
|
3708
|
+
className: "h-4 w-4 text-muted-foreground shrink-0",
|
|
3709
|
+
fill: "none",
|
|
3710
|
+
stroke: "currentColor",
|
|
3711
|
+
strokeWidth: "2",
|
|
3712
|
+
strokeLinecap: "round",
|
|
3713
|
+
strokeLinejoin: "round",
|
|
3714
|
+
children: [
|
|
3715
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
|
|
3716
|
+
/* @__PURE__ */ jsx("path", { d: "M3 10h18M8 2v4M16 2v4" })
|
|
3717
|
+
]
|
|
3718
|
+
}
|
|
3719
|
+
)
|
|
3720
|
+
]
|
|
3721
|
+
}
|
|
3722
|
+
),
|
|
3723
|
+
name && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3724
|
+
/* @__PURE__ */ jsx("input", { type: "hidden", name: `${name}Start`, value: selected.start ? toISODateString(selected.start) : "" }),
|
|
3725
|
+
/* @__PURE__ */ jsx("input", { type: "hidden", name: `${name}End`, value: selected.end ? toISODateString(selected.end) : "" })
|
|
3726
|
+
] }),
|
|
3727
|
+
open && /* @__PURE__ */ jsx(
|
|
3728
|
+
"div",
|
|
3729
|
+
{
|
|
3730
|
+
onKeyDown: onPanelKeyDown,
|
|
3731
|
+
className: "absolute z-40 top-[calc(100%+6px)] left-0 bg-surface border border-border rounded-kj-md shadow-kj-lg p-3 animate-[kjpop_.12s_ease]",
|
|
3732
|
+
children: /* @__PURE__ */ jsx(RangeCalendar, { value: selected, onChange: handleRangeChange, min, max, disabledDates })
|
|
3733
|
+
}
|
|
3734
|
+
)
|
|
3735
|
+
] });
|
|
3736
|
+
}
|
|
3737
|
+
);
|
|
3738
|
+
DateRangePicker.displayName = "DateRangePicker";
|
|
3739
|
+
var DateRangePickerField = React52.forwardRef(
|
|
3740
|
+
function DateRangePickerField2({ label, hint, error, required, className, ...props }, ref) {
|
|
3741
|
+
return /* @__PURE__ */ jsx(FormField, { label, hint, error, required, className, children: /* @__PURE__ */ jsx(DateRangePicker, { ref, error: !!error, ...props }) });
|
|
3742
|
+
}
|
|
3743
|
+
);
|
|
3744
|
+
DateRangePickerField.displayName = "DateRangePickerField";
|
|
3745
|
+
var Dropzone = React52.forwardRef(
|
|
3746
|
+
function Dropzone2({
|
|
3747
|
+
onFiles,
|
|
3748
|
+
accept,
|
|
3749
|
+
multiple = true,
|
|
3750
|
+
disabled = false,
|
|
3751
|
+
className,
|
|
3752
|
+
children,
|
|
3753
|
+
id,
|
|
3754
|
+
"aria-describedby": describedBy,
|
|
3755
|
+
"aria-invalid": ariaInvalid,
|
|
3756
|
+
"aria-required": ariaRequired
|
|
3757
|
+
}, ref) {
|
|
3758
|
+
const inputRef = React52.useRef(null);
|
|
3759
|
+
const [dragActive, setDragActive] = React52.useState(false);
|
|
3760
|
+
const dragCounter = React52.useRef(0);
|
|
3761
|
+
const invalid = ariaInvalid === true || ariaInvalid === "true";
|
|
3762
|
+
const openPicker = () => {
|
|
3763
|
+
if (!disabled) inputRef.current?.click();
|
|
3764
|
+
};
|
|
3765
|
+
const handleInputChange = (e) => {
|
|
3766
|
+
const files = e.target.files ? Array.from(e.target.files) : [];
|
|
3767
|
+
if (files.length > 0) onFiles(files);
|
|
3768
|
+
e.target.value = "";
|
|
3769
|
+
};
|
|
3770
|
+
const handleDragEnter = (e) => {
|
|
3771
|
+
e.preventDefault();
|
|
3772
|
+
if (disabled) return;
|
|
3773
|
+
dragCounter.current += 1;
|
|
3774
|
+
setDragActive(true);
|
|
3775
|
+
};
|
|
3776
|
+
const handleDragOver = (e) => {
|
|
3777
|
+
e.preventDefault();
|
|
3778
|
+
};
|
|
3779
|
+
const handleDragLeave = (e) => {
|
|
3780
|
+
e.preventDefault();
|
|
3781
|
+
if (disabled) return;
|
|
3782
|
+
dragCounter.current -= 1;
|
|
3783
|
+
if (dragCounter.current <= 0) {
|
|
3784
|
+
dragCounter.current = 0;
|
|
3785
|
+
setDragActive(false);
|
|
3786
|
+
}
|
|
3787
|
+
};
|
|
3788
|
+
const handleDrop = (e) => {
|
|
3789
|
+
e.preventDefault();
|
|
3790
|
+
dragCounter.current = 0;
|
|
3791
|
+
setDragActive(false);
|
|
3792
|
+
if (disabled) return;
|
|
3793
|
+
const files = e.dataTransfer?.files ? Array.from(e.dataTransfer.files) : [];
|
|
3794
|
+
if (files.length > 0) onFiles(files);
|
|
3795
|
+
};
|
|
3796
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("w-full", className), children: [
|
|
3797
|
+
/* @__PURE__ */ jsx(
|
|
3798
|
+
"button",
|
|
3799
|
+
{
|
|
3800
|
+
ref,
|
|
3801
|
+
type: "button",
|
|
3802
|
+
id,
|
|
3803
|
+
disabled,
|
|
3804
|
+
"aria-describedby": describedBy,
|
|
3805
|
+
"aria-invalid": ariaInvalid,
|
|
3806
|
+
"aria-required": ariaRequired,
|
|
3807
|
+
"data-drag-active": dragActive || void 0,
|
|
3808
|
+
onClick: openPicker,
|
|
3809
|
+
onDragEnter: handleDragEnter,
|
|
3810
|
+
onDragOver: handleDragOver,
|
|
3811
|
+
onDragLeave: handleDragLeave,
|
|
3812
|
+
onDrop: handleDrop,
|
|
3813
|
+
className: cn(
|
|
3814
|
+
"w-full flex flex-col items-center justify-center gap-2 rounded-kj-md border-2 border-dashed px-6 py-8 text-center text-sm transition-colors",
|
|
3815
|
+
"focus:outline-none focus:ring-[3px] focus:ring-ring/30",
|
|
3816
|
+
disabled ? "border-input bg-muted text-muted-foreground cursor-not-allowed" : dragActive ? "border-primary bg-primary/5 text-foreground cursor-pointer" : invalid ? "border-danger bg-surface text-muted-foreground hover:text-foreground cursor-pointer" : "border-input bg-surface text-muted-foreground hover:border-primary hover:text-foreground cursor-pointer"
|
|
3817
|
+
),
|
|
3818
|
+
children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3819
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", className: "h-7 w-7", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
3820
|
+
/* @__PURE__ */ jsx("path", { d: "M12 16V4M7 9l5-5 5 5" }),
|
|
3821
|
+
/* @__PURE__ */ jsx("path", { d: "M20 16v3a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-3" })
|
|
3822
|
+
] }),
|
|
3823
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
3824
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: "Drag and drop files here" }),
|
|
3825
|
+
", or click to browse"
|
|
3826
|
+
] })
|
|
3827
|
+
] })
|
|
3828
|
+
}
|
|
3829
|
+
),
|
|
3830
|
+
/* @__PURE__ */ jsx(
|
|
3831
|
+
"input",
|
|
3832
|
+
{
|
|
3833
|
+
ref: inputRef,
|
|
3834
|
+
type: "file",
|
|
3835
|
+
accept,
|
|
3836
|
+
multiple,
|
|
3837
|
+
disabled,
|
|
3838
|
+
onChange: handleInputChange,
|
|
3839
|
+
className: "sr-only",
|
|
3840
|
+
tabIndex: -1,
|
|
3841
|
+
"aria-hidden": "true"
|
|
3842
|
+
}
|
|
3843
|
+
)
|
|
3844
|
+
] });
|
|
3845
|
+
}
|
|
3846
|
+
);
|
|
3847
|
+
Dropzone.displayName = "Dropzone";
|
|
3848
|
+
|
|
3849
|
+
// src/components/file-upload-internals.ts
|
|
3850
|
+
var KB = 1024;
|
|
3851
|
+
function formatBytes(bytes) {
|
|
3852
|
+
if (bytes < KB) return `${bytes} B`;
|
|
3853
|
+
const units = ["KB", "MB", "GB", "TB"];
|
|
3854
|
+
let value = bytes / KB;
|
|
3855
|
+
let unitIndex = 0;
|
|
3856
|
+
while (value >= KB && unitIndex < units.length - 1) {
|
|
3857
|
+
value /= KB;
|
|
3858
|
+
unitIndex++;
|
|
3859
|
+
}
|
|
3860
|
+
return `${value.toFixed(1)} ${units[unitIndex]}`;
|
|
3861
|
+
}
|
|
3862
|
+
function matchesAccept(file, accept) {
|
|
3863
|
+
if (!accept || accept.trim() === "") return true;
|
|
3864
|
+
const tokens = accept.split(",").map((t) => t.trim()).filter(Boolean);
|
|
3865
|
+
if (tokens.length === 0) return true;
|
|
3866
|
+
const fileType = file.type.toLowerCase();
|
|
3867
|
+
const fileName = file.name.toLowerCase();
|
|
3868
|
+
return tokens.some((token) => {
|
|
3869
|
+
const t = token.toLowerCase();
|
|
3870
|
+
if (t.startsWith(".")) return fileName.endsWith(t);
|
|
3871
|
+
if (t.endsWith("/*")) return fileType.startsWith(t.slice(0, -1));
|
|
3872
|
+
return fileType === t;
|
|
3873
|
+
});
|
|
3874
|
+
}
|
|
3875
|
+
var idCounter = 0;
|
|
3876
|
+
function generateId() {
|
|
3877
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
3878
|
+
return crypto.randomUUID();
|
|
3879
|
+
}
|
|
3880
|
+
idCounter += 1;
|
|
3881
|
+
return `file-${Date.now().toString(36)}-${idCounter}-${Math.random().toString(36).slice(2, 8)}`;
|
|
3882
|
+
}
|
|
3883
|
+
function validateFiles(files, { accept, maxSize, maxFiles, existingCount = 0, multiple = true }) {
|
|
3884
|
+
const accepted = [];
|
|
3885
|
+
const rejected = [];
|
|
3886
|
+
const effectiveExisting = multiple ? existingCount : 0;
|
|
3887
|
+
for (const file of files) {
|
|
3888
|
+
if (!matchesAccept(file, accept)) {
|
|
3889
|
+
rejected.push({ file, reason: "type" });
|
|
3890
|
+
continue;
|
|
3891
|
+
}
|
|
3892
|
+
if (maxSize != null && file.size > maxSize) {
|
|
3893
|
+
rejected.push({ file, reason: "size" });
|
|
3894
|
+
continue;
|
|
3895
|
+
}
|
|
3896
|
+
if (!multiple) {
|
|
3897
|
+
if (accepted.length === 0) accepted.push(file);
|
|
3898
|
+
else rejected.push({ file, reason: "count" });
|
|
3899
|
+
continue;
|
|
3900
|
+
}
|
|
3901
|
+
const capacity = maxFiles != null ? maxFiles - effectiveExisting - accepted.length : Infinity;
|
|
3902
|
+
if (capacity <= 0) {
|
|
3903
|
+
rejected.push({ file, reason: "count" });
|
|
3904
|
+
continue;
|
|
3905
|
+
}
|
|
3906
|
+
accepted.push(file);
|
|
3907
|
+
}
|
|
3908
|
+
return { accepted, rejected };
|
|
3909
|
+
}
|
|
3910
|
+
function FileIcon({ className }) {
|
|
3911
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", className, fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
3912
|
+
/* @__PURE__ */ jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
|
|
3913
|
+
/* @__PURE__ */ jsx("path", { d: "M14 2v6h6" })
|
|
3914
|
+
] });
|
|
3915
|
+
}
|
|
3916
|
+
var FileUpload = React52.forwardRef(
|
|
3917
|
+
function FileUpload2({
|
|
3918
|
+
value,
|
|
3919
|
+
defaultValue,
|
|
3920
|
+
onChange,
|
|
3921
|
+
onReject,
|
|
3922
|
+
accept,
|
|
3923
|
+
maxSize,
|
|
3924
|
+
maxFiles,
|
|
3925
|
+
multiple = true,
|
|
3926
|
+
disabled = false,
|
|
3927
|
+
error = false,
|
|
3928
|
+
required = false,
|
|
3929
|
+
className,
|
|
3930
|
+
id,
|
|
3931
|
+
"aria-describedby": describedBy
|
|
3932
|
+
}, ref) {
|
|
3933
|
+
const controlled = value !== void 0;
|
|
3934
|
+
const [internalItems, setInternalItems] = React52.useState(defaultValue ?? []);
|
|
3935
|
+
const items = controlled ? value : internalItems;
|
|
3936
|
+
const commit = (next) => {
|
|
3937
|
+
if (!controlled) setInternalItems(next);
|
|
3938
|
+
onChange?.(next);
|
|
3939
|
+
};
|
|
3940
|
+
const handleFiles = (files) => {
|
|
3941
|
+
const { accepted, rejected } = validateFiles(files, {
|
|
3942
|
+
accept,
|
|
3943
|
+
maxSize,
|
|
3944
|
+
maxFiles,
|
|
3945
|
+
existingCount: items.length,
|
|
3946
|
+
multiple
|
|
3947
|
+
});
|
|
3948
|
+
if (rejected.length > 0) onReject?.(rejected);
|
|
3949
|
+
if (accepted.length === 0) return;
|
|
3950
|
+
const newItems = accepted.map((file) => ({
|
|
3951
|
+
id: generateId(),
|
|
3952
|
+
file,
|
|
3953
|
+
status: "pending"
|
|
3954
|
+
}));
|
|
3955
|
+
commit(multiple ? [...items, ...newItems] : newItems.slice(0, 1));
|
|
3956
|
+
};
|
|
3957
|
+
const removeItem = (itemId) => {
|
|
3958
|
+
commit(items.filter((it) => it.id !== itemId));
|
|
3959
|
+
};
|
|
3960
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("w-full flex flex-col gap-3", className), children: [
|
|
3961
|
+
/* @__PURE__ */ jsx(
|
|
3962
|
+
Dropzone,
|
|
3963
|
+
{
|
|
3964
|
+
ref,
|
|
3965
|
+
onFiles: handleFiles,
|
|
3966
|
+
accept,
|
|
3967
|
+
multiple,
|
|
3968
|
+
disabled,
|
|
3969
|
+
id,
|
|
3970
|
+
"aria-describedby": describedBy,
|
|
3971
|
+
"aria-invalid": error || void 0,
|
|
3972
|
+
"aria-required": required || void 0
|
|
3973
|
+
}
|
|
3974
|
+
),
|
|
3975
|
+
items.length > 0 && /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-2", children: items.map((item) => /* @__PURE__ */ jsxs(
|
|
3976
|
+
"li",
|
|
3977
|
+
{
|
|
3978
|
+
className: "flex items-center gap-3 rounded-kj-md border border-border bg-surface px-3 py-2",
|
|
3979
|
+
children: [
|
|
3980
|
+
/* @__PURE__ */ jsx(FileIcon, { className: "h-5 w-5 shrink-0 text-muted-foreground" }),
|
|
3981
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
3982
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
3983
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-sm text-foreground", children: item.file.name }),
|
|
3984
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: formatBytes(item.file.size) })
|
|
3985
|
+
] }),
|
|
3986
|
+
item.status === "uploading" && item.progress != null && /* @__PURE__ */ jsxs("div", { className: "mt-1.5 flex items-center gap-2", children: [
|
|
3987
|
+
/* @__PURE__ */ jsx(Progress, { value: item.progress, className: "h-1.5" }),
|
|
3988
|
+
/* @__PURE__ */ jsxs("span", { className: "shrink-0 text-xs text-muted-foreground", children: [
|
|
3989
|
+
Math.round(item.progress),
|
|
3990
|
+
"%"
|
|
3991
|
+
] })
|
|
3992
|
+
] }),
|
|
3993
|
+
item.status === "success" && /* @__PURE__ */ jsx("span", { className: "mt-0.5 block text-xs text-success", children: "Uploaded" }),
|
|
3994
|
+
item.status === "error" && /* @__PURE__ */ jsx("span", { className: "mt-0.5 block text-xs text-danger", children: item.error ?? "Upload failed" })
|
|
3995
|
+
] }),
|
|
3996
|
+
!disabled && /* @__PURE__ */ jsx(
|
|
3997
|
+
"button",
|
|
3998
|
+
{
|
|
3999
|
+
type: "button",
|
|
4000
|
+
"aria-label": `Remove ${item.file.name}`,
|
|
4001
|
+
onClick: () => removeItem(item.id),
|
|
4002
|
+
className: "shrink-0 rounded-kj-sm p-1 text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
4003
|
+
children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) })
|
|
4004
|
+
}
|
|
4005
|
+
)
|
|
4006
|
+
]
|
|
4007
|
+
},
|
|
4008
|
+
item.id
|
|
4009
|
+
)) })
|
|
4010
|
+
] });
|
|
4011
|
+
}
|
|
4012
|
+
);
|
|
4013
|
+
FileUpload.displayName = "FileUpload";
|
|
4014
|
+
var FileUploadField = React52.forwardRef(
|
|
4015
|
+
function FileUploadField2({ label, hint, error, required, className, ...props }, ref) {
|
|
4016
|
+
return /* @__PURE__ */ jsx(FormField, { label, hint, error, required, className, children: /* @__PURE__ */ jsx(FileUpload, { ref, error: !!error, ...props }) });
|
|
4017
|
+
}
|
|
4018
|
+
);
|
|
4019
|
+
FileUploadField.displayName = "FileUploadField";
|
|
4020
|
+
var TimelineContext = React52.createContext(null);
|
|
4021
|
+
var TimelineItemContext = React52.createContext(null);
|
|
4022
|
+
var Timeline = React52.forwardRef(
|
|
4023
|
+
({ align = "left", className, children, ...props }, ref) => {
|
|
4024
|
+
const validChildren = React52.Children.toArray(children).filter(React52.isValidElement);
|
|
4025
|
+
const count = validChildren.length;
|
|
4026
|
+
const childrenWithIndex = validChildren.map((child, index) => {
|
|
4027
|
+
return React52.cloneElement(child, {
|
|
4028
|
+
index
|
|
4029
|
+
});
|
|
4030
|
+
});
|
|
4031
|
+
return /* @__PURE__ */ jsx(TimelineContext.Provider, { value: { align, count }, children: /* @__PURE__ */ jsx(
|
|
4032
|
+
"div",
|
|
4033
|
+
{
|
|
4034
|
+
ref,
|
|
4035
|
+
role: "list",
|
|
4036
|
+
className: cn("flex flex-col gap-6 w-full", className),
|
|
4037
|
+
...props,
|
|
4038
|
+
children: childrenWithIndex
|
|
4039
|
+
}
|
|
4040
|
+
) });
|
|
4041
|
+
}
|
|
4042
|
+
);
|
|
4043
|
+
Timeline.displayName = "Timeline";
|
|
4044
|
+
var TimelineItem = React52.forwardRef(
|
|
4045
|
+
({ className, index = 0, children, ...props }, ref) => {
|
|
4046
|
+
const ctx = React52.useContext(TimelineContext);
|
|
4047
|
+
if (!ctx) throw new Error("TimelineItem must be used within a Timeline");
|
|
4048
|
+
const { align, count } = ctx;
|
|
4049
|
+
const isEven = index % 2 === 0;
|
|
4050
|
+
const isLast = index === count - 1;
|
|
4051
|
+
const gridClass = cn(
|
|
4052
|
+
"grid w-full gap-4 items-start group/timeline-item",
|
|
4053
|
+
align === "left" && "grid-cols-[auto_1fr]",
|
|
4054
|
+
align === "right" && "grid-cols-[1fr_auto] text-right",
|
|
4055
|
+
align === "alternate" && "md:grid-cols-[1fr_auto_1fr] grid-cols-[auto_1fr]"
|
|
4056
|
+
);
|
|
4057
|
+
return /* @__PURE__ */ jsx(TimelineItemContext.Provider, { value: { isEven, align, isLast }, children: /* @__PURE__ */ jsx(
|
|
4058
|
+
"div",
|
|
4059
|
+
{
|
|
4060
|
+
ref,
|
|
4061
|
+
role: "listitem",
|
|
4062
|
+
className: cn(gridClass, className),
|
|
4063
|
+
...props,
|
|
4064
|
+
children
|
|
4065
|
+
}
|
|
4066
|
+
) });
|
|
4067
|
+
}
|
|
4068
|
+
);
|
|
4069
|
+
TimelineItem.displayName = "TimelineItem";
|
|
4070
|
+
var TimelineSeparator = React52.forwardRef(
|
|
4071
|
+
({ className, align: propAlign, ...props }, ref) => {
|
|
4072
|
+
const ctx = React52.useContext(TimelineItemContext);
|
|
4073
|
+
const align = propAlign ?? ctx?.align ?? "left";
|
|
4074
|
+
const separatorClass = cn(
|
|
4075
|
+
"flex flex-col items-center justify-self-center h-full min-h-[40px]",
|
|
4076
|
+
align === "alternate" && "md:col-start-2 col-start-1",
|
|
4077
|
+
align === "left" && "col-start-1",
|
|
4078
|
+
align === "right" && "col-start-2",
|
|
4079
|
+
className
|
|
4080
|
+
);
|
|
4081
|
+
return /* @__PURE__ */ jsx("div", { ref, className: separatorClass, ...props });
|
|
4082
|
+
}
|
|
4083
|
+
);
|
|
4084
|
+
TimelineSeparator.displayName = "TimelineSeparator";
|
|
4085
|
+
var TimelineConnector = React52.forwardRef(
|
|
4086
|
+
({ className, dashed, ...props }, ref) => {
|
|
4087
|
+
const ctx = React52.useContext(TimelineItemContext);
|
|
4088
|
+
const isLast = ctx?.isLast ?? false;
|
|
4089
|
+
const connectorClass = cn(
|
|
4090
|
+
"grow",
|
|
4091
|
+
isLast && "hidden",
|
|
4092
|
+
dashed ? "w-0 border-l border-dashed border-border" : "w-0.5 bg-border",
|
|
4093
|
+
className
|
|
4094
|
+
);
|
|
4095
|
+
return /* @__PURE__ */ jsx("div", { ref, className: connectorClass, ...props });
|
|
4096
|
+
}
|
|
4097
|
+
);
|
|
4098
|
+
TimelineConnector.displayName = "TimelineConnector";
|
|
4099
|
+
var TimelineDot = React52.forwardRef(
|
|
4100
|
+
({ className, variant = "default", size = "md", ...props }, ref) => {
|
|
4101
|
+
const sizeClasses2 = {
|
|
4102
|
+
sm: "w-2.5 h-2.5 rounded-full my-1.5",
|
|
4103
|
+
md: "w-4 h-4 rounded-full my-1 border-2 bg-background border-border",
|
|
4104
|
+
lg: "w-8 h-8 rounded-full border flex items-center justify-center bg-background text-[0.8rem]"
|
|
4105
|
+
};
|
|
4106
|
+
const variantClasses = {
|
|
4107
|
+
default: "border-border text-muted-foreground",
|
|
4108
|
+
primary: "border-primary bg-primary/5 text-primary",
|
|
4109
|
+
secondary: "border-secondary bg-secondary/5 text-secondary",
|
|
4110
|
+
success: "border-success bg-success-surface text-success",
|
|
4111
|
+
warning: "border-warning bg-warning-surface text-warning",
|
|
4112
|
+
danger: "border-danger bg-danger-surface text-danger"
|
|
4113
|
+
};
|
|
4114
|
+
return /* @__PURE__ */ jsx(
|
|
4115
|
+
"div",
|
|
4116
|
+
{
|
|
4117
|
+
ref,
|
|
4118
|
+
className: cn(
|
|
4119
|
+
"shrink-0 select-none",
|
|
4120
|
+
sizeClasses2[size],
|
|
4121
|
+
variantClasses[variant],
|
|
4122
|
+
className
|
|
4123
|
+
),
|
|
4124
|
+
...props
|
|
4125
|
+
}
|
|
4126
|
+
);
|
|
4127
|
+
}
|
|
4128
|
+
);
|
|
4129
|
+
TimelineDot.displayName = "TimelineDot";
|
|
4130
|
+
var TimelineContent = React52.forwardRef(
|
|
4131
|
+
({ className, isEven: propIsEven, align: propAlign, ...props }, ref) => {
|
|
4132
|
+
const ctx = React52.useContext(TimelineItemContext);
|
|
4133
|
+
const isEven = propIsEven ?? ctx?.isEven ?? false;
|
|
4134
|
+
const align = propAlign ?? ctx?.align ?? "left";
|
|
4135
|
+
const contentClass = cn(
|
|
4136
|
+
"pb-8 flex flex-col gap-1 w-full",
|
|
4137
|
+
align === "left" && "col-start-2 text-left",
|
|
4138
|
+
align === "right" && "col-start-1 text-right",
|
|
4139
|
+
align === "alternate" && (isEven ? "md:col-start-3 col-start-2 text-left" : "md:col-start-1 col-start-2 md:text-right text-left"),
|
|
4140
|
+
className
|
|
4141
|
+
);
|
|
4142
|
+
return /* @__PURE__ */ jsx("div", { ref, className: contentClass, ...props });
|
|
4143
|
+
}
|
|
4144
|
+
);
|
|
4145
|
+
TimelineContent.displayName = "TimelineContent";
|
|
4146
|
+
var TimelineTitle = React52.forwardRef(
|
|
4147
|
+
({ className, children, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
4148
|
+
"h4",
|
|
4149
|
+
{
|
|
4150
|
+
ref,
|
|
4151
|
+
className: cn("m-0 text-sm font-semibold tracking-[-0.01em] text-foreground", className),
|
|
4152
|
+
...props,
|
|
4153
|
+
children
|
|
4154
|
+
}
|
|
4155
|
+
)
|
|
4156
|
+
);
|
|
4157
|
+
TimelineTitle.displayName = "TimelineTitle";
|
|
4158
|
+
var TimelineTime = React52.forwardRef(
|
|
4159
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
4160
|
+
"time",
|
|
4161
|
+
{
|
|
4162
|
+
ref,
|
|
4163
|
+
className: cn("text-[0.75rem] text-muted-foreground font-normal", className),
|
|
4164
|
+
...props
|
|
4165
|
+
}
|
|
4166
|
+
)
|
|
4167
|
+
);
|
|
4168
|
+
TimelineTime.displayName = "TimelineTime";
|
|
4169
|
+
function useStepper({ initialStep = 0, stepsCount }) {
|
|
4170
|
+
const safeStepsCount = Math.max(1, stepsCount);
|
|
4171
|
+
const [activeStep, setActiveStep] = React52.useState(initialStep);
|
|
4172
|
+
const [completedSteps, setCompletedSteps] = React52.useState([]);
|
|
4173
|
+
const setStep = React52.useCallback((step) => {
|
|
4174
|
+
if (step >= 0 && step < safeStepsCount) {
|
|
4175
|
+
setActiveStep(step);
|
|
4176
|
+
}
|
|
4177
|
+
}, [safeStepsCount]);
|
|
4178
|
+
const nextStep = React52.useCallback(() => {
|
|
4179
|
+
setActiveStep((prev) => Math.min(prev + 1, safeStepsCount - 1));
|
|
4180
|
+
}, [safeStepsCount]);
|
|
4181
|
+
const prevStep = React52.useCallback(() => {
|
|
4182
|
+
setActiveStep((prev) => Math.max(prev - 1, 0));
|
|
4183
|
+
}, []);
|
|
4184
|
+
const completeStep = React52.useCallback((step) => {
|
|
4185
|
+
setCompletedSteps((prev) => {
|
|
4186
|
+
if (step < 0 || step >= safeStepsCount) return prev;
|
|
4187
|
+
if (prev.includes(step)) return prev;
|
|
4188
|
+
return [...prev, step].sort((a, b) => a - b);
|
|
4189
|
+
});
|
|
4190
|
+
}, [safeStepsCount]);
|
|
4191
|
+
const uncompleteStep = React52.useCallback((step) => {
|
|
4192
|
+
setCompletedSteps((prev) => prev.filter((s) => s !== step));
|
|
4193
|
+
}, []);
|
|
4194
|
+
const reset = React52.useCallback(() => {
|
|
4195
|
+
setActiveStep(initialStep);
|
|
4196
|
+
setCompletedSteps([]);
|
|
4197
|
+
}, [initialStep]);
|
|
4198
|
+
return {
|
|
4199
|
+
activeStep,
|
|
4200
|
+
completedSteps,
|
|
4201
|
+
setStep,
|
|
4202
|
+
nextStep,
|
|
4203
|
+
prevStep,
|
|
4204
|
+
completeStep,
|
|
4205
|
+
uncompleteStep,
|
|
4206
|
+
reset,
|
|
4207
|
+
isFirstStep: activeStep === 0,
|
|
4208
|
+
isLastStep: activeStep === safeStepsCount - 1
|
|
4209
|
+
};
|
|
4210
|
+
}
|
|
4211
|
+
var StepperContext = React52.createContext(null);
|
|
4212
|
+
var StepperItemContext = React52.createContext(null);
|
|
4213
|
+
var Stepper = React52.forwardRef(
|
|
4214
|
+
({ value, defaultValue, onValueChange, orientation = "horizontal", linear = true, children, className, ...props }, ref) => {
|
|
4215
|
+
const [localValue, setLocalValue] = React52.useState(defaultValue ?? 0);
|
|
4216
|
+
const isControlled = value !== void 0;
|
|
4217
|
+
const activeStep = isControlled ? value : localValue;
|
|
4218
|
+
const [steps, setSteps] = React52.useState([]);
|
|
4219
|
+
const [completedSteps, setCompletedSteps] = React52.useState([]);
|
|
4220
|
+
const registerStep = React52.useCallback((val) => {
|
|
4221
|
+
setSteps((prev) => prev.includes(val) ? prev : [...prev, val].sort((a, b) => a - b));
|
|
4222
|
+
}, []);
|
|
4223
|
+
const unregisterStep = React52.useCallback((val) => {
|
|
4224
|
+
setSteps((prev) => prev.filter((v) => v !== val));
|
|
4225
|
+
}, []);
|
|
4226
|
+
const setStep = React52.useCallback(
|
|
4227
|
+
(step) => {
|
|
4228
|
+
if (!isControlled) {
|
|
4229
|
+
setLocalValue(step);
|
|
4230
|
+
}
|
|
4231
|
+
onValueChange?.(step);
|
|
4232
|
+
if (linear) {
|
|
4233
|
+
setCompletedSteps((prev) => {
|
|
4234
|
+
const priorSteps = steps.filter((s) => s < step);
|
|
4235
|
+
const nextCompleted = Array.from(/* @__PURE__ */ new Set([...prev, ...priorSteps])).sort((a, b) => a - b);
|
|
4236
|
+
return nextCompleted;
|
|
4237
|
+
});
|
|
4238
|
+
}
|
|
4239
|
+
},
|
|
4240
|
+
[isControlled, onValueChange, linear, steps]
|
|
4241
|
+
);
|
|
4242
|
+
const contextValue = React52.useMemo(
|
|
4243
|
+
() => ({
|
|
4244
|
+
activeStep,
|
|
4245
|
+
orientation,
|
|
4246
|
+
linear,
|
|
4247
|
+
setStep,
|
|
4248
|
+
completedSteps,
|
|
4249
|
+
registerStep,
|
|
4250
|
+
unregisterStep,
|
|
4251
|
+
steps
|
|
4252
|
+
}),
|
|
4253
|
+
[activeStep, orientation, linear, setStep, completedSteps, registerStep, unregisterStep, steps]
|
|
4254
|
+
);
|
|
4255
|
+
return /* @__PURE__ */ jsx(StepperContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
|
|
4256
|
+
"div",
|
|
4257
|
+
{
|
|
4258
|
+
ref,
|
|
4259
|
+
className: cn(
|
|
4260
|
+
"w-full",
|
|
4261
|
+
orientation === "vertical" ? "flex flex-col gap-6" : "space-y-6",
|
|
4262
|
+
className
|
|
4263
|
+
),
|
|
4264
|
+
...props,
|
|
4265
|
+
children
|
|
4266
|
+
}
|
|
4267
|
+
) });
|
|
4268
|
+
}
|
|
4269
|
+
);
|
|
4270
|
+
Stepper.displayName = "Stepper";
|
|
4271
|
+
var StepperContent = React52.forwardRef(
|
|
4272
|
+
({ value, className, children, ...props }, ref) => {
|
|
4273
|
+
const ctx = React52.useContext(StepperContext);
|
|
4274
|
+
if (!ctx) throw new Error("StepperContent must be used within a Stepper");
|
|
4275
|
+
const isActive = ctx.activeStep === value;
|
|
4276
|
+
if (!isActive) return null;
|
|
4277
|
+
return /* @__PURE__ */ jsx(
|
|
4278
|
+
"div",
|
|
4279
|
+
{
|
|
4280
|
+
ref,
|
|
4281
|
+
role: "tabpanel",
|
|
4282
|
+
tabIndex: 0,
|
|
4283
|
+
className: cn("focus-visible:outline-none w-full", className),
|
|
4284
|
+
...props,
|
|
4285
|
+
children
|
|
4286
|
+
}
|
|
4287
|
+
);
|
|
4288
|
+
}
|
|
4289
|
+
);
|
|
4290
|
+
StepperContent.displayName = "StepperContent";
|
|
4291
|
+
var StepperList = React52.forwardRef(
|
|
4292
|
+
({ className, children, ...props }, ref) => {
|
|
4293
|
+
const ctx = React52.useContext(StepperContext);
|
|
4294
|
+
if (!ctx) throw new Error("StepperList must be used within a Stepper");
|
|
4295
|
+
return /* @__PURE__ */ jsx(
|
|
4296
|
+
"div",
|
|
4297
|
+
{
|
|
4298
|
+
ref,
|
|
4299
|
+
role: "tablist",
|
|
4300
|
+
"aria-orientation": ctx.orientation,
|
|
4301
|
+
className: cn(
|
|
4302
|
+
ctx.orientation === "horizontal" ? "flex items-center justify-between w-full relative gap-4" : "flex flex-col items-start gap-6 relative w-full",
|
|
4303
|
+
className
|
|
4304
|
+
),
|
|
4305
|
+
...props,
|
|
4306
|
+
children
|
|
4307
|
+
}
|
|
4308
|
+
);
|
|
4309
|
+
}
|
|
4310
|
+
);
|
|
4311
|
+
StepperList.displayName = "StepperList";
|
|
4312
|
+
var StepperItem = React52.forwardRef(
|
|
4313
|
+
({ value, disabled = false, className, children, ...props }, ref) => {
|
|
4314
|
+
const ctx = React52.useContext(StepperContext);
|
|
4315
|
+
if (!ctx) throw new Error("StepperItem must be used within a Stepper");
|
|
4316
|
+
const { registerStep, unregisterStep } = ctx;
|
|
4317
|
+
React52.useEffect(() => {
|
|
4318
|
+
registerStep(value);
|
|
4319
|
+
return () => unregisterStep(value);
|
|
4320
|
+
}, [value, registerStep, unregisterStep]);
|
|
4321
|
+
const isLast = ctx.steps[ctx.steps.length - 1] === value;
|
|
4322
|
+
return /* @__PURE__ */ jsx(StepperItemContext.Provider, { value: { value, disabled, isLast }, children: /* @__PURE__ */ jsx(
|
|
4323
|
+
"div",
|
|
4324
|
+
{
|
|
4325
|
+
ref,
|
|
4326
|
+
className: cn(
|
|
4327
|
+
"group relative flex items-center gap-3 z-10",
|
|
4328
|
+
ctx.orientation === "vertical" ? "flex items-start w-full" : "",
|
|
4329
|
+
className
|
|
4330
|
+
),
|
|
4331
|
+
...props,
|
|
4332
|
+
children
|
|
4333
|
+
}
|
|
4334
|
+
) });
|
|
4335
|
+
}
|
|
4336
|
+
);
|
|
4337
|
+
StepperItem.displayName = "StepperItem";
|
|
4338
|
+
var StepperSeparator = React52.forwardRef(
|
|
4339
|
+
({ className, ...props }, ref) => {
|
|
4340
|
+
const ctx = React52.useContext(StepperContext);
|
|
4341
|
+
if (!ctx) throw new Error("StepperSeparator must be used within a Stepper");
|
|
4342
|
+
return /* @__PURE__ */ jsx(
|
|
4343
|
+
"div",
|
|
4344
|
+
{
|
|
4345
|
+
ref,
|
|
4346
|
+
className: cn(
|
|
4347
|
+
ctx.orientation === "horizontal" ? "flex-1 h-[2px] bg-border transition-all duration-300" : "w-[2px] bg-border absolute left-[17px] top-8 bottom-0 -ml-px z-0",
|
|
4348
|
+
className
|
|
4349
|
+
),
|
|
4350
|
+
...props
|
|
4351
|
+
}
|
|
4352
|
+
);
|
|
4353
|
+
}
|
|
4354
|
+
);
|
|
4355
|
+
StepperSeparator.displayName = "StepperSeparator";
|
|
4356
|
+
var StepperTrigger = React52.forwardRef(
|
|
4357
|
+
({ className, children, onClick, onKeyDown, ...props }, ref) => {
|
|
4358
|
+
const ctx = React52.useContext(StepperContext);
|
|
4359
|
+
const itemCtx = React52.useContext(StepperItemContext);
|
|
4360
|
+
if (!ctx || !itemCtx) {
|
|
4361
|
+
throw new Error("StepperTrigger must be used within a StepperItem inside a Stepper");
|
|
4362
|
+
}
|
|
4363
|
+
const isCompleted = ctx.completedSteps.includes(itemCtx.value) || itemCtx.value < ctx.activeStep;
|
|
4364
|
+
const isActive = ctx.activeStep === itemCtx.value;
|
|
4365
|
+
const isClickable = !ctx.linear || isCompleted || isActive;
|
|
4366
|
+
const disabled = itemCtx.disabled || !isClickable;
|
|
4367
|
+
const handleClick = (e) => {
|
|
4368
|
+
if (disabled) {
|
|
4369
|
+
e.preventDefault();
|
|
4370
|
+
return;
|
|
4371
|
+
}
|
|
4372
|
+
ctx.setStep(itemCtx.value);
|
|
4373
|
+
onClick?.(e);
|
|
4374
|
+
};
|
|
4375
|
+
const handleKeyDown = (e) => {
|
|
4376
|
+
onKeyDown?.(e);
|
|
4377
|
+
const triggers = Array.from(
|
|
4378
|
+
e.currentTarget.closest('[role="tablist"]')?.querySelectorAll('[role="tab"]') || []
|
|
4379
|
+
);
|
|
4380
|
+
const currentIndex = triggers.indexOf(e.currentTarget);
|
|
4381
|
+
if (currentIndex === -1) return;
|
|
4382
|
+
let targetIndex = -1;
|
|
4383
|
+
if (e.key === "ArrowRight" || e.key === "ArrowDown") {
|
|
4384
|
+
targetIndex = Math.min(currentIndex + 1, triggers.length - 1);
|
|
4385
|
+
} else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
|
|
4386
|
+
targetIndex = Math.max(currentIndex - 1, 0);
|
|
4387
|
+
}
|
|
4388
|
+
if (targetIndex !== -1 && triggers[targetIndex]) {
|
|
4389
|
+
e.preventDefault();
|
|
4390
|
+
triggers[targetIndex].focus();
|
|
4391
|
+
}
|
|
4392
|
+
};
|
|
4393
|
+
return /* @__PURE__ */ jsx(
|
|
4394
|
+
"button",
|
|
4395
|
+
{
|
|
4396
|
+
ref,
|
|
4397
|
+
type: "button",
|
|
4398
|
+
role: "tab",
|
|
4399
|
+
"aria-selected": isActive,
|
|
4400
|
+
"aria-disabled": disabled,
|
|
4401
|
+
tabIndex: isActive ? 0 : -1,
|
|
4402
|
+
onClick: handleClick,
|
|
4403
|
+
onKeyDown: handleKeyDown,
|
|
4404
|
+
className: cn(
|
|
4405
|
+
"flex items-center gap-3 bg-transparent border-0 p-0 text-left cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary rounded-md disabled:cursor-not-allowed disabled:opacity-50",
|
|
4406
|
+
ctx.orientation === "vertical" ? "w-full" : "",
|
|
4407
|
+
className
|
|
4408
|
+
),
|
|
4409
|
+
...props,
|
|
4410
|
+
children
|
|
4411
|
+
}
|
|
4412
|
+
);
|
|
4413
|
+
}
|
|
4414
|
+
);
|
|
4415
|
+
StepperTrigger.displayName = "StepperTrigger";
|
|
4416
|
+
var StepperIndicator = React52.forwardRef(
|
|
4417
|
+
({ className, children, ...props }, ref) => {
|
|
4418
|
+
const ctx = React52.useContext(StepperContext);
|
|
4419
|
+
const itemCtx = React52.useContext(StepperItemContext);
|
|
4420
|
+
if (!ctx || !itemCtx) {
|
|
4421
|
+
throw new Error("StepperIndicator must be used within a StepperItem inside a Stepper");
|
|
4422
|
+
}
|
|
4423
|
+
const isCompleted = ctx.completedSteps.includes(itemCtx.value) || itemCtx.value < ctx.activeStep;
|
|
4424
|
+
const isActive = ctx.activeStep === itemCtx.value;
|
|
4425
|
+
return /* @__PURE__ */ jsx(
|
|
4426
|
+
"span",
|
|
4427
|
+
{
|
|
4428
|
+
ref,
|
|
4429
|
+
className: cn(
|
|
4430
|
+
"flex h-9 w-9 shrink-0 items-center justify-center rounded-full border text-sm font-semibold transition-all duration-200",
|
|
4431
|
+
isCompleted ? "bg-primary border-primary text-primary-foreground" : isActive ? "border-primary text-primary bg-primary/10 ring-2 ring-primary/20" : "border-border text-muted-foreground bg-muted/40",
|
|
4432
|
+
className
|
|
4433
|
+
),
|
|
4434
|
+
...props,
|
|
4435
|
+
children: children ?? (isCompleted ? "\u2713" : itemCtx.value + 1)
|
|
4436
|
+
}
|
|
4437
|
+
);
|
|
4438
|
+
}
|
|
4439
|
+
);
|
|
4440
|
+
StepperIndicator.displayName = "StepperIndicator";
|
|
4441
|
+
var StepperTitle = React52.forwardRef(
|
|
4442
|
+
({ className, ...props }, ref) => {
|
|
4443
|
+
const ctx = React52.useContext(StepperContext);
|
|
4444
|
+
const itemCtx = React52.useContext(StepperItemContext);
|
|
4445
|
+
if (!ctx || !itemCtx) {
|
|
4446
|
+
throw new Error("StepperTitle must be used within a StepperItem inside a Stepper");
|
|
4447
|
+
}
|
|
4448
|
+
const isActive = ctx.activeStep === itemCtx.value;
|
|
4449
|
+
return /* @__PURE__ */ jsx(
|
|
4450
|
+
"span",
|
|
4451
|
+
{
|
|
4452
|
+
ref,
|
|
4453
|
+
className: cn(
|
|
4454
|
+
"text-sm font-medium transition-colors duration-200",
|
|
4455
|
+
isActive ? "text-foreground font-semibold" : "text-muted-foreground",
|
|
4456
|
+
className
|
|
4457
|
+
),
|
|
4458
|
+
...props
|
|
4459
|
+
}
|
|
4460
|
+
);
|
|
4461
|
+
}
|
|
4462
|
+
);
|
|
4463
|
+
StepperTitle.displayName = "StepperTitle";
|
|
4464
|
+
var StepperDescription = React52.forwardRef(
|
|
4465
|
+
({ className, ...props }, ref) => {
|
|
4466
|
+
const itemCtx = React52.useContext(StepperItemContext);
|
|
4467
|
+
if (!itemCtx) throw new Error("StepperDescription must be used within a StepperItem");
|
|
4468
|
+
return /* @__PURE__ */ jsx(
|
|
4469
|
+
"span",
|
|
4470
|
+
{
|
|
4471
|
+
ref,
|
|
4472
|
+
className: cn("text-xs text-muted-foreground text-left block", className),
|
|
4473
|
+
...props
|
|
4474
|
+
}
|
|
4475
|
+
);
|
|
4476
|
+
}
|
|
4477
|
+
);
|
|
4478
|
+
StepperDescription.displayName = "StepperDescription";
|
|
4479
|
+
var AppShellBanner = React52.forwardRef(
|
|
4480
|
+
({ className, variant = "primary", closable, onClose, children, ...props }, ref) => {
|
|
4481
|
+
const variantClass = {
|
|
4482
|
+
primary: "bg-primary text-primary-foreground",
|
|
4483
|
+
accent: "bg-secondary text-secondary-foreground",
|
|
4484
|
+
muted: "bg-surface border-b border-border text-muted-foreground"
|
|
4485
|
+
}[variant];
|
|
4486
|
+
return /* @__PURE__ */ jsxs(
|
|
4487
|
+
"div",
|
|
4488
|
+
{
|
|
4489
|
+
ref,
|
|
4490
|
+
className: cn("px-4 py-2 text-sm font-medium text-center flex items-center justify-center relative z-30", variantClass, className),
|
|
4491
|
+
...props,
|
|
4492
|
+
children: [
|
|
4493
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children }),
|
|
4494
|
+
closable && /* @__PURE__ */ jsx(
|
|
4495
|
+
"button",
|
|
4496
|
+
{
|
|
4497
|
+
type: "button",
|
|
4498
|
+
onClick: onClose,
|
|
4499
|
+
className: "p-1 rounded opacity-80 hover:opacity-100 transition-opacity ml-2 cursor-pointer",
|
|
4500
|
+
"aria-label": "Close banner",
|
|
4501
|
+
children: /* @__PURE__ */ jsx("svg", { width: 14, height: 14, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) })
|
|
4502
|
+
}
|
|
4503
|
+
)
|
|
4504
|
+
]
|
|
4505
|
+
}
|
|
4506
|
+
);
|
|
4507
|
+
}
|
|
4508
|
+
);
|
|
4509
|
+
AppShellBanner.displayName = "AppShellBanner";
|
|
4510
|
+
var AppShellHeader = React52.forwardRef(
|
|
4511
|
+
({
|
|
4512
|
+
className,
|
|
4513
|
+
variant = "glass",
|
|
4514
|
+
position = "sticky",
|
|
4515
|
+
bordered = true,
|
|
4516
|
+
children,
|
|
4517
|
+
mobileNav,
|
|
4518
|
+
...props
|
|
4519
|
+
}, ref) => {
|
|
4520
|
+
const [mobileOpen, setMobileOpen] = React52.useState(false);
|
|
4521
|
+
React52.useEffect(() => {
|
|
4522
|
+
const handleKeyDown = (e) => {
|
|
4523
|
+
if (e.key === "Escape" && mobileOpen) {
|
|
4524
|
+
setMobileOpen(false);
|
|
4525
|
+
}
|
|
4526
|
+
};
|
|
4527
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
4528
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
4529
|
+
}, [mobileOpen]);
|
|
4530
|
+
const positionClass = {
|
|
4531
|
+
sticky: "sticky top-0 z-40",
|
|
4532
|
+
fixed: "fixed top-0 left-0 right-0 z-40",
|
|
4533
|
+
static: "relative z-40"
|
|
4534
|
+
}[position];
|
|
4535
|
+
const variantClass = {
|
|
4536
|
+
glass: "bg-surface/80 backdrop-blur-md text-foreground",
|
|
4537
|
+
solid: "bg-surface text-foreground",
|
|
4538
|
+
transparent: "bg-transparent text-foreground"
|
|
4539
|
+
}[variant];
|
|
4540
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4541
|
+
/* @__PURE__ */ jsx(
|
|
4542
|
+
"header",
|
|
4543
|
+
{
|
|
4544
|
+
ref,
|
|
4545
|
+
className: cn(
|
|
4546
|
+
"w-full transition-colors",
|
|
4547
|
+
positionClass,
|
|
4548
|
+
variantClass,
|
|
4549
|
+
bordered && "border-b border-border/60",
|
|
4550
|
+
className
|
|
4551
|
+
),
|
|
4552
|
+
...props,
|
|
4553
|
+
children: /* @__PURE__ */ jsxs("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between gap-4", children: [
|
|
4554
|
+
mobileNav && /* @__PURE__ */ jsx(
|
|
4555
|
+
"button",
|
|
4556
|
+
{
|
|
4557
|
+
type: "button",
|
|
4558
|
+
onClick: () => setMobileOpen(true),
|
|
4559
|
+
className: "md:hidden p-2 -ml-2 rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted transition-colors cursor-pointer",
|
|
4560
|
+
"aria-label": "Open navigation",
|
|
4561
|
+
children: /* @__PURE__ */ jsxs("svg", { width: 20, height: 20, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
4562
|
+
/* @__PURE__ */ jsx("line", { x1: "3", y1: "12", x2: "21", y2: "12" }),
|
|
4563
|
+
/* @__PURE__ */ jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6" }),
|
|
4564
|
+
/* @__PURE__ */ jsx("line", { x1: "3", y1: "18", x2: "21", y2: "18" })
|
|
4565
|
+
] })
|
|
4566
|
+
}
|
|
4567
|
+
),
|
|
4568
|
+
children
|
|
4569
|
+
] })
|
|
4570
|
+
}
|
|
4571
|
+
),
|
|
4572
|
+
mobileNav && mobileOpen && /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 md:hidden flex", children: [
|
|
4573
|
+
/* @__PURE__ */ jsx(
|
|
4574
|
+
"div",
|
|
4575
|
+
{
|
|
4576
|
+
"aria-hidden": "true",
|
|
4577
|
+
onClick: () => setMobileOpen(false),
|
|
4578
|
+
className: "fixed inset-0 bg-overlay backdrop-blur-[2px] transition-opacity duration-300"
|
|
4579
|
+
}
|
|
4580
|
+
),
|
|
4581
|
+
/* @__PURE__ */ jsxs(
|
|
4582
|
+
"aside",
|
|
4583
|
+
{
|
|
4584
|
+
role: "dialog",
|
|
4585
|
+
"aria-modal": "true",
|
|
4586
|
+
"aria-label": "Mobile Navigation",
|
|
4587
|
+
className: "relative z-10 w-72 h-full bg-surface border-r border-border flex flex-col shadow-kj-lg",
|
|
4588
|
+
children: [
|
|
4589
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-4 border-b border-border", children: [
|
|
4590
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-bold uppercase tracking-wider text-muted-foreground", children: "Navigation" }),
|
|
4591
|
+
/* @__PURE__ */ jsx(
|
|
4592
|
+
"button",
|
|
4593
|
+
{
|
|
4594
|
+
type: "button",
|
|
4595
|
+
onClick: () => setMobileOpen(false),
|
|
4596
|
+
className: "p-1 rounded text-muted-foreground hover:text-foreground hover:bg-muted transition-colors cursor-pointer",
|
|
4597
|
+
"aria-label": "Close navigation",
|
|
4598
|
+
children: /* @__PURE__ */ jsx("svg", { width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) })
|
|
4599
|
+
}
|
|
4600
|
+
)
|
|
4601
|
+
] }),
|
|
4602
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto p-4", children: mobileNav })
|
|
4603
|
+
]
|
|
4604
|
+
}
|
|
4605
|
+
)
|
|
4606
|
+
] })
|
|
4607
|
+
] });
|
|
4608
|
+
}
|
|
4609
|
+
);
|
|
4610
|
+
AppShellHeader.displayName = "AppShellHeader";
|
|
4611
|
+
var AppShellMain = React52.forwardRef(
|
|
4612
|
+
({ className, width = "default", padded = true, children, ...props }, ref) => {
|
|
4613
|
+
const widthClass = {
|
|
4614
|
+
default: "max-w-7xl mx-auto w-full",
|
|
4615
|
+
narrow: "max-w-5xl mx-auto w-full",
|
|
4616
|
+
wide: "max-w-screen-2xl mx-auto w-full",
|
|
4617
|
+
full: "max-w-none w-full"
|
|
4618
|
+
}[width];
|
|
4619
|
+
return /* @__PURE__ */ jsx(
|
|
4620
|
+
"main",
|
|
4621
|
+
{
|
|
4622
|
+
ref,
|
|
4623
|
+
className: cn("flex-1", padded && "px-4 sm:px-6 lg:px-8 py-8 md:py-12", widthClass, className),
|
|
4624
|
+
...props,
|
|
4625
|
+
children
|
|
4626
|
+
}
|
|
4627
|
+
);
|
|
4628
|
+
}
|
|
4629
|
+
);
|
|
4630
|
+
AppShellMain.displayName = "AppShellMain";
|
|
4631
|
+
var AppShellFooter = React52.forwardRef(
|
|
4632
|
+
({ className, bordered = true, children, ...props }, ref) => {
|
|
4633
|
+
return /* @__PURE__ */ jsx(
|
|
4634
|
+
"footer",
|
|
4635
|
+
{
|
|
4636
|
+
ref,
|
|
4637
|
+
className: cn(
|
|
4638
|
+
"w-full bg-surface text-foreground py-12 mt-auto",
|
|
4639
|
+
bordered && "border-t border-border",
|
|
4640
|
+
className
|
|
4641
|
+
),
|
|
4642
|
+
...props,
|
|
4643
|
+
children: /* @__PURE__ */ jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children })
|
|
4644
|
+
}
|
|
4645
|
+
);
|
|
4646
|
+
}
|
|
4647
|
+
);
|
|
4648
|
+
AppShellFooter.displayName = "AppShellFooter";
|
|
4649
|
+
var AppShell = React52.forwardRef(
|
|
4650
|
+
({
|
|
4651
|
+
className,
|
|
4652
|
+
banner,
|
|
4653
|
+
header,
|
|
4654
|
+
children,
|
|
4655
|
+
footer,
|
|
4656
|
+
contentWidth = "default",
|
|
4657
|
+
headerPosition = "sticky",
|
|
4658
|
+
headerVariant = "glass",
|
|
4659
|
+
paddedContent = true,
|
|
4660
|
+
mobileNav,
|
|
4661
|
+
...props
|
|
4662
|
+
}, ref) => {
|
|
4663
|
+
return /* @__PURE__ */ jsxs(
|
|
4664
|
+
"div",
|
|
4665
|
+
{
|
|
4666
|
+
ref,
|
|
4667
|
+
className: cn("min-h-screen bg-canvas text-foreground flex flex-col antialiased", className),
|
|
4668
|
+
...props,
|
|
4669
|
+
children: [
|
|
4670
|
+
banner && /* @__PURE__ */ jsx(AppShellBanner, { children: banner }),
|
|
4671
|
+
header && /* @__PURE__ */ jsx(AppShellHeader, { variant: headerVariant, position: headerPosition, mobileNav, children: header }),
|
|
4672
|
+
/* @__PURE__ */ jsx(AppShellMain, { width: contentWidth, padded: paddedContent, children }),
|
|
4673
|
+
footer && /* @__PURE__ */ jsx(AppShellFooter, { children: footer })
|
|
4674
|
+
]
|
|
4675
|
+
}
|
|
4676
|
+
);
|
|
4677
|
+
}
|
|
4678
|
+
);
|
|
4679
|
+
AppShell.displayName = "AppShell";
|
|
4680
|
+
AppShell.Banner = AppShellBanner;
|
|
4681
|
+
AppShell.Header = AppShellHeader;
|
|
4682
|
+
AppShell.Main = AppShellMain;
|
|
4683
|
+
AppShell.Footer = AppShellFooter;
|
|
4684
|
+
var sizeClasses = {
|
|
4685
|
+
sm: { wrapper: "kj-rating-sm gap-1", star: "w-4 h-4", text: "text-xs" },
|
|
4686
|
+
md: { wrapper: "kj-rating-md gap-1.5", star: "w-5 h-5", text: "text-sm" },
|
|
4687
|
+
lg: { wrapper: "kj-rating-lg gap-2", star: "w-6 h-6", text: "text-base" },
|
|
4688
|
+
xl: { wrapper: "kj-rating-xl gap-2.5", star: "w-8 h-8", text: "text-lg" }
|
|
4689
|
+
};
|
|
4690
|
+
var iconPaths = {
|
|
4691
|
+
star: "M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z",
|
|
4692
|
+
heart: "M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z",
|
|
4693
|
+
flame: "M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z",
|
|
4694
|
+
shield: "M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67 0C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",
|
|
4695
|
+
thumb: "M7 10v12M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z"
|
|
4696
|
+
};
|
|
4697
|
+
function roundToPrecision(value, precision) {
|
|
4698
|
+
const inv = 1 / precision;
|
|
4699
|
+
return Math.round(value * inv) / inv;
|
|
4700
|
+
}
|
|
4701
|
+
function isCssColor(str) {
|
|
4702
|
+
return str.startsWith("#") || str.startsWith("rgb") || str.startsWith("hsl");
|
|
4703
|
+
}
|
|
4704
|
+
var Rating = React52.forwardRef(
|
|
4705
|
+
({
|
|
4706
|
+
value: controlledValue,
|
|
4707
|
+
defaultValue = 0,
|
|
4708
|
+
max = 5,
|
|
4709
|
+
precision = 1,
|
|
4710
|
+
size = "md",
|
|
4711
|
+
icon = "star",
|
|
4712
|
+
color = "#f59e0b",
|
|
4713
|
+
emptyColor = "#e5e7eb",
|
|
4714
|
+
allowClear = true,
|
|
4715
|
+
disabled = false,
|
|
4716
|
+
readOnly = false,
|
|
4717
|
+
hoverPreview = true,
|
|
4718
|
+
showValue = false,
|
|
4719
|
+
showCount = false,
|
|
4720
|
+
count,
|
|
4721
|
+
name,
|
|
4722
|
+
onChange,
|
|
4723
|
+
onHoverChange,
|
|
4724
|
+
className,
|
|
4725
|
+
onKeyDown,
|
|
4726
|
+
onMouseLeave,
|
|
4727
|
+
"aria-label": ariaLabel,
|
|
4728
|
+
...props
|
|
4729
|
+
}, ref) => {
|
|
4730
|
+
const isControlled = controlledValue !== void 0;
|
|
4731
|
+
const [internalValue, setInternalValue] = React52.useState(
|
|
4732
|
+
controlledValue ?? defaultValue
|
|
4733
|
+
);
|
|
4734
|
+
const [hoverValue, setHoverValue] = React52.useState(null);
|
|
4735
|
+
const instanceId = React52.useId();
|
|
4736
|
+
React52.useEffect(() => {
|
|
4737
|
+
if (isControlled) {
|
|
4738
|
+
setInternalValue(controlledValue);
|
|
4739
|
+
}
|
|
4740
|
+
}, [isControlled, controlledValue]);
|
|
4741
|
+
const currentValue = isControlled ? controlledValue : internalValue;
|
|
4742
|
+
const displayValue = hoverPreview && hoverValue !== null ? hoverValue : currentValue;
|
|
4743
|
+
const handleValueChange = (newValue) => {
|
|
4744
|
+
if (disabled || readOnly) return;
|
|
4745
|
+
const clamped = Math.max(0, Math.min(max, roundToPrecision(newValue, precision)));
|
|
4746
|
+
if (!isControlled) {
|
|
4747
|
+
setInternalValue(clamped);
|
|
4748
|
+
}
|
|
4749
|
+
onChange?.(clamped);
|
|
4750
|
+
};
|
|
4751
|
+
const getEventValue = (e, starIndex) => {
|
|
4752
|
+
const target = e.currentTarget;
|
|
4753
|
+
const width = target.offsetWidth;
|
|
4754
|
+
const nativeEv = e.nativeEvent;
|
|
4755
|
+
const offsetX = nativeEv?.offsetX ?? nativeEv?.nativeEvent?.offsetX ?? e.offsetX;
|
|
4756
|
+
if (width > 0 && offsetX !== void 0 && offsetX !== null) {
|
|
4757
|
+
const ratio = Math.max(1e-3, Math.min(1, offsetX / width));
|
|
4758
|
+
const stepInStar = Math.ceil(ratio / precision) * precision;
|
|
4759
|
+
return Math.min(max, Math.max(precision, starIndex - 1 + stepInStar));
|
|
4760
|
+
}
|
|
4761
|
+
return starIndex;
|
|
4762
|
+
};
|
|
4763
|
+
const handleStarClick = (e, starIndex) => {
|
|
4764
|
+
if (disabled || readOnly) return;
|
|
4765
|
+
const targetValue = getEventValue(e, starIndex);
|
|
4766
|
+
let nextValue = targetValue;
|
|
4767
|
+
if (allowClear && targetValue === currentValue) {
|
|
4768
|
+
nextValue = 0;
|
|
4769
|
+
}
|
|
4770
|
+
handleValueChange(nextValue);
|
|
4771
|
+
};
|
|
4772
|
+
const handleStarMouseMove = (e, starIndex) => {
|
|
4773
|
+
if (disabled || readOnly) return;
|
|
4774
|
+
if (hoverPreview) {
|
|
4775
|
+
const hoveredVal = getEventValue(e, starIndex);
|
|
4776
|
+
setHoverValue(hoveredVal);
|
|
4777
|
+
onHoverChange?.(hoveredVal);
|
|
4778
|
+
}
|
|
4779
|
+
};
|
|
4780
|
+
const handleKeyDown = (e) => {
|
|
4781
|
+
if (disabled || readOnly) {
|
|
4782
|
+
onKeyDown?.(e);
|
|
4783
|
+
return;
|
|
4784
|
+
}
|
|
4785
|
+
let nextVal = null;
|
|
4786
|
+
switch (e.key) {
|
|
4787
|
+
case "ArrowRight":
|
|
4788
|
+
case "ArrowUp":
|
|
4789
|
+
nextVal = Math.min(max, currentValue + precision);
|
|
4790
|
+
break;
|
|
4791
|
+
case "ArrowLeft":
|
|
4792
|
+
case "ArrowDown":
|
|
4793
|
+
nextVal = Math.max(0, currentValue - precision);
|
|
4794
|
+
break;
|
|
4795
|
+
case "Home":
|
|
4796
|
+
nextVal = 0;
|
|
4797
|
+
break;
|
|
4798
|
+
case "End":
|
|
4799
|
+
nextVal = max;
|
|
4800
|
+
break;
|
|
4801
|
+
}
|
|
4802
|
+
if (nextVal !== null) {
|
|
4803
|
+
e.preventDefault();
|
|
4804
|
+
handleValueChange(nextVal);
|
|
4805
|
+
}
|
|
4806
|
+
onKeyDown?.(e);
|
|
4807
|
+
};
|
|
4808
|
+
const handleContainerMouseLeave = (e) => {
|
|
4809
|
+
if (hoverPreview) {
|
|
4810
|
+
setHoverValue(null);
|
|
4811
|
+
onHoverChange?.(null);
|
|
4812
|
+
}
|
|
4813
|
+
onMouseLeave?.(e);
|
|
4814
|
+
};
|
|
4815
|
+
const sizing = sizeClasses[size] || sizeClasses.md;
|
|
4816
|
+
return /* @__PURE__ */ jsxs(
|
|
4817
|
+
"div",
|
|
4818
|
+
{
|
|
4819
|
+
ref,
|
|
4820
|
+
role: readOnly ? "img" : "radiogroup",
|
|
4821
|
+
tabIndex: disabled || readOnly ? -1 : 0,
|
|
4822
|
+
"aria-valuenow": displayValue,
|
|
4823
|
+
"aria-valuemin": 0,
|
|
4824
|
+
"aria-valuemax": max,
|
|
4825
|
+
"aria-disabled": disabled || void 0,
|
|
4826
|
+
"aria-readonly": readOnly || void 0,
|
|
4827
|
+
"aria-label": ariaLabel || (readOnly ? `Rated ${displayValue} out of ${max}` : "Rating"),
|
|
4828
|
+
onKeyDown: handleKeyDown,
|
|
4829
|
+
onMouseLeave: handleContainerMouseLeave,
|
|
4830
|
+
className: cn(
|
|
4831
|
+
"inline-flex items-center select-none focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded-md",
|
|
4832
|
+
disabled && "opacity-50 cursor-not-allowed",
|
|
4833
|
+
readOnly && "cursor-default",
|
|
4834
|
+
!disabled && !readOnly && "cursor-pointer",
|
|
4835
|
+
sizing.wrapper,
|
|
4836
|
+
className
|
|
4837
|
+
),
|
|
4838
|
+
...props,
|
|
4839
|
+
children: [
|
|
4840
|
+
name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: currentValue }),
|
|
4841
|
+
/* @__PURE__ */ jsx("div", { className: "inline-flex items-center gap-0.5", children: Array.from({ length: max }, (_, index) => {
|
|
4842
|
+
const starIndex = index + 1;
|
|
4843
|
+
const fillFraction = Math.max(
|
|
4844
|
+
0,
|
|
4845
|
+
Math.min(1, displayValue - index)
|
|
4846
|
+
);
|
|
4847
|
+
const gradientId = `rating-grad-${instanceId}-${index}-${Math.round(
|
|
4848
|
+
fillFraction * 100
|
|
4849
|
+
)}`;
|
|
4850
|
+
const CustomIconComponent = typeof icon === "function" || typeof icon === "object" ? icon : null;
|
|
4851
|
+
const builtInIconType = typeof icon === "string" ? icon : "star";
|
|
4852
|
+
const pathD = iconPaths[builtInIconType] || iconPaths.star;
|
|
4853
|
+
return /* @__PURE__ */ jsx(
|
|
4854
|
+
"button",
|
|
4855
|
+
{
|
|
4856
|
+
type: "button",
|
|
4857
|
+
tabIndex: -1,
|
|
4858
|
+
"data-testid": "rating-star",
|
|
4859
|
+
role: readOnly ? void 0 : "radio",
|
|
4860
|
+
"aria-checked": readOnly ? void 0 : displayValue >= starIndex,
|
|
4861
|
+
"aria-label": `${starIndex} of ${max} stars`,
|
|
4862
|
+
disabled,
|
|
4863
|
+
onClick: (e) => handleStarClick(e, starIndex),
|
|
4864
|
+
onMouseMove: (e) => handleStarMouseMove(e, starIndex),
|
|
4865
|
+
onMouseEnter: (e) => handleStarMouseMove(e, starIndex),
|
|
4866
|
+
className: cn(
|
|
4867
|
+
"p-0.5 focus:outline-none transition-transform active:scale-95 disabled:scale-100",
|
|
4868
|
+
sizing.star
|
|
4869
|
+
),
|
|
4870
|
+
children: CustomIconComponent ? /* @__PURE__ */ jsx(
|
|
4871
|
+
CustomIconComponent,
|
|
4872
|
+
{
|
|
4873
|
+
className: cn(
|
|
4874
|
+
"w-full h-full",
|
|
4875
|
+
fillFraction >= 0.5 ? !isCssColor(color) && color : !isCssColor(emptyColor) && emptyColor
|
|
4876
|
+
)
|
|
4877
|
+
}
|
|
4878
|
+
) : /* @__PURE__ */ jsxs(
|
|
4879
|
+
"svg",
|
|
4880
|
+
{
|
|
4881
|
+
viewBox: "0 0 24 24",
|
|
4882
|
+
className: "w-full h-full",
|
|
4883
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
4884
|
+
children: [
|
|
4885
|
+
fillFraction > 0 && fillFraction < 1 && /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs(
|
|
4886
|
+
"linearGradient",
|
|
4887
|
+
{
|
|
4888
|
+
id: gradientId,
|
|
4889
|
+
x1: "0%",
|
|
4890
|
+
y1: "0%",
|
|
4891
|
+
x2: "100%",
|
|
4892
|
+
y2: "0%",
|
|
4893
|
+
children: [
|
|
4894
|
+
isCssColor(color) ? /* @__PURE__ */ jsx(
|
|
4895
|
+
"stop",
|
|
4896
|
+
{
|
|
4897
|
+
offset: `${fillFraction * 100}%`,
|
|
4898
|
+
stopColor: color
|
|
4899
|
+
}
|
|
4900
|
+
) : /* @__PURE__ */ jsx(
|
|
4901
|
+
"stop",
|
|
4902
|
+
{
|
|
4903
|
+
offset: `${fillFraction * 100}%`,
|
|
4904
|
+
stopColor: "currentColor",
|
|
4905
|
+
className: color
|
|
4906
|
+
}
|
|
4907
|
+
),
|
|
4908
|
+
isCssColor(emptyColor) ? /* @__PURE__ */ jsx(
|
|
4909
|
+
"stop",
|
|
4910
|
+
{
|
|
4911
|
+
offset: `${fillFraction * 100}%`,
|
|
4912
|
+
stopColor: emptyColor
|
|
4913
|
+
}
|
|
4914
|
+
) : /* @__PURE__ */ jsx(
|
|
4915
|
+
"stop",
|
|
4916
|
+
{
|
|
4917
|
+
offset: `${fillFraction * 100}%`,
|
|
4918
|
+
stopColor: "currentColor",
|
|
4919
|
+
className: emptyColor
|
|
4920
|
+
}
|
|
4921
|
+
)
|
|
4922
|
+
]
|
|
4923
|
+
}
|
|
4924
|
+
) }),
|
|
4925
|
+
/* @__PURE__ */ jsx(
|
|
4926
|
+
"path",
|
|
4927
|
+
{
|
|
4928
|
+
d: pathD,
|
|
4929
|
+
fill: fillFraction === 1 ? isCssColor(color) ? color : void 0 : fillFraction === 0 ? isCssColor(emptyColor) ? emptyColor : void 0 : `url(#${gradientId})`,
|
|
4930
|
+
className: fillFraction === 1 ? !isCssColor(color) ? color : void 0 : fillFraction === 0 ? !isCssColor(emptyColor) ? emptyColor : void 0 : void 0,
|
|
4931
|
+
stroke: "none"
|
|
4932
|
+
}
|
|
4933
|
+
)
|
|
4934
|
+
]
|
|
4935
|
+
}
|
|
4936
|
+
)
|
|
4937
|
+
},
|
|
4938
|
+
index
|
|
4939
|
+
);
|
|
4940
|
+
}) }),
|
|
4941
|
+
showValue && /* @__PURE__ */ jsx("span", { className: cn("font-medium text-foreground ml-1.5", sizing.text), children: displayValue }),
|
|
4942
|
+
showCount && count !== void 0 && /* @__PURE__ */ jsxs("span", { className: cn("text-muted-foreground ml-1", sizing.text), children: [
|
|
4943
|
+
"(",
|
|
4944
|
+
count,
|
|
4945
|
+
")"
|
|
4946
|
+
] })
|
|
4947
|
+
]
|
|
4948
|
+
}
|
|
4949
|
+
);
|
|
4950
|
+
}
|
|
4951
|
+
);
|
|
4952
|
+
Rating.displayName = "Rating";
|
|
4953
|
+
var RatingField = React52.forwardRef(
|
|
4954
|
+
({
|
|
4955
|
+
id: customId,
|
|
4956
|
+
label,
|
|
4957
|
+
helperText,
|
|
4958
|
+
errorMessage,
|
|
4959
|
+
required,
|
|
4960
|
+
className,
|
|
4961
|
+
containerClassName,
|
|
4962
|
+
"aria-describedby": ariaDescribedBy,
|
|
4963
|
+
...ratingProps
|
|
4964
|
+
}, ref) => {
|
|
4965
|
+
const generatedId = React52.useId();
|
|
4966
|
+
const id = customId || generatedId;
|
|
4967
|
+
const helperId = helperText ? `${id}-helper` : void 0;
|
|
4968
|
+
const errorId = errorMessage ? `${id}-error` : void 0;
|
|
4969
|
+
const describedBy = [ariaDescribedBy, helperId, errorId].filter(Boolean).join(" ") || void 0;
|
|
4970
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1.5", containerClassName), children: [
|
|
4971
|
+
label && /* @__PURE__ */ jsx(Label, { htmlFor: id, required, children: label }),
|
|
4972
|
+
/* @__PURE__ */ jsx(
|
|
4973
|
+
Rating,
|
|
4974
|
+
{
|
|
4975
|
+
ref,
|
|
4976
|
+
id,
|
|
4977
|
+
className,
|
|
4978
|
+
"aria-describedby": describedBy,
|
|
4979
|
+
...ratingProps
|
|
4980
|
+
}
|
|
4981
|
+
),
|
|
4982
|
+
helperText && /* @__PURE__ */ jsx(Hint, { id: helperId, children: helperText }),
|
|
4983
|
+
errorMessage && /* @__PURE__ */ jsx(Hint, { id: errorId, error: true, children: errorMessage })
|
|
4984
|
+
] });
|
|
4985
|
+
}
|
|
4986
|
+
);
|
|
4987
|
+
RatingField.displayName = "RatingField";
|
|
4988
|
+
var RatingSummary = React52.forwardRef(
|
|
4989
|
+
({
|
|
4990
|
+
average,
|
|
4991
|
+
totalCount,
|
|
4992
|
+
distribution,
|
|
4993
|
+
max = 5,
|
|
4994
|
+
size = "md",
|
|
4995
|
+
icon = "star",
|
|
4996
|
+
className,
|
|
4997
|
+
...props
|
|
4998
|
+
}, ref) => {
|
|
4999
|
+
const formattedAverage = Number.isInteger(average) ? average.toString() : average.toFixed(1);
|
|
5000
|
+
const formattedTotalCount = totalCount.toLocaleString();
|
|
5001
|
+
return /* @__PURE__ */ jsxs(
|
|
5002
|
+
"div",
|
|
5003
|
+
{
|
|
5004
|
+
ref,
|
|
5005
|
+
className: cn(
|
|
5006
|
+
"flex flex-col gap-6 sm:flex-row sm:items-center sm:gap-8 p-4 rounded-xl border bg-card text-card-foreground shadow-sm",
|
|
5007
|
+
className
|
|
5008
|
+
),
|
|
5009
|
+
...props,
|
|
5010
|
+
children: [
|
|
5011
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center p-4 rounded-lg bg-muted/40 text-center min-w-[140px]", children: [
|
|
5012
|
+
/* @__PURE__ */ jsx("div", { className: "text-4xl font-bold tracking-tight text-foreground", children: formattedAverage }),
|
|
5013
|
+
/* @__PURE__ */ jsx("div", { className: "my-1.5", children: /* @__PURE__ */ jsx(
|
|
5014
|
+
Rating,
|
|
5015
|
+
{
|
|
5016
|
+
value: average,
|
|
5017
|
+
max,
|
|
5018
|
+
size,
|
|
5019
|
+
icon,
|
|
5020
|
+
readOnly: true,
|
|
5021
|
+
allowClear: false,
|
|
5022
|
+
hoverPreview: false
|
|
5023
|
+
}
|
|
5024
|
+
) }),
|
|
5025
|
+
/* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground", children: [
|
|
5026
|
+
formattedTotalCount,
|
|
5027
|
+
" total ratings"
|
|
5028
|
+
] })
|
|
5029
|
+
] }),
|
|
5030
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 flex flex-col gap-2.5", children: distribution.map((item) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-sm", children: [
|
|
5031
|
+
/* @__PURE__ */ jsxs("span", { className: "w-12 text-right font-medium text-foreground whitespace-nowrap", children: [
|
|
5032
|
+
item.stars,
|
|
5033
|
+
" star"
|
|
5034
|
+
] }),
|
|
5035
|
+
/* @__PURE__ */ jsx(
|
|
5036
|
+
Progress,
|
|
5037
|
+
{
|
|
5038
|
+
value: item.percentage,
|
|
5039
|
+
"aria-label": `${item.stars} star ratings: ${item.percentage}%`,
|
|
5040
|
+
className: "h-2.5 flex-1"
|
|
5041
|
+
}
|
|
5042
|
+
),
|
|
5043
|
+
/* @__PURE__ */ jsxs("span", { className: "w-12 text-right text-xs text-muted-foreground font-mono", children: [
|
|
5044
|
+
item.percentage,
|
|
5045
|
+
"%"
|
|
5046
|
+
] })
|
|
5047
|
+
] }, item.stars)) })
|
|
5048
|
+
]
|
|
5049
|
+
}
|
|
5050
|
+
);
|
|
5051
|
+
}
|
|
5052
|
+
);
|
|
5053
|
+
RatingSummary.displayName = "RatingSummary";
|
|
5054
|
+
var PricingCard = React52.forwardRef(
|
|
5055
|
+
({
|
|
5056
|
+
className,
|
|
5057
|
+
name,
|
|
5058
|
+
price,
|
|
5059
|
+
period,
|
|
5060
|
+
description,
|
|
5061
|
+
features = [],
|
|
5062
|
+
variant,
|
|
5063
|
+
badge,
|
|
5064
|
+
popular = false,
|
|
5065
|
+
ctaText = "Get Started",
|
|
5066
|
+
onCtaClick,
|
|
5067
|
+
ctaHref,
|
|
5068
|
+
cta,
|
|
5069
|
+
disabled = false,
|
|
5070
|
+
...props
|
|
5071
|
+
}, ref) => {
|
|
5072
|
+
const resolvedVariant = variant || (popular ? "featured" : "default");
|
|
5073
|
+
const displayBadge = badge || (popular ? "Most Popular" : void 0);
|
|
5074
|
+
const isFeatured = resolvedVariant === "featured";
|
|
5075
|
+
return /* @__PURE__ */ jsxs(
|
|
5076
|
+
Card,
|
|
5077
|
+
{
|
|
5078
|
+
ref,
|
|
5079
|
+
className: cn(
|
|
5080
|
+
"relative flex flex-col p-6 transition-all duration-300",
|
|
5081
|
+
isFeatured ? "border-amber-500/80 shadow-kj-lg ring-1 ring-amber-500/30" : resolvedVariant === "outline" ? "border-border/80 bg-transparent shadow-none" : "border-border shadow-kj-sm",
|
|
5082
|
+
className
|
|
5083
|
+
),
|
|
5084
|
+
...props,
|
|
5085
|
+
children: [
|
|
5086
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-start justify-between gap-3", children: [
|
|
5087
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5088
|
+
/* @__PURE__ */ jsx("h3", { className: "text-xl font-bold tracking-tight text-foreground", children: name }),
|
|
5089
|
+
description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: description })
|
|
5090
|
+
] }),
|
|
5091
|
+
displayBadge && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-amber-500 px-3 py-0.5 text-[0.7rem] font-bold uppercase tracking-wider text-black shadow-sm shrink-0 mt-0.5", children: displayBadge })
|
|
5092
|
+
] }),
|
|
5093
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-baseline gap-1", children: [
|
|
5094
|
+
/* @__PURE__ */ jsx("span", { className: "text-3xl font-extrabold tracking-tight text-foreground", children: price }),
|
|
5095
|
+
period && /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: period })
|
|
5096
|
+
] }),
|
|
5097
|
+
features.length > 0 && /* @__PURE__ */ jsx("ul", { className: "mb-6 flex flex-1 flex-col gap-2.5 p-0 text-xs list-none", children: features.map((feature, idx) => {
|
|
5098
|
+
const text = typeof feature === "string" ? feature : feature.text;
|
|
5099
|
+
const included = typeof feature === "string" ? true : feature.included ?? true;
|
|
5100
|
+
return /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-2.5", children: [
|
|
5101
|
+
/* @__PURE__ */ jsx(
|
|
5102
|
+
"span",
|
|
5103
|
+
{
|
|
5104
|
+
className: cn(
|
|
5105
|
+
"flex h-4 w-4 shrink-0 items-center justify-center rounded-full text-[0.65rem] font-bold",
|
|
5106
|
+
included ? "bg-amber-500/15 text-amber-600 dark:text-amber-400" : "bg-muted text-muted-foreground/60"
|
|
5107
|
+
),
|
|
5108
|
+
children: included ? "\u2713" : "\u2715"
|
|
5109
|
+
}
|
|
5110
|
+
),
|
|
5111
|
+
/* @__PURE__ */ jsx("span", { className: cn(included ? "text-foreground" : "text-muted-foreground line-through opacity-70"), children: text })
|
|
5112
|
+
] }, idx);
|
|
5113
|
+
}) }),
|
|
5114
|
+
/* @__PURE__ */ jsx("div", { className: "mt-auto pt-2", children: cta ? cta : ctaHref ? (
|
|
5115
|
+
/* eslint-disable-next-line jsx-a11y/anchor-is-valid */
|
|
5116
|
+
/* @__PURE__ */ jsx(
|
|
5117
|
+
"a",
|
|
5118
|
+
{
|
|
5119
|
+
href: disabled ? void 0 : ctaHref,
|
|
5120
|
+
"aria-disabled": disabled || void 0,
|
|
5121
|
+
tabIndex: disabled ? -1 : void 0,
|
|
5122
|
+
className: cn(
|
|
5123
|
+
buttonVariants({ variant: isFeatured ? "primary" : "outline" }),
|
|
5124
|
+
"w-full justify-center",
|
|
5125
|
+
disabled && "pointer-events-none opacity-50"
|
|
5126
|
+
),
|
|
5127
|
+
children: ctaText
|
|
5128
|
+
}
|
|
5129
|
+
)
|
|
5130
|
+
) : /* @__PURE__ */ jsx(
|
|
5131
|
+
Button,
|
|
5132
|
+
{
|
|
5133
|
+
variant: isFeatured ? "primary" : "outline",
|
|
5134
|
+
className: "w-full justify-center",
|
|
5135
|
+
onClick: onCtaClick,
|
|
5136
|
+
disabled,
|
|
5137
|
+
children: ctaText
|
|
5138
|
+
}
|
|
5139
|
+
) })
|
|
5140
|
+
]
|
|
5141
|
+
}
|
|
5142
|
+
);
|
|
5143
|
+
}
|
|
5144
|
+
);
|
|
5145
|
+
PricingCard.displayName = "PricingCard";
|
|
5146
|
+
var BlogCard = React52.forwardRef(
|
|
5147
|
+
({
|
|
5148
|
+
className,
|
|
5149
|
+
title,
|
|
5150
|
+
description,
|
|
5151
|
+
coverUrl,
|
|
5152
|
+
coverAlt,
|
|
5153
|
+
category,
|
|
5154
|
+
readTime,
|
|
5155
|
+
publishedAt,
|
|
5156
|
+
author,
|
|
5157
|
+
orientation = "vertical",
|
|
5158
|
+
href,
|
|
5159
|
+
...props
|
|
5160
|
+
}, ref) => {
|
|
5161
|
+
const isHorizontal = orientation === "horizontal";
|
|
5162
|
+
const isInteractive = Boolean(href || props.onClick);
|
|
5163
|
+
return /* @__PURE__ */ jsxs(
|
|
5164
|
+
Card,
|
|
5165
|
+
{
|
|
5166
|
+
ref,
|
|
5167
|
+
as: href ? "a" : "div",
|
|
5168
|
+
href,
|
|
5169
|
+
interactive: isInteractive,
|
|
5170
|
+
className: cn(
|
|
5171
|
+
"group flex overflow-hidden transition-all duration-300",
|
|
5172
|
+
isHorizontal ? "flex-col sm:flex-row" : "flex-col",
|
|
5173
|
+
className
|
|
5174
|
+
),
|
|
5175
|
+
...props,
|
|
5176
|
+
children: [
|
|
5177
|
+
coverUrl && /* @__PURE__ */ jsx(
|
|
5178
|
+
"div",
|
|
5179
|
+
{
|
|
5180
|
+
className: cn(
|
|
5181
|
+
"relative overflow-hidden bg-muted shrink-0",
|
|
5182
|
+
isHorizontal ? "w-full sm:w-2/5 min-h-[160px]" : "w-full aspect-[16/9]"
|
|
5183
|
+
),
|
|
5184
|
+
children: /* @__PURE__ */ jsx(
|
|
5185
|
+
"img",
|
|
5186
|
+
{
|
|
5187
|
+
src: coverUrl,
|
|
5188
|
+
alt: coverAlt || title,
|
|
5189
|
+
className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105"
|
|
5190
|
+
}
|
|
5191
|
+
)
|
|
5192
|
+
}
|
|
5193
|
+
),
|
|
5194
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col justify-between p-5", children: [
|
|
5195
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5196
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center gap-2 text-[0.75rem] text-muted-foreground", children: [
|
|
5197
|
+
category && /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: category }),
|
|
5198
|
+
(readTime || publishedAt) && /* @__PURE__ */ jsxs("span", { children: [
|
|
5199
|
+
publishedAt,
|
|
5200
|
+
" ",
|
|
5201
|
+
publishedAt && readTime && "\u2022",
|
|
5202
|
+
" ",
|
|
5203
|
+
readTime
|
|
5204
|
+
] })
|
|
5205
|
+
] }),
|
|
5206
|
+
/* @__PURE__ */ jsx("h3", { className: "text-base font-bold tracking-tight text-foreground transition-colors group-hover:text-primary", children: title }),
|
|
5207
|
+
description && /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-xs text-muted-foreground line-clamp-2", children: description })
|
|
5208
|
+
] }),
|
|
5209
|
+
author && /* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-center gap-2.5 border-t border-border/60 pt-3", children: [
|
|
5210
|
+
/* @__PURE__ */ jsx(Avatar, { src: author.avatarUrl, alt: author.name, size: "sm", children: author.name.charAt(0) }),
|
|
5211
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col text-xs", children: [
|
|
5212
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: author.name }),
|
|
5213
|
+
author.role && /* @__PURE__ */ jsx("span", { className: "text-[0.7rem] text-muted-foreground", children: author.role })
|
|
5214
|
+
] })
|
|
5215
|
+
] })
|
|
5216
|
+
] })
|
|
5217
|
+
]
|
|
5218
|
+
}
|
|
5219
|
+
);
|
|
5220
|
+
}
|
|
5221
|
+
);
|
|
5222
|
+
BlogCard.displayName = "BlogCard";
|
|
5223
|
+
var ProjectCard = React52.forwardRef(
|
|
5224
|
+
({
|
|
5225
|
+
className,
|
|
5226
|
+
title,
|
|
5227
|
+
description,
|
|
5228
|
+
status,
|
|
5229
|
+
techStack = [],
|
|
5230
|
+
metrics = [],
|
|
5231
|
+
updatedAt,
|
|
5232
|
+
actions,
|
|
5233
|
+
href,
|
|
5234
|
+
...props
|
|
5235
|
+
}, ref) => {
|
|
5236
|
+
const isInteractive = Boolean(href || props.onClick);
|
|
5237
|
+
return /* @__PURE__ */ jsxs(
|
|
5238
|
+
Card,
|
|
5239
|
+
{
|
|
5240
|
+
ref,
|
|
5241
|
+
as: href ? "a" : "div",
|
|
5242
|
+
href,
|
|
5243
|
+
interactive: isInteractive,
|
|
5244
|
+
className: cn(
|
|
5245
|
+
"group flex flex-col justify-between p-5 transition-all duration-300",
|
|
5246
|
+
className
|
|
5247
|
+
),
|
|
5248
|
+
...props,
|
|
5249
|
+
children: [
|
|
5250
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5251
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
5252
|
+
/* @__PURE__ */ jsx("h3", { className: "text-base font-bold tracking-tight text-foreground transition-colors group-hover:text-primary", children: title }),
|
|
5253
|
+
status && /* @__PURE__ */ jsx(Badge, { variant: status.variant || "neutral", className: "shrink-0 text-[0.65rem]", children: status.label })
|
|
5254
|
+
] }),
|
|
5255
|
+
description && /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-xs text-muted-foreground line-clamp-2", children: description }),
|
|
5256
|
+
techStack.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-3.5 flex flex-wrap gap-1.5", children: techStack.map((tech) => /* @__PURE__ */ jsx(Badge, { variant: "neutral", className: "text-[0.65rem] py-0 px-2 font-mono", children: tech }, tech)) })
|
|
5257
|
+
] }),
|
|
5258
|
+
(metrics.length > 0 || updatedAt || actions) && /* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-center justify-between border-t border-border/60 pt-3 text-[0.75rem] text-muted-foreground", children: [
|
|
5259
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
5260
|
+
metrics.map((metric, idx) => /* @__PURE__ */ jsxs(
|
|
5261
|
+
"span",
|
|
5262
|
+
{
|
|
5263
|
+
className: "flex items-center gap-1 font-mono font-medium",
|
|
5264
|
+
children: [
|
|
5265
|
+
metric.icon && /* @__PURE__ */ jsx("span", { className: "shrink-0", children: metric.icon }),
|
|
5266
|
+
/* @__PURE__ */ jsx("span", { children: metric.value }),
|
|
5267
|
+
metric.label && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/70", children: metric.label })
|
|
5268
|
+
]
|
|
5269
|
+
},
|
|
5270
|
+
metric.label ? `${metric.label}-${idx}` : idx
|
|
5271
|
+
)),
|
|
5272
|
+
updatedAt && /* @__PURE__ */ jsx("span", { children: updatedAt })
|
|
5273
|
+
] }),
|
|
5274
|
+
actions && /* @__PURE__ */ jsx(
|
|
5275
|
+
"div",
|
|
5276
|
+
{
|
|
5277
|
+
onClick: (e) => e.stopPropagation(),
|
|
5278
|
+
className: "flex items-center gap-1 shrink-0",
|
|
5279
|
+
children: actions
|
|
5280
|
+
}
|
|
5281
|
+
)
|
|
5282
|
+
] })
|
|
5283
|
+
]
|
|
5284
|
+
}
|
|
5285
|
+
);
|
|
5286
|
+
}
|
|
5287
|
+
);
|
|
5288
|
+
ProjectCard.displayName = "ProjectCard";
|
|
5289
|
+
function formatRelativeTime(date) {
|
|
5290
|
+
const d = typeof date === "string" ? new Date(date) : date;
|
|
5291
|
+
if (isNaN(d.getTime())) return "";
|
|
5292
|
+
const diffMs = Date.now() - d.getTime();
|
|
5293
|
+
const diffMin = Math.floor(diffMs / 6e4);
|
|
5294
|
+
const diffH = Math.floor(diffMs / 36e5);
|
|
5295
|
+
const diffD = Math.floor(diffMs / 864e5);
|
|
5296
|
+
if (diffMin < 1) return "Just now";
|
|
5297
|
+
if (diffMin < 60) return `${diffMin} min ago`;
|
|
5298
|
+
if (diffH < 24) return diffH === 1 ? "1 hour ago" : `${diffH} hours ago`;
|
|
5299
|
+
if (diffD === 1) return "Yesterday";
|
|
5300
|
+
if (diffD < 7) return `${diffD} days ago`;
|
|
5301
|
+
return d.toLocaleDateString(void 0, { month: "short", day: "numeric" });
|
|
5302
|
+
}
|
|
5303
|
+
function useInboxState(initial) {
|
|
5304
|
+
const [items, setItems] = React52.useState(initial);
|
|
5305
|
+
const unreadCount = items.filter((i) => !i.read).length;
|
|
5306
|
+
const markRead = React52.useCallback((id) => {
|
|
5307
|
+
setItems(
|
|
5308
|
+
(prev) => prev.map((item) => item.id === id ? { ...item, read: true } : item)
|
|
5309
|
+
);
|
|
5310
|
+
}, []);
|
|
5311
|
+
const markAllRead = React52.useCallback(() => {
|
|
5312
|
+
setItems((prev) => prev.map((item) => ({ ...item, read: true })));
|
|
5313
|
+
}, []);
|
|
5314
|
+
const dismiss = React52.useCallback((id) => {
|
|
5315
|
+
setItems((prev) => prev.filter((item) => item.id !== id));
|
|
5316
|
+
}, []);
|
|
5317
|
+
return { items, unreadCount, markRead, markAllRead, dismiss };
|
|
5318
|
+
}
|
|
5319
|
+
var InboxContext = React52.createContext(null);
|
|
5320
|
+
function useInboxContext(part) {
|
|
5321
|
+
const ctx = React52.useContext(InboxContext);
|
|
5322
|
+
if (!ctx) throw new Error(`${part} must be used inside <InboxPopover>`);
|
|
5323
|
+
return ctx;
|
|
5324
|
+
}
|
|
5325
|
+
function InboxPopover({
|
|
5326
|
+
children,
|
|
5327
|
+
className,
|
|
5328
|
+
open: openProp,
|
|
5329
|
+
onOpenChange
|
|
5330
|
+
}) {
|
|
5331
|
+
const [openState, setOpenState] = React52.useState(false);
|
|
5332
|
+
const controlled = openProp !== void 0;
|
|
5333
|
+
const open = controlled ? openProp : openState;
|
|
5334
|
+
const triggerRef = React52.useRef(null);
|
|
5335
|
+
const ref = React52.useRef(null);
|
|
5336
|
+
const setOpen = React52.useCallback(
|
|
5337
|
+
(v) => {
|
|
5338
|
+
if (!controlled) setOpenState(v);
|
|
5339
|
+
onOpenChange?.(v);
|
|
5340
|
+
},
|
|
5341
|
+
[controlled, onOpenChange]
|
|
5342
|
+
);
|
|
5343
|
+
React52.useEffect(() => {
|
|
5344
|
+
if (!open) return;
|
|
5345
|
+
const onDoc = (e) => {
|
|
5346
|
+
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
5347
|
+
};
|
|
5348
|
+
const onEsc = (e) => {
|
|
5349
|
+
if (e.key === "Escape") {
|
|
5350
|
+
setOpen(false);
|
|
5351
|
+
triggerRef.current?.focus();
|
|
5352
|
+
}
|
|
5353
|
+
};
|
|
5354
|
+
document.addEventListener("mousedown", onDoc);
|
|
5355
|
+
document.addEventListener("keydown", onEsc);
|
|
5356
|
+
return () => {
|
|
5357
|
+
document.removeEventListener("mousedown", onDoc);
|
|
5358
|
+
document.removeEventListener("keydown", onEsc);
|
|
5359
|
+
};
|
|
5360
|
+
}, [open, setOpen]);
|
|
5361
|
+
const ctxValue = React52.useMemo(() => ({ open, setOpen, triggerRef }), [open, setOpen]);
|
|
5362
|
+
return /* @__PURE__ */ jsx(InboxContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsx("div", { ref, className: cn("relative inline-block", className), children }) });
|
|
5363
|
+
}
|
|
5364
|
+
function InboxTrigger({
|
|
5365
|
+
unreadCount,
|
|
5366
|
+
label = "Notifications",
|
|
5367
|
+
className,
|
|
5368
|
+
onClick,
|
|
5369
|
+
...props
|
|
5370
|
+
}) {
|
|
5371
|
+
const ctx = useInboxContext("InboxTrigger");
|
|
5372
|
+
const setRef = (node) => {
|
|
5373
|
+
ctx.triggerRef.current = node;
|
|
5374
|
+
};
|
|
5375
|
+
const displayCount = unreadCount == null || unreadCount <= 0 ? null : unreadCount > 99 ? "99+" : String(unreadCount);
|
|
5376
|
+
const defaultLabel = displayCount ? `${label} (${displayCount} unread)` : label;
|
|
5377
|
+
return /* @__PURE__ */ jsxs(
|
|
5378
|
+
"button",
|
|
5379
|
+
{
|
|
5380
|
+
ref: setRef,
|
|
5381
|
+
type: "button",
|
|
5382
|
+
"aria-label": defaultLabel,
|
|
5383
|
+
"aria-haspopup": "dialog",
|
|
5384
|
+
"aria-expanded": ctx.open,
|
|
5385
|
+
className: cn(
|
|
5386
|
+
"relative inline-flex items-center justify-center w-9 h-9 rounded-full",
|
|
5387
|
+
"bg-surface border border-border text-muted-foreground",
|
|
5388
|
+
"hover:bg-muted hover:text-foreground transition-colors",
|
|
5389
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary",
|
|
5390
|
+
className
|
|
5391
|
+
),
|
|
5392
|
+
onClick: (e) => {
|
|
5393
|
+
ctx.setOpen(!ctx.open);
|
|
5394
|
+
onClick?.(e);
|
|
5395
|
+
},
|
|
5396
|
+
...props,
|
|
5397
|
+
children: [
|
|
5398
|
+
/* @__PURE__ */ jsxs(
|
|
5399
|
+
"svg",
|
|
5400
|
+
{
|
|
5401
|
+
width: 16,
|
|
5402
|
+
height: 16,
|
|
5403
|
+
viewBox: "0 0 24 24",
|
|
5404
|
+
fill: "none",
|
|
5405
|
+
stroke: "currentColor",
|
|
5406
|
+
strokeWidth: 2,
|
|
5407
|
+
strokeLinecap: "round",
|
|
5408
|
+
strokeLinejoin: "round",
|
|
5409
|
+
"aria-hidden": "true",
|
|
5410
|
+
children: [
|
|
5411
|
+
/* @__PURE__ */ jsx("path", { d: "M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" }),
|
|
5412
|
+
/* @__PURE__ */ jsx("path", { d: "M13.73 21a2 2 0 0 1-3.46 0" })
|
|
5413
|
+
]
|
|
5414
|
+
}
|
|
5415
|
+
),
|
|
5416
|
+
displayCount != null && /* @__PURE__ */ jsx(
|
|
5417
|
+
"span",
|
|
5418
|
+
{
|
|
5419
|
+
"aria-hidden": "true",
|
|
5420
|
+
className: cn(
|
|
5421
|
+
"absolute -top-0.5 -right-0.5 min-w-[16px] h-4 px-1",
|
|
5422
|
+
"rounded-full bg-primary text-primary-foreground",
|
|
5423
|
+
"text-[9px] font-bold leading-4 flex items-center justify-center"
|
|
5424
|
+
),
|
|
5425
|
+
children: displayCount
|
|
5426
|
+
}
|
|
5427
|
+
)
|
|
5428
|
+
]
|
|
5429
|
+
}
|
|
5430
|
+
);
|
|
5431
|
+
}
|
|
5432
|
+
function NotificationItem({ item, onDismiss, className }) {
|
|
5433
|
+
const leadingSlot = item.avatarSrc ? /* @__PURE__ */ jsx(
|
|
5434
|
+
"img",
|
|
5435
|
+
{
|
|
5436
|
+
src: item.avatarSrc,
|
|
5437
|
+
alt: item.avatarFallback ?? "",
|
|
5438
|
+
className: "w-7 h-7 rounded-full object-cover flex-shrink-0"
|
|
5439
|
+
}
|
|
5440
|
+
) : item.avatarFallback ? /* @__PURE__ */ jsx("span", { className: "w-7 h-7 rounded-full bg-primary/20 text-primary text-[10px] font-bold flex items-center justify-center flex-shrink-0 select-none", children: item.avatarFallback }) : item.icon ? /* @__PURE__ */ jsx("span", { className: "w-7 h-7 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0 text-primary", children: item.icon }) : null;
|
|
5441
|
+
const textBlock = /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
5442
|
+
/* @__PURE__ */ jsx("p", { className: cn("text-sm leading-snug", item.read ? "text-muted-foreground" : "text-foreground"), children: item.title }),
|
|
5443
|
+
item.body && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5 truncate", children: item.body }),
|
|
5444
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-1", children: formatRelativeTime(item.timestamp) })
|
|
5445
|
+
] });
|
|
5446
|
+
const sharedClass = cn(
|
|
5447
|
+
"group relative flex items-start gap-2.5 px-3.5 py-2.5 w-full text-left",
|
|
5448
|
+
"border-b border-border last:border-0",
|
|
5449
|
+
!item.read && "bg-primary/[0.04]",
|
|
5450
|
+
"hover:bg-muted/50 transition-colors",
|
|
5451
|
+
className
|
|
5452
|
+
);
|
|
5453
|
+
const mainContent = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5454
|
+
leadingSlot,
|
|
5455
|
+
textBlock
|
|
5456
|
+
] });
|
|
5457
|
+
const mainAction = item.href ? /* @__PURE__ */ jsx(
|
|
5458
|
+
"a",
|
|
5459
|
+
{
|
|
5460
|
+
href: item.href,
|
|
5461
|
+
onClick: () => item.onClick?.(item.id),
|
|
5462
|
+
className: "flex-1 flex items-start gap-2.5 min-w-0 text-left outline-none",
|
|
5463
|
+
children: mainContent
|
|
5464
|
+
}
|
|
5465
|
+
) : /* @__PURE__ */ jsx(
|
|
5466
|
+
"button",
|
|
5467
|
+
{
|
|
5468
|
+
type: "button",
|
|
5469
|
+
onClick: () => item.onClick?.(item.id),
|
|
5470
|
+
className: "flex-1 flex items-start gap-2.5 min-w-0 text-left outline-none",
|
|
5471
|
+
children: mainContent
|
|
5472
|
+
}
|
|
5473
|
+
);
|
|
5474
|
+
return /* @__PURE__ */ jsxs("div", { className: sharedClass, children: [
|
|
5475
|
+
mainAction,
|
|
5476
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 flex-shrink-0 mt-0.5", children: [
|
|
5477
|
+
/* @__PURE__ */ jsx(
|
|
5478
|
+
"button",
|
|
5479
|
+
{
|
|
5480
|
+
type: "button",
|
|
5481
|
+
"aria-label": "Dismiss",
|
|
5482
|
+
onClick: (e) => {
|
|
5483
|
+
e.preventDefault();
|
|
5484
|
+
e.stopPropagation();
|
|
5485
|
+
onDismiss?.(item.id);
|
|
5486
|
+
},
|
|
5487
|
+
className: cn(
|
|
5488
|
+
"text-muted-foreground hover:text-foreground transition-colors",
|
|
5489
|
+
"opacity-0 group-hover:opacity-100 focus:opacity-100",
|
|
5490
|
+
"text-base leading-none p-0.5 rounded"
|
|
5491
|
+
),
|
|
5492
|
+
children: "\xD7"
|
|
5493
|
+
}
|
|
5494
|
+
),
|
|
5495
|
+
/* @__PURE__ */ jsx(
|
|
5496
|
+
"span",
|
|
5497
|
+
{
|
|
5498
|
+
"aria-label": item.read ? void 0 : "Unread",
|
|
5499
|
+
"aria-hidden": item.read ? true : void 0,
|
|
5500
|
+
className: cn(
|
|
5501
|
+
"w-[7px] h-[7px] rounded-full flex-shrink-0 mt-1",
|
|
5502
|
+
item.read ? "border border-border" : "bg-primary"
|
|
5503
|
+
)
|
|
5504
|
+
}
|
|
5505
|
+
)
|
|
5506
|
+
] })
|
|
5507
|
+
] });
|
|
5508
|
+
}
|
|
5509
|
+
function InboxContent({
|
|
5510
|
+
items,
|
|
5511
|
+
onMarkAllRead,
|
|
5512
|
+
onDismiss,
|
|
5513
|
+
viewAllHref,
|
|
5514
|
+
viewAllLabel = "View all notifications",
|
|
5515
|
+
emptyState,
|
|
5516
|
+
width = 360,
|
|
5517
|
+
maxHeight = 320,
|
|
5518
|
+
className
|
|
5519
|
+
}) {
|
|
5520
|
+
const ctx = useInboxContext("InboxContent");
|
|
5521
|
+
const ref = React52.useRef(null);
|
|
5522
|
+
React52.useEffect(() => {
|
|
5523
|
+
if (ctx.open) ref.current?.focus();
|
|
5524
|
+
}, [ctx.open]);
|
|
5525
|
+
if (!ctx.open) return null;
|
|
5526
|
+
const allRead = items.length === 0 || items.every((i) => i.read);
|
|
5527
|
+
const defaultEmptyState = /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-10 px-5 text-center", children: [
|
|
5528
|
+
/* @__PURE__ */ jsx("span", { className: "w-10 h-10 rounded-full bg-muted flex items-center justify-center mb-3 text-muted-foreground", children: /* @__PURE__ */ jsxs("svg", { width: 18, height: 18, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
5529
|
+
/* @__PURE__ */ jsx("path", { d: "M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" }),
|
|
5530
|
+
/* @__PURE__ */ jsx("path", { d: "M13.73 21a2 2 0 0 1-3.46 0" })
|
|
5531
|
+
] }) }),
|
|
5532
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground", children: "All caught up" }),
|
|
5533
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5", children: "No new notifications" })
|
|
5534
|
+
] });
|
|
5535
|
+
return /* @__PURE__ */ jsxs(
|
|
5536
|
+
"div",
|
|
5537
|
+
{
|
|
5538
|
+
ref,
|
|
5539
|
+
role: "dialog",
|
|
5540
|
+
"aria-label": "Notifications",
|
|
5541
|
+
tabIndex: -1,
|
|
5542
|
+
style: { width },
|
|
5543
|
+
className: cn(
|
|
5544
|
+
"absolute right-0 top-[calc(100%+6px)] z-40",
|
|
5545
|
+
"bg-surface border border-border rounded-kj-md shadow-kj-lg outline-none",
|
|
5546
|
+
"animate-[kjpop_.12s_ease]",
|
|
5547
|
+
className
|
|
5548
|
+
),
|
|
5549
|
+
children: [
|
|
5550
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-3.5 py-2.5 border-b border-border", children: [
|
|
5551
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground", children: "Notifications" }),
|
|
5552
|
+
/* @__PURE__ */ jsx(
|
|
5553
|
+
"button",
|
|
5554
|
+
{
|
|
5555
|
+
type: "button",
|
|
5556
|
+
disabled: allRead,
|
|
5557
|
+
"aria-disabled": allRead,
|
|
5558
|
+
onClick: allRead ? void 0 : onMarkAllRead,
|
|
5559
|
+
className: cn(
|
|
5560
|
+
"text-xs font-medium transition-colors",
|
|
5561
|
+
allRead ? "text-muted-foreground cursor-not-allowed" : "text-primary hover:text-primary/80"
|
|
5562
|
+
),
|
|
5563
|
+
children: "Mark all read"
|
|
5564
|
+
}
|
|
5565
|
+
)
|
|
5566
|
+
] }),
|
|
5567
|
+
items.length === 0 ? emptyState ?? defaultEmptyState : /* @__PURE__ */ jsx(
|
|
5568
|
+
"div",
|
|
5569
|
+
{
|
|
5570
|
+
className: "overflow-y-auto",
|
|
5571
|
+
style: { maxHeight },
|
|
5572
|
+
children: items.map((item) => /* @__PURE__ */ jsx(
|
|
5573
|
+
NotificationItem,
|
|
5574
|
+
{
|
|
5575
|
+
item,
|
|
5576
|
+
onDismiss
|
|
5577
|
+
},
|
|
5578
|
+
item.id
|
|
5579
|
+
))
|
|
5580
|
+
}
|
|
5581
|
+
),
|
|
5582
|
+
items.length > 0 && viewAllHref && /* @__PURE__ */ jsx("div", { className: "border-t border-border px-3.5 py-2.5 text-center", children: /* @__PURE__ */ jsxs(
|
|
5583
|
+
"a",
|
|
5584
|
+
{
|
|
5585
|
+
href: viewAllHref,
|
|
5586
|
+
className: "text-xs font-medium text-primary hover:text-primary/80 transition-colors",
|
|
5587
|
+
children: [
|
|
5588
|
+
viewAllLabel,
|
|
5589
|
+
" \u2192"
|
|
5590
|
+
]
|
|
5591
|
+
}
|
|
5592
|
+
) })
|
|
5593
|
+
]
|
|
5594
|
+
}
|
|
5595
|
+
);
|
|
5596
|
+
}
|
|
5597
|
+
var DEFAULT_COLOR_SWATCHES = [
|
|
5598
|
+
"#EF4444",
|
|
5599
|
+
// Red
|
|
5600
|
+
"#F97316",
|
|
5601
|
+
// Orange
|
|
5602
|
+
"#F59E0B",
|
|
5603
|
+
// Amber
|
|
5604
|
+
"#10B981",
|
|
5605
|
+
// Emerald
|
|
5606
|
+
"#14B8A6",
|
|
5607
|
+
// Teal
|
|
5608
|
+
"#06B6D4",
|
|
5609
|
+
// Sky
|
|
5610
|
+
"#3B82F6",
|
|
5611
|
+
// Blue
|
|
5612
|
+
"#6366F1",
|
|
5613
|
+
// Indigo
|
|
5614
|
+
"#8B5CF6",
|
|
5615
|
+
// Purple
|
|
5616
|
+
"#EC4899",
|
|
5617
|
+
// Pink
|
|
5618
|
+
"#64748B",
|
|
5619
|
+
// Slate
|
|
5620
|
+
"#1E293B"
|
|
5621
|
+
// Charcoal
|
|
5622
|
+
];
|
|
5623
|
+
function isValidHex(hex) {
|
|
5624
|
+
const trimmed = hex.trim();
|
|
5625
|
+
const clean = trimmed.startsWith("#") ? trimmed.slice(1) : trimmed;
|
|
5626
|
+
return /^[0-9A-Fa-f]{3}$|^[0-9A-Fa-f]{6}$/.test(clean);
|
|
5627
|
+
}
|
|
5628
|
+
function normalizeHex(hex) {
|
|
5629
|
+
let clean = hex.trim();
|
|
5630
|
+
if (!clean.startsWith("#")) clean = `#${clean}`;
|
|
5631
|
+
if (/^#[0-9A-Fa-f]{3}$/.test(clean)) {
|
|
5632
|
+
clean = `#${clean[1]}${clean[1]}${clean[2]}${clean[2]}${clean[3]}${clean[3]}`;
|
|
5633
|
+
}
|
|
5634
|
+
return clean.toUpperCase();
|
|
5635
|
+
}
|
|
5636
|
+
function hexToHsl(hex) {
|
|
5637
|
+
if (!isValidHex(hex)) return { h: 0, s: 0, l: 0 };
|
|
5638
|
+
const normalized = normalizeHex(hex);
|
|
5639
|
+
const r = parseInt(normalized.slice(1, 3), 16) / 255;
|
|
5640
|
+
const g = parseInt(normalized.slice(3, 5), 16) / 255;
|
|
5641
|
+
const b = parseInt(normalized.slice(5, 7), 16) / 255;
|
|
5642
|
+
const max = Math.max(r, g, b);
|
|
5643
|
+
const min = Math.min(r, g, b);
|
|
5644
|
+
let h = 0;
|
|
5645
|
+
let s = 0;
|
|
5646
|
+
const l = (max + min) / 2;
|
|
5647
|
+
if (max !== min) {
|
|
5648
|
+
const d = max - min;
|
|
5649
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
5650
|
+
switch (max) {
|
|
5651
|
+
case r:
|
|
5652
|
+
h = (g - b) / d + (g < b ? 6 : 0);
|
|
5653
|
+
break;
|
|
5654
|
+
case g:
|
|
5655
|
+
h = (b - r) / d + 2;
|
|
5656
|
+
break;
|
|
5657
|
+
case b:
|
|
5658
|
+
h = (r - g) / d + 4;
|
|
5659
|
+
break;
|
|
5660
|
+
}
|
|
5661
|
+
h /= 6;
|
|
5662
|
+
}
|
|
5663
|
+
return {
|
|
5664
|
+
h: Math.round(h * 360),
|
|
5665
|
+
s: Math.round(s * 100),
|
|
5666
|
+
l: Math.round(l * 100)
|
|
5667
|
+
};
|
|
5668
|
+
}
|
|
5669
|
+
function hslToHex(h, s, l) {
|
|
5670
|
+
const normalizedH = (h % 360 + 360) % 360;
|
|
5671
|
+
const sPct = s / 100;
|
|
5672
|
+
const lPct = l / 100;
|
|
5673
|
+
const c = (1 - Math.abs(2 * lPct - 1)) * sPct;
|
|
5674
|
+
const x = c * (1 - Math.abs(normalizedH / 60 % 2 - 1));
|
|
5675
|
+
const m = lPct - c / 2;
|
|
5676
|
+
let r = 0;
|
|
5677
|
+
let g = 0;
|
|
5678
|
+
let b = 0;
|
|
5679
|
+
if (0 <= normalizedH && normalizedH < 60) {
|
|
5680
|
+
r = c;
|
|
5681
|
+
g = x;
|
|
5682
|
+
b = 0;
|
|
5683
|
+
} else if (60 <= normalizedH && normalizedH < 120) {
|
|
5684
|
+
r = x;
|
|
5685
|
+
g = c;
|
|
5686
|
+
b = 0;
|
|
5687
|
+
} else if (120 <= normalizedH && normalizedH < 180) {
|
|
5688
|
+
r = 0;
|
|
5689
|
+
g = c;
|
|
5690
|
+
b = x;
|
|
5691
|
+
} else if (180 <= normalizedH && normalizedH < 240) {
|
|
5692
|
+
r = 0;
|
|
5693
|
+
g = x;
|
|
5694
|
+
b = c;
|
|
5695
|
+
} else if (240 <= normalizedH && normalizedH < 300) {
|
|
5696
|
+
r = x;
|
|
5697
|
+
g = 0;
|
|
5698
|
+
b = c;
|
|
5699
|
+
} else if (300 <= normalizedH && normalizedH < 360) {
|
|
5700
|
+
r = c;
|
|
5701
|
+
g = 0;
|
|
5702
|
+
b = x;
|
|
5703
|
+
}
|
|
5704
|
+
const toHex = (n) => {
|
|
5705
|
+
const hex = Math.round((n + m) * 255).toString(16);
|
|
5706
|
+
return hex.length === 1 ? `0${hex}` : hex;
|
|
5707
|
+
};
|
|
5708
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();
|
|
5709
|
+
}
|
|
5710
|
+
var ColorPickerSwatch = React52.forwardRef(({ color, selected = false, size = "md", className, style, ...props }, ref) => {
|
|
5711
|
+
const sizeClasses2 = {
|
|
5712
|
+
sm: "w-5 h-5",
|
|
5713
|
+
md: "w-7 h-7",
|
|
5714
|
+
lg: "w-9 h-9"
|
|
5715
|
+
};
|
|
5716
|
+
return /* @__PURE__ */ jsx(
|
|
5717
|
+
"button",
|
|
5718
|
+
{
|
|
5719
|
+
ref,
|
|
5720
|
+
type: "button",
|
|
5721
|
+
"aria-pressed": selected,
|
|
5722
|
+
style: { backgroundColor: color, ...style },
|
|
5723
|
+
className: cn(
|
|
5724
|
+
"relative shrink-0 rounded-full border border-border/50 shadow-sm transition-transform hover:scale-110 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary flex items-center justify-center text-white text-xs font-bold",
|
|
5725
|
+
sizeClasses2[size],
|
|
5726
|
+
selected && "ring-2 ring-primary ring-offset-2 ring-offset-surface",
|
|
5727
|
+
className
|
|
5728
|
+
),
|
|
5729
|
+
...props,
|
|
5730
|
+
children: selected && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2713" })
|
|
5731
|
+
}
|
|
5732
|
+
);
|
|
5733
|
+
});
|
|
5734
|
+
ColorPickerSwatch.displayName = "ColorPickerSwatch";
|
|
5735
|
+
var ColorPicker = React52.forwardRef(
|
|
5736
|
+
({
|
|
5737
|
+
value,
|
|
5738
|
+
defaultValue = "#3B82F6",
|
|
5739
|
+
onChange,
|
|
5740
|
+
swatches = DEFAULT_COLOR_SWATCHES,
|
|
5741
|
+
disabled = false,
|
|
5742
|
+
showHexInput = true,
|
|
5743
|
+
className,
|
|
5744
|
+
...props
|
|
5745
|
+
}, ref) => {
|
|
5746
|
+
const isControlled = value !== void 0;
|
|
5747
|
+
const [localColor, setLocalColor] = React52.useState(
|
|
5748
|
+
normalizeHex(defaultValue)
|
|
5749
|
+
);
|
|
5750
|
+
const color = isControlled ? normalizeHex(value) : localColor;
|
|
5751
|
+
const [hexInputValue, setHexInputValue] = React52.useState(color);
|
|
5752
|
+
React52.useEffect(() => {
|
|
5753
|
+
setHexInputValue(color);
|
|
5754
|
+
}, [color]);
|
|
5755
|
+
const handleColorChange = React52.useCallback(
|
|
5756
|
+
(newHex) => {
|
|
5757
|
+
const normalized = normalizeHex(newHex);
|
|
5758
|
+
if (!isControlled) {
|
|
5759
|
+
setLocalColor(normalized);
|
|
5760
|
+
}
|
|
5761
|
+
onChange?.(normalized);
|
|
5762
|
+
},
|
|
5763
|
+
[isControlled, onChange]
|
|
5764
|
+
);
|
|
5765
|
+
const hsl = React52.useMemo(() => hexToHsl(color), [color]);
|
|
5766
|
+
const handleHueChange = (e) => {
|
|
5767
|
+
const h = Number(e.target.value);
|
|
5768
|
+
const s = hsl.s === 0 ? 100 : hsl.s;
|
|
5769
|
+
const l = hsl.s === 0 ? 50 : hsl.l;
|
|
5770
|
+
const newHex = hslToHex(h, s, l);
|
|
5771
|
+
handleColorChange(newHex);
|
|
5772
|
+
};
|
|
5773
|
+
const handleHexSubmit = () => {
|
|
5774
|
+
if (isValidHex(hexInputValue)) {
|
|
5775
|
+
handleColorChange(hexInputValue);
|
|
5776
|
+
} else {
|
|
5777
|
+
setHexInputValue(color);
|
|
5778
|
+
}
|
|
5779
|
+
};
|
|
5780
|
+
return /* @__PURE__ */ jsx("div", { ref, className: cn("inline-block", className), ...props, children: /* @__PURE__ */ jsxs(Popover, { children: [
|
|
5781
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
5782
|
+
"button",
|
|
5783
|
+
{
|
|
5784
|
+
type: "button",
|
|
5785
|
+
disabled,
|
|
5786
|
+
"aria-label": `Select color, current ${color}`,
|
|
5787
|
+
className: cn(
|
|
5788
|
+
"inline-flex items-center gap-2.5 px-3 py-1.5 rounded-kj-md border border-border bg-surface text-sm font-medium text-foreground transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary disabled:cursor-not-allowed disabled:opacity-50"
|
|
5789
|
+
),
|
|
5790
|
+
children: [
|
|
5791
|
+
/* @__PURE__ */ jsx(
|
|
5792
|
+
"span",
|
|
5793
|
+
{
|
|
5794
|
+
style: { backgroundColor: color },
|
|
5795
|
+
className: "w-5 h-5 rounded-full border border-border/50 shadow-sm shrink-0"
|
|
5796
|
+
}
|
|
5797
|
+
),
|
|
5798
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-xs uppercase", children: color }),
|
|
5799
|
+
/* @__PURE__ */ jsx(
|
|
5800
|
+
"svg",
|
|
5801
|
+
{
|
|
5802
|
+
width: 12,
|
|
5803
|
+
height: 12,
|
|
5804
|
+
viewBox: "0 0 24 24",
|
|
5805
|
+
fill: "none",
|
|
5806
|
+
stroke: "currentColor",
|
|
5807
|
+
strokeWidth: 2,
|
|
5808
|
+
className: "text-muted-foreground ml-auto",
|
|
5809
|
+
"aria-hidden": "true",
|
|
5810
|
+
children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
|
|
5811
|
+
}
|
|
5812
|
+
)
|
|
5813
|
+
]
|
|
5814
|
+
}
|
|
5815
|
+
) }),
|
|
5816
|
+
/* @__PURE__ */ jsxs(PopoverContent, { side: "bottom", align: "start", className: "w-64 p-3.5 space-y-3.5", children: [
|
|
5817
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 p-2 rounded-kj-md bg-muted/40 border border-border", children: [
|
|
5818
|
+
/* @__PURE__ */ jsx(
|
|
5819
|
+
"span",
|
|
5820
|
+
{
|
|
5821
|
+
style: { backgroundColor: color },
|
|
5822
|
+
className: "w-8 h-8 rounded-full border border-border/50 shadow-sm shrink-0"
|
|
5823
|
+
}
|
|
5824
|
+
),
|
|
5825
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5826
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Selected Color" }),
|
|
5827
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-mono font-bold text-foreground uppercase", children: color })
|
|
5828
|
+
] })
|
|
5829
|
+
] }),
|
|
5830
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
5831
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-muted-foreground", children: "Preset Swatches" }),
|
|
5832
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-6 gap-2", children: swatches.map((swatch) => {
|
|
5833
|
+
const normalizedSwatch = normalizeHex(swatch);
|
|
5834
|
+
return /* @__PURE__ */ jsx(
|
|
5835
|
+
ColorPickerSwatch,
|
|
5836
|
+
{
|
|
5837
|
+
color: swatch,
|
|
5838
|
+
selected: normalizedSwatch === color,
|
|
5839
|
+
"aria-label": `Select color ${swatch}`,
|
|
5840
|
+
onClick: () => handleColorChange(swatch)
|
|
5841
|
+
},
|
|
5842
|
+
swatch
|
|
5843
|
+
);
|
|
5844
|
+
}) })
|
|
5845
|
+
] }),
|
|
5846
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
5847
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-muted-foreground", children: "Hue" }),
|
|
5848
|
+
/* @__PURE__ */ jsx(
|
|
5849
|
+
"input",
|
|
5850
|
+
{
|
|
5851
|
+
type: "range",
|
|
5852
|
+
min: 0,
|
|
5853
|
+
max: 360,
|
|
5854
|
+
value: hsl.h,
|
|
5855
|
+
onChange: handleHueChange,
|
|
5856
|
+
"aria-label": "Hue slider",
|
|
5857
|
+
className: "w-full h-3 rounded-full appearance-none cursor-pointer",
|
|
5858
|
+
style: {
|
|
5859
|
+
background: "linear-gradient(to right, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%)"
|
|
5860
|
+
}
|
|
5861
|
+
}
|
|
5862
|
+
)
|
|
5863
|
+
] }),
|
|
5864
|
+
showHexInput && /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
5865
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-muted-foreground", children: "Hex Code" }),
|
|
5866
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx(
|
|
5867
|
+
"input",
|
|
5868
|
+
{
|
|
5869
|
+
type: "text",
|
|
5870
|
+
"aria-label": "Hex Code",
|
|
5871
|
+
value: hexInputValue,
|
|
5872
|
+
onChange: (e) => setHexInputValue(e.target.value),
|
|
5873
|
+
onBlur: handleHexSubmit,
|
|
5874
|
+
onKeyDown: (e) => {
|
|
5875
|
+
if (e.key === "Enter") handleHexSubmit();
|
|
5876
|
+
},
|
|
5877
|
+
placeholder: "#000000",
|
|
5878
|
+
className: "flex-1 px-2.5 py-1 text-xs font-mono rounded-kj-md border border-border bg-surface text-foreground uppercase focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
|
|
5879
|
+
}
|
|
5880
|
+
) })
|
|
5881
|
+
] })
|
|
5882
|
+
] })
|
|
5883
|
+
] }) });
|
|
5884
|
+
}
|
|
5885
|
+
);
|
|
5886
|
+
ColorPicker.displayName = "ColorPicker";
|
|
5887
|
+
var ColorPickerField = React52.forwardRef(({ label, hint, error, required, ...props }, ref) => {
|
|
5888
|
+
return /* @__PURE__ */ jsxs(Field, { children: [
|
|
5889
|
+
label && /* @__PURE__ */ jsx(Label, { required, children: label }),
|
|
5890
|
+
/* @__PURE__ */ jsx(ColorPicker, { ref, ...props }),
|
|
5891
|
+
hint && /* @__PURE__ */ jsx(Hint, { children: hint }),
|
|
5892
|
+
error && /* @__PURE__ */ jsx(Hint, { error: true, children: error })
|
|
5893
|
+
] });
|
|
5894
|
+
});
|
|
5895
|
+
ColorPickerField.displayName = "ColorPickerField";
|
|
5896
|
+
var COLUMN_CLASSES = {
|
|
5897
|
+
2: "grid-cols-2",
|
|
5898
|
+
3: "grid-cols-3",
|
|
5899
|
+
4: "grid-cols-4",
|
|
5900
|
+
5: "grid-cols-5"
|
|
5901
|
+
};
|
|
5902
|
+
var ASPECT_CLASSES = {
|
|
5903
|
+
square: "aspect-square",
|
|
5904
|
+
video: "aspect-video",
|
|
5905
|
+
"4/3": "aspect-[4/3]",
|
|
5906
|
+
auto: "aspect-auto"
|
|
5907
|
+
};
|
|
5908
|
+
function ImageGallery({
|
|
5909
|
+
images = [],
|
|
5910
|
+
columns = 3,
|
|
5911
|
+
aspectRatio = "square",
|
|
5912
|
+
maxVisible,
|
|
5913
|
+
className,
|
|
5914
|
+
thumbnailClassName,
|
|
5915
|
+
lightboxClassName,
|
|
5916
|
+
onImageClick
|
|
5917
|
+
}) {
|
|
5918
|
+
const [selectedIndex, setSelectedIndex] = React52.useState(null);
|
|
5919
|
+
const columnClass = COLUMN_CLASSES[columns] || "grid-cols-3";
|
|
5920
|
+
const aspectClass = ASPECT_CLASSES[aspectRatio] || (aspectRatio.startsWith("aspect-") ? aspectRatio : `aspect-[${aspectRatio}]`);
|
|
5921
|
+
const hasMaxVisible = typeof maxVisible === "number" && maxVisible > 0 && images.length > maxVisible;
|
|
5922
|
+
const visibleImages = hasMaxVisible ? images.slice(0, maxVisible) : images;
|
|
5923
|
+
const remainingCount = hasMaxVisible ? images.length - maxVisible + 1 : 0;
|
|
5924
|
+
const handleThumbnailClick = (index) => {
|
|
5925
|
+
setSelectedIndex(index);
|
|
5926
|
+
if (onImageClick) {
|
|
5927
|
+
onImageClick(index);
|
|
5928
|
+
}
|
|
5929
|
+
};
|
|
5930
|
+
const handleClose = React52.useCallback(() => {
|
|
5931
|
+
setSelectedIndex(null);
|
|
5932
|
+
}, []);
|
|
5933
|
+
const handleNext = React52.useCallback(() => {
|
|
5934
|
+
setSelectedIndex((prev) => prev === null ? null : (prev + 1) % images.length);
|
|
5935
|
+
}, [images.length]);
|
|
5936
|
+
const handlePrev = React52.useCallback(() => {
|
|
5937
|
+
setSelectedIndex((prev) => prev === null ? null : (prev - 1 + images.length) % images.length);
|
|
5938
|
+
}, [images.length]);
|
|
5939
|
+
React52.useEffect(() => {
|
|
5940
|
+
if (selectedIndex === null) return;
|
|
5941
|
+
const originalOverflow = document.body.style.overflow;
|
|
5942
|
+
const handleKeyDown = (e) => {
|
|
5943
|
+
if (e.key === "Escape") {
|
|
5944
|
+
handleClose();
|
|
5945
|
+
} else if (e.key === "ArrowRight") {
|
|
5946
|
+
handleNext();
|
|
5947
|
+
} else if (e.key === "ArrowLeft") {
|
|
5948
|
+
handlePrev();
|
|
5949
|
+
}
|
|
5950
|
+
};
|
|
5951
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
5952
|
+
document.body.style.overflow = "hidden";
|
|
5953
|
+
return () => {
|
|
5954
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
5955
|
+
document.body.style.overflow = originalOverflow;
|
|
5956
|
+
};
|
|
5957
|
+
}, [selectedIndex, handleClose, handleNext, handlePrev]);
|
|
5958
|
+
const activeImage = selectedIndex !== null ? images[selectedIndex] : null;
|
|
5959
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5960
|
+
/* @__PURE__ */ jsx("div", { className: cn("grid gap-3 w-full", columnClass, className), children: visibleImages.map((img, idx) => {
|
|
5961
|
+
const isLastOverlay = hasMaxVisible && idx === maxVisible - 1;
|
|
5962
|
+
const thumbSrc = img.thumbnailSrc || img.src;
|
|
5963
|
+
const defaultAlt = img.alt || `Thumbnail ${idx + 1}`;
|
|
5964
|
+
const buttonAriaLabel = isLastOverlay ? `View all ${images.length} images` : defaultAlt;
|
|
5965
|
+
return /* @__PURE__ */ jsxs(
|
|
5966
|
+
"button",
|
|
5967
|
+
{
|
|
5968
|
+
type: "button",
|
|
5969
|
+
"aria-label": buttonAriaLabel,
|
|
5970
|
+
onClick: () => handleThumbnailClick(idx),
|
|
5971
|
+
className: cn(
|
|
5972
|
+
"relative group overflow-hidden rounded-kj-lg bg-muted focus:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 cursor-pointer transition-all duration-200",
|
|
5973
|
+
aspectClass,
|
|
5974
|
+
thumbnailClassName
|
|
5975
|
+
),
|
|
5976
|
+
children: [
|
|
5977
|
+
/* @__PURE__ */ jsx(
|
|
5978
|
+
"img",
|
|
5979
|
+
{
|
|
5980
|
+
src: thumbSrc,
|
|
5981
|
+
alt: img.alt || `Thumbnail ${idx + 1}`,
|
|
5982
|
+
className: "w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
|
|
5983
|
+
}
|
|
5984
|
+
),
|
|
5985
|
+
isLastOverlay && /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 bg-black/60 backdrop-blur-[2px] flex items-center justify-center text-white font-bold text-xl group-hover:bg-black/70 transition-colors", children: [
|
|
5986
|
+
"+",
|
|
5987
|
+
remainingCount
|
|
5988
|
+
] })
|
|
5989
|
+
]
|
|
5990
|
+
},
|
|
5991
|
+
idx
|
|
5992
|
+
);
|
|
5993
|
+
}) }),
|
|
5994
|
+
selectedIndex !== null && activeImage && /* @__PURE__ */ jsxs(
|
|
5995
|
+
"div",
|
|
5996
|
+
{
|
|
5997
|
+
role: "dialog",
|
|
5998
|
+
"aria-modal": "true",
|
|
5999
|
+
"aria-label": "Image gallery lightbox",
|
|
6000
|
+
onClick: (e) => e.target === e.currentTarget && handleClose(),
|
|
6001
|
+
className: cn(
|
|
6002
|
+
"fixed inset-0 z-[100] bg-black/85 backdrop-blur-md flex flex-col justify-between p-4 sm:p-6 transition-opacity duration-200 animate-in fade-in-0",
|
|
6003
|
+
lightboxClassName
|
|
6004
|
+
),
|
|
6005
|
+
children: [
|
|
6006
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between w-full text-white z-10", children: [
|
|
6007
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm font-medium text-white/80 select-none", children: [
|
|
6008
|
+
selectedIndex + 1,
|
|
6009
|
+
" of ",
|
|
6010
|
+
images.length
|
|
6011
|
+
] }),
|
|
6012
|
+
/* @__PURE__ */ jsx(
|
|
6013
|
+
"button",
|
|
6014
|
+
{
|
|
6015
|
+
type: "button",
|
|
6016
|
+
onClick: handleClose,
|
|
6017
|
+
"aria-label": "Close",
|
|
6018
|
+
className: "p-2 rounded-full text-white/80 hover:text-white hover:bg-white/10 transition-colors cursor-pointer",
|
|
6019
|
+
children: /* @__PURE__ */ jsx("svg", { width: 20, height: 20, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) })
|
|
6020
|
+
}
|
|
6021
|
+
)
|
|
6022
|
+
] }),
|
|
6023
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex-1 flex items-center justify-between gap-4 my-auto min-h-0 w-full", children: [
|
|
6024
|
+
images.length > 1 && /* @__PURE__ */ jsx(
|
|
6025
|
+
"button",
|
|
6026
|
+
{
|
|
6027
|
+
type: "button",
|
|
6028
|
+
onClick: handlePrev,
|
|
6029
|
+
"aria-label": "Previous image",
|
|
6030
|
+
className: "p-3 rounded-full text-white/80 hover:text-white hover:bg-white/10 transition-colors cursor-pointer z-10 shrink-0",
|
|
6031
|
+
children: /* @__PURE__ */ jsx("svg", { width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" }) })
|
|
6032
|
+
}
|
|
6033
|
+
),
|
|
6034
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 flex flex-col items-center justify-center h-full min-h-0 px-2 select-none", children: /* @__PURE__ */ jsx(
|
|
6035
|
+
"img",
|
|
6036
|
+
{
|
|
6037
|
+
src: activeImage.src,
|
|
6038
|
+
alt: activeImage.alt || `Image ${selectedIndex + 1}`,
|
|
6039
|
+
className: "max-h-[70vh] max-w-full object-contain rounded-kj-md shadow-2xl transition-transform"
|
|
6040
|
+
}
|
|
6041
|
+
) }),
|
|
6042
|
+
images.length > 1 && /* @__PURE__ */ jsx(
|
|
6043
|
+
"button",
|
|
6044
|
+
{
|
|
6045
|
+
type: "button",
|
|
6046
|
+
onClick: handleNext,
|
|
6047
|
+
"aria-label": "Next image",
|
|
6048
|
+
className: "p-3 rounded-full text-white/80 hover:text-white hover:bg-white/10 transition-colors cursor-pointer z-10 shrink-0",
|
|
6049
|
+
children: /* @__PURE__ */ jsx("svg", { width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }) })
|
|
6050
|
+
}
|
|
6051
|
+
)
|
|
6052
|
+
] }),
|
|
6053
|
+
(activeImage.title || activeImage.caption) && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center text-center text-white pb-2 z-10", children: [
|
|
6054
|
+
activeImage.title && /* @__PURE__ */ jsx("h3", { className: "text-base font-semibold text-white", children: activeImage.title }),
|
|
6055
|
+
activeImage.caption && /* @__PURE__ */ jsx("p", { className: "text-xs sm:text-sm text-white/70 max-w-lg mt-0.5", children: activeImage.caption })
|
|
6056
|
+
] })
|
|
6057
|
+
]
|
|
6058
|
+
}
|
|
6059
|
+
)
|
|
6060
|
+
] });
|
|
6061
|
+
}
|
|
2477
6062
|
|
|
2478
|
-
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, Avatar, AvatarGroup, Badge, BottomNavigation, BottomSheet, BottomSheetContent, BottomSheetDescription, BottomSheetFooter, BottomSheetHeader, BottomSheetTitle, Breadcrumb, BreadcrumbItem, BreadcrumbSeparator, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, CheckboxField, CodeBlock, CommandPalette, ConfirmDialog, DashboardShell, DataTable, DetailPageLayout, Drawer, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, EmptyState, ErrorState, Fab, Field, FormField, Hint, Input, Kbd, Label, MetricCard, Modal, ModalActions, ModalDescription, ModalTitle, PageHeader, Pagination, Popover, PopoverContent, PopoverTrigger, Progress, Radio, Segmented, Select, SelectField, SettingsLayout, SidebarNav, Skeleton, Slider, Spinner, Stat, Switch, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, TableToolbar, TableWrap, Tabs, TabsContent, TabsList, TabsTrigger, TextField, Textarea, ToastProvider, Tooltip, badgeVariants, buttonVariants, cn, sliderThumbCSS, useToast };
|
|
6063
|
+
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AppShell, AppShellBanner, AppShellFooter, AppShellHeader, AppShellMain, Avatar, AvatarGroup, Badge, BlogCard, BottomNavigation, BottomSheet, BottomSheetContent, BottomSheetDescription, BottomSheetFooter, BottomSheetHeader, BottomSheetTitle, Breadcrumb, BreadcrumbItem, BreadcrumbSeparator, Button, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, CheckboxField, CodeBlock, ColorPicker, ColorPickerField, ColorPickerSwatch, Combobox, ComboboxField, CommandPalette, ConfirmDialog, DEFAULT_COLOR_SWATCHES, DashboardShell, DataTable, DatePicker, DatePickerField, DateRangePicker, DateRangePickerField, DetailPageLayout, Drawer, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, Dropzone, EmptyState, ErrorState, Fab, Field, FileUpload, FileUploadField, FormField, Hint, ImageGallery, InboxContent, InboxPopover, InboxTrigger, Input, Kbd, Label, MetricCard, Modal, ModalActions, ModalDescription, ModalTitle, NotificationItem, PageHeader, Pagination, Popover, PopoverContent, PopoverTrigger, PricingCard, Progress, ProgressRing, ProgressRingField, ProjectCard, Radio, RangeCalendar, Rating, RatingField, RatingSummary, SectionHeader, Segmented, Select, SelectField, Separator, SettingsLayout, SidebarNav, Skeleton, Slider, Spinner, Stat, Stepper, StepperContent, StepperDescription, StepperIndicator, StepperItem, StepperList, StepperSeparator, StepperTitle, StepperTrigger, Switch, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, TableToolbar, TableWrap, Tabs, TabsContent, TabsList, TabsTrigger, TextField, Textarea, Timeline, TimelineConnector, TimelineContent, TimelineDot, TimelineItem, TimelineSeparator, TimelineTime, TimelineTitle, ToastProvider, ToggleGroup, Tooltip, badgeVariants, buttonVariants, cn, formatRelativeTime, hexToHsl, hslToHex, isValidHex, normalizeHex, sliderThumbCSS, useInboxState, useStepper, useToast };
|
|
2479
6064
|
//# sourceMappingURL=index.js.map
|
|
2480
6065
|
//# sourceMappingURL=index.js.map
|