@wow-two-beta/ui 0.0.21 → 0.0.23

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.
Files changed (67) hide show
  1. package/dist/{chunk-WRPLV6H2.js → chunk-5KVTU5TX.js} +262 -105
  2. package/dist/chunk-5KVTU5TX.js.map +1 -0
  3. package/dist/chunk-XAJKBU6P.js +145 -0
  4. package/dist/chunk-XAJKBU6P.js.map +1 -0
  5. package/dist/{chunk-CBOC2DT2.js → chunk-XLPFX4WK.js} +1323 -3
  6. package/dist/chunk-XLPFX4WK.js.map +1 -0
  7. package/dist/display/animatedNumber/AnimatedNumber.d.ts +14 -0
  8. package/dist/display/animatedNumber/AnimatedNumber.d.ts.map +1 -0
  9. package/dist/display/animatedNumber/index.d.ts +2 -0
  10. package/dist/display/animatedNumber/index.d.ts.map +1 -0
  11. package/dist/display/confetti/Confetti.d.ts +31 -0
  12. package/dist/display/confetti/Confetti.d.ts.map +1 -0
  13. package/dist/display/confetti/index.d.ts +2 -0
  14. package/dist/display/confetti/index.d.ts.map +1 -0
  15. package/dist/display/countUp/CountUp.d.ts +17 -0
  16. package/dist/display/countUp/CountUp.d.ts.map +1 -0
  17. package/dist/display/countUp/index.d.ts +2 -0
  18. package/dist/display/countUp/index.d.ts.map +1 -0
  19. package/dist/display/eventCalendar/EventCalendar.d.ts +30 -0
  20. package/dist/display/eventCalendar/EventCalendar.d.ts.map +1 -0
  21. package/dist/display/eventCalendar/index.d.ts +2 -0
  22. package/dist/display/eventCalendar/index.d.ts.map +1 -0
  23. package/dist/display/gantt/Gantt.d.ts +38 -0
  24. package/dist/display/gantt/Gantt.d.ts.map +1 -0
  25. package/dist/display/gantt/index.d.ts +2 -0
  26. package/dist/display/gantt/index.d.ts.map +1 -0
  27. package/dist/display/gradientText/GradientText.d.ts +18 -0
  28. package/dist/display/gradientText/GradientText.d.ts.map +1 -0
  29. package/dist/display/gradientText/index.d.ts +2 -0
  30. package/dist/display/gradientText/index.d.ts.map +1 -0
  31. package/dist/display/index.d.ts +11 -0
  32. package/dist/display/index.d.ts.map +1 -1
  33. package/dist/display/index.js +2 -2
  34. package/dist/display/marquee/Marquee.d.ts +16 -0
  35. package/dist/display/marquee/Marquee.d.ts.map +1 -0
  36. package/dist/display/marquee/index.d.ts +2 -0
  37. package/dist/display/marquee/index.d.ts.map +1 -0
  38. package/dist/display/scheduleView/ScheduleView.d.ts +30 -0
  39. package/dist/display/scheduleView/ScheduleView.d.ts.map +1 -0
  40. package/dist/display/scheduleView/index.d.ts +2 -0
  41. package/dist/display/scheduleView/index.d.ts.map +1 -0
  42. package/dist/display/scrollReveal/ScrollReveal.d.ts +18 -0
  43. package/dist/display/scrollReveal/ScrollReveal.d.ts.map +1 -0
  44. package/dist/display/scrollReveal/index.d.ts +2 -0
  45. package/dist/display/scrollReveal/index.d.ts.map +1 -0
  46. package/dist/display/tilt/Tilt.d.ts +15 -0
  47. package/dist/display/tilt/Tilt.d.ts.map +1 -0
  48. package/dist/display/tilt/index.d.ts +2 -0
  49. package/dist/display/tilt/index.d.ts.map +1 -0
  50. package/dist/display/typewriter/Typewriter.d.ts +18 -0
  51. package/dist/display/typewriter/Typewriter.d.ts.map +1 -0
  52. package/dist/display/typewriter/index.d.ts +2 -0
  53. package/dist/display/typewriter/index.d.ts.map +1 -0
  54. package/dist/forms/index.d.ts +1 -0
  55. package/dist/forms/index.d.ts.map +1 -1
  56. package/dist/forms/index.js +2 -2
  57. package/dist/forms/recurrenceEditor/RecurrenceEditor.d.ts +28 -0
  58. package/dist/forms/recurrenceEditor/RecurrenceEditor.d.ts.map +1 -0
  59. package/dist/forms/recurrenceEditor/index.d.ts +2 -0
  60. package/dist/forms/recurrenceEditor/index.d.ts.map +1 -0
  61. package/dist/index.css +24 -0
  62. package/dist/index.js +3 -3
  63. package/package.json +1 -1
  64. package/dist/chunk-CBOC2DT2.js.map +0 -1
  65. package/dist/chunk-WRPLV6H2.js.map +0 -1
  66. package/dist/chunk-ZCA365IX.js +0 -44
  67. package/dist/chunk-ZCA365IX.js.map +0 -1
@@ -1,3 +1,4 @@
1
+ import { addDays, MONTHS_LONG, startOfDay, buildMonthGrid, WEEKDAYS_SHORT, isToday, addMonths, isSameDay } from './chunk-XAJKBU6P.js';
1
2
  import { tv, dataAttr } from './chunk-BMBIZLO4.js';
2
3
  import { useClipboard, useControlled } from './chunk-4P2TFUVW.js';
3
4
  import { Icon } from './chunk-TDX22OWF.js';
@@ -3588,7 +3589,1326 @@ var PDFViewer = forwardRef(function PDFViewer2({
3588
3589
  }
3589
3590
  );
3590
3591
  });
