@wow-two-beta/ui 0.0.21 → 0.0.22

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 (34) 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-CBOC2DT2.js → chunk-6UJX6YAP.js} +813 -3
  4. package/dist/chunk-6UJX6YAP.js.map +1 -0
  5. package/dist/chunk-XAJKBU6P.js +145 -0
  6. package/dist/chunk-XAJKBU6P.js.map +1 -0
  7. package/dist/display/eventCalendar/EventCalendar.d.ts +30 -0
  8. package/dist/display/eventCalendar/EventCalendar.d.ts.map +1 -0
  9. package/dist/display/eventCalendar/index.d.ts +2 -0
  10. package/dist/display/eventCalendar/index.d.ts.map +1 -0
  11. package/dist/display/gantt/Gantt.d.ts +38 -0
  12. package/dist/display/gantt/Gantt.d.ts.map +1 -0
  13. package/dist/display/gantt/index.d.ts +2 -0
  14. package/dist/display/gantt/index.d.ts.map +1 -0
  15. package/dist/display/index.d.ts +3 -0
  16. package/dist/display/index.d.ts.map +1 -1
  17. package/dist/display/index.js +2 -2
  18. package/dist/display/scheduleView/ScheduleView.d.ts +30 -0
  19. package/dist/display/scheduleView/ScheduleView.d.ts.map +1 -0
  20. package/dist/display/scheduleView/index.d.ts +2 -0
  21. package/dist/display/scheduleView/index.d.ts.map +1 -0
  22. package/dist/forms/index.d.ts +1 -0
  23. package/dist/forms/index.d.ts.map +1 -1
  24. package/dist/forms/index.js +2 -2
  25. package/dist/forms/recurrenceEditor/RecurrenceEditor.d.ts +28 -0
  26. package/dist/forms/recurrenceEditor/RecurrenceEditor.d.ts.map +1 -0
  27. package/dist/forms/recurrenceEditor/index.d.ts +2 -0
  28. package/dist/forms/recurrenceEditor/index.d.ts.map +1 -0
  29. package/dist/index.js +3 -3
  30. package/package.json +1 -1
  31. package/dist/chunk-CBOC2DT2.js.map +0 -1
  32. package/dist/chunk-WRPLV6H2.js.map +0 -1
  33. package/dist/chunk-ZCA365IX.js +0 -44
  34. 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,816 @@ 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
+ }
3591
4401
 
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
4402
+ 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, EventCalendar, Gantt, Heading, HeatmapCalendar, Highlight, Image, InfoRow, Kbd, KeyboardShortcut, List, ListItem, Mark, NodeEditor, NotificationDot, PDFViewer, Quote, ScheduleView, 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 };
4403
+ //# sourceMappingURL=chunk-6UJX6YAP.js.map
4404
+ //# sourceMappingURL=chunk-6UJX6YAP.js.map