@wow-two-beta/ui 0.0.20 → 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 (50) 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-RK6VB3II.js → chunk-6UJX6YAP.js} +1575 -5
  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/audioPlayer/AudioPlayer.d.ts +18 -0
  8. package/dist/display/audioPlayer/AudioPlayer.d.ts.map +1 -0
  9. package/dist/display/audioPlayer/index.d.ts +2 -0
  10. package/dist/display/audioPlayer/index.d.ts.map +1 -0
  11. package/dist/display/audioWaveform/AudioWaveform.d.ts +19 -0
  12. package/dist/display/audioWaveform/AudioWaveform.d.ts.map +1 -0
  13. package/dist/display/audioWaveform/index.d.ts +2 -0
  14. package/dist/display/audioWaveform/index.d.ts.map +1 -0
  15. package/dist/display/eventCalendar/EventCalendar.d.ts +30 -0
  16. package/dist/display/eventCalendar/EventCalendar.d.ts.map +1 -0
  17. package/dist/display/eventCalendar/index.d.ts +2 -0
  18. package/dist/display/eventCalendar/index.d.ts.map +1 -0
  19. package/dist/display/gantt/Gantt.d.ts +38 -0
  20. package/dist/display/gantt/Gantt.d.ts.map +1 -0
  21. package/dist/display/gantt/index.d.ts +2 -0
  22. package/dist/display/gantt/index.d.ts.map +1 -0
  23. package/dist/display/index.d.ts +7 -0
  24. package/dist/display/index.d.ts.map +1 -1
  25. package/dist/display/index.js +2 -2
  26. package/dist/display/pdfViewer/PDFViewer.d.ts +22 -0
  27. package/dist/display/pdfViewer/PDFViewer.d.ts.map +1 -0
  28. package/dist/display/pdfViewer/index.d.ts +2 -0
  29. package/dist/display/pdfViewer/index.d.ts.map +1 -0
  30. package/dist/display/scheduleView/ScheduleView.d.ts +30 -0
  31. package/dist/display/scheduleView/ScheduleView.d.ts.map +1 -0
  32. package/dist/display/scheduleView/index.d.ts +2 -0
  33. package/dist/display/scheduleView/index.d.ts.map +1 -0
  34. package/dist/display/videoPlayer/VideoPlayer.d.ts +22 -0
  35. package/dist/display/videoPlayer/VideoPlayer.d.ts.map +1 -0
  36. package/dist/display/videoPlayer/index.d.ts +2 -0
  37. package/dist/display/videoPlayer/index.d.ts.map +1 -0
  38. package/dist/forms/index.d.ts +1 -0
  39. package/dist/forms/index.d.ts.map +1 -1
  40. package/dist/forms/index.js +2 -2
  41. package/dist/forms/recurrenceEditor/RecurrenceEditor.d.ts +28 -0
  42. package/dist/forms/recurrenceEditor/RecurrenceEditor.d.ts.map +1 -0
  43. package/dist/forms/recurrenceEditor/index.d.ts +2 -0
  44. package/dist/forms/recurrenceEditor/index.d.ts.map +1 -0
  45. package/dist/index.js +3 -3
  46. package/package.json +1 -1
  47. package/dist/chunk-RK6VB3II.js.map +0 -1
  48. package/dist/chunk-WRPLV6H2.js.map +0 -1
  49. package/dist/chunk-ZCA365IX.js +0 -44
  50. package/dist/chunk-ZCA365IX.js.map +0 -1
@@ -1,12 +1,13 @@
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';
4
5
  import { Slot, RovingFocusGroup, useRovingFocusItem, Portal, AnchoredPositioner } from './chunk-NC2CBGX2.js';
5
6
  import { composeRefs } from './chunk-DN7WBRIV.js';
6
7
  import { cn } from './chunk-KZ4VFY2T.js';
7
- import { forwardRef, useState, Children, isValidElement, Fragment as Fragment$1, createContext, useId, useMemo, useCallback, useContext, useEffect, useRef, cloneElement } from 'react';
8
+ import { forwardRef, useState, Children, isValidElement, Fragment as Fragment$1, createContext, useId, useMemo, useCallback, useContext, useEffect, useRef, useImperativeHandle, cloneElement } from 'react';
8
9
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
9
- import { TrendingUp, TrendingDown, Check, Copy, ChevronDown, ChevronLeft, ChevronRight, Plus, Minus, Maximize, ArrowUp, ArrowDown, ArrowUpDown } from 'lucide-react';
10
+ import { TrendingUp, TrendingDown, Check, Copy, ChevronDown, ChevronLeft, ChevronRight, Plus, Minus, Maximize, Pause, Play, VolumeX, Volume2, Captions, CaptionsOff, PictureInPicture2, Minimize, ZoomOut, ZoomIn, Download, ArrowUp, ArrowDown, ArrowUpDown } from 'lucide-react';
10
11
 