3592
+ function dateAtHour(base, hour, minute = 0) {
3593
+ const c = new Date(base);
3594
+ c.setHours(hour, minute, 0, 0);
3595
+ return c;
3596
+ }
3597
+ function diffMinutes(a, b) {
3598
+ return (b.getTime() - a.getTime()) / 6e4;
3599
+ }
3600
+ var ScheduleView = forwardRef(function ScheduleView2({
3601
+ resources,
3602
+ bookings,
3603
+ date = /* @__PURE__ */ new Date(),
3604
+ hourRange = [8, 20],
3605
+ slotMinutes = 30,
3606
+ onBookingClick,
3607
+ onSlotClick,
3608
+ renderBooking,
3609
+ className,
3610
+ ...rest
3611
+ }, ref) {
3612
+ const [startHour, endHour] = hourRange;
3613
+ const totalMinutes = (endHour - startHour) * 60;
3614
+ const slotCount = Math.ceil(totalMinutes / slotMinutes);
3615
+ const dayStart = useMemo(() => dateAtHour(date, startHour), [date, startHour]);
3616
+ const bookingsByResource = useMemo(() => {
3617
+ const map = /* @__PURE__ */ new Map();
3618
+ for (const b of bookings) {
3619
+ const list = map.get(b.resourceId);
3620
+ if (list) list.push(b);
3621
+ else map.set(b.resourceId, [b]);
3622
+ }
3623
+ return map;
3624
+ }, [bookings]);
3625
+ return /* @__PURE__ */ jsxs(
3626
+ "div",
3627
+ {
3628
+ ref,
3629
+ role: "grid",
3630
+ "aria-label": "Schedule",
3631
+ className: cn(
3632
+ "overflow-auto rounded-md border border-border bg-card text-sm shadow-sm",
3633
+ className
3634
+ ),
3635
+ ...rest,
3636
+ children: [
3637
+ /* @__PURE__ */ jsxs("div", { className: "sticky top-0 z-10 flex border-b border-border bg-muted/40", children: [
3638
+ /* @__PURE__ */ jsx("div", { className: "w-32 shrink-0 border-r border-border px-3 py-2 text-xs font-medium text-muted-foreground", children: date.toLocaleDateString(void 0, { weekday: "short", month: "short", day: "numeric" }) }),
3639
+ /* @__PURE__ */ jsx("div", { className: "flex-1 grid", style: { gridTemplateColumns: `repeat(${endHour - startHour}, 1fr)` }, children: Array.from({ length: endHour - startHour }, (_, i) => /* @__PURE__ */ jsxs("div", { className: "border-l border-border px-2 py-1 text-xs text-muted-foreground tabular-nums", children: [
3640
+ String((startHour + i) % 24).padStart(2, "0"),
3641
+ ":00"
3642
+ ] }, i)) })
3643
+ ] }),
3644
+ resources.map((resource) => {
3645
+ const items = bookingsByResource.get(resource.id) ?? [];
3646
+ return /* @__PURE__ */ jsxs("div", { role: "row", className: "flex border-b border-border last:border-b-0", children: [
3647
+ /* @__PURE__ */ jsx("div", { className: "w-32 shrink-0 border-r border-border bg-muted/20 px-3 py-2 text-xs font-medium", children: resource.label }),
3648
+ /* @__PURE__ */ jsxs("div", { className: "relative flex-1", style: { height: 56 }, children: [
3649
+ /* @__PURE__ */ jsx(
3650
+ "div",
3651
+ {
3652
+ "aria-hidden": "true",
3653
+ className: "absolute inset-0 grid pointer-events-none",
3654
+ style: { gridTemplateColumns: `repeat(${endHour - startHour}, 1fr)` },
3655
+ children: Array.from({ length: endHour - startHour }, (_, i) => /* @__PURE__ */ jsx("div", { className: "border-l border-border" }, i))
3656
+ }
3657
+ ),
3658
+ onSlotClick && /* @__PURE__ */ jsx(
3659
+ "div",
3660
+ {
3661
+ className: "absolute inset-0 grid",
3662
+ style: { gridTemplateColumns: `repeat(${slotCount}, 1fr)` },
3663
+ children: Array.from({ length: slotCount }, (_, i) => {
3664
+ const slotTime = new Date(dayStart);
3665
+ slotTime.setMinutes(slotTime.getMinutes() + i * slotMinutes);
3666
+ return /* @__PURE__ */ jsx(
3667
+ "button",
3668
+ {
3669
+ type: "button",
3670
+ "aria-label": `Empty slot at ${slotTime.toLocaleTimeString()}`,
3671
+ onClick: () => onSlotClick(resource.id, slotTime),
3672
+ className: "hover:bg-primary-soft/30"
3673
+ },
3674
+ i
3675
+ );
3676
+ })
3677
+ }
3678
+ ),
3679
+ items.map((booking) => {
3680
+ const offsetMin = Math.max(0, diffMinutes(dayStart, booking.start));
3681
+ const durMin = Math.max(15, diffMinutes(booking.start, booking.end));
3682
+ const left = offsetMin / totalMinutes * 100;
3683
+ const width = durMin / totalMinutes * 100;
3684
+ const color = booking.color ?? resource.color;
3685
+ return /* @__PURE__ */ jsx(
3686
+ "button",
3687
+ {
3688
+ type: "button",
3689
+ role: "button",
3690
+ "aria-label": `${resource.label} ${booking.start.toLocaleTimeString()} \u2013 ${booking.end.toLocaleTimeString()}: ${booking.label ?? ""}`,
3691
+ onClick: (e) => {
3692
+ e.stopPropagation();
3693
+ onBookingClick?.(booking);
3694
+ },
3695
+ style: {
3696
+ left: `${left}%`,
3697
+ width: `${width}%`,
3698
+ top: 4,
3699
+ bottom: 4,
3700
+ background: color
3701
+ },
3702
+ className: cn(
3703
+ "absolute overflow-hidden rounded-md border border-border/60 px-2 py-1 text-left text-xs font-medium transition-colors hover:brightness-95 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
3704
+ !color && "bg-primary-soft text-primary-soft-foreground"
3705
+ ),
3706
+ children: renderBooking ? renderBooking(booking) : /* @__PURE__ */ jsxs(Fragment, { children: [
3707
+ /* @__PURE__ */ jsx("div", { className: "truncate", children: booking.label ?? booking.id }),
3708
+ /* @__PURE__ */ jsxs("div", { className: "text-[10px] opacity-70 tabular-nums", children: [
3709
+ booking.start.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }),
3710
+ " \u2013 ",
3711
+ booking.end.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })
3712
+ ] })
3713
+ ] })
3714
+ },
3715
+ booking.id
3716
+ );
3717
+ })
3718
+ ] })
3719
+ ] }, resource.id);
3720
+ })
3721
+ ]
3722
+ }
3723
+ );
3724
+ });
3725
+ var MS_PER_DAY = 24 * 60 * 60 * 1e3;
3726
+ function startOfDayLocal(d) {
3727
+ const c = new Date(d);
3728
+ c.setHours(0, 0, 0, 0);
3729
+ return c;
3730
+ }
3731
+ function dayDiff(a, b) {
3732
+ return Math.round((startOfDayLocal(b).getTime() - startOfDayLocal(a).getTime()) / MS_PER_DAY);
3733
+ }
3734
+ function* eachDay(from, to) {
3735
+ const cur = startOfDayLocal(from);
3736
+ const end = startOfDayLocal(to);
3737
+ while (cur <= end) {
3738
+ yield new Date(cur);
3739
+ cur.setDate(cur.getDate() + 1);
3740
+ }
3741
+ }
3742
+ var Gantt = forwardRef(function Gantt2({
3743
+ tasks,
3744
+ dependencies = [],
3745
+ milestones = [],
3746
+ from: fromProp,
3747
+ to: toProp,
3748
+ cellWidth = 40,
3749
+ rowHeight = 36,
3750
+ labelWidth = 200,
3751
+ showWeekends = true,
3752
+ onTaskClick,
3753
+ className,
3754
+ ...rest
3755
+ }, ref) {
3756
+ const { from, to, totalDays } = useMemo(() => {
3757
+ if (tasks.length === 0) {
3758
+ const now = /* @__PURE__ */ new Date();
3759
+ return { from: now, to: now, totalDays: 1 };
3760
+ }
3761
+ const minStart = fromProp ?? new Date(Math.min(...tasks.map((t) => t.start.getTime())));
3762
+ const maxEnd = toProp ?? new Date(Math.max(...tasks.map((t) => t.end.getTime())));
3763
+ return {
3764
+ from: startOfDayLocal(minStart),
3765
+ to: startOfDayLocal(maxEnd),
3766
+ totalDays: dayDiff(minStart, maxEnd) + 1
3767
+ };
3768
+ }, [tasks, fromProp, toProp]);
3769
+ const headerDates = useMemo(() => Array.from(eachDay(from, to)), [from, to]);
3770
+ const today = startOfDayLocal(/* @__PURE__ */ new Date());
3771
+ const todayOffset = dayDiff(from, today);
3772
+ const todayInRange = todayOffset >= 0 && todayOffset < totalDays;
3773
+ const taskIndex = useMemo(() => new Map(tasks.map((t, i) => [t.id, i])), [tasks]);
3774
+ const timelineWidth = totalDays * cellWidth;
3775
+ return /* @__PURE__ */ jsx(
3776
+ "div",
3777
+ {
3778
+ ref,
3779
+ role: "grid",
3780
+ "aria-label": "Gantt chart",
3781
+ className: cn("overflow-auto rounded-md border border-border bg-card text-sm shadow-sm", className),
3782
+ ...rest,
3783
+ children: /* @__PURE__ */ jsxs("div", { className: "flex", children: [
3784
+ /* @__PURE__ */ jsxs("div", { className: "shrink-0 border-r border-border bg-muted/30", style: { width: labelWidth }, children: [
3785
+ /* @__PURE__ */ jsx("div", { className: "border-b border-border px-3 py-2 text-xs font-medium text-muted-foreground", style: { height: rowHeight }, children: "Task" }),
3786
+ tasks.map((task) => /* @__PURE__ */ jsx(
3787
+ "div",
3788
+ {
3789
+ className: "flex items-center border-b border-border px-3 text-xs last:border-b-0",
3790
+ style: { height: rowHeight },
3791
+ children: /* @__PURE__ */ jsx("span", { className: "truncate font-medium", children: task.label })
3792
+ },
3793
+ task.id
3794
+ ))
3795
+ ] }),
3796
+ /* @__PURE__ */ jsxs("div", { className: "relative flex-1 overflow-x-auto", style: { minWidth: 0 }, children: [
3797
+ /* @__PURE__ */ jsxs("div", { style: { width: timelineWidth }, children: [
3798
+ /* @__PURE__ */ jsx("div", { className: "flex border-b border-border", style: { height: rowHeight }, children: headerDates.map((d, i) => {
3799
+ const isWeekend = d.getDay() === 0 || d.getDay() === 6;
3800
+ const isFirstOfMonth = d.getDate() === 1;
3801
+ return /* @__PURE__ */ jsxs(
3802
+ "div",
3803
+ {
3804
+ className: cn(
3805
+ "border-r border-border text-[10px] tabular-nums",
3806
+ isWeekend && showWeekends && "bg-muted/40",
3807
+ isFirstOfMonth && "border-l-2 border-l-border-strong"
3808
+ ),
3809
+ style: { width: cellWidth },
3810
+ children: [
3811
+ isFirstOfMonth && /* @__PURE__ */ jsx("div", { className: "border-b border-border bg-muted px-1 py-0.5 text-center font-medium text-muted-foreground", children: d.toLocaleDateString(void 0, { month: "short", year: "2-digit" }) }),
3812
+ /* @__PURE__ */ jsx("div", { className: "px-1 py-0.5 text-center text-muted-foreground", children: d.getDate() })
3813
+ ]
3814
+ },
3815
+ i
3816
+ );
3817
+ }) }),
3818
+ tasks.map((task) => {
3819
+ const offset = dayDiff(from, task.start);
3820
+ const length = Math.max(1, dayDiff(task.start, task.end) + 1);
3821
+ const left = offset * cellWidth;
3822
+ const width = length * cellWidth;
3823
+ const progress = task.progress ?? 0;
3824
+ return /* @__PURE__ */ jsxs(
3825
+ "div",
3826
+ {
3827
+ className: "relative border-b border-border last:border-b-0",
3828
+ style: { height: rowHeight },
3829
+ children: [
3830
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex pointer-events-none", children: headerDates.map((d, i) => {
3831
+ const isWeekend = d.getDay() === 0 || d.getDay() === 6;
3832
+ return /* @__PURE__ */ jsx(
3833
+ "div",
3834
+ {
3835
+ style: { width: cellWidth },
3836
+ className: cn(
3837
+ "border-r border-border/60",
3838
+ isWeekend && showWeekends && "bg-muted/30"
3839
+ )
3840
+ },
3841
+ i
3842
+ );
3843
+ }) }),
3844
+ /* @__PURE__ */ jsxs(
3845
+ "button",
3846
+ {
3847
+ type: "button",
3848
+ role: "button",
3849
+ "aria-label": `${typeof task.label === "string" ? task.label : task.id}: ${task.start.toLocaleDateString()} \u2013 ${task.end.toLocaleDateString()}`,
3850
+ onClick: () => onTaskClick?.(task),
3851
+ style: {
3852
+ left: left + 4,
3853
+ width: width - 8,
3854
+ top: 6,
3855
+ bottom: 6,
3856
+ background: task.color
3857
+ },
3858
+ className: cn(
3859
+ "absolute overflow-hidden rounded-md border border-border/60 px-2 text-left text-[11px] font-medium transition-colors hover:brightness-95 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
3860
+ !task.color && "bg-primary text-primary-foreground"
3861
+ ),
3862
+ children: [
3863
+ progress > 0 && /* @__PURE__ */ jsx(
3864
+ "div",
3865
+ {
3866
+ "aria-hidden": "true",
3867
+ className: "absolute inset-y-0 left-0 bg-foreground/20",
3868
+ style: { width: `${progress * 100}%` }
3869
+ }
3870
+ ),
3871
+ /* @__PURE__ */ jsx("span", { className: "relative inline-flex h-full items-center truncate", children: task.label })
3872
+ ]
3873
+ }
3874
+ )
3875
+ ]
3876
+ },
3877
+ task.id
3878
+ );
3879
+ })
3880
+ ] }),
3881
+ /* @__PURE__ */ jsxs(
3882
+ "svg",
3883
+ {
3884
+ className: "pointer-events-none absolute",
3885
+ style: {
3886
+ left: 0,
3887
+ top: rowHeight,
3888
+ width: timelineWidth,
3889
+ height: tasks.length * rowHeight
3890
+ },
3891
+ children: [
3892
+ dependencies.map((dep, i) => {
3893
+ const fromIdx = taskIndex.get(dep.from);
3894
+ const toIdx = taskIndex.get(dep.to);
3895
+ if (fromIdx == null || toIdx == null) return null;
3896
+ const fromTask = tasks[fromIdx];
3897
+ const toTask = tasks[toIdx];
3898
+ const fromX = (dayDiff(from, fromTask.end) + 1) * cellWidth;
3899
+ const fromY = fromIdx * rowHeight + rowHeight / 2;
3900
+ const toX = dayDiff(from, toTask.start) * cellWidth;
3901
+ const toY = toIdx * rowHeight + rowHeight / 2;
3902
+ const midX = Math.max(fromX + 8, (fromX + toX) / 2);
3903
+ const path = `M ${fromX} ${fromY} L ${midX} ${fromY} L ${midX} ${toY} L ${toX - 4} ${toY}`;
3904
+ return /* @__PURE__ */ jsxs("g", { children: [
3905
+ /* @__PURE__ */ jsx("path", { d: path, fill: "none", stroke: "currentColor", strokeWidth: 1, className: "text-border-strong" }),
3906
+ /* @__PURE__ */ jsx(
3907
+ "polygon",
3908
+ {
3909
+ points: `${toX - 4},${toY - 3} ${toX - 4},${toY + 3} ${toX},${toY}`,
3910
+ className: "fill-border-strong"
3911
+ }
3912
+ )
3913
+ ] }, i);
3914
+ }),
3915
+ milestones.map((m) => {
3916
+ const x = dayDiff(from, m.date) * cellWidth + cellWidth / 2;
3917
+ return /* @__PURE__ */ jsx("g", { children: /* @__PURE__ */ jsx(
3918
+ "polygon",
3919
+ {
3920
+ points: `${x},2 ${x + 6},10 ${x},18 ${x - 6},10`,
3921
+ className: "fill-warning stroke-warning-foreground",
3922
+ strokeWidth: 1
3923
+ }
3924
+ ) }, m.id);
3925
+ }),
3926
+ todayInRange && /* @__PURE__ */ jsx(
3927
+ "line",
3928
+ {
3929
+ x1: todayOffset * cellWidth + cellWidth / 2,
3930
+ x2: todayOffset * cellWidth + cellWidth / 2,
3931
+ y1: 0,
3932
+ y2: tasks.length * rowHeight,
3933
+ className: "stroke-primary",
3934
+ strokeWidth: 1.5,
3935
+ strokeDasharray: "4 3"
3936
+ }
3937
+ )
3938
+ ]
3939
+ }
3940
+ )
3941
+ ] })
3942
+ ] })
3943
+ }
3944
+ );
3945
+ });
3946
+ function startOfWeek(d, weekStart) {
3947
+ const c = startOfDay(d);
3948
+ const diff = (c.getDay() - weekStart + 7) % 7;
3949
+ c.setDate(c.getDate() - diff);
3950
+ return c;
3951
+ }
3952
+ function isInRange(d, start, end) {
3953
+ return d >= startOfDay(start) && d <= startOfDay(end);
3954
+ }
3955
+ function minutesSince(reference, target) {
3956
+ return (target.getTime() - reference.getTime()) / 6e4;
3957
+ }
3958
+ var EventCalendar = forwardRef(
3959
+ function EventCalendar2({
3960
+ events,
3961
+ view: viewProp,
3962
+ defaultView = "month",
3963
+ onViewChange,
3964
+ date: dateProp,
3965
+ defaultDate,
3966
+ onDateChange,
3967
+ weekStart = 0,
3968
+ hourRange = [0, 24],
3969
+ onEventClick,
3970
+ onSlotClick,
3971
+ className,
3972
+ ...rest
3973
+ }, ref) {
3974
+ const [view, setView] = useControlled({
3975
+ controlled: viewProp,
3976
+ default: defaultView,
3977
+ onChange: onViewChange
3978
+ });
3979
+ const [date, setDate] = useControlled({
3980
+ controlled: dateProp,
3981
+ default: defaultDate ?? /* @__PURE__ */ new Date(),
3982
+ onChange: onDateChange
3983
+ });
3984
+ const sortedEvents = useMemo(
3985
+ () => [...events].sort((a, b) => a.start.getTime() - b.start.getTime()),
3986
+ [events]
3987
+ );
3988
+ const goPrev = () => {
3989
+ switch (view) {
3990
+ case "month":
3991
+ setDate(addMonths(date, -1));
3992
+ break;
3993
+ case "week":
3994
+ setDate(addDays(date, -7));
3995
+ break;
3996
+ case "day":
3997
+ case "agenda":
3998
+ setDate(addDays(date, -1));
3999
+ break;
4000
+ }
4001
+ };
4002
+ const goNext = () => {
4003
+ switch (view) {
4004
+ case "month":
4005
+ setDate(addMonths(date, 1));
4006
+ break;
4007
+ case "week":
4008
+ setDate(addDays(date, 7));
4009
+ break;
4010
+ case "day":
4011
+ case "agenda":
4012
+ setDate(addDays(date, 1));
4013
+ break;
4014
+ }
4015
+ };
4016
+ const goToday = () => setDate(/* @__PURE__ */ new Date());
4017
+ const title = useMemo(() => {
4018
+ switch (view) {
4019
+ case "month":
4020
+ return `${MONTHS_LONG[date.getMonth()]} ${date.getFullYear()}`;
4021
+ case "week": {
4022
+ const ws = startOfWeek(date, weekStart);
4023
+ const we = addDays(ws, 6);
4024
+ return `${ws.toLocaleDateString(void 0, { month: "short", day: "numeric" })} \u2013 ${we.toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" })}`;
4025
+ }
4026
+ case "day":
4027
+ return date.toLocaleDateString(void 0, { weekday: "long", month: "long", day: "numeric", year: "numeric" });
4028
+ case "agenda":
4029
+ return `Upcoming from ${date.toLocaleDateString(void 0, { month: "short", day: "numeric" })}`;
4030
+ }
4031
+ }, [view, date, weekStart]);
4032
+ return /* @__PURE__ */ jsxs(
4033
+ "div",
4034
+ {
4035
+ ref,
4036
+ className: cn("flex flex-col overflow-hidden rounded-md border border-border bg-card text-sm shadow-sm", className),
4037
+ ...rest,
4038
+ children: [
4039
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-border bg-muted/30 px-3 py-2", children: [
4040
+ /* @__PURE__ */ jsx(
4041
+ "button",
4042
+ {
4043
+ type: "button",
4044
+ onClick: goToday,
4045
+ className: "inline-flex h-7 items-center rounded-md border border-border bg-background px-2.5 text-xs font-medium hover:bg-muted",
4046
+ children: "Today"
4047
+ }
4048
+ ),
4049
+ /* @__PURE__ */ jsx(
4050
+ "button",
4051
+ {
4052
+ type: "button",
4053
+ "aria-label": "Previous",
4054
+ onClick: goPrev,
4055
+ className: "inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:bg-muted",
4056
+ children: /* @__PURE__ */ jsx(Icon, { icon: ChevronLeft, size: 14 })
4057
+ }
4058
+ ),
4059
+ /* @__PURE__ */ jsx(
4060
+ "button",
4061
+ {
4062
+ type: "button",
4063
+ "aria-label": "Next",
4064
+ onClick: goNext,
4065
+ className: "inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:bg-muted",
4066
+ children: /* @__PURE__ */ jsx(Icon, { icon: ChevronRight, size: 14 })
4067
+ }
4068
+ ),
4069
+ /* @__PURE__ */ jsx("h3", { className: "ml-1 text-base font-semibold", children: title }),
4070
+ /* @__PURE__ */ jsx("div", { role: "radiogroup", "aria-label": "View", className: "ml-auto flex items-center gap-0.5 rounded-md bg-card p-0.5 ring-1 ring-border", children: ["month", "week", "day", "agenda"].map((v) => /* @__PURE__ */ jsx(
4071
+ "button",
4072
+ {
4073
+ type: "button",
4074
+ role: "radio",
4075
+ "aria-checked": view === v,
4076
+ onClick: () => setView(v),
4077
+ className: cn(
4078
+ "inline-flex h-6 items-center rounded px-2 text-xs font-medium transition-colors",
4079
+ view === v ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground"
4080
+ ),
4081
+ children: v
4082
+ },
4083
+ v
4084
+ )) })
4085
+ ] }),
4086
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-auto", style: { minHeight: 0 }, children: [
4087
+ view === "month" && /* @__PURE__ */ jsx(
4088
+ MonthView,
4089
+ {
4090
+ date,
4091
+ events: sortedEvents,
4092
+ weekStart,
4093
+ onEventClick,
4094
+ onSlotClick
4095
+ }
4096
+ ),
4097
+ view === "week" && /* @__PURE__ */ jsx(
4098
+ TimeGridView,
4099
+ {
4100
+ date,
4101
+ events: sortedEvents,
4102
+ days: 7,
4103
+ firstDay: startOfWeek(date, weekStart),
4104
+ hourRange,
4105
+ onEventClick,
4106
+ onSlotClick
4107
+ }
4108
+ ),
4109
+ view === "day" && /* @__PURE__ */ jsx(
4110
+ TimeGridView,
4111
+ {
4112
+ date,
4113
+ events: sortedEvents,
4114
+ days: 1,
4115
+ firstDay: startOfDay(date),
4116
+ hourRange,
4117
+ onEventClick,
4118
+ onSlotClick
4119
+ }
4120
+ ),
4121
+ view === "agenda" && /* @__PURE__ */ jsx(AgendaView, { date, events: sortedEvents, onEventClick })
4122
+ ] })
4123
+ ]
4124
+ }
4125
+ );
4126
+ }
4127
+ );
4128
+ function MonthView({ date, events, weekStart, onEventClick, onSlotClick }) {
4129
+ const grid = buildMonthGrid(date.getFullYear(), date.getMonth());
4130
+ const weekdayHeaders = Array.from({ length: 7 }, (_, i) => WEEKDAYS_SHORT[(i + weekStart) % 7]);
4131
+ const cells = weekStart === 0 ? grid : (() => {
4132
+ const out = grid.slice();
4133
+ return out;
4134
+ })();
4135
+ const eventsForDay = (d) => events.filter((e) => isInRange(d, e.start, e.end));
4136
+ return /* @__PURE__ */ jsxs("div", { className: "grid h-full grid-cols-7 border-l border-t border-border", children: [
4137
+ weekdayHeaders.map((wd) => /* @__PURE__ */ jsx(
4138
+ "div",
4139
+ {
4140
+ className: "border-b border-r border-border bg-muted/40 px-2 py-1 text-xs font-medium uppercase text-muted-foreground",
4141
+ children: wd
4142
+ },
4143
+ wd
4144
+ )),
4145
+ cells.map((cell, i) => {
4146
+ const cellEvents = eventsForDay(cell.date);
4147
+ return /* @__PURE__ */ jsxs(
4148
+ "div",
4149
+ {
4150
+ className: cn(
4151
+ "flex flex-col border-b border-r border-border p-1 text-xs",
4152
+ cell.outOfMonth && "bg-muted/20",
4153
+ isToday(cell.date) && "bg-primary-soft/20"
4154
+ ),
4155
+ style: { minHeight: 96 },
4156
+ children: [
4157
+ /* @__PURE__ */ jsx(
4158
+ "button",
4159
+ {
4160
+ type: "button",
4161
+ onClick: () => onSlotClick?.(cell.date),
4162
+ className: cn(
4163
+ "mb-1 self-start rounded-sm px-1 text-xs tabular-nums transition-colors",
4164
+ cell.outOfMonth ? "text-muted-foreground" : "text-foreground",
4165
+ isToday(cell.date) && "bg-primary text-primary-foreground",
4166
+ "hover:bg-muted"
4167
+ ),
4168
+ children: cell.date.getDate()
4169
+ }
4170
+ ),
4171
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
4172
+ cellEvents.slice(0, 3).map((e) => /* @__PURE__ */ jsxs(
4173
+ "button",
4174
+ {
4175
+ type: "button",
4176
+ onClick: (ev) => {
4177
+ ev.stopPropagation();
4178
+ onEventClick?.(e);
4179
+ },
4180
+ style: { background: e.color },
4181
+ className: cn(
4182
+ "truncate rounded-sm px-1.5 py-0.5 text-left text-[11px] font-medium transition-colors hover:brightness-95",
4183
+ !e.color && "bg-primary-soft text-primary-soft-foreground"
4184
+ ),
4185
+ "aria-label": `${typeof e.title === "string" ? e.title : e.id} at ${e.start.toLocaleTimeString()}`,
4186
+ children: [
4187
+ e.allDay ? "\u2022 " : "",
4188
+ e.title ?? "(no title)"
4189
+ ]
4190
+ },
4191
+ e.id
4192
+ )),
4193
+ cellEvents.length > 3 && /* @__PURE__ */ jsxs("span", { className: "px-1 text-[10px] text-muted-foreground", children: [
4194
+ "+",
4195
+ cellEvents.length - 3,
4196
+ " more"
4197
+ ] })
4198
+ ] })
4199
+ ]
4200
+ },
4201
+ i
4202
+ );
4203
+ })
4204
+ ] });
4205
+ }
4206
+ function TimeGridView({ events, days, firstDay, hourRange, onEventClick, onSlotClick }) {
4207
+ const [startHour, endHour] = hourRange;
4208
+ const visibleHours = endHour - startHour;
4209
+ const HOUR_PX = 48;
4210
+ const dayDates = Array.from({ length: days }, (_, i) => addDays(firstDay, i));
4211
+ const eventsForDay = (d) => events.filter((e) => !e.allDay && isSameDay(d, e.start));
4212
+ const allDayForDay = (d) => events.filter((e) => e.allDay && isInRange(d, e.start, e.end));
4213
+ return /* @__PURE__ */ jsxs("div", { className: "flex", children: [
4214
+ /* @__PURE__ */ jsxs("div", { className: "w-14 shrink-0 border-r border-border", children: [
4215
+ /* @__PURE__ */ jsx("div", { className: "h-6 border-b border-border bg-muted/40" }),
4216
+ /* @__PURE__ */ jsx("div", { className: "h-7 border-b border-border bg-muted/20" }),
4217
+ Array.from({ length: visibleHours }, (_, i) => /* @__PURE__ */ jsxs(
4218
+ "div",
4219
+ {
4220
+ className: "border-b border-border bg-muted/10 px-1 text-[10px] tabular-nums text-muted-foreground",
4221
+ style: { height: HOUR_PX },
4222
+ children: [
4223
+ String((startHour + i) % 24).padStart(2, "0"),
4224
+ ":00"
4225
+ ]
4226
+ },
4227
+ i
4228
+ ))
4229
+ ] }),
4230
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-x-auto", children: /* @__PURE__ */ jsxs("div", { className: "grid", style: { gridTemplateColumns: `repeat(${days}, minmax(120px, 1fr))` }, children: [
4231
+ dayDates.map((d, i) => /* @__PURE__ */ jsx(
4232
+ "div",
4233
+ {
4234
+ className: cn(
4235
+ "h-6 border-b border-r border-border bg-muted/40 px-2 text-xs font-medium",
4236
+ isToday(d) && "bg-primary-soft/30"
4237
+ ),
4238
+ children: d.toLocaleDateString(void 0, { weekday: "short", day: "numeric" })
4239
+ },
4240
+ `h-${i}`
4241
+ )),
4242
+ dayDates.map((d, i) => {
4243
+ const list = allDayForDay(d);
4244
+ return /* @__PURE__ */ jsx(
4245
+ "div",
4246
+ {
4247
+ className: "h-7 border-b border-r border-border bg-muted/10 p-0.5 text-[11px]",
4248
+ children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-0.5", children: list.map((e) => /* @__PURE__ */ jsx(
4249
+ "button",
4250
+ {
4251
+ type: "button",
4252
+ onClick: (ev) => {
4253
+ ev.stopPropagation();
4254
+ onEventClick?.(e);
4255
+ },
4256
+ style: { background: e.color },
4257
+ className: cn(
4258
+ "truncate rounded-sm px-1 py-0.5",
4259
+ !e.color && "bg-primary-soft text-primary-soft-foreground"
4260
+ ),
4261
+ children: e.title ?? "(no title)"
4262
+ },
4263
+ e.id
4264
+ )) })
4265
+ },
4266
+ `ad-${i}`
4267
+ );
4268
+ }),
4269
+ dayDates.map((d, di) => {
4270
+ const list = eventsForDay(d);
4271
+ const dayStart = new Date(d);
4272
+ dayStart.setHours(startHour, 0, 0, 0);
4273
+ return /* @__PURE__ */ jsxs(
4274
+ "div",
4275
+ {
4276
+ className: "relative border-r border-border",
4277
+ style: { height: visibleHours * HOUR_PX },
4278
+ children: [
4279
+ Array.from({ length: visibleHours }, (_, i) => /* @__PURE__ */ jsx(
4280
+ "div",
4281
+ {
4282
+ className: "border-b border-border/60",
4283
+ style: { height: HOUR_PX },
4284
+ onClick: () => onSlotClick?.(d, startHour + i)
4285
+ },
4286
+ i
4287
+ )),
4288
+ isToday(d) && (() => {
4289
+ const now = /* @__PURE__ */ new Date();
4290
+ const minutes = now.getHours() * 60 + now.getMinutes() - startHour * 60;
4291
+ if (minutes < 0 || minutes > visibleHours * 60) return null;
4292
+ const top = minutes / 60 * HOUR_PX;
4293
+ return /* @__PURE__ */ jsx(
4294
+ "div",
4295
+ {
4296
+ "aria-hidden": "true",
4297
+ className: "absolute inset-x-0 z-10 border-t border-primary",
4298
+ style: { top }
4299
+ }
4300
+ );
4301
+ })(),
4302
+ list.map((e) => {
4303
+ const start = e.start < dayStart ? dayStart : e.start;
4304
+ const dayEnd = new Date(d);
4305
+ dayEnd.setHours(endHour, 0, 0, 0);
4306
+ const end = e.end > dayEnd ? dayEnd : e.end;
4307
+ const topMin = minutesSince(dayStart, start);
4308
+ const durMin = Math.max(15, minutesSince(start, end));
4309
+ const top = topMin / 60 * HOUR_PX;
4310
+ const height = durMin / 60 * HOUR_PX;
4311
+ return /* @__PURE__ */ jsxs(
4312
+ "button",
4313
+ {
4314
+ type: "button",
4315
+ onClick: (ev) => {
4316
+ ev.stopPropagation();
4317
+ onEventClick?.(e);
4318
+ },
4319
+ style: {
4320
+ top,
4321
+ height,
4322
+ left: 2,
4323
+ right: 2,
4324
+ background: e.color
4325
+ },
4326
+ className: cn(
4327
+ "absolute overflow-hidden rounded-sm border border-border/60 px-1 py-0.5 text-left text-[11px] font-medium transition-colors hover:brightness-95",
4328
+ !e.color && "bg-primary text-primary-foreground"
4329
+ ),
4330
+ children: [
4331
+ /* @__PURE__ */ jsx("div", { className: "truncate", children: e.title ?? "(no title)" }),
4332
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] opacity-80 tabular-nums", children: start.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) })
4333
+ ]
4334
+ },
4335
+ e.id
4336
+ );
4337
+ })
4338
+ ]
4339
+ },
4340
+ `g-${di}`
4341
+ );
4342
+ })
4343
+ ] }) })
4344
+ ] });
4345
+ }
4346
+ function AgendaView({ date, events, onEventClick }) {
4347
+ const horizon = addDays(date, 30);
4348
+ const upcoming = events.filter((e) => e.end >= date && e.start <= horizon);
4349
+ const groups = /* @__PURE__ */ new Map();
4350
+ for (const e of upcoming) {
4351
+ const key = e.start.toDateString();
4352
+ const list = groups.get(key);
4353
+ if (list) list.push(e);
4354
+ else groups.set(key, [e]);
4355
+ }
4356
+ if (groups.size === 0) {
4357
+ return /* @__PURE__ */ jsx("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No upcoming events." });
4358
+ }
4359
+ return /* @__PURE__ */ jsx("ul", { className: "divide-y divide-border", children: Array.from(groups.entries()).map(([key, list]) => {
4360
+ const groupDate = list[0].start;
4361
+ return /* @__PURE__ */ jsxs("li", { className: "px-4 py-3", children: [
4362
+ /* @__PURE__ */ jsx(
4363
+ "div",
4364
+ {
4365
+ className: cn(
4366
+ "mb-2 text-xs font-semibold uppercase text-muted-foreground",
4367
+ isToday(groupDate) && "text-primary"
4368
+ ),
4369
+ children: groupDate.toLocaleDateString(void 0, {
4370
+ weekday: "long",
4371
+ month: "short",
4372
+ day: "numeric"
4373
+ })
4374
+ }
4375
+ ),
4376
+ /* @__PURE__ */ jsx("ul", { className: "space-y-1", children: list.map((e) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
4377
+ "button",
4378
+ {
4379
+ type: "button",
4380
+ onClick: () => onEventClick?.(e),
4381
+ className: "flex w-full items-start gap-3 rounded-md p-2 text-left transition-colors hover:bg-muted",
4382
+ children: [
4383
+ /* @__PURE__ */ jsx(
4384
+ "span",
4385
+ {
4386
+ "aria-hidden": "true",
4387
+ className: "mt-1 h-2 w-2 shrink-0 rounded-full",
4388
+ style: { background: e.color || "var(--color-primary)" }
4389
+ }
4390
+ ),
4391
+ /* @__PURE__ */ jsxs("span", { className: "flex-1", children: [
4392
+ /* @__PURE__ */ jsx("span", { className: "block text-sm font-medium", children: e.title ?? "(no title)" }),
4393
+ /* @__PURE__ */ jsx("span", { className: "block text-xs text-muted-foreground tabular-nums", children: e.allDay ? "All day" : `${e.start.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })} \u2013 ${e.end.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}` })
4394
+ ] })
4395
+ ]
4396
+ }
4397
+ ) }, e.id)) })
4398
+ ] }, key);
4399
+ }) });
4400
+ }
4401
+ var DIR_TO_DEG = {
4402
+ r: 90,
4403
+ l: 270,
4404
+ t: 0,
4405
+ b: 180,
4406
+ tr: 45,
4407
+ br: 135,
4408
+ bl: 225,
4409
+ tl: 315
4410
+ };
4411
+ var GradientText = forwardRef(
4412
+ function GradientText2({
4413
+ from = "var(--color-primary)",
4414
+ via,
4415
+ to = "var(--color-accent, var(--color-primary))",
4416
+ direction = "r",
4417
+ animated,
4418
+ as = "span",
4419
+ className,
4420
+ style,
4421
+ children,
4422
+ ...rest
4423
+ }, ref) {
4424
+ const stops = via ? `${from}, ${via}, ${to}` : `${from}, ${to}`;
4425
+ const Tag = as;
4426
+ return /* @__PURE__ */ jsx(
4427
+ Tag,
4428
+ {
4429
+ ref,
4430
+ className: cn(
4431
+ "inline-block bg-clip-text text-transparent",
4432
+ animated && "motion-safe:animate-[gradient-shift_4s_ease-in-out_infinite]",
4433
+ className
4434
+ ),
4435
+ style: {
4436
+ backgroundImage: `linear-gradient(${DIR_TO_DEG[direction]}deg, ${stops})`,
4437
+ backgroundSize: animated ? "200% 100%" : void 0,
4438
+ ...style
4439
+ },
4440
+ ...rest,
4441
+ children
4442
+ }
4443
+ );
4444
+ }
4445
+ );
4446
+ var easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
4447
+ var defaultFormat = (v) => v.toFixed(0);
4448
+ function prefersReducedMotion() {
4449
+ if (typeof window === "undefined") return false;
4450
+ return window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false;
4451
+ }
4452
+ var CountUp = forwardRef(function CountUp2({
4453
+ to,
4454
+ from = 0,
4455
+ duration = 1500,
4456
+ easing = easeOutCubic,
4457
+ format = defaultFormat,
4458
+ triggerOnView = false,
4459
+ as = "span",
4460
+ className,
4461
+ ...rest
4462
+ }, ref) {
4463
+ const [value, setValue] = useState(triggerOnView ? from : to);
4464
+ const elRef = useRef(null);
4465
+ const startedRef = useRef(false);
4466
+ useEffect(() => {
4467
+ if (prefersReducedMotion()) {
4468
+ setValue(to);
4469
+ return;
4470
+ }
4471
+ if (triggerOnView && typeof IntersectionObserver !== "undefined" && elRef.current) {
4472
+ const obs = new IntersectionObserver(
4473
+ (entries) => {
4474
+ for (const entry of entries) {
4475
+ if (entry.isIntersecting && !startedRef.current) {
4476
+ startedRef.current = true;
4477
+ animate();
4478
+ }
4479
+ }
4480
+ },
4481
+ { threshold: 0.2 }
4482
+ );
4483
+ obs.observe(elRef.current);
4484
+ return () => obs.disconnect();
4485
+ }
4486
+ animate();
4487
+ function animate() {
4488
+ const start = performance.now();
4489
+ let raf = 0;
4490
+ const tick = (now) => {
4491
+ const t = Math.min(1, (now - start) / duration);
4492
+ const eased = easing(t);
4493
+ setValue(from + (to - from) * eased);
4494
+ if (t < 1) raf = requestAnimationFrame(tick);
4495
+ };
4496
+ raf = requestAnimationFrame(tick);
4497
+ return () => cancelAnimationFrame(raf);
4498
+ }
4499
+ }, [to, from, duration, easing, triggerOnView]);
4500
+ const Tag = as;
4501
+ return /* @__PURE__ */ jsx(
4502
+ Tag,
4503
+ {
4504
+ ref: (el) => {
4505
+ elRef.current = el;
4506
+ if (typeof ref === "function") ref(el);
4507
+ else if (ref) ref.current = el;
4508
+ },
4509
+ className: cn("tabular-nums", className),
4510
+ ...rest,
4511
+ children: format(value)
4512
+ }
4513
+ );
4514
+ });
4515
+ var easeOutCubic2 = (t) => 1 - Math.pow(1 - t, 3);
4516
+ var defaultFormat2 = (v) => v.toFixed(0);
4517
+ function prefersReducedMotion2() {
4518
+ if (typeof window === "undefined") return false;
4519
+ return window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false;
4520
+ }
4521
+ var AnimatedNumber = forwardRef(
4522
+ function AnimatedNumber2({
4523
+ value,
4524
+ duration = 500,
4525
+ easing = easeOutCubic2,
4526
+ format = defaultFormat2,
4527
+ as = "span",
4528
+ className,
4529
+ ...rest
4530
+ }, ref) {
4531
+ const [display, setDisplay] = useState(value);
4532
+ const fromRef = useRef(value);
4533
+ useEffect(() => {
4534
+ if (prefersReducedMotion2()) {
4535
+ setDisplay(value);
4536
+ fromRef.current = value;
4537
+ return;
4538
+ }
4539
+ const from = fromRef.current;
4540
+ const to = value;
4541
+ if (from === to) return;
4542
+ const start = performance.now();
4543
+ let raf = 0;
4544
+ const tick = (now) => {
4545
+ const t = Math.min(1, (now - start) / duration);
4546
+ const eased = easing(t);
4547
+ const next = from + (to - from) * eased;
4548
+ setDisplay(next);
4549
+ if (t < 1) raf = requestAnimationFrame(tick);
4550
+ else fromRef.current = to;
4551
+ };
4552
+ raf = requestAnimationFrame(tick);
4553
+ return () => cancelAnimationFrame(raf);
4554
+ }, [value, duration, easing]);
4555
+ const Tag = as;
4556
+ return /* @__PURE__ */ jsx(
4557
+ Tag,
4558
+ {
4559
+ ref,
4560
+ className: cn("tabular-nums", className),
4561
+ ...rest,
4562
+ children: format(display)
4563
+ }
4564
+ );
4565
+ }
4566
+ );
4567
+ var HIDDEN_TRANSFORMS = {
4568
+ fade: "",
4569
+ "slide-up": "translateY(20px)",
4570
+ "slide-down": "translateY(-20px)",
4571
+ "slide-left": "translateX(20px)",
4572
+ "slide-right": "translateX(-20px)",
4573
+ zoom: "scale(0.95)"
4574
+ };
4575
+ var ScrollReveal = forwardRef(
4576
+ function ScrollReveal2({
4577
+ effect = "fade",
4578
+ duration = 600,
4579
+ delay = 0,
4580
+ threshold = 0.1,
4581
+ once = true,
4582
+ as = "div",
4583
+ className,
4584
+ style,
4585
+ children,
4586
+ ...rest
4587
+ }, ref) {
4588
+ const [revealed, setRevealed] = useState(false);
4589
+ const elRef = useRef(null);
4590
+ const reduced = typeof window !== "undefined" && (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false);
4591
+ useEffect(() => {
4592
+ if (reduced) {
4593
+ setRevealed(true);
4594
+ return;
4595
+ }
4596
+ const el = elRef.current;
4597
+ if (!el || typeof IntersectionObserver === "undefined") {
4598
+ setRevealed(true);
4599
+ return;
4600
+ }
4601
+ const obs = new IntersectionObserver(
4602
+ (entries) => {
4603
+ for (const entry of entries) {
4604
+ if (entry.isIntersecting) {
4605
+ setRevealed(true);
4606
+ if (once) obs.disconnect();
4607
+ } else if (!once) {
4608
+ setRevealed(false);
4609
+ }
4610
+ }
4611
+ },
4612
+ { threshold }
4613
+ );
4614
+ obs.observe(el);
4615
+ return () => obs.disconnect();
4616
+ }, [reduced, threshold, once]);
4617
+ const Tag = as;
4618
+ return /* @__PURE__ */ jsx(
4619
+ Tag,
4620
+ {
4621
+ ref: (el) => {
4622
+ elRef.current = el;
4623
+ if (typeof ref === "function") ref(el);
4624
+ else if (ref) ref.current = el;
4625
+ },
4626
+ "data-revealed": revealed || void 0,
4627
+ className: cn(className),
4628
+ style: {
4629
+ opacity: revealed || reduced ? 1 : 0,
4630
+ transform: revealed || reduced ? "none" : HIDDEN_TRANSFORMS[effect],
4631
+ transition: reduced ? void 0 : `opacity ${duration}ms ease-out ${delay}ms, transform ${duration}ms ease-out ${delay}ms`,
4632
+ ...style
4633
+ },
4634
+ ...rest,
4635
+ children
4636
+ }
4637
+ );
4638
+ }
4639
+ );
4640
+ var Tilt = forwardRef(function Tilt2({
4641
+ maxAngle = 12,
4642
+ perspective = 800,
4643
+ glare,
4644
+ scale = 1,
4645
+ as = "div",
4646
+ className,
4647
+ style,
4648
+ children,
4649
+ onPointerMove,
4650
+ onPointerLeave,
4651
+ ...rest
4652
+ }, ref) {
4653
+ const elRef = useRef(null);
4654
+ const [tilt, setTilt] = useState({
4655
+ rotateX: 0,
4656
+ rotateY: 0,
4657
+ glareX: 50,
4658
+ glareY: 50,
4659
+ active: false
4660
+ });
4661
+ const reduced = typeof window !== "undefined" && (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false);
4662
+ const handleMove = (e) => {
4663
+ onPointerMove?.(e);
4664
+ if (reduced) return;
4665
+ const el = elRef.current;
4666
+ if (!el) return;
4667
+ const rect = el.getBoundingClientRect();
4668
+ const x = (e.clientX - rect.left) / rect.width;
4669
+ const y = (e.clientY - rect.top) / rect.height;
4670
+ const rotateY = (x - 0.5) * 2 * maxAngle;
4671
+ const rotateX = (0.5 - y) * 2 * maxAngle;
4672
+ setTilt({ rotateX, rotateY, glareX: x * 100, glareY: y * 100, active: true });
4673
+ };
4674
+ const handleLeave = (e) => {
4675
+ onPointerLeave?.(e);
4676
+ setTilt({ rotateX: 0, rotateY: 0, glareX: 50, glareY: 50, active: false });
4677
+ };
4678
+ const Tag = as;
4679
+ return /* @__PURE__ */ jsxs(
4680
+ Tag,
4681
+ {
4682
+ ref: (el) => {
4683
+ elRef.current = el;
4684
+ if (typeof ref === "function") ref(el);
4685
+ else if (ref) ref.current = el;
4686
+ },
4687
+ onPointerMove: handleMove,
4688
+ onPointerLeave: handleLeave,
4689
+ className: cn("relative", className),
4690
+ style: {
4691
+ perspective,
4692
+ transform: tilt.active && !reduced ? `rotateX(${tilt.rotateX}deg) rotateY(${tilt.rotateY}deg) scale(${scale})` : "rotateX(0) rotateY(0) scale(1)",
4693
+ transition: tilt.active ? "transform 80ms ease-out" : "transform 220ms ease-out",
4694
+ transformStyle: "preserve-3d",
4695
+ ...style
4696
+ },
4697
+ ...rest,
4698
+ children: [
4699
+ children,
4700
+ glare && !reduced && tilt.active && /* @__PURE__ */ jsx(
4701
+ "span",
4702
+ {
4703
+ "aria-hidden": "true",
4704
+ className: "pointer-events-none absolute inset-0 rounded-[inherit] mix-blend-overlay",
4705
+ style: {
4706
+ background: `radial-gradient(circle at ${tilt.glareX}% ${tilt.glareY}%, rgba(255,255,255,0.35) 0%, transparent 50%)`
4707
+ }
4708
+ }
4709
+ )
4710
+ ]
4711
+ }
4712
+ );
4713
+ });
4714
+ var Marquee = forwardRef(function Marquee2({ direction = "left", speed = 30, pauseOnHover = true, gap = 48, className, children, ...rest }, ref) {
4715
+ const horizontal = direction === "left" || direction === "right";
4716
+ const reverse = direction === "right" || direction === "down";
4717
+ const animationName = horizontal ? "marquee-x" : "marquee-y";
4718
+ return /* @__PURE__ */ jsx(
4719
+ "div",
4720
+ {
4721
+ ref,
4722
+ role: "marquee",
4723
+ "data-direction": direction,
4724
+ className: cn(
4725
+ "group/marquee relative overflow-hidden",
4726
+ horizontal ? "flex" : "flex flex-col",
4727
+ className
4728
+ ),
4729
+ style: { "--marquee-gap": `${gap}px` },
4730
+ ...rest,
4731
+ children: [0, 1].map((i) => /* @__PURE__ */ jsx(
4732
+ "div",
4733
+ {
4734
+ "aria-hidden": i === 1,
4735
+ className: cn(
4736
+ "shrink-0 motion-safe:animate-(--marquee-anim) motion-reduce:animation-none",
4737
+ horizontal ? "flex shrink-0 items-center" : "flex flex-col items-center",
4738
+ pauseOnHover && "group-hover/marquee:[animation-play-state:paused]"
4739
+ ),
4740
+ style: {
4741
+ gap,
4742
+ paddingInline: horizontal ? gap / 2 : 0,
4743
+ paddingBlock: horizontal ? 0 : gap / 2,
4744
+ animation: `${animationName} ${speed}s linear infinite ${reverse ? "reverse" : "normal"}`
4745
+ },
4746
+ children
4747
+ },
4748
+ i
4749
+ ))
4750
+ }
4751
+ );
4752
+ });
4753
+ var Typewriter = forwardRef(function Typewriter2({
4754
+ text,
4755
+ typeSpeed = 60,
4756
+ deleteSpeed = 40,
4757
+ pauseBetween = 1500,
4758
+ loop,
4759
+ cursor = true,
4760
+ cursorChar = "\u2502",
4761
+ as = "span",
4762
+ className,
4763
+ ...rest
4764
+ }, ref) {
4765
+ const phrases = useMemo(() => Array.isArray(text) ? text : [text], [text]);
4766
+ const shouldLoop = loop ?? phrases.length > 1;
4767
+ const reduced = typeof window !== "undefined" && (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false);
4768
+ const [phraseIndex, setPhraseIndex] = useState(0);
4769
+ const [charIndex, setCharIndex] = useState(0);
4770
+ const [deleting, setDeleting] = useState(false);
4771
+ useEffect(() => {
4772
+ if (reduced) return;
4773
+ const current = phrases[phraseIndex] ?? "";
4774
+ let timeout;
4775
+ if (!deleting) {
4776
+ if (charIndex < current.length) {
4777
+ timeout = window.setTimeout(() => setCharIndex(charIndex + 1), typeSpeed);
4778
+ } else {
4779
+ if (phrases.length === 1 && !shouldLoop) return;
4780
+ timeout = window.setTimeout(() => setDeleting(true), pauseBetween);
4781
+ }
4782
+ } else {
4783
+ if (charIndex > 0) {
4784
+ timeout = window.setTimeout(() => setCharIndex(charIndex - 1), deleteSpeed);
4785
+ } else {
4786
+ const nextIdx = (phraseIndex + 1) % phrases.length;
4787
+ if (!shouldLoop && nextIdx === 0) return;
4788
+ setDeleting(false);
4789
+ setPhraseIndex(nextIdx);
4790
+ }
4791
+ }
4792
+ return () => window.clearTimeout(timeout);
4793
+ }, [charIndex, deleting, phraseIndex, phrases, typeSpeed, deleteSpeed, pauseBetween, shouldLoop, reduced]);
4794
+ const fullText = reduced ? Array.isArray(text) ? text[0] ?? "" : text : (phrases[phraseIndex] ?? "").slice(0, charIndex);
4795
+ const Tag = as;
4796
+ return /* @__PURE__ */ jsxs(Tag, { ref, className: cn("inline-block", className), ...rest, children: [
4797
+ fullText,
4798
+ cursor && !reduced && /* @__PURE__ */ jsx(
4799
+ "span",
4800
+ {
4801
+ "aria-hidden": "true",
4802
+ className: "ml-0.5 inline-block motion-safe:animate-[blink-caret_1s_step-end_infinite]",
4803
+ children: cursorChar
4804
+ }
4805
+ )
4806
+ ] });
4807
+ });
4808
+ var DEFAULT_COLORS = ["#ef4444", "#f59e0b", "#10b981", "#3b82f6", "#a855f7", "#ec4899"];
4809
+ var nextParticleId = 0;
4810
+ var Confetti = forwardRef(function Confetti2({
4811
+ particleCount = 60,
4812
+ colors = DEFAULT_COLORS,
4813
+ gravity = 1200,
4814
+ spread = 60,
4815
+ velocity = 500,
4816
+ lifetime = 3e3,
4817
+ origin,
4818
+ autoFire
4819
+ }, forwardedRef) {
4820
+ const [particles, setParticles] = useState([]);
4821
+ const rafRef = useRef(null);
4822
+ const lastTimeRef = useRef(0);
4823
+ const reducedRef = useRef(
4824
+ typeof window !== "undefined" && (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false)
4825
+ );
4826
+ const spawn = useCallback(
4827
+ (opts) => {
4828
+ if (reducedRef.current) return;
4829
+ const count = opts?.particleCount ?? particleCount;
4830
+ const palette = opts?.colors ?? colors;
4831
+ const sp = opts?.spread ?? spread;
4832
+ const vel = opts?.velocity ?? velocity;
4833
+ const o = opts?.origin ?? origin;
4834
+ const x = o?.x ?? (typeof window !== "undefined" ? window.innerWidth / 2 : 200);
4835
+ const y = o?.y ?? (typeof window !== "undefined" ? window.innerHeight / 2 : 200);
4836
+ const burst = [];
4837
+ const now = performance.now();
4838
+ for (let i = 0; i < count; i++) {
4839
+ const angle = -Math.PI / 2 + (Math.random() - 0.5) * (sp * Math.PI) / 180;
4840
+ const speed = vel * (0.7 + Math.random() * 0.6);
4841
+ burst.push({
4842
+ id: ++nextParticleId,
4843
+ x,
4844
+ y,
4845
+ vx: Math.cos(angle) * speed + (Math.random() - 0.5) * 60,
4846
+ vy: Math.sin(angle) * speed,
4847
+ rotation: Math.random() * 360,
4848
+ rotationSpeed: (Math.random() - 0.5) * 720,
4849
+ size: 6 + Math.random() * 6,
4850
+ color: palette[Math.floor(Math.random() * palette.length)] ?? "#000",
4851
+ shape: Math.random() > 0.5 ? "rect" : "circle",
4852
+ bornAt: now
4853
+ });
4854
+ }
4855
+ setParticles((prev) => [...prev, ...burst]);
4856
+ },
4857
+ [particleCount, colors, spread, velocity, origin]
4858
+ );
4859
+ useImperativeHandle(forwardedRef, () => ({ fire: spawn }), [spawn]);
4860
+ useEffect(() => {
4861
+ if (autoFire) spawn();
4862
+ }, [autoFire, spawn]);
4863
+ useEffect(() => {
4864
+ if (particles.length === 0) return;
4865
+ const tick = (now) => {
4866
+ const last = lastTimeRef.current || now;
4867
+ const dt = (now - last) / 1e3;
4868
+ lastTimeRef.current = now;
4869
+ const viewportH = typeof window !== "undefined" ? window.innerHeight : 800;
4870
+ setParticles(
4871
+ (prev) => prev.map((p) => ({
4872
+ ...p,
4873
+ x: p.x + p.vx * dt,
4874
+ y: p.y + p.vy * dt,
4875
+ vy: p.vy + gravity * dt,
4876
+ rotation: p.rotation + p.rotationSpeed * dt
4877
+ })).filter((p) => p.y < viewportH + 80 && now - p.bornAt < lifetime)
4878
+ );
4879
+ rafRef.current = requestAnimationFrame(tick);
4880
+ };
4881
+ rafRef.current = requestAnimationFrame(tick);
4882
+ return () => {
4883
+ if (rafRef.current != null) cancelAnimationFrame(rafRef.current);
4884
+ lastTimeRef.current = 0;
4885
+ };
4886
+ }, [particles.length, gravity, lifetime]);
4887
+ if (particles.length === 0) return null;
4888
+ return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(
4889
+ "svg",
4890
+ {
4891
+ "aria-hidden": "true",
4892
+ className: "pointer-events-none fixed inset-0 z-50",
4893
+ style: { width: "100vw", height: "100vh" },
4894
+ children: particles.map(
4895
+ (p) => p.shape === "rect" ? /* @__PURE__ */ jsx(
4896
+ "rect",
4897
+ {
4898
+ x: p.x - p.size / 2,
4899
+ y: p.y - p.size / 2,
4900
+ width: p.size,
4901
+ height: p.size * 0.5,
4902
+ fill: p.color,
4903
+ transform: `rotate(${p.rotation} ${p.x} ${p.y})`
4904
+ },
4905
+ p.id
4906
+ ) : /* @__PURE__ */ jsx("circle", { cx: p.x, cy: p.y, r: p.size / 2, fill: p.color }, p.id)
4907
+ )
4908
+ }
4909
+ ) });
4910
+ });
3591
4911
 
