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