11
12
  // src/display/heading/Heading.variants.ts
12
13
  var headingVariants = tv({
@@ -2828,7 +2829,1576 @@ var NodeEditor = forwardRef(function NodeEditor2({
2828
2829
  }
2829
2830
  );
2830
2831
  });
2832
+ var TONE_CLASS2 = {
2833
+ brand: "text-primary",
2834
+ success: "text-success",
2835
+ warning: "text-warning",
2836
+ danger: "text-destructive",
2837
+ muted: "text-muted-foreground",
2838
+ current: ""
2839
+ };
2840
+ var AudioWaveform = forwardRef(
2841
+ function AudioWaveform2({
2842
+ peaks,
2843
+ progress = 0,
2844
+ width = 320,
2845
+ height = 48,
2846
+ barWidth = 2,
2847
+ gap = 1,
2848
+ tone = "brand",
2849
+ onSeek,
2850
+ interactive,
2851
+ className,
2852
+ ...rest
2853
+ }, ref) {
2854
+ const stepX = barWidth + gap;
2855
+ const barCount = Math.max(1, Math.floor(width / stepX));
2856
+ const sampled = useMemo(() => sampleTo(peaks, barCount), [peaks, barCount]);
2857
+ const playedBars = Math.round(progress * barCount);
2858
+ const isInteractive = interactive ?? onSeek != null;
2859
+ const seekFromX = (clientX, rect) => {
2860
+ const x = Math.max(0, Math.min(rect.width, clientX - rect.left));
2861
+ onSeek?.(x / rect.width);
2862
+ };
2863
+ const svgRef = useRef(null);
2864
+ const handleKey = (e) => {
2865
+ if (!onSeek) return;
2866
+ if (e.key === "ArrowRight") {
2867
+ e.preventDefault();
2868
+ onSeek(Math.min(1, progress + 0.05));
2869
+ } else if (e.key === "ArrowLeft") {
2870
+ e.preventDefault();
2871
+ onSeek(Math.max(0, progress - 0.05));
2872
+ } else if (e.key === "Home") {
2873
+ e.preventDefault();
2874
+ onSeek(0);
2875
+ } else if (e.key === "End") {
2876
+ e.preventDefault();
2877
+ onSeek(1);
2878
+ }
2879
+ };
2880
+ return /* @__PURE__ */ jsx(
2881
+ "svg",
2882
+ {
2883
+ ref: (el) => {
2884
+ svgRef.current = el;
2885
+ if (typeof ref === "function") ref(el);
2886
+ else if (ref) ref.current = el;
2887
+ },
2888
+ role: isInteractive ? "slider" : "img",
2889
+ "aria-label": "Audio waveform",
2890
+ "aria-valuenow": Math.round(progress * 100),
2891
+ "aria-valuemin": 0,
2892
+ "aria-valuemax": 100,
2893
+ tabIndex: isInteractive ? 0 : -1,
2894
+ width,
2895
+ height,
2896
+ viewBox: `0 0 ${width} ${height}`,
2897
+ preserveAspectRatio: "none",
2898
+ onClick: isInteractive ? (e) => {
2899
+ const rect = svgRef.current?.getBoundingClientRect();
2900
+ if (rect) seekFromX(e.clientX, rect);
2901
+ } : void 0,
2902
+ onKeyDown: handleKey,
2903
+ className: cn(
2904
+ "inline-block",
2905
+ isInteractive && "cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm",
2906
+ TONE_CLASS2[tone],
2907
+ className
2908
+ ),
2909
+ ...rest,
2910
+ children: sampled.map((amp, i) => {
2911
+ const h = Math.max(1, amp * height);
2912
+ const x = i * stepX;
2913
+ const y = (height - h) / 2;
2914
+ const played = i < playedBars;
2915
+ return /* @__PURE__ */ jsx(
2916
+ "rect",
2917
+ {
2918
+ x,
2919
+ y,
2920
+ width: barWidth,
2921
+ height: h,
2922
+ rx: 1,
2923
+ className: played ? "fill-current" : "fill-current opacity-30"
2924
+ },
2925
+ i
2926
+ );
2927
+ })
2928
+ }
2929
+ );
2930
+ }
2931
+ );
2932
+ function sampleTo(peaks, n) {
2933
+ if (peaks.length === 0) return new Array(n).fill(0);
2934
+ if (peaks.length === n) return peaks;
2935
+ const out = new Array(n);
2936
+ const ratio = peaks.length / n;
2937
+ for (let i = 0; i < n; i++) {
2938
+ const start = Math.floor(i * ratio);
2939
+ const end = Math.max(start + 1, Math.floor((i + 1) * ratio));
2940
+ let max = 0;
2941
+ for (let j = start; j < end && j < peaks.length; j++) {
2942
+ const v = Math.abs(peaks[j]);
2943
+ if (v > max) max = v;
2944
+ }
2945
+ out[i] = max;
2946
+ }
2947
+ return out;
2948
+ }
2949
+ var SPEEDS = [0.5, 0.75, 1, 1.25, 1.5, 2];
2950
+ function formatTime(s) {
2951
+ if (!Number.isFinite(s) || s < 0) return "0:00";
2952
+ const total = Math.floor(s);
2953
+ const h = Math.floor(total / 3600);
2954
+ const m = Math.floor(total % 3600 / 60);
2955
+ const sec = total % 60;
2956
+ if (h > 0) return `${h}:${String(m).padStart(2, "0")}:${String(sec).padStart(2, "0")}`;
2957
+ return `${m}:${String(sec).padStart(2, "0")}`;
2958
+ }
2959
+ var AudioPlayer = forwardRef(
2960
+ function AudioPlayer2({
2961
+ src,
2962
+ peaks,
2963
+ autoPlay,
2964
+ loop,
2965
+ defaultVolume = 1,
2966
+ defaultPlaybackRate = 1,
2967
+ compact,
2968
+ onPlay,
2969
+ onPause,
2970
+ onTimeUpdate,
2971
+ onEnded,
2972
+ className,
2973
+ ...rest
2974
+ }, forwardedRef) {
2975
+ const audioRef = useRef(null);
2976
+ useImperativeHandle(forwardedRef, () => audioRef.current);
2977
+ const [playing, setPlaying] = useState(!!autoPlay);
2978
+ const [currentTime, setCurrentTime] = useState(0);
2979
+ const [duration, setDuration] = useState(0);
2980
+ const [volume, setVolume] = useState(defaultVolume);
2981
+ const [muted, setMuted] = useState(false);
2982
+ const [speed, setSpeed] = useState(defaultPlaybackRate);
2983
+ useEffect(() => {
2984
+ const a = audioRef.current;
2985
+ if (!a) return;
2986
+ a.volume = volume;
2987
+ a.muted = muted;
2988
+ a.playbackRate = speed;
2989
+ }, [volume, muted, speed]);
2990
+ const togglePlay = useCallback(() => {
2991
+ const a = audioRef.current;
2992
+ if (!a) return;
2993
+ if (a.paused) {
2994
+ a.play().catch(() => {
2995
+ });
2996
+ } else {
2997
+ a.pause();
2998
+ }
2999
+ }, []);
3000
+ const seekTo = useCallback((seconds) => {
3001
+ const a = audioRef.current;
3002
+ if (!a || !Number.isFinite(seconds)) return;
3003
+ a.currentTime = Math.max(0, Math.min(a.duration || 0, seconds));
3004
+ }, []);
3005
+ const seekProgress = useCallback(
3006
+ (p) => {
3007
+ if (duration > 0) seekTo(p * duration);
3008
+ },
3009
+ [duration, seekTo]
3010
+ );
3011
+ const handleKey = (e) => {
3012
+ switch (e.key) {
3013
+ case " ":
3014
+ case "Spacebar":
3015
+ e.preventDefault();
3016
+ togglePlay();
3017
+ break;
3018
+ case "ArrowRight":
3019
+ e.preventDefault();
3020
+ seekTo(currentTime + 5);
3021
+ break;
3022
+ case "ArrowLeft":
3023
+ e.preventDefault();
3024
+ seekTo(currentTime - 5);
3025
+ break;
3026
+ case "ArrowUp":
3027
+ e.preventDefault();
3028
+ setVolume((v) => Math.min(1, v + 0.1));
3029
+ break;
3030
+ case "ArrowDown":
3031
+ e.preventDefault();
3032
+ setVolume((v) => Math.max(0, v - 0.1));
3033
+ break;
3034
+ case "m":
3035
+ case "M":
3036
+ e.preventDefault();
3037
+ setMuted((m) => !m);
3038
+ break;
3039
+ }
3040
+ };
3041
+ const progress = duration > 0 ? currentTime / duration : 0;
3042
+ return /* @__PURE__ */ jsxs(
3043
+ "div",
3044
+ {
3045
+ role: "region",
3046
+ "aria-label": "Audio player",
3047
+ tabIndex: 0,
3048
+ onKeyDown: handleKey,
3049
+ "data-playing": playing || void 0,
3050
+ className: cn(
3051
+ "flex items-center gap-3 rounded-md border border-border bg-card p-2 text-card-foreground shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
3052
+ compact && "gap-2 p-1.5",
3053
+ className
3054
+ ),
3055
+ children: [
3056
+ /* @__PURE__ */ jsx(
3057
+ "audio",
3058
+ {
3059
+ ref: audioRef,
3060
+ src,
3061
+ autoPlay,
3062
+ loop,
3063
+ onPlay: () => {
3064
+ setPlaying(true);
3065
+ onPlay?.();
3066
+ },
3067
+ onPause: () => {
3068
+ setPlaying(false);
3069
+ onPause?.();
3070
+ },
3071
+ onTimeUpdate: () => {
3072
+ const a = audioRef.current;
3073
+ if (!a) return;
3074
+ setCurrentTime(a.currentTime);
3075
+ onTimeUpdate?.(a.currentTime, a.duration);
3076
+ },
3077
+ onLoadedMetadata: () => {
3078
+ const a = audioRef.current;
3079
+ if (a) setDuration(a.duration || 0);
3080
+ },
3081
+ onEnded: () => {
3082
+ setPlaying(false);
3083
+ onEnded?.();
3084
+ },
3085
+ ...rest
3086
+ }
3087
+ ),
3088
+ /* @__PURE__ */ jsx(
3089
+ "button",
3090
+ {
3091
+ type: "button",
3092
+ "aria-label": playing ? "Pause" : "Play",
3093
+ onClick: togglePlay,
3094
+ className: cn(
3095
+ "inline-flex shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
3096
+ compact ? "h-7 w-7" : "h-9 w-9"
3097
+ ),
3098
+ children: /* @__PURE__ */ jsx(Icon, { icon: playing ? Pause : Play, size: compact ? 12 : 14 })
3099
+ }
3100
+ ),
3101
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", style: { minWidth: "3.5rem" }, children: formatTime(currentTime) }),
3102
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children: peaks ? /* @__PURE__ */ jsx(
3103
+ AudioWaveform,
3104
+ {
3105
+ peaks,
3106
+ progress,
3107
+ onSeek: seekProgress,
3108
+ width: compact ? 200 : 320,
3109
+ height: compact ? 32 : 40
3110
+ }
3111
+ ) : /* @__PURE__ */ jsx(
3112
+ "input",
3113
+ {
3114
+ type: "range",
3115
+ role: "slider",
3116
+ "aria-label": "Seek",
3117
+ "aria-valuetext": formatTime(currentTime),
3118
+ min: 0,
3119
+ max: duration || 0,
3120
+ step: "any",
3121
+ value: currentTime,
3122
+ onChange: (e) => seekTo(Number(e.target.value)),
3123
+ className: "w-full accent-primary"
3124
+ }
3125
+ ) }),
3126
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", style: { minWidth: "3.5rem", textAlign: "right" }, children: formatTime(duration) }),
3127
+ /* @__PURE__ */ jsx(
3128
+ "button",
3129
+ {
3130
+ type: "button",
3131
+ "aria-label": muted ? "Unmute" : "Mute",
3132
+ onClick: () => setMuted((m) => !m),
3133
+ className: "inline-flex h-7 w-7 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground",
3134
+ children: /* @__PURE__ */ jsx(Icon, { icon: muted || volume === 0 ? VolumeX : Volume2, size: 14 })
3135
+ }
3136
+ ),
3137
+ /* @__PURE__ */ jsx(
3138
+ "select",
3139
+ {
3140
+ "aria-label": "Playback speed",
3141
+ value: speed,
3142
+ onChange: (e) => setSpeed(Number(e.target.value)),
3143
+ className: "h-7 rounded-sm border border-input bg-background px-1 text-xs",
3144
+ children: SPEEDS.map((s) => /* @__PURE__ */ jsxs("option", { value: s, children: [
3145
+ s,
3146
+ "\xD7"
3147
+ ] }, s))
3148
+ }
3149
+ )
3150
+ ]
3151
+ }
3152
+ );
3153
+ }
3154
+ );
3155
+ var SPEEDS2 = [0.5, 0.75, 1, 1.25, 1.5, 2];
3156
+ function formatTime2(s) {
3157
+ if (!Number.isFinite(s) || s < 0) return "0:00";
3158
+ const total = Math.floor(s);
3159
+ const h = Math.floor(total / 3600);
3160
+ const m = Math.floor(total % 3600 / 60);
3161
+ const sec = total % 60;
3162
+ if (h > 0) return `${h}:${String(m).padStart(2, "0")}:${String(sec).padStart(2, "0")}`;
3163
+ return `${m}:${String(sec).padStart(2, "0")}`;
3164
+ }
3165
+ var VideoPlayer = forwardRef(
3166
+ function VideoPlayer2({
3167
+ src,
3168
+ poster,
3169
+ tracks,
3170
+ aspectRatio = "16 / 9",
3171
+ autoPlay,
3172
+ loop,
3173
+ muted: mutedProp,
3174
+ defaultVolume = 1,
3175
+ defaultPlaybackRate = 1,
3176
+ className,
3177
+ ...rest
3178
+ }, forwardedRef) {
3179
+ const containerRef = useRef(null);
3180
+ const videoRef = useRef(null);
3181
+ useImperativeHandle(forwardedRef, () => videoRef.current);
3182
+ const [playing, setPlaying] = useState(!!autoPlay);
3183
+ const [currentTime, setCurrentTime] = useState(0);
3184
+ const [duration, setDuration] = useState(0);
3185
+ const [volume, setVolume] = useState(defaultVolume);
3186
+ const [muted, setMuted] = useState(!!mutedProp);
3187
+ const [speed, setSpeed] = useState(defaultPlaybackRate);
3188
+ const [fullscreen, setFullscreen] = useState(false);
3189
+ const [captionsOn, setCaptionsOn] = useState(false);
3190
+ const [showControls, setShowControls] = useState(true);
3191
+ const idleTimerRef = useRef(null);
3192
+ useEffect(() => {
3193
+ const v = videoRef.current;
3194
+ if (!v) return;
3195
+ v.volume = volume;
3196
+ v.muted = muted;
3197
+ v.playbackRate = speed;
3198
+ }, [volume, muted, speed]);
3199
+ useEffect(() => {
3200
+ const v = videoRef.current;
3201
+ if (!v || !v.textTracks) return;
3202
+ for (let i = 0; i < v.textTracks.length; i++) {
3203
+ v.textTracks[i].mode = captionsOn ? "showing" : "hidden";
3204
+ }
3205
+ }, [captionsOn, tracks]);
3206
+ useEffect(() => {
3207
+ const onFsChange = () => setFullscreen(document.fullscreenElement === containerRef.current);
3208
+ document.addEventListener("fullscreenchange", onFsChange);
3209
+ return () => document.removeEventListener("fullscreenchange", onFsChange);
3210
+ }, []);
3211
+ const togglePlay = useCallback(() => {
3212
+ const v = videoRef.current;
3213
+ if (!v) return;
3214
+ if (v.paused) v.play().catch(() => {
3215
+ });
3216
+ else v.pause();
3217
+ }, []);
3218
+ const seekTo = useCallback((seconds) => {
3219
+ const v = videoRef.current;
3220
+ if (!v || !Number.isFinite(seconds)) return;
3221
+ v.currentTime = Math.max(0, Math.min(v.duration || 0, seconds));
3222
+ }, []);
3223
+ const toggleFullscreen = useCallback(async () => {
3224
+ const c = containerRef.current;
3225
+ if (!c) return;
3226
+ if (!document.fullscreenElement) {
3227
+ await c.requestFullscreen?.().catch(() => {
3228
+ });
3229
+ } else {
3230
+ await document.exitFullscreen?.().catch(() => {
3231
+ });
3232
+ }
3233
+ }, []);
3234
+ const togglePiP = useCallback(async () => {
3235
+ const v = videoRef.current;
3236
+ if (!v) return;
3237
+ if ("pictureInPictureElement" in document && document.pictureInPictureElement) {
3238
+ await document.exitPictureInPicture?.()?.catch(() => {
3239
+ });
3240
+ } else if ("requestPictureInPicture" in v) {
3241
+ await v.requestPictureInPicture?.()?.catch(() => {
3242
+ });
3243
+ }
3244
+ }, []);
3245
+ const bumpControls = useCallback(() => {
3246
+ setShowControls(true);
3247
+ if (idleTimerRef.current != null) window.clearTimeout(idleTimerRef.current);
3248
+ if (playing) {
3249
+ idleTimerRef.current = window.setTimeout(() => setShowControls(false), 3e3);
3250
+ }
3251
+ }, [playing]);
3252
+ useEffect(() => {
3253
+ bumpControls();
3254
+ return () => {
3255
+ if (idleTimerRef.current != null) window.clearTimeout(idleTimerRef.current);
3256
+ };
3257
+ }, [playing, bumpControls]);
3258
+ const handleKey = (e) => {
3259
+ switch (e.key) {
3260
+ case " ":
3261
+ case "Spacebar":
3262
+ e.preventDefault();
3263
+ togglePlay();
3264
+ break;
3265
+ case "ArrowRight":
3266
+ e.preventDefault();
3267
+ seekTo(currentTime + 5);
3268
+ break;
3269
+ case "ArrowLeft":
3270
+ e.preventDefault();
3271
+ seekTo(currentTime - 5);
3272
+ break;
3273
+ case "ArrowUp":
3274
+ e.preventDefault();
3275
+ setVolume((v) => Math.min(1, v + 0.1));
3276
+ break;
3277
+ case "ArrowDown":
3278
+ e.preventDefault();
3279
+ setVolume((v) => Math.max(0, v - 0.1));
3280
+ break;
3281
+ case "m":
3282
+ case "M":
3283
+ e.preventDefault();
3284
+ setMuted((m) => !m);
3285
+ break;
3286
+ case "f":
3287
+ case "F":
3288
+ e.preventDefault();
3289
+ toggleFullscreen();
3290
+ break;
3291
+ case "c":
3292
+ case "C":
3293
+ e.preventDefault();
3294
+ if (tracks && tracks.length > 0) setCaptionsOn((c) => !c);
3295
+ break;
3296
+ }
3297
+ };
3298
+ return /* @__PURE__ */ jsxs(
3299
+ "div",
3300
+ {
3301
+ ref: containerRef,
3302
+ role: "region",
3303
+ "aria-label": "Video player",
3304
+ tabIndex: 0,
3305
+ onKeyDown: handleKey,
3306
+ onMouseMove: bumpControls,
3307
+ onMouseLeave: () => {
3308
+ if (playing) setShowControls(false);
3309
+ },
3310
+ className: cn(
3311
+ "group relative overflow-hidden rounded-md bg-black text-white shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
3312
+ className
3313
+ ),
3314
+ style: { aspectRatio: typeof aspectRatio === "number" ? String(aspectRatio) : aspectRatio },
3315
+ children: [
3316
+ /* @__PURE__ */ jsx(
3317
+ "video",
3318
+ {
3319
+ ref: videoRef,
3320
+ src,
3321
+ poster,
3322
+ autoPlay,
3323
+ loop,
3324
+ muted,
3325
+ onPlay: () => setPlaying(true),
3326
+ onPause: () => setPlaying(false),
3327
+ onTimeUpdate: () => {
3328
+ const v = videoRef.current;
3329
+ if (!v) return;
3330
+ setCurrentTime(v.currentTime);
3331
+ },
3332
+ onLoadedMetadata: () => {
3333
+ const v = videoRef.current;
3334
+ if (v) setDuration(v.duration || 0);
3335
+ },
3336
+ onClick: togglePlay,
3337
+ className: "h-full w-full bg-black",
3338
+ ...rest,
3339
+ children: tracks?.map((t, i) => /* @__PURE__ */ jsx(
3340
+ "track",
3341
+ {
3342
+ src: t.src,
3343
+ srcLang: t.srcLang,
3344
+ label: t.label,
3345
+ kind: t.kind ?? "captions",
3346
+ default: t.default
3347
+ },
3348
+ i
3349
+ ))
3350
+ }
3351
+ ),
3352
+ !playing && /* @__PURE__ */ jsx(
3353
+ "button",
3354
+ {
3355
+ type: "button",
3356
+ "aria-label": "Play",
3357
+ onClick: togglePlay,
3358
+ className: "absolute inset-0 grid place-items-center bg-black/30 transition-opacity hover:bg-black/40",
3359
+ children: /* @__PURE__ */ jsx("span", { className: "grid h-16 w-16 place-items-center rounded-full bg-white/90 text-foreground shadow-lg", children: /* @__PURE__ */ jsx(Icon, { icon: Play, size: 28 }) })
3360
+ }
3361
+ ),
3362
+ /* @__PURE__ */ jsxs(
3363
+ "div",
3364
+ {
3365
+ className: cn(
3366
+ "absolute inset-x-0 bottom-0 flex items-center gap-2 bg-gradient-to-t from-black/80 to-transparent px-3 pb-2 pt-6 transition-opacity",
3367
+ showControls ? "opacity-100" : "opacity-0"
3368
+ ),
3369
+ children: [
3370
+ /* @__PURE__ */ jsx(
3371
+ "button",
3372
+ {
3373
+ type: "button",
3374
+ "aria-label": playing ? "Pause" : "Play",
3375
+ onClick: togglePlay,
3376
+ className: "inline-flex h-8 w-8 items-center justify-center rounded-full bg-white/20 text-white hover:bg-white/30",
3377
+ children: /* @__PURE__ */ jsx(Icon, { icon: playing ? Pause : Play, size: 14 })
3378
+ }
3379
+ ),
3380
+ /* @__PURE__ */ jsxs("span", { className: "text-xs tabular-nums", children: [
3381
+ formatTime2(currentTime),
3382
+ " / ",
3383
+ formatTime2(duration)
3384
+ ] }),
3385
+ /* @__PURE__ */ jsx(
3386
+ "input",
3387
+ {
3388
+ type: "range",
3389
+ role: "slider",
3390
+ "aria-label": "Seek",
3391
+ "aria-valuetext": formatTime2(currentTime),
3392
+ min: 0,
3393
+ max: duration || 0,
3394
+ step: "any",
3395
+ value: currentTime,
3396
+ onChange: (e) => seekTo(Number(e.target.value)),
3397
+ className: "flex-1 accent-primary"
3398
+ }
3399
+ ),
3400
+ /* @__PURE__ */ jsx(
3401
+ "button",
3402
+ {
3403
+ type: "button",
3404
+ "aria-label": muted || volume === 0 ? "Unmute" : "Mute",
3405
+ onClick: () => setMuted((m) => !m),
3406
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-white/80 hover:bg-white/20 hover:text-white",
3407
+ children: /* @__PURE__ */ jsx(Icon, { icon: muted || volume === 0 ? VolumeX : Volume2, size: 14 })
3408
+ }
3409
+ ),
3410
+ /* @__PURE__ */ jsx(
3411
+ "select",
3412
+ {
3413
+ "aria-label": "Playback speed",
3414
+ value: speed,
3415
+ onChange: (e) => setSpeed(Number(e.target.value)),
3416
+ className: "h-7 rounded-sm border border-white/20 bg-black/40 px-1 text-xs",
3417
+ children: SPEEDS2.map((s) => /* @__PURE__ */ jsxs("option", { value: s, className: "text-foreground", children: [
3418
+ s,
3419
+ "\xD7"
3420
+ ] }, s))
3421
+ }
3422
+ ),
3423
+ tracks && tracks.length > 0 && /* @__PURE__ */ jsx(
3424
+ "button",
3425
+ {
3426
+ type: "button",
3427
+ "aria-label": captionsOn ? "Hide captions" : "Show captions",
3428
+ "aria-pressed": captionsOn,
3429
+ onClick: () => setCaptionsOn((c) => !c),
3430
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-white/80 hover:bg-white/20 hover:text-white",
3431
+ children: /* @__PURE__ */ jsx(Icon, { icon: captionsOn ? Captions : CaptionsOff, size: 14 })
3432
+ }
3433
+ ),
3434
+ /* @__PURE__ */ jsx(
3435
+ "button",
3436
+ {
3437
+ type: "button",
3438
+ "aria-label": "Picture in picture",
3439
+ onClick: togglePiP,
3440
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-white/80 hover:bg-white/20 hover:text-white",
3441
+ children: /* @__PURE__ */ jsx(Icon, { icon: PictureInPicture2, size: 14 })
3442
+ }
3443
+ ),
3444
+ /* @__PURE__ */ jsx(
3445
+ "button",
3446
+ {
3447
+ type: "button",
3448
+ "aria-label": fullscreen ? "Exit fullscreen" : "Enter fullscreen",
3449
+ onClick: toggleFullscreen,
3450
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-white/80 hover:bg-white/20 hover:text-white",
3451
+ children: /* @__PURE__ */ jsx(Icon, { icon: fullscreen ? Minimize : Maximize, size: 14 })
3452
+ }
3453
+ )
3454
+ ]
3455
+ }
3456
+ )
3457
+ ]
3458
+ }
3459
+ );
3460
+ }
3461
+ );
3462
+ var ZOOM_LEVELS = [50, 75, 100, 125, 150, 175, 200, 250, 300, 400];
3463
+ var PDFViewer = forwardRef(function PDFViewer2({
3464
+ src,
3465
+ page: pageProp,
3466
+ defaultPage = 1,
3467
+ onPageChange,
3468
+ zoom: zoomProp,
3469
+ defaultZoom = 100,
3470
+ onZoomChange,
3471
+ pageCount,
3472
+ title = "PDF document",
3473
+ download = true,
3474
+ height = "70vh",
3475
+ className,
3476
+ ...rest
3477
+ }, ref) {
3478
+ const [page, setPage] = useControlled({
3479
+ controlled: pageProp,
3480
+ default: defaultPage,
3481
+ onChange: onPageChange
3482
+ });
3483
+ const [zoom, setZoom] = useControlled({
3484
+ controlled: zoomProp,
3485
+ default: defaultZoom,
3486
+ onChange: onZoomChange
3487
+ });
3488
+ const hashedSrc = useMemo(() => {
3489
+ try {
3490
+ const url = new URL(src, typeof window !== "undefined" ? window.location.href : "http://localhost");
3491
+ url.hash = `page=${page}&zoom=${zoom}`;
3492
+ return url.toString();
3493
+ } catch {
3494
+ return `${src}#page=${page}&zoom=${zoom}`;
3495
+ }
3496
+ }, [src, page, zoom]);
3497
+ const goPrev = () => setPage(Math.max(1, page - 1));
3498
+ const goNext = () => setPage(pageCount ? Math.min(pageCount, page + 1) : page + 1);
3499
+ const zoomIn = () => {
3500
+ const next = ZOOM_LEVELS.find((z) => z > zoom);
3501
+ if (next) setZoom(next);
3502
+ };
3503
+ const zoomOut = () => {
3504
+ const next = [...ZOOM_LEVELS].reverse().find((z) => z < zoom);
3505
+ if (next) setZoom(next);
3506
+ };
3507
+ return /* @__PURE__ */ jsxs(
3508
+ "div",
3509
+ {
3510
+ ref,
3511
+ className: cn(
3512
+ "flex flex-col overflow-hidden rounded-md border border-border bg-card text-card-foreground shadow-sm",
3513
+ className
3514
+ ),
3515
+ ...rest,
3516
+ children: [
3517
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 border-b border-border bg-muted/40 px-2 py-1.5", children: [
3518
+ /* @__PURE__ */ jsx(
3519
+ "button",
3520
+ {
3521
+ type: "button",
3522
+ "aria-label": "Previous page",
3523
+ onClick: goPrev,
3524
+ disabled: page <= 1,
3525
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40",
3526
+ children: /* @__PURE__ */ jsx(Icon, { icon: ChevronLeft, size: 14 })
3527
+ }
3528
+ ),
3529
+ /* @__PURE__ */ jsx("span", { className: "px-1 text-xs tabular-nums text-foreground", children: pageCount ? `${page} / ${pageCount}` : `Page ${page}` }),
3530
+ /* @__PURE__ */ jsx(
3531
+ "button",
3532
+ {
3533
+ type: "button",
3534
+ "aria-label": "Next page",
3535
+ onClick: goNext,
3536
+ disabled: pageCount != null && page >= pageCount,
3537
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40",
3538
+ children: /* @__PURE__ */ jsx(Icon, { icon: ChevronRight, size: 14 })
3539
+ }
3540
+ ),
3541
+ /* @__PURE__ */ jsx("span", { className: "mx-2 h-4 w-px bg-border", "aria-hidden": "true" }),
3542
+ /* @__PURE__ */ jsx(
3543
+ "button",
3544
+ {
3545
+ type: "button",
3546
+ "aria-label": "Zoom out",
3547
+ onClick: zoomOut,
3548
+ disabled: zoom <= ZOOM_LEVELS[0],
3549
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40",
3550
+ children: /* @__PURE__ */ jsx(Icon, { icon: ZoomOut, size: 14 })
3551
+ }
3552
+ ),
3553
+ /* @__PURE__ */ jsxs("span", { className: "px-1 text-xs tabular-nums text-foreground", children: [
3554
+ zoom,
3555
+ "%"
3556
+ ] }),
3557
+ /* @__PURE__ */ jsx(
3558
+ "button",
3559
+ {
3560
+ type: "button",
3561
+ "aria-label": "Zoom in",
3562
+ onClick: zoomIn,
3563
+ disabled: zoom >= ZOOM_LEVELS[ZOOM_LEVELS.length - 1],
3564
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40",
3565
+ children: /* @__PURE__ */ jsx(Icon, { icon: ZoomIn, size: 14 })
3566
+ }
3567
+ ),
3568
+ /* @__PURE__ */ jsx("div", { className: "ml-auto flex items-center gap-1", children: download && /* @__PURE__ */ jsx(
3569
+ "a",
3570
+ {
3571
+ href: src,
3572
+ download: true,
3573
+ "aria-label": "Download PDF",
3574
+ className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground",
3575
+ children: /* @__PURE__ */ jsx(Icon, { icon: Download, size: 14 })
3576
+ }
3577
+ ) })
3578
+ ] }),
3579
+ /* @__PURE__ */ jsx(
3580
+ "iframe",
3581
+ {
3582
+ src: hashedSrc,
3583
+ title,
3584
+ style: { height, border: 0 },
3585
+ className: "w-full bg-muted"
3586
+ }
3587
+ )
3588
+ ]
3589
+ }
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
+ }
2831
4401
 
2832
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, 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, 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, avatarVariants, badgeVariants, codeVariants, headingVariants, textVariants };
2833
- //# sourceMappingURL=chunk-RK6VB3II.js.map
2834
- //# sourceMappingURL=chunk-RK6VB3II.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