3592
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, AudioPlayer, AudioWaveform, Avatar, AvatarGroup, Badge, BadgeOverlay, Card, Carousel, CarouselDot, CarouselDots, CarouselNext, CarouselPrev, CarouselSlide, CarouselSlides, CarouselViewport, Code, Collapsible, CollapsibleContent, CollapsibleTrigger, CountBadge, DataGrid, DataTable, DescriptionList, DiffViewer, EmptyState, Heading, HeatmapCalendar, Highlight, Image, InfoRow, Kbd, KeyboardShortcut, List, ListItem, Mark, NodeEditor, NotificationDot, PDFViewer, Quote, SectionHeader, Separator, Snippet, Sparkline, Stat, Status, SwipeActions, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeaderCell, TableRow, Tabs, TabsList, TabsPanel, TabsTab, Text, Timeline, TimelineDescription, TimelineItem, TimelineTitle, Tooltip, Tree, TreeGroup, TreeItem, VideoPlayer, avatarVariants, badgeVariants, codeVariants, headingVariants, textVariants };
3593
- //# sourceMappingURL=chunk-CBOC2DT2.js.map
3594
- //# sourceMappingURL=chunk-CBOC2DT2.js.map
4912
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, AnimatedNumber, AudioPlayer, AudioWaveform, Avatar, AvatarGroup, Badge, BadgeOverlay, Card, Carousel, CarouselDot, CarouselDots, CarouselNext, CarouselPrev, CarouselSlide, CarouselSlides, CarouselViewport, Code, Collapsible, CollapsibleContent, CollapsibleTrigger, Confetti, CountBadge, CountUp, DataGrid, DataTable, DescriptionList, DiffViewer, EmptyState, EventCalendar, Gantt, GradientText, Heading, HeatmapCalendar, Highlight, Image, InfoRow, Kbd, KeyboardShortcut, List, ListItem, Mark, Marquee, NodeEditor, NotificationDot, PDFViewer, Quote, ScheduleView, ScrollReveal, SectionHeader, Separator, Snippet, Sparkline, Stat, Status, SwipeActions, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeaderCell, TableRow, Tabs, TabsList, TabsPanel, TabsTab, Text, Tilt, Timeline, TimelineDescription, TimelineItem, TimelineTitle, Tooltip, Tree, TreeGroup, TreeItem, Typewriter, VideoPlayer, avatarVariants, badgeVariants, codeVariants, headingVariants, textVariants };
4913
+ //# sourceMappingURL=chunk-XLPFX4WK.js.map
4914
+ //# sourceMappingURL=chunk-XLPFX4WK.js.map