calendarkit-pro 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -3
- package/dist/index.d.mts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +1424 -1014
- package/dist/index.mjs +1381 -953
- package/package.json +29 -6
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
import React15__default, { useState, useId, useMemo, useCallback, useEffect, useRef } from 'react';
|
|
3
|
-
import { useDraggable, useSensors, useSensor, PointerSensor, DndContext, DragOverlay, useDroppable } from '@dnd-kit/core';
|
|
4
|
-
import { createSnapModifier, restrictToWindowEdges } from '@dnd-kit/modifiers';
|
|
5
|
-
import { motion, AnimatePresence } from 'framer-motion';
|
|
6
|
-
import { format, differenceInMinutes, subDays, addDays, differenceInMilliseconds, startOfWeek, endOfWeek, eachDayOfInterval, isSameMonth, isToday, isSameDay, startOfDay, addWeeks, addMonths, subWeeks, subMonths, startOfMonth, endOfMonth, isTomorrow } from 'date-fns';
|
|
7
|
-
import { cva } from 'class-variance-authority';
|
|
8
|
-
import { clsx } from 'clsx';
|
|
9
|
-
import { twMerge } from 'tailwind-merge';
|
|
10
|
-
import { Menu, ChevronLeft, ChevronRight, Sun, Moon, CalendarDays, CalendarRange, Calendar, ListTodo, Plus, ChevronDown, Globe, Clock, Users, Paperclip, Bell, Edit3, Copy, Trash2, X, Edit2, MapPin, ExternalLink, AlignLeft, File, Download, BellRing, Check, Repeat, Tag } from 'lucide-react';
|
|
11
|
-
import { toZonedTime, fromZonedTime } from 'date-fns-tz';
|
|
12
|
-
import { CSS } from '@dnd-kit/utilities';
|
|
13
|
-
import { RRule } from 'rrule';
|
|
1
|
+
"use client";
|
|
14
2
|
|
|
3
|
+
// src/index.tsx
|
|
4
|
+
import React18, { useCallback as useCallback5, useId, useState as useState6, useMemo as useMemo4 } from "react";
|
|
5
|
+
import { DndContext, useSensor, useSensors, PointerSensor, DragOverlay } from "@dnd-kit/core";
|
|
6
|
+
import { createSnapModifier, restrictToWindowEdges } from "@dnd-kit/modifiers";
|
|
7
|
+
import { AnimatePresence as AnimatePresence3, motion as motion4 } from "framer-motion";
|
|
8
|
+
|
|
9
|
+
// src/components/CalendarHeader.tsx
|
|
10
|
+
import React2 from "react";
|
|
11
|
+
import { format } from "date-fns";
|
|
12
|
+
|
|
13
|
+
// src/components/ui/button.tsx
|
|
14
|
+
import * as React from "react";
|
|
15
|
+
import { cva } from "class-variance-authority";
|
|
16
|
+
|
|
17
|
+
// src/utils.ts
|
|
18
|
+
import { clsx } from "clsx";
|
|
19
|
+
import { twMerge } from "tailwind-merge";
|
|
15
20
|
function cn(...inputs) {
|
|
16
21
|
return twMerge(clsx(inputs));
|
|
17
22
|
}
|
|
@@ -44,10 +49,10 @@ var buttonVariants = cva(
|
|
|
44
49
|
}
|
|
45
50
|
}
|
|
46
51
|
);
|
|
47
|
-
var Button =
|
|
48
|
-
({ className, variant, size,
|
|
52
|
+
var Button = React.forwardRef(
|
|
53
|
+
({ className, variant, size, ...props }, ref) => {
|
|
49
54
|
const Comp = "button";
|
|
50
|
-
return /* @__PURE__ */
|
|
55
|
+
return /* @__PURE__ */ React.createElement(
|
|
51
56
|
Comp,
|
|
52
57
|
{
|
|
53
58
|
className: cn(buttonVariants({ variant, size, className })),
|
|
@@ -58,6 +63,9 @@ var Button = React15.forwardRef(
|
|
|
58
63
|
}
|
|
59
64
|
);
|
|
60
65
|
Button.displayName = "Button";
|
|
66
|
+
|
|
67
|
+
// src/components/CalendarHeader.tsx
|
|
68
|
+
import { ChevronLeft, ChevronRight, Menu, Moon, Sun, CalendarDays, CalendarRange, Calendar, ListTodo } from "lucide-react";
|
|
61
69
|
var CalendarHeader = ({
|
|
62
70
|
currentDate,
|
|
63
71
|
onPrev,
|
|
@@ -80,7 +88,7 @@ var CalendarHeader = ({
|
|
|
80
88
|
{ key: "day", icon: Calendar },
|
|
81
89
|
{ key: "agenda", icon: ListTodo }
|
|
82
90
|
];
|
|
83
|
-
return /* @__PURE__ */
|
|
91
|
+
return /* @__PURE__ */ React2.createElement("div", { className: "flex flex-col md:flex-row items-center justify-between px-3 md:px-5 py-3 border-b-[0px] border-border/50 bg-gradient-to-r from-background via-background to-muted/20 gap-3 md:gap-0 min-h-[64px]" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2 w-full md:w-auto justify-between md:justify-start" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2 md:gap-3" }, /* @__PURE__ */ React2.createElement(
|
|
84
92
|
Button,
|
|
85
93
|
{
|
|
86
94
|
variant: "ghost",
|
|
@@ -88,8 +96,8 @@ var CalendarHeader = ({
|
|
|
88
96
|
className: "text-muted-foreground hover:text-foreground hover:bg-accent/80 rounded-xl h-10 w-10 hidden md:inline-flex transition-all duration-200",
|
|
89
97
|
onClick: onMenuClick
|
|
90
98
|
},
|
|
91
|
-
/* @__PURE__ */
|
|
92
|
-
), /* @__PURE__ */
|
|
99
|
+
/* @__PURE__ */ React2.createElement(Menu, { className: "h-5 w-5" })
|
|
100
|
+
), /* @__PURE__ */ React2.createElement(
|
|
93
101
|
Button,
|
|
94
102
|
{
|
|
95
103
|
variant: "outline",
|
|
@@ -97,7 +105,7 @@ var CalendarHeader = ({
|
|
|
97
105
|
className: "h-9 px-5 rounded-xl text-sm font-medium hidden sm:inline-flex border-[0.5px] border-border/60 hover:border-primary/40 hover:bg-primary/5 hover:text-primary transition-all duration-200"
|
|
98
106
|
},
|
|
99
107
|
translations.today
|
|
100
|
-
), /* @__PURE__ */
|
|
108
|
+
), /* @__PURE__ */ React2.createElement("div", { className: "flex items-center bg-muted/40 rounded-xl p-0.5" }, /* @__PURE__ */ React2.createElement(
|
|
101
109
|
Button,
|
|
102
110
|
{
|
|
103
111
|
variant: "ghost",
|
|
@@ -105,8 +113,8 @@ var CalendarHeader = ({
|
|
|
105
113
|
onClick: onPrev,
|
|
106
114
|
className: "rounded-lg h-8 w-8 hover:bg-background/80 transition-all duration-200"
|
|
107
115
|
},
|
|
108
|
-
/* @__PURE__ */
|
|
109
|
-
), /* @__PURE__ */
|
|
116
|
+
/* @__PURE__ */ React2.createElement(ChevronLeft, { className: "h-4 w-4 text-muted-foreground" })
|
|
117
|
+
), /* @__PURE__ */ React2.createElement(
|
|
110
118
|
Button,
|
|
111
119
|
{
|
|
112
120
|
variant: "ghost",
|
|
@@ -114,8 +122,8 @@ var CalendarHeader = ({
|
|
|
114
122
|
onClick: onNext,
|
|
115
123
|
className: "rounded-lg h-8 w-8 hover:bg-background/80 transition-all duration-200"
|
|
116
124
|
},
|
|
117
|
-
/* @__PURE__ */
|
|
118
|
-
)), /* @__PURE__ */
|
|
125
|
+
/* @__PURE__ */ React2.createElement(ChevronRight, { className: "h-4 w-4 text-muted-foreground" })
|
|
126
|
+
)), /* @__PURE__ */ React2.createElement("div", { className: "ml-2 md:ml-4" }, /* @__PURE__ */ React2.createElement("h2", { className: "text-lg md:text-xl font-semibold text-foreground whitespace-nowrap capitalize tracking-tight" }, format(currentDate, "MMMM yyyy", { locale }))))), /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2 md:gap-3 w-full md:w-auto justify-end" }, onLanguageChange && language && /* @__PURE__ */ React2.createElement(
|
|
119
127
|
Button,
|
|
120
128
|
{
|
|
121
129
|
variant: "ghost",
|
|
@@ -124,7 +132,7 @@ var CalendarHeader = ({
|
|
|
124
132
|
className: "h-9 px-3 rounded-xl text-xs font-semibold text-muted-foreground hover:text-foreground hover:bg-accent/80 transition-all duration-200 uppercase tracking-wider"
|
|
125
133
|
},
|
|
126
134
|
language
|
|
127
|
-
), onThemeToggle && /* @__PURE__ */
|
|
135
|
+
), onThemeToggle && /* @__PURE__ */ React2.createElement(
|
|
128
136
|
Button,
|
|
129
137
|
{
|
|
130
138
|
variant: "ghost",
|
|
@@ -132,8 +140,8 @@ var CalendarHeader = ({
|
|
|
132
140
|
className: "rounded-xl h-9 w-9 hover:bg-accent/80 transition-all duration-200",
|
|
133
141
|
onClick: onThemeToggle
|
|
134
142
|
},
|
|
135
|
-
isDarkMode ? /* @__PURE__ */
|
|
136
|
-
), !hideViewSwitcher && /* @__PURE__ */
|
|
143
|
+
isDarkMode ? /* @__PURE__ */ React2.createElement(Sun, { className: "h-4 w-4 text-amber-500" }) : /* @__PURE__ */ React2.createElement(Moon, { className: "h-4 w-4 text-muted-foreground" })
|
|
144
|
+
), !hideViewSwitcher && /* @__PURE__ */ React2.createElement("div", { className: "flex items-center bg-muted/50 backdrop-blur-sm rounded-xl p-1" }, viewConfig.map(({ key, icon: Icon }) => /* @__PURE__ */ React2.createElement(
|
|
137
145
|
Button,
|
|
138
146
|
{
|
|
139
147
|
key,
|
|
@@ -145,24 +153,40 @@ var CalendarHeader = ({
|
|
|
145
153
|
view === key ? "bg-background shadow-sm text-foreground font-medium border-[0.5px] border-border/50" : "text-muted-foreground hover:text-foreground hover:bg-background/50"
|
|
146
154
|
)
|
|
147
155
|
},
|
|
148
|
-
/* @__PURE__ */
|
|
149
|
-
/* @__PURE__ */
|
|
156
|
+
/* @__PURE__ */ React2.createElement(Icon, { className: "h-3.5 w-3.5" }),
|
|
157
|
+
/* @__PURE__ */ React2.createElement("span", { className: "hidden sm:inline" }, translations[key])
|
|
150
158
|
)))));
|
|
151
159
|
};
|
|
152
|
-
|
|
160
|
+
|
|
161
|
+
// src/components/Sidebar.tsx
|
|
162
|
+
import React4, { useState, useEffect } from "react";
|
|
163
|
+
import { Plus, ChevronDown, Globe } from "lucide-react";
|
|
164
|
+
|
|
165
|
+
// src/components/MiniCalendar.tsx
|
|
166
|
+
import React3 from "react";
|
|
167
|
+
import { format as format2, isSameMonth, isSameDay, isToday, addMonths, subMonths } from "date-fns";
|
|
168
|
+
|
|
169
|
+
// src/core/week.ts
|
|
170
|
+
import { eachDayOfInterval, endOfMonth, endOfWeek, startOfMonth, startOfWeek } from "date-fns";
|
|
171
|
+
function resolveWeekStartsOn(locale, fallback) {
|
|
172
|
+
return locale?.options?.weekStartsOn ?? fallback;
|
|
173
|
+
}
|
|
174
|
+
function getWeekDays(date, weekStartsOn) {
|
|
175
|
+
return eachDayOfInterval({
|
|
176
|
+
start: startOfWeek(date, { weekStartsOn }),
|
|
177
|
+
end: endOfWeek(date, { weekStartsOn })
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
function getMonthGrid(date, weekStartsOn) {
|
|
153
181
|
const monthStart = startOfMonth(date);
|
|
154
|
-
const monthEnd = endOfMonth(monthStart);
|
|
155
|
-
const startDate = startOfWeek(monthStart, { weekStartsOn: weekStartOn });
|
|
156
|
-
const endDate = endOfWeek(monthEnd, { weekStartsOn: weekStartOn });
|
|
157
182
|
return eachDayOfInterval({
|
|
158
|
-
start:
|
|
159
|
-
end:
|
|
183
|
+
start: startOfWeek(monthStart, { weekStartsOn }),
|
|
184
|
+
end: endOfWeek(endOfMonth(monthStart), { weekStartsOn })
|
|
160
185
|
});
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// src/components/MiniCalendar.tsx
|
|
189
|
+
import { ChevronLeft as ChevronLeft2, ChevronRight as ChevronRight2 } from "lucide-react";
|
|
166
190
|
var MiniCalendar = ({
|
|
167
191
|
currentDate,
|
|
168
192
|
onDateChange,
|
|
@@ -170,12 +194,12 @@ var MiniCalendar = ({
|
|
|
170
194
|
className,
|
|
171
195
|
locale
|
|
172
196
|
}) => {
|
|
173
|
-
const [viewDate, setViewDate] =
|
|
174
|
-
|
|
197
|
+
const [viewDate, setViewDate] = React3.useState(currentDate);
|
|
198
|
+
React3.useEffect(() => {
|
|
175
199
|
setViewDate(currentDate);
|
|
176
200
|
}, [currentDate]);
|
|
177
|
-
const weekStartsOn = locale
|
|
178
|
-
const days =
|
|
201
|
+
const weekStartsOn = resolveWeekStartsOn(locale, 0);
|
|
202
|
+
const days = React3.useMemo(
|
|
179
203
|
() => getMonthGrid(viewDate, weekStartsOn),
|
|
180
204
|
[viewDate, weekStartsOn]
|
|
181
205
|
);
|
|
@@ -199,7 +223,7 @@ var MiniCalendar = ({
|
|
|
199
223
|
onDateChange(day);
|
|
200
224
|
if (onViewChange) onViewChange("day");
|
|
201
225
|
};
|
|
202
|
-
return /* @__PURE__ */
|
|
226
|
+
return /* @__PURE__ */ React3.createElement("div", { className: cn("px-4 w-[260px]", className) }, /* @__PURE__ */ React3.createElement("div", { className: "flex items-center justify-between mb-4" }, /* @__PURE__ */ React3.createElement("span", { className: "text-sm font-semibold text-foreground capitalize" }, format2(viewDate, "MMMM yyyy", { locale })), /* @__PURE__ */ React3.createElement("div", { className: "flex items-center bg-muted/40 rounded-lg p-0.5" }, /* @__PURE__ */ React3.createElement(
|
|
203
227
|
Button,
|
|
204
228
|
{
|
|
205
229
|
variant: "ghost",
|
|
@@ -208,8 +232,8 @@ var MiniCalendar = ({
|
|
|
208
232
|
onClick: handlePrev,
|
|
209
233
|
"aria-label": "Previous month"
|
|
210
234
|
},
|
|
211
|
-
/* @__PURE__ */
|
|
212
|
-
), /* @__PURE__ */
|
|
235
|
+
/* @__PURE__ */ React3.createElement(ChevronLeft2, { className: "h-4 w-4" })
|
|
236
|
+
), /* @__PURE__ */ React3.createElement(
|
|
213
237
|
Button,
|
|
214
238
|
{
|
|
215
239
|
variant: "ghost",
|
|
@@ -218,12 +242,12 @@ var MiniCalendar = ({
|
|
|
218
242
|
onClick: handleNext,
|
|
219
243
|
"aria-label": "Next month"
|
|
220
244
|
},
|
|
221
|
-
/* @__PURE__ */
|
|
222
|
-
))), /* @__PURE__ */
|
|
245
|
+
/* @__PURE__ */ React3.createElement(ChevronRight2, { className: "h-4 w-4" })
|
|
246
|
+
))), /* @__PURE__ */ React3.createElement("div", { className: "grid grid-cols-7 gap-y-2 text-center mb-2" }, weekDays.map((day, i) => /* @__PURE__ */ React3.createElement("div", { key: `${day}-${i}`, className: "text-[10px] text-muted-foreground/70 font-semibold uppercase" }, day))), /* @__PURE__ */ React3.createElement("div", { className: "grid grid-cols-7 gap-y-1 text-center" }, days.map((day) => {
|
|
223
247
|
const isSelected = isSameDay(day, currentDate);
|
|
224
248
|
const isCurrentMonth = isSameMonth(day, viewDate);
|
|
225
249
|
const isTodayDate = isToday(day);
|
|
226
|
-
return /* @__PURE__ */
|
|
250
|
+
return /* @__PURE__ */ React3.createElement(
|
|
227
251
|
"button",
|
|
228
252
|
{
|
|
229
253
|
key: day.toISOString(),
|
|
@@ -236,10 +260,202 @@ var MiniCalendar = ({
|
|
|
236
260
|
!isSelected && isTodayDate && "bg-primary/10 text-primary ring-1 ring-primary/30"
|
|
237
261
|
)
|
|
238
262
|
},
|
|
239
|
-
|
|
263
|
+
format2(day, "d")
|
|
240
264
|
);
|
|
241
265
|
})));
|
|
242
266
|
};
|
|
267
|
+
|
|
268
|
+
// src/components/Sidebar.tsx
|
|
269
|
+
import { toZonedTime } from "date-fns-tz";
|
|
270
|
+
import { format as format4 } from "date-fns";
|
|
271
|
+
|
|
272
|
+
// src/core/format.ts
|
|
273
|
+
import { format as format3 } from "date-fns";
|
|
274
|
+
|
|
275
|
+
// src/locales/index.ts
|
|
276
|
+
var en = {
|
|
277
|
+
today: "Today",
|
|
278
|
+
month: "Month",
|
|
279
|
+
week: "Week",
|
|
280
|
+
day: "Day",
|
|
281
|
+
agenda: "Agenda",
|
|
282
|
+
resource: "Resources",
|
|
283
|
+
createEvent: "Create Event",
|
|
284
|
+
editEvent: "Edit Event",
|
|
285
|
+
delete: "Delete",
|
|
286
|
+
save: "Save",
|
|
287
|
+
cancel: "Cancel",
|
|
288
|
+
title: "Add title",
|
|
289
|
+
start: "Start",
|
|
290
|
+
end: "End",
|
|
291
|
+
allDay: "All Day",
|
|
292
|
+
description: "Description",
|
|
293
|
+
repeat: "Repeat",
|
|
294
|
+
noRepeat: "Does not repeat",
|
|
295
|
+
selectCalendar: "Select Calendar",
|
|
296
|
+
selectType: "Select Type",
|
|
297
|
+
daily: "Daily",
|
|
298
|
+
weekly: "Weekly",
|
|
299
|
+
monthly: "Monthly",
|
|
300
|
+
yearly: "Yearly",
|
|
301
|
+
search: "Search events...",
|
|
302
|
+
allTypes: "All Types",
|
|
303
|
+
timezone: "Time zone",
|
|
304
|
+
localTime: "Local Time",
|
|
305
|
+
create: "Create",
|
|
306
|
+
calendars: "Calendars",
|
|
307
|
+
event: "Event",
|
|
308
|
+
task: "Task",
|
|
309
|
+
appointmentSchedule: "Appointment schedule",
|
|
310
|
+
new: "New",
|
|
311
|
+
dateAndTime: "Date & Time",
|
|
312
|
+
whosJoining: "Who's joining?",
|
|
313
|
+
suggestedTimes: "Suggested times for guests",
|
|
314
|
+
viewSuggestions: "View suggestions",
|
|
315
|
+
whereWillItBe: "Where will it be?",
|
|
316
|
+
location: "Enter address",
|
|
317
|
+
descriptionAndAttachments: "Description",
|
|
318
|
+
dragAndDrop: "Drag & drop files or",
|
|
319
|
+
guests: "Guests",
|
|
320
|
+
addGuests: "Add guests",
|
|
321
|
+
addAttachment: "Upload files",
|
|
322
|
+
moreOptions: "More options",
|
|
323
|
+
doesNotRepeat: "Does not repeat",
|
|
324
|
+
locationHelpText: 'Enter a physical address (e.g. "123 Main St") to generate the map.',
|
|
325
|
+
notes: "Notes...",
|
|
326
|
+
guestCount: "guest",
|
|
327
|
+
guestsCount: "guests",
|
|
328
|
+
repeatFor: "Repeat for",
|
|
329
|
+
times: "times",
|
|
330
|
+
until: "Until",
|
|
331
|
+
endRepeat: "End repeat",
|
|
332
|
+
never: "Never",
|
|
333
|
+
afterOccurrences: "After occurrences",
|
|
334
|
+
onDate: "On date",
|
|
335
|
+
tomorrow: "Tomorrow",
|
|
336
|
+
edit: "Edit",
|
|
337
|
+
duplicate: "Duplicate",
|
|
338
|
+
eventSingular: "event",
|
|
339
|
+
eventPlural: "events",
|
|
340
|
+
allCaughtUp: "All caught up!",
|
|
341
|
+
noEventsScheduled: "No events scheduled",
|
|
342
|
+
noUpcoming: "Your schedule is clear",
|
|
343
|
+
noUpcomingHint: "No upcoming events to show. Create a new event to start planning."
|
|
344
|
+
};
|
|
345
|
+
var fr = {
|
|
346
|
+
today: "Aujourd'hui",
|
|
347
|
+
month: "Mois",
|
|
348
|
+
week: "Semaine",
|
|
349
|
+
day: "Jour",
|
|
350
|
+
agenda: "Agenda",
|
|
351
|
+
resource: "Ressources",
|
|
352
|
+
createEvent: "Cr\xE9er un \xE9v\xE9nement",
|
|
353
|
+
editEvent: "Modifier l'\xE9v\xE9nement",
|
|
354
|
+
delete: "Supprimer",
|
|
355
|
+
save: "Enregistrer",
|
|
356
|
+
cancel: "Annuler",
|
|
357
|
+
title: "Ajouter un titre",
|
|
358
|
+
start: "D\xE9but",
|
|
359
|
+
end: "Fin",
|
|
360
|
+
allDay: "Toute la journ\xE9e",
|
|
361
|
+
description: "Description",
|
|
362
|
+
repeat: "R\xE9p\xE9ter",
|
|
363
|
+
noRepeat: "Ne se r\xE9p\xE8te pas",
|
|
364
|
+
selectCalendar: "S\xE9lectionner un calendrier",
|
|
365
|
+
selectType: "S\xE9lectionner un type",
|
|
366
|
+
daily: "Quotidien",
|
|
367
|
+
weekly: "Hebdomadaire",
|
|
368
|
+
monthly: "Mensuel",
|
|
369
|
+
yearly: "Annuel",
|
|
370
|
+
search: "Rechercher...",
|
|
371
|
+
allTypes: "Tous les types",
|
|
372
|
+
timezone: "Fuseau horaire",
|
|
373
|
+
localTime: "Heure locale",
|
|
374
|
+
create: "Cr\xE9er",
|
|
375
|
+
calendars: "Calendriers",
|
|
376
|
+
event: "\xC9v\xE9nement",
|
|
377
|
+
task: "T\xE2che",
|
|
378
|
+
appointmentSchedule: "Planning de rendez-vous",
|
|
379
|
+
new: "Nouveau",
|
|
380
|
+
dateAndTime: "Date et heure",
|
|
381
|
+
whosJoining: "Qui participe ?",
|
|
382
|
+
suggestedTimes: "Horaires sugg\xE9r\xE9s",
|
|
383
|
+
viewSuggestions: "Voir les suggestions",
|
|
384
|
+
whereWillItBe: "O\xF9 aura lieu ?",
|
|
385
|
+
location: "Entrer une adresse",
|
|
386
|
+
descriptionAndAttachments: "Description",
|
|
387
|
+
dragAndDrop: "Glisser-d\xE9poser des fichiers ou",
|
|
388
|
+
guests: "Invit\xE9s",
|
|
389
|
+
addGuests: "Ajouter des invit\xE9s",
|
|
390
|
+
addAttachment: "T\xE9l\xE9charger des fichiers",
|
|
391
|
+
moreOptions: "Plus d'options",
|
|
392
|
+
doesNotRepeat: "Ne se r\xE9p\xE8te pas",
|
|
393
|
+
locationHelpText: 'Entrez une adresse physique (ex: "123 Rue Principale") pour g\xE9n\xE9rer la carte.',
|
|
394
|
+
notes: "Notes...",
|
|
395
|
+
guestCount: "invit\xE9",
|
|
396
|
+
guestsCount: "invit\xE9s",
|
|
397
|
+
repeatFor: "R\xE9p\xE9ter pendant",
|
|
398
|
+
times: "fois",
|
|
399
|
+
until: "Jusqu'au",
|
|
400
|
+
endRepeat: "Fin de r\xE9p\xE9tition",
|
|
401
|
+
never: "Jamais",
|
|
402
|
+
afterOccurrences: "Apr\xE8s occurrences",
|
|
403
|
+
onDate: "\xC0 la date",
|
|
404
|
+
tomorrow: "Demain",
|
|
405
|
+
edit: "Modifier",
|
|
406
|
+
duplicate: "Dupliquer",
|
|
407
|
+
eventSingular: "\xE9v\xE9nement",
|
|
408
|
+
eventPlural: "\xE9v\xE9nements",
|
|
409
|
+
allCaughtUp: "Tout est \xE0 jour !",
|
|
410
|
+
noEventsScheduled: "Aucun \xE9v\xE9nement pr\xE9vu",
|
|
411
|
+
noUpcoming: "Votre planning est libre",
|
|
412
|
+
noUpcomingHint: "Aucun \xE9v\xE9nement \xE0 venir. Cr\xE9ez un nouvel \xE9v\xE9nement pour commencer \xE0 planifier."
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
// src/core/format.ts
|
|
416
|
+
var clockCache = /* @__PURE__ */ new WeakMap();
|
|
417
|
+
function uses24HourClock(locale) {
|
|
418
|
+
if (!locale) return false;
|
|
419
|
+
let is24 = clockCache.get(locale);
|
|
420
|
+
if (is24 === void 0) {
|
|
421
|
+
is24 = !/[ap]\.?m/i.test(format3(new Date(2e3, 0, 1, 13, 0), "p", { locale }));
|
|
422
|
+
clockCache.set(locale, is24);
|
|
423
|
+
}
|
|
424
|
+
return is24;
|
|
425
|
+
}
|
|
426
|
+
var H24 = { hour: "H:mm", time: "H:mm", compact: "H:mm" };
|
|
427
|
+
var H12 = { hour: "h a", time: "h:mm a", compact: "h:mm" };
|
|
428
|
+
function timeFormats(locale) {
|
|
429
|
+
return uses24HourClock(locale) ? H24 : H12;
|
|
430
|
+
}
|
|
431
|
+
var MAX_FORMATTERS = 50;
|
|
432
|
+
var formatters = /* @__PURE__ */ new Map();
|
|
433
|
+
function getDateTimeFormat(locale, timeZone, options) {
|
|
434
|
+
const key = `${locale}|${timeZone ?? ""}|${JSON.stringify(options)}`;
|
|
435
|
+
let formatter = formatters.get(key);
|
|
436
|
+
if (formatter) {
|
|
437
|
+
formatters.delete(key);
|
|
438
|
+
} else {
|
|
439
|
+
formatter = new Intl.DateTimeFormat(locale, { ...options, timeZone });
|
|
440
|
+
if (formatters.size >= MAX_FORMATTERS) {
|
|
441
|
+
formatters.delete(formatters.keys().next().value);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
formatters.set(key, formatter);
|
|
445
|
+
return formatter;
|
|
446
|
+
}
|
|
447
|
+
function timeZoneAbbreviation(date, timeZone) {
|
|
448
|
+
try {
|
|
449
|
+
return getDateTimeFormat("en-US", timeZone, { timeZoneName: "short" }).formatToParts(date).find((part) => part.type === "timeZoneName")?.value ?? "";
|
|
450
|
+
} catch {
|
|
451
|
+
return "";
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
function resolveTranslations(language, overrides) {
|
|
455
|
+
return { ...language === "fr" ? fr : en, ...overrides };
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// src/components/Sidebar.tsx
|
|
243
459
|
var Sidebar = ({
|
|
244
460
|
currentDate,
|
|
245
461
|
onDateChange,
|
|
@@ -270,14 +486,7 @@ var Sidebar = ({
|
|
|
270
486
|
{ id: "3", label: "Tasks", color: "#6366f1", active: true }
|
|
271
487
|
];
|
|
272
488
|
const displayCalendars = calendars || defaultCalendars;
|
|
273
|
-
const getAcronym = (tz) =>
|
|
274
|
-
if (!tz || !now) return "LOC";
|
|
275
|
-
try {
|
|
276
|
-
return new Intl.DateTimeFormat("en-US", { timeZone: tz, timeZoneName: "short" }).formatToParts(now).find((part) => part.type === "timeZoneName")?.value || "";
|
|
277
|
-
} catch (e) {
|
|
278
|
-
return "";
|
|
279
|
-
}
|
|
280
|
-
};
|
|
489
|
+
const getAcronym = (tz) => !tz || !now ? "LOC" : timeZoneAbbreviation(now, tz);
|
|
281
490
|
const timezones = [
|
|
282
491
|
{ value: "", label: translations?.localTime || "Local Time", acronym: "LOC" },
|
|
283
492
|
{ value: "UTC", label: "UTC", acronym: "UTC" },
|
|
@@ -295,56 +504,53 @@ var Sidebar = ({
|
|
|
295
504
|
];
|
|
296
505
|
const formatTzLabel = (tz, showTime = true) => {
|
|
297
506
|
if (!hasMounted || !now || !showTime) {
|
|
298
|
-
return /* @__PURE__ */
|
|
507
|
+
return /* @__PURE__ */ React4.createElement("span", null, tz.label);
|
|
299
508
|
}
|
|
300
509
|
let time = "";
|
|
301
510
|
let acronym = tz.acronym;
|
|
302
511
|
try {
|
|
303
512
|
if (!tz.value) {
|
|
304
|
-
time =
|
|
305
|
-
|
|
306
|
-
acronym = new Intl.DateTimeFormat("en-US", { timeZoneName: "short" }).formatToParts(now).find((part) => part.type === "timeZoneName")?.value || "LOC";
|
|
307
|
-
} catch (e) {
|
|
308
|
-
}
|
|
513
|
+
time = format4(now, "HH:mm");
|
|
514
|
+
acronym = timeZoneAbbreviation(now) || "LOC";
|
|
309
515
|
} else {
|
|
310
516
|
const zDate = toZonedTime(now, tz.value);
|
|
311
|
-
time =
|
|
517
|
+
time = format4(zDate, "HH:mm");
|
|
312
518
|
const dynAcronym = getAcronym(tz.value);
|
|
313
519
|
if (dynAcronym && !dynAcronym.includes("GMT") && !dynAcronym.includes("Time")) {
|
|
314
520
|
acronym = dynAcronym;
|
|
315
521
|
}
|
|
316
522
|
}
|
|
317
|
-
} catch
|
|
318
|
-
return /* @__PURE__ */
|
|
523
|
+
} catch {
|
|
524
|
+
return /* @__PURE__ */ React4.createElement("span", null, tz.label);
|
|
319
525
|
}
|
|
320
|
-
return /* @__PURE__ */
|
|
526
|
+
return /* @__PURE__ */ React4.createElement("div", { className: "flex justify-between w-full" }, /* @__PURE__ */ React4.createElement("span", null, tz.label), /* @__PURE__ */ React4.createElement("span", { className: "text-muted-foreground ml-2 tabular-nums" }, time, " ", /* @__PURE__ */ React4.createElement("span", { className: "text-xs opacity-75" }, "(", acronym, ")")));
|
|
321
527
|
};
|
|
322
528
|
const selectedTzObj = timezones.find((t) => t.value === (timezone || ""));
|
|
323
529
|
const selectedTimezoneLabel = selectedTzObj ? formatTzLabel(selectedTzObj) : translations?.localTime || "Local Time";
|
|
324
|
-
return /* @__PURE__ */
|
|
530
|
+
return /* @__PURE__ */ React4.createElement("div", { className: cn("flex flex-col w-[260px] overflow-x-hidden h-full bg-gradient-to-b scrollbar-hide from-background via-background to-muted/10 pt-4 pb-4 overflow-y-auto hidden lg:flex", className) }, !readOnly && /* @__PURE__ */ React4.createElement("div", { className: "px-4 mb-6" }, /* @__PURE__ */ React4.createElement(
|
|
325
531
|
Button,
|
|
326
532
|
{
|
|
327
533
|
className: "w-full rounded-2xl shadow-lg bg-primary text-primary-foreground hover:bg-primary/90 h-12 gap-3 justify-center transition-all duration-300 hover:shadow-xl hover:shadow-primary/20 hover:scale-[1.02] active:scale-[0.98] font-semibold",
|
|
328
534
|
onClick: onEventCreate
|
|
329
535
|
},
|
|
330
|
-
/* @__PURE__ */
|
|
331
|
-
/* @__PURE__ */
|
|
332
|
-
)), /* @__PURE__ */
|
|
536
|
+
/* @__PURE__ */ React4.createElement(Plus, { className: "w-5 h-5" }),
|
|
537
|
+
/* @__PURE__ */ React4.createElement("span", { className: "text-sm" }, translations?.create || "Create")
|
|
538
|
+
)), /* @__PURE__ */ React4.createElement(MiniCalendar, { currentDate, onDateChange, onViewChange, locale }), /* @__PURE__ */ React4.createElement("div", { className: "flex-1 px-4 space-y-5 mt-5" }, /* @__PURE__ */ React4.createElement("div", { className: "bg-muted/20 rounded-2xl p-3 border-[0px] border-border/30" }, /* @__PURE__ */ React4.createElement(
|
|
333
539
|
"div",
|
|
334
540
|
{
|
|
335
541
|
className: "flex items-center justify-between cursor-pointer hover:bg-accent/50 p-2 -m-1 rounded-xl mb-2 transition-all duration-200",
|
|
336
542
|
onClick: () => setCalendarsOpen(!calendarsOpen)
|
|
337
543
|
},
|
|
338
|
-
/* @__PURE__ */
|
|
339
|
-
/* @__PURE__ */
|
|
340
|
-
), calendarsOpen && /* @__PURE__ */
|
|
544
|
+
/* @__PURE__ */ React4.createElement("span", { className: "text-sm font-semibold text-foreground" }, translations?.calendars || "Calendars"),
|
|
545
|
+
/* @__PURE__ */ React4.createElement(ChevronDown, { className: cn("w-4 h-4 text-muted-foreground transition-transform duration-200", calendarsOpen && "rotate-180") })
|
|
546
|
+
), calendarsOpen && /* @__PURE__ */ React4.createElement("div", { className: "space-y-1" }, displayCalendars.map((cal) => /* @__PURE__ */ React4.createElement(
|
|
341
547
|
"div",
|
|
342
548
|
{
|
|
343
549
|
key: cal.id,
|
|
344
550
|
className: "flex items-center gap-3 py-2 px-2 hover:bg-accent/60 rounded-xl cursor-pointer group transition-all duration-200",
|
|
345
551
|
onClick: () => onCalendarToggle?.(cal.id, !(cal.active ?? true))
|
|
346
552
|
},
|
|
347
|
-
/* @__PURE__ */
|
|
553
|
+
/* @__PURE__ */ React4.createElement("div", { className: "relative flex items-center justify-center" }, /* @__PURE__ */ React4.createElement(
|
|
348
554
|
"input",
|
|
349
555
|
{
|
|
350
556
|
type: "checkbox",
|
|
@@ -357,7 +563,7 @@ var Sidebar = ({
|
|
|
357
563
|
style: { "--primary-color": cal.color },
|
|
358
564
|
"data-cal-id": cal.id
|
|
359
565
|
}
|
|
360
|
-
), /* @__PURE__ */
|
|
566
|
+
), /* @__PURE__ */ React4.createElement("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none opacity-0 peer-checked:opacity-100 transition-opacity" }, /* @__PURE__ */ React4.createElement("svg", { className: "w-3.5 h-3.5 text-white", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 3 }, /* @__PURE__ */ React4.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" }))), /* @__PURE__ */ React4.createElement("style", null, `
|
|
361
567
|
input[type="checkbox"][data-cal-id="${cal.id}"]:checked {
|
|
362
568
|
background-color: ${cal.color} !important;
|
|
363
569
|
border-color: ${cal.color} !important;
|
|
@@ -366,23 +572,23 @@ var Sidebar = ({
|
|
|
366
572
|
--tw-ring-color: ${cal.color}40 !important;
|
|
367
573
|
}
|
|
368
574
|
`)),
|
|
369
|
-
/* @__PURE__ */
|
|
370
|
-
/* @__PURE__ */
|
|
575
|
+
/* @__PURE__ */ React4.createElement("div", { className: "flex items-center gap-2 flex-1 min-w-0" }, /* @__PURE__ */ React4.createElement("span", { className: "text-sm text-foreground/90 truncate font-medium" }, cal.label)),
|
|
576
|
+
/* @__PURE__ */ React4.createElement(
|
|
371
577
|
"div",
|
|
372
578
|
{
|
|
373
579
|
className: "w-2 h-2 rounded-full opacity-60 group-hover:opacity-100 transition-opacity",
|
|
374
580
|
style: { backgroundColor: cal.color }
|
|
375
581
|
}
|
|
376
582
|
)
|
|
377
|
-
))))), /* @__PURE__ */
|
|
583
|
+
))))), /* @__PURE__ */ React4.createElement("div", { className: "mt-auto px-4 pt-5" }, /* @__PURE__ */ React4.createElement("div", { className: "bg-muted/20 rounded-2xl p-3" }, /* @__PURE__ */ React4.createElement("div", { className: "flex items-center gap-2 mb-3" }, /* @__PURE__ */ React4.createElement("div", { className: "p-1.5 bg-primary/10 rounded-lg" }, /* @__PURE__ */ React4.createElement(Globe, { className: "w-4 h-4 text-primary" })), /* @__PURE__ */ React4.createElement("span", { className: "text-sm font-semibold text-foreground" }, translations?.timezone || "Timezone")), /* @__PURE__ */ React4.createElement("div", { className: "relative" }, /* @__PURE__ */ React4.createElement(
|
|
378
584
|
"button",
|
|
379
585
|
{
|
|
380
586
|
onClick: () => setTimezoneOpen(!timezoneOpen),
|
|
381
587
|
className: "w-full flex items-center justify-between bg-blue-200/40 hover:bg-blue-200/80 rounded-xl py-2.5 pl-4 pr-3 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary/50 transition-all duration-200 text-left"
|
|
382
588
|
},
|
|
383
|
-
/* @__PURE__ */
|
|
384
|
-
/* @__PURE__ */
|
|
385
|
-
), timezoneOpen && /* @__PURE__ */
|
|
589
|
+
/* @__PURE__ */ React4.createElement("div", { className: "flex-1 truncate mr-2 font-medium" }, selectedTimezoneLabel),
|
|
590
|
+
/* @__PURE__ */ React4.createElement(ChevronDown, { className: cn("w-4 h-4 text-muted-foreground transition-transform duration-200", timezoneOpen && "rotate-180") })
|
|
591
|
+
), timezoneOpen && /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement("div", { className: "fixed inset-0 z-40", onClick: () => setTimezoneOpen(false) }), /* @__PURE__ */ React4.createElement("div", { className: "absolute bottom-full left-0 w-full mb-2 bg-background rounded-xl shadow-2xl z-50 max-h-[260px] overflow-y-auto p-1.5 animate-in fade-in zoom-in-95 duration-200 backdrop-blur-none" }, timezones.map((tz) => /* @__PURE__ */ React4.createElement(
|
|
386
592
|
"div",
|
|
387
593
|
{
|
|
388
594
|
key: tz.value,
|
|
@@ -398,26 +604,202 @@ var Sidebar = ({
|
|
|
398
604
|
formatTzLabel(tz)
|
|
399
605
|
))))))));
|
|
400
606
|
};
|
|
401
|
-
|
|
607
|
+
|
|
608
|
+
// src/views/MonthView.tsx
|
|
609
|
+
import React7, { useMemo } from "react";
|
|
610
|
+
import { format as format7, isSameDay as isSameDay3, isSameMonth as isSameMonth2 } from "date-fns";
|
|
611
|
+
|
|
612
|
+
// src/core/events.ts
|
|
613
|
+
import { addDays, format as format6, startOfDay as startOfDay2 } from "date-fns";
|
|
614
|
+
|
|
615
|
+
// src/core/timezone.ts
|
|
616
|
+
import { format as format5, isSameDay as isSameDay2, startOfDay } from "date-fns";
|
|
617
|
+
import { fromZonedTime, toZonedTime as toZonedTime2 } from "date-fns-tz";
|
|
618
|
+
|
|
619
|
+
// src/core/warn.ts
|
|
620
|
+
var warned = /* @__PURE__ */ new Set();
|
|
621
|
+
function isProductionBuild() {
|
|
622
|
+
try {
|
|
623
|
+
return process.env.NODE_ENV === "production";
|
|
624
|
+
} catch {
|
|
625
|
+
return false;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
function warnOnce(key, message) {
|
|
629
|
+
if (isProductionBuild() || warned.has(key)) return;
|
|
630
|
+
warned.add(key);
|
|
631
|
+
console.warn(`[calendarkit-pro] ${message}`);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// src/core/timezone.ts
|
|
635
|
+
var validity = /* @__PURE__ */ new Map();
|
|
636
|
+
function resolveTimeZone(tz) {
|
|
637
|
+
if (!tz) return void 0;
|
|
638
|
+
let valid = validity.get(tz);
|
|
639
|
+
if (valid === void 0) {
|
|
640
|
+
try {
|
|
641
|
+
new Intl.DateTimeFormat("en-US", { timeZone: tz });
|
|
642
|
+
valid = true;
|
|
643
|
+
} catch {
|
|
644
|
+
valid = false;
|
|
645
|
+
}
|
|
646
|
+
validity.set(tz, valid);
|
|
647
|
+
}
|
|
648
|
+
if (!valid) {
|
|
649
|
+
warnOnce(`invalid-timezone:${tz}`, `Unknown timezone "${tz}", falling back to local time.`);
|
|
650
|
+
return void 0;
|
|
651
|
+
}
|
|
652
|
+
return tz;
|
|
653
|
+
}
|
|
654
|
+
function toDisplay(date, tz) {
|
|
655
|
+
const zone = resolveTimeZone(tz);
|
|
656
|
+
return zone ? toZonedTime2(date, zone) : date;
|
|
657
|
+
}
|
|
658
|
+
function fromDisplay(wall, tz) {
|
|
659
|
+
const zone = resolveTimeZone(tz);
|
|
660
|
+
return zone ? fromZonedTime(wall, zone) : new Date(wall);
|
|
661
|
+
}
|
|
662
|
+
function minutesSinceDisplayMidnight(date, tz) {
|
|
663
|
+
const wall = toDisplay(date, tz);
|
|
664
|
+
return wall.getHours() * 60 + wall.getMinutes();
|
|
665
|
+
}
|
|
666
|
+
function isSameDisplayDay(date, day, tz) {
|
|
667
|
+
return isSameDay2(toDisplay(date, tz), day);
|
|
668
|
+
}
|
|
669
|
+
function displayToday(tz, now = /* @__PURE__ */ new Date()) {
|
|
670
|
+
return startOfDay(toDisplay(now, tz));
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// src/core/events.ts
|
|
674
|
+
var toDate = (value) => value === void 0 || value === null ? void 0 : value instanceof Date ? value : new Date(value);
|
|
675
|
+
var isValidDate = (date) => !!date && !Number.isNaN(date.getTime());
|
|
676
|
+
var normalizedEvent = /* @__PURE__ */ new WeakMap();
|
|
677
|
+
var normalizedList = /* @__PURE__ */ new WeakMap();
|
|
678
|
+
function normalizeEvent(event) {
|
|
679
|
+
const cached = normalizedEvent.get(event);
|
|
680
|
+
if (cached !== void 0) return cached;
|
|
681
|
+
const start = toDate(event.start);
|
|
682
|
+
const end = toDate(event.end);
|
|
683
|
+
let result = null;
|
|
684
|
+
if (!isValidDate(start) || !isValidDate(end)) {
|
|
685
|
+
warnOnce(`invalid-date:${event.id}`, `Event "${event.id}" has an invalid start or end date and was skipped.`);
|
|
686
|
+
} else if (end.getTime() < start.getTime()) {
|
|
687
|
+
warnOnce(`end-before-start:${event.id}`, `Event "${event.id}" ends before it starts and was skipped.`);
|
|
688
|
+
} else {
|
|
689
|
+
const needsCopy = start !== event.start || end !== event.end || needsRecurrenceCopy(event);
|
|
690
|
+
result = needsCopy ? {
|
|
691
|
+
...event,
|
|
692
|
+
start,
|
|
693
|
+
end,
|
|
694
|
+
recurrence: event.recurrence && {
|
|
695
|
+
...event.recurrence,
|
|
696
|
+
until: toDate(event.recurrence.until),
|
|
697
|
+
exdates: event.recurrence.exdates?.map((d) => toDate(d))
|
|
698
|
+
}
|
|
699
|
+
} : event;
|
|
700
|
+
}
|
|
701
|
+
normalizedEvent.set(event, result);
|
|
702
|
+
return result;
|
|
703
|
+
}
|
|
704
|
+
function needsRecurrenceCopy(event) {
|
|
705
|
+
const r = event.recurrence;
|
|
706
|
+
if (!r) return false;
|
|
707
|
+
return r.until !== void 0 && !(r.until instanceof Date) || !!r.exdates?.some((d) => !(d instanceof Date));
|
|
708
|
+
}
|
|
709
|
+
function normalizeEvents(events) {
|
|
710
|
+
const cached = normalizedList.get(events);
|
|
711
|
+
if (cached) return cached;
|
|
712
|
+
const seen = /* @__PURE__ */ new Set();
|
|
713
|
+
const result = [];
|
|
714
|
+
for (const event of events) {
|
|
715
|
+
const normalized = normalizeEvent(event);
|
|
716
|
+
if (!normalized) continue;
|
|
717
|
+
if (seen.has(normalized.id)) {
|
|
718
|
+
warnOnce(`duplicate-id:${normalized.id}`, `Duplicate event id "${normalized.id}": ids must be unique.`);
|
|
719
|
+
}
|
|
720
|
+
seen.add(normalized.id);
|
|
721
|
+
result.push(normalized);
|
|
722
|
+
}
|
|
723
|
+
normalizedList.set(events, result);
|
|
724
|
+
return result;
|
|
725
|
+
}
|
|
726
|
+
var dayKey = (wall) => format6(wall, "yyyy-MM-dd");
|
|
727
|
+
function memoByZone(cache, events, tz, compute) {
|
|
728
|
+
let byZone = cache.get(events);
|
|
729
|
+
if (!byZone) {
|
|
730
|
+
byZone = /* @__PURE__ */ new Map();
|
|
731
|
+
cache.set(events, byZone);
|
|
732
|
+
}
|
|
733
|
+
const key = tz ?? "";
|
|
734
|
+
let value = byZone.get(key);
|
|
735
|
+
if (value === void 0) {
|
|
736
|
+
value = compute();
|
|
737
|
+
byZone.set(key, value);
|
|
738
|
+
}
|
|
739
|
+
return value;
|
|
740
|
+
}
|
|
741
|
+
var startDayCache = /* @__PURE__ */ new WeakMap();
|
|
742
|
+
function eventsByStartDay(events, tz) {
|
|
743
|
+
return memoByZone(startDayCache, events, tz, () => {
|
|
744
|
+
const map = /* @__PURE__ */ new Map();
|
|
745
|
+
for (const event of events) {
|
|
746
|
+
const key = dayKey(toDisplay(event.start, tz));
|
|
747
|
+
const bucket = map.get(key);
|
|
748
|
+
if (bucket) bucket.push(event);
|
|
749
|
+
else map.set(key, [event]);
|
|
750
|
+
}
|
|
751
|
+
return map;
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
var segmentCache = /* @__PURE__ */ new WeakMap();
|
|
755
|
+
function segmentsByDisplayDay(events, tz, range) {
|
|
756
|
+
const cacheKey = range ? `${tz ?? ""}|${range.start.getTime()}|${range.end.getTime()}` : tz;
|
|
757
|
+
return memoByZone(segmentCache, events, cacheKey, () => {
|
|
758
|
+
const map = /* @__PURE__ */ new Map();
|
|
759
|
+
for (const event of events) {
|
|
760
|
+
const wallStart = toDisplay(event.start, tz);
|
|
761
|
+
const rawEnd = toDisplay(event.end, tz);
|
|
762
|
+
const wallEnd = rawEnd < wallStart ? wallStart : rawEnd;
|
|
763
|
+
if (range && (wallEnd < range.start || wallStart >= range.end)) continue;
|
|
764
|
+
let day = startOfDay2(range && wallStart < range.start ? range.start : wallStart);
|
|
765
|
+
const stop = range && wallEnd > range.end ? range.end : wallEnd;
|
|
766
|
+
do {
|
|
767
|
+
const next = addDays(day, 1);
|
|
768
|
+
const start = wallStart > day ? wallStart : day;
|
|
769
|
+
const end = wallEnd < next ? wallEnd : next;
|
|
770
|
+
if (end > start || wallStart.getTime() === wallEnd.getTime()) {
|
|
771
|
+
const key = dayKey(day);
|
|
772
|
+
const bucket = map.get(key);
|
|
773
|
+
const segment = { event, start, end };
|
|
774
|
+
if (bucket) bucket.push(segment);
|
|
775
|
+
else map.set(key, [segment]);
|
|
776
|
+
}
|
|
777
|
+
day = next;
|
|
778
|
+
} while (day < stop);
|
|
779
|
+
}
|
|
780
|
+
return map;
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// src/components/dnd/DraggableEvent.tsx
|
|
785
|
+
import React5 from "react";
|
|
786
|
+
import { useDraggable } from "@dnd-kit/core";
|
|
787
|
+
import { CSS } from "@dnd-kit/utilities";
|
|
788
|
+
var DraggableEventImpl = ({ event, dragId, grabOffsetMs, children, className, style: propStyle, ...props }) => {
|
|
402
789
|
const dragDisabled = isEventDragDisabled(event);
|
|
403
790
|
const { attributes, listeners, setNodeRef, transform, isDragging } = useDraggable({
|
|
404
|
-
id: event.id,
|
|
405
|
-
data: { event },
|
|
791
|
+
id: dragId ?? event.id,
|
|
792
|
+
data: { event, grabOffsetMs },
|
|
406
793
|
disabled: dragDisabled
|
|
407
794
|
});
|
|
408
795
|
const style = {
|
|
409
796
|
...propStyle,
|
|
410
797
|
transform: CSS.Translate.toString(transform),
|
|
411
798
|
zIndex: isDragging ? 100 : propStyle?.zIndex,
|
|
412
|
-
//
|
|
413
|
-
// so that the DragOverlay (the "ghost") is the only thing the user focuses on.
|
|
414
|
-
// If we set opacity: 0, it disappears. If 0.1, it's a faint placeholder.
|
|
415
|
-
// The user asked for "Make the entire dragged item transparent".
|
|
416
|
-
// Usually this means the original item should be faded out while the drag overlay is moving.
|
|
799
|
+
// Hide the original while dragging: the DragOverlay ghost is what the user follows.
|
|
417
800
|
opacity: isDragging ? 0 : propStyle?.opacity
|
|
418
|
-
// Hiding original element completely
|
|
419
801
|
};
|
|
420
|
-
return /* @__PURE__ */
|
|
802
|
+
return /* @__PURE__ */ React5.createElement(
|
|
421
803
|
"div",
|
|
422
804
|
{
|
|
423
805
|
ref: setNodeRef,
|
|
@@ -430,32 +812,59 @@ var DraggableEvent = ({ event, children, className, style: propStyle, ...props }
|
|
|
430
812
|
children
|
|
431
813
|
);
|
|
432
814
|
};
|
|
433
|
-
var
|
|
815
|
+
var DraggableEvent = React5.memo(DraggableEventImpl);
|
|
816
|
+
|
|
817
|
+
// src/components/dnd/DroppableCell.tsx
|
|
818
|
+
import React6 from "react";
|
|
819
|
+
import { useDroppable } from "@dnd-kit/core";
|
|
820
|
+
var QUARTER_HOVER = [
|
|
821
|
+
"hover:bg-blue-50/50 dark:hover:bg-blue-900/10",
|
|
822
|
+
"hover:bg-blue-50/80 dark:hover:bg-blue-900/20",
|
|
823
|
+
"hover:bg-blue-100/50 dark:hover:bg-blue-900/30",
|
|
824
|
+
"hover:bg-blue-100/80 dark:hover:bg-blue-900/40"
|
|
825
|
+
];
|
|
826
|
+
var QUARTER_ACTIVE = [
|
|
827
|
+
"bg-blue-50/50 dark:bg-blue-900/10 ring-2 ring-primary ring-inset",
|
|
828
|
+
"bg-blue-50/80 dark:bg-blue-900/20 ring-2 ring-primary ring-inset",
|
|
829
|
+
"bg-blue-100/50 dark:bg-blue-900/30 ring-2 ring-primary ring-inset",
|
|
830
|
+
"bg-blue-100/80 dark:bg-blue-900/40 ring-2 ring-primary ring-inset"
|
|
831
|
+
];
|
|
832
|
+
var quarterOf = (date) => Math.min(3, Math.floor(date.getMinutes() / 15));
|
|
833
|
+
var DroppableCellImpl = ({ id, date, resourceId, children, className, style, onClick, onSlotClick }) => {
|
|
434
834
|
const { isOver, setNodeRef } = useDroppable({
|
|
435
835
|
id,
|
|
436
836
|
data: { date, resourceId }
|
|
437
837
|
});
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
const activeQuarterClass = minutes === 0 ? "bg-blue-50/50 dark:bg-blue-900/10 ring-2 ring-primary ring-inset" : minutes === 15 ? "bg-blue-50/80 dark:bg-blue-900/20 ring-2 ring-primary ring-inset" : minutes === 30 ? "bg-blue-100/50 dark:bg-blue-900/30 ring-2 ring-primary ring-inset" : "bg-blue-100/80 dark:bg-blue-900/40 ring-2 ring-primary ring-inset";
|
|
441
|
-
return /* @__PURE__ */ React15__default.createElement(
|
|
838
|
+
const quarter = quarterOf(date);
|
|
839
|
+
return /* @__PURE__ */ React6.createElement(
|
|
442
840
|
"div",
|
|
443
841
|
{
|
|
444
842
|
ref: setNodeRef,
|
|
445
843
|
className: cn(
|
|
446
844
|
className,
|
|
447
|
-
|
|
448
|
-
isOver &&
|
|
845
|
+
QUARTER_HOVER[quarter],
|
|
846
|
+
isOver && QUARTER_ACTIVE[quarter],
|
|
847
|
+
onSlotClick && "cursor-pointer"
|
|
449
848
|
),
|
|
450
849
|
style,
|
|
451
|
-
onClick
|
|
850
|
+
onClick: onSlotClick ? () => onSlotClick(date, resourceId) : onClick
|
|
452
851
|
},
|
|
453
852
|
children
|
|
454
853
|
);
|
|
455
854
|
};
|
|
855
|
+
var shallowEqualStyle = (a, b) => {
|
|
856
|
+
if (a === b) return true;
|
|
857
|
+
if (!a || !b) return false;
|
|
858
|
+
const keys = Object.keys(a);
|
|
859
|
+
return keys.length === Object.keys(b).length && keys.every((key) => a[key] === b[key]);
|
|
860
|
+
};
|
|
861
|
+
var DroppableCell = React6.memo(
|
|
862
|
+
DroppableCellImpl,
|
|
863
|
+
(prev, next) => prev.id === next.id && prev.date.getTime() === next.date.getTime() && prev.resourceId === next.resourceId && prev.children === next.children && prev.className === next.className && prev.onClick === next.onClick && prev.onSlotClick === next.onSlotClick && shallowEqualStyle(prev.style, next.style)
|
|
864
|
+
);
|
|
456
865
|
|
|
457
866
|
// src/views/MonthView.tsx
|
|
458
|
-
var EventItem =
|
|
867
|
+
var EventItem = React7.memo(({ event, onEventClick }) => /* @__PURE__ */ React7.createElement(DraggableEvent, { event }, /* @__PURE__ */ React7.createElement(
|
|
459
868
|
"div",
|
|
460
869
|
{
|
|
461
870
|
className: cn(
|
|
@@ -473,7 +882,7 @@ var EventItem = React15__default.memo(({ event, onEventClick }) => /* @__PURE__
|
|
|
473
882
|
onEventClick?.(event);
|
|
474
883
|
}
|
|
475
884
|
},
|
|
476
|
-
/* @__PURE__ */
|
|
885
|
+
/* @__PURE__ */ React7.createElement("span", { className: "font-medium" }, event.title)
|
|
477
886
|
)));
|
|
478
887
|
EventItem.displayName = "EventItem";
|
|
479
888
|
var MonthView = ({
|
|
@@ -484,44 +893,24 @@ var MonthView = ({
|
|
|
484
893
|
timezone,
|
|
485
894
|
locale
|
|
486
895
|
}) => {
|
|
487
|
-
const weekStartsOn = locale
|
|
488
|
-
const days = useMemo(
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
);
|
|
492
|
-
|
|
493
|
-
const start = startOfWeek(currentDate, { weekStartsOn });
|
|
494
|
-
const end = endOfWeek(currentDate, { weekStartsOn });
|
|
495
|
-
return eachDayOfInterval({ start, end });
|
|
496
|
-
}, [currentDate, weekStartsOn]);
|
|
497
|
-
const getZonedDate = useCallback((date) => {
|
|
498
|
-
return timezone ? toZonedTime(date, timezone) : date;
|
|
499
|
-
}, [timezone]);
|
|
500
|
-
const eventsByDay = useMemo(() => {
|
|
501
|
-
const map = /* @__PURE__ */ new Map();
|
|
502
|
-
events.forEach((event) => {
|
|
503
|
-
const zonedStart = getZonedDate(event.start);
|
|
504
|
-
const key = format(zonedStart, "yyyy-MM-dd");
|
|
505
|
-
if (!map.has(key)) {
|
|
506
|
-
map.set(key, []);
|
|
507
|
-
}
|
|
508
|
-
map.get(key).push(event);
|
|
509
|
-
});
|
|
510
|
-
return map;
|
|
511
|
-
}, [events, getZonedDate]);
|
|
512
|
-
return /* @__PURE__ */ React15__default.createElement("div", { className: "flex flex-col h-full bg-background border-[0.5px] scrollbar-hide border-border/50 rounded-2xl overflow-hidden min-w-[800px] md:min-w-0 shadow-sm" }, /* @__PURE__ */ React15__default.createElement("div", { className: "overflow-y-auto flex-1 relative" }, /* @__PURE__ */ React15__default.createElement("div", { className: "grid grid-cols-7 border-b-[0.5px] border-border/50 bg-gradient-to-r from-muted/30 via-muted/40 to-muted/30 sticky top-0 z-20 backdrop-blur-sm" }, weekDays.map((day) => /* @__PURE__ */ React15__default.createElement(
|
|
896
|
+
const weekStartsOn = resolveWeekStartsOn(locale, 1);
|
|
897
|
+
const days = useMemo(() => getMonthGrid(currentDate, weekStartsOn), [currentDate, weekStartsOn]);
|
|
898
|
+
const weekDays = useMemo(() => getWeekDays(currentDate, weekStartsOn), [currentDate, weekStartsOn]);
|
|
899
|
+
const eventsByDay = eventsByStartDay(events, timezone);
|
|
900
|
+
const today = displayToday(timezone);
|
|
901
|
+
return /* @__PURE__ */ React7.createElement("div", { className: "flex flex-col h-full bg-background border-[0.5px] scrollbar-hide border-border/50 rounded-2xl overflow-hidden min-w-[800px] md:min-w-0 shadow-sm" }, /* @__PURE__ */ React7.createElement("div", { className: "overflow-y-auto flex-1 relative" }, /* @__PURE__ */ React7.createElement("div", { className: "grid grid-cols-7 border-b-[0.5px] border-border/50 bg-gradient-to-r from-muted/30 via-muted/40 to-muted/30 sticky top-0 z-20 backdrop-blur-sm" }, weekDays.map((day) => /* @__PURE__ */ React7.createElement(
|
|
513
902
|
"div",
|
|
514
903
|
{
|
|
515
904
|
key: day.toISOString(),
|
|
516
905
|
className: "py-3 text-center text-xs font-semibold text-muted-foreground border-r-[0.5px] border-border/30 last:border-r-0 uppercase tracking-wider"
|
|
517
906
|
},
|
|
518
|
-
|
|
519
|
-
))), /* @__PURE__ */
|
|
520
|
-
const
|
|
521
|
-
const dayEvents = eventsByDay.get(
|
|
522
|
-
const isCurrentMonth =
|
|
907
|
+
format7(day, "EEE", { locale })
|
|
908
|
+
))), /* @__PURE__ */ React7.createElement("div", { className: "grid grid-cols-7", style: { gridAutoRows: "130px" } }, days.map((day) => {
|
|
909
|
+
const dayKey2 = format7(day, "yyyy-MM-dd");
|
|
910
|
+
const dayEvents = eventsByDay.get(dayKey2) || [];
|
|
911
|
+
const isCurrentMonth = isSameMonth2(day, currentDate);
|
|
523
912
|
const cellId = day.toISOString();
|
|
524
|
-
return /* @__PURE__ */
|
|
913
|
+
return /* @__PURE__ */ React7.createElement(
|
|
525
914
|
DroppableCell,
|
|
526
915
|
{
|
|
527
916
|
key: cellId,
|
|
@@ -530,18 +919,87 @@ var MonthView = ({
|
|
|
530
919
|
className: cn(
|
|
531
920
|
"h-[130px] p-2 border-b-[0.5px] border-r-[0.5px] border-border/30 last:border-r-0 relative transition-all duration-200 hover:bg-accent/5 flex flex-col gap-1.5 overflow-hidden group",
|
|
532
921
|
!isCurrentMonth && "bg-muted/5 text-muted-foreground/60",
|
|
533
|
-
|
|
922
|
+
isSameDay3(day, today) && "bg-primary/5 ring-1 ring-inset ring-primary/20"
|
|
534
923
|
),
|
|
535
924
|
onClick: () => onDateClick?.(day)
|
|
536
925
|
},
|
|
537
|
-
/* @__PURE__ */
|
|
926
|
+
/* @__PURE__ */ React7.createElement("div", { className: "flex justify-between items-start" }, /* @__PURE__ */ React7.createElement("div", { className: cn(
|
|
538
927
|
"text-sm font-semibold w-7 h-7 flex items-center justify-center rounded-full transition-all duration-200",
|
|
539
|
-
|
|
540
|
-
) },
|
|
541
|
-
/* @__PURE__ */
|
|
928
|
+
isSameDay3(day, today) ? "bg-primary text-primary-foreground shadow-md shadow-primary/30" : "group-hover:bg-accent"
|
|
929
|
+
) }, format7(day, "d", { locale })), dayEvents.length > 0 && /* @__PURE__ */ React7.createElement("div", { className: "text-[10px] font-medium text-muted-foreground/60 bg-muted/50 px-1.5 py-0.5 rounded-full" }, dayEvents.length)),
|
|
930
|
+
/* @__PURE__ */ React7.createElement("div", { className: "flex-1 flex flex-col gap-1 scrollbar-hide overflow-y-auto overflow-x-hidden" }, dayEvents.slice(0, 4).map((event) => /* @__PURE__ */ React7.createElement(EventItem, { key: `${event.id}-${dayKey2}`, event, onEventClick })), dayEvents.length > 4 && /* @__PURE__ */ React7.createElement("div", { className: "text-[10px] text-primary font-semibold text-center py-1 px-2 rounded-md bg-primary/5 hover:bg-primary/10 cursor-pointer transition-colors" }, "+", dayEvents.length - 4, " more"))
|
|
542
931
|
);
|
|
543
932
|
}))));
|
|
544
933
|
};
|
|
934
|
+
|
|
935
|
+
// src/views/WeekView.tsx
|
|
936
|
+
import React9 from "react";
|
|
937
|
+
import { addDays as addDays2, format as format8, differenceInMinutes, isSameDay as isSameDay4 } from "date-fns";
|
|
938
|
+
|
|
939
|
+
// src/core/layout.ts
|
|
940
|
+
var layoutCache = /* @__PURE__ */ new WeakMap();
|
|
941
|
+
function layoutDayEvents(segments) {
|
|
942
|
+
const cached = layoutCache.get(segments);
|
|
943
|
+
if (cached) return cached;
|
|
944
|
+
const sorted = [...segments].sort(
|
|
945
|
+
(a, b) => a.start.getTime() - b.start.getTime() || b.end.getTime() - a.end.getTime() || (a.event.id < b.event.id ? -1 : a.event.id > b.event.id ? 1 : 0)
|
|
946
|
+
);
|
|
947
|
+
const result = [];
|
|
948
|
+
let cluster = [];
|
|
949
|
+
let columnEnds = [];
|
|
950
|
+
let clusterEnd = -Infinity;
|
|
951
|
+
const closeCluster = () => {
|
|
952
|
+
for (const placed of cluster) placed.columns = columnEnds.length;
|
|
953
|
+
cluster = [];
|
|
954
|
+
columnEnds = [];
|
|
955
|
+
};
|
|
956
|
+
for (const segment of sorted) {
|
|
957
|
+
const start = segment.start.getTime();
|
|
958
|
+
const end = Math.max(segment.end.getTime(), start + 1);
|
|
959
|
+
if (start >= clusterEnd) closeCluster();
|
|
960
|
+
let column = columnEnds.findIndex((columnEnd) => columnEnd <= start);
|
|
961
|
+
if (column === -1) {
|
|
962
|
+
column = columnEnds.length;
|
|
963
|
+
columnEnds.push(end);
|
|
964
|
+
} else {
|
|
965
|
+
columnEnds[column] = end;
|
|
966
|
+
}
|
|
967
|
+
const placed = { ...segment, column, columns: 1 };
|
|
968
|
+
cluster.push(placed);
|
|
969
|
+
result.push(placed);
|
|
970
|
+
clusterEnd = Math.max(clusterEnd, end);
|
|
971
|
+
}
|
|
972
|
+
closeCluster();
|
|
973
|
+
layoutCache.set(segments, result);
|
|
974
|
+
return result;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// src/components/dnd/ResizableEvent.tsx
|
|
978
|
+
import React8, { useState as useState2, useRef, useCallback } from "react";
|
|
979
|
+
|
|
980
|
+
// src/core/dnd.ts
|
|
981
|
+
function computeDrop({ event, overDate, overResourceId, grabOffsetMs = 0, view, tz }) {
|
|
982
|
+
let wallStart;
|
|
983
|
+
if (view === "month") {
|
|
984
|
+
const original = toDisplay(event.start, tz);
|
|
985
|
+
wallStart = new Date(overDate);
|
|
986
|
+
wallStart.setHours(original.getHours(), original.getMinutes(), original.getSeconds(), original.getMilliseconds());
|
|
987
|
+
} else {
|
|
988
|
+
wallStart = new Date(overDate.getTime() - grabOffsetMs);
|
|
989
|
+
wallStart.setSeconds(0, 0);
|
|
990
|
+
}
|
|
991
|
+
const start = fromDisplay(wallStart, tz);
|
|
992
|
+
const end = new Date(start.getTime() + (event.end.getTime() - event.start.getTime()));
|
|
993
|
+
const resourceId = overResourceId ?? event.resourceId;
|
|
994
|
+
if (start.getTime() === event.start.getTime() && resourceId === event.resourceId) return null;
|
|
995
|
+
return { start, end, resourceId };
|
|
996
|
+
}
|
|
997
|
+
function computeResizeEnd(event, deltaMinutes, minDuration = 15) {
|
|
998
|
+
const minEnd = event.start.getTime() + minDuration * 6e4;
|
|
999
|
+
return new Date(Math.max(event.end.getTime() + deltaMinutes * 6e4, minEnd));
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// src/components/dnd/ResizableEvent.tsx
|
|
545
1003
|
var ResizableEvent = ({
|
|
546
1004
|
event,
|
|
547
1005
|
hourHeight,
|
|
@@ -553,8 +1011,8 @@ var ResizableEvent = ({
|
|
|
553
1011
|
readonly
|
|
554
1012
|
}) => {
|
|
555
1013
|
const resizeLocked = readonly || isEventResizeDisabled(event);
|
|
556
|
-
const [isResizing, setIsResizing] =
|
|
557
|
-
const [resizeHeight, setResizeHeight] =
|
|
1014
|
+
const [isResizing, setIsResizing] = useState2(false);
|
|
1015
|
+
const [resizeHeight, setResizeHeight] = useState2(null);
|
|
558
1016
|
const containerRef = useRef(null);
|
|
559
1017
|
const startYRef = useRef(0);
|
|
560
1018
|
const startHeightRef = useRef(0);
|
|
@@ -583,14 +1041,8 @@ var ResizableEvent = ({
|
|
|
583
1041
|
setIsResizing(false);
|
|
584
1042
|
const finalHeight = resizeHeightRef.current;
|
|
585
1043
|
if (finalHeight !== null && containerRef.current && onResize) {
|
|
586
|
-
const
|
|
587
|
-
|
|
588
|
-
const minutesDiff = heightDiff / hourHeight * 60;
|
|
589
|
-
const newEnd = new Date(event.end);
|
|
590
|
-
newEnd.setMinutes(newEnd.getMinutes() + minutesDiff);
|
|
591
|
-
if (newEnd > event.start) {
|
|
592
|
-
onResize(event, newEnd);
|
|
593
|
-
}
|
|
1044
|
+
const minutesDiff = (finalHeight - startHeightRef.current) / hourHeight * 60;
|
|
1045
|
+
onResize(event, computeResizeEnd(event, minutesDiff, minDuration));
|
|
594
1046
|
}
|
|
595
1047
|
resizeHeightRef.current = null;
|
|
596
1048
|
setResizeHeight(null);
|
|
@@ -607,7 +1059,7 @@ var ResizableEvent = ({
|
|
|
607
1059
|
const handlePointerDown = useCallback((e) => {
|
|
608
1060
|
e.stopPropagation();
|
|
609
1061
|
}, []);
|
|
610
|
-
return /* @__PURE__ */
|
|
1062
|
+
return /* @__PURE__ */ React8.createElement(
|
|
611
1063
|
"div",
|
|
612
1064
|
{
|
|
613
1065
|
ref: containerRef,
|
|
@@ -618,7 +1070,7 @@ var ResizableEvent = ({
|
|
|
618
1070
|
}
|
|
619
1071
|
},
|
|
620
1072
|
children,
|
|
621
|
-
/* @__PURE__ */
|
|
1073
|
+
/* @__PURE__ */ React8.createElement(
|
|
622
1074
|
"div",
|
|
623
1075
|
{
|
|
624
1076
|
className: cn(
|
|
@@ -631,17 +1083,26 @@ var ResizableEvent = ({
|
|
|
631
1083
|
onMouseDown: resizeLocked ? void 0 : handleResizeStart,
|
|
632
1084
|
onTouchStart: resizeLocked ? void 0 : handleResizeStart
|
|
633
1085
|
},
|
|
634
|
-
/* @__PURE__ */
|
|
1086
|
+
/* @__PURE__ */ React8.createElement("div", { className: cn(
|
|
635
1087
|
"w-8 h-1 rounded-full transition-all",
|
|
636
1088
|
isResizing ? "bg-primary" : "bg-muted-foreground/30 group-hover:bg-primary/50"
|
|
637
1089
|
) })
|
|
638
1090
|
),
|
|
639
|
-
isResizing && /* @__PURE__ */
|
|
1091
|
+
isResizing && /* @__PURE__ */ React8.createElement("div", { className: "absolute inset-0 border-2 border-primary border-dashed rounded-md pointer-events-none bg-primary/5" })
|
|
640
1092
|
);
|
|
641
1093
|
};
|
|
642
1094
|
|
|
643
1095
|
// src/views/WeekView.tsx
|
|
644
|
-
var
|
|
1096
|
+
var HOUR_HEIGHT = 60;
|
|
1097
|
+
var HOURS = Array.from({ length: 24 }, (_, i) => i);
|
|
1098
|
+
var QUARTERS = [0, 15, 30, 45];
|
|
1099
|
+
var NO_SEGMENTS = [];
|
|
1100
|
+
var slotDate = (day, hour, minute) => {
|
|
1101
|
+
const date = new Date(day);
|
|
1102
|
+
date.setHours(hour, minute, 0, 0);
|
|
1103
|
+
return date;
|
|
1104
|
+
};
|
|
1105
|
+
var WeekViewImpl = ({
|
|
645
1106
|
currentDate,
|
|
646
1107
|
events,
|
|
647
1108
|
onEventClick,
|
|
@@ -651,151 +1112,110 @@ var WeekView = ({
|
|
|
651
1112
|
locale,
|
|
652
1113
|
readonly
|
|
653
1114
|
}) => {
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
const
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
1115
|
+
const weekDays = React9.useMemo(
|
|
1116
|
+
() => getWeekDays(currentDate, resolveWeekStartsOn(locale, 1)),
|
|
1117
|
+
[currentDate, locale]
|
|
1118
|
+
);
|
|
1119
|
+
const visibleRange = React9.useMemo(
|
|
1120
|
+
() => ({ start: weekDays[0], end: addDays2(weekDays[weekDays.length - 1], 1) }),
|
|
1121
|
+
[weekDays]
|
|
1122
|
+
);
|
|
1123
|
+
const scrollContainerRef = React9.useRef(null);
|
|
1124
|
+
const [now, setNow] = React9.useState(() => /* @__PURE__ */ new Date());
|
|
1125
|
+
React9.useEffect(() => {
|
|
663
1126
|
const interval = setInterval(() => setNow(/* @__PURE__ */ new Date()), 6e4);
|
|
664
1127
|
return () => clearInterval(interval);
|
|
665
1128
|
}, []);
|
|
666
|
-
|
|
1129
|
+
React9.useEffect(() => {
|
|
667
1130
|
if (scrollContainerRef.current) {
|
|
668
|
-
const
|
|
669
|
-
|
|
670
|
-
const offsetHour = 4;
|
|
671
|
-
return (currentHour - offsetHour) * hourHeight;
|
|
672
|
-
};
|
|
673
|
-
scrollContainerRef.current.scrollTop = scrollToActualTime();
|
|
674
|
-
}
|
|
675
|
-
}, []);
|
|
676
|
-
const getZonedDate = (date) => {
|
|
677
|
-
return timezone ? toZonedTime(date, timezone) : date;
|
|
678
|
-
};
|
|
679
|
-
const zonedNow = getZonedDate(/* @__PURE__ */ new Date());
|
|
680
|
-
const getTimezoneDisplay = (tz) => {
|
|
681
|
-
const date = now;
|
|
682
|
-
let displayTime = "";
|
|
683
|
-
let acronym = "";
|
|
684
|
-
if (!tz) {
|
|
685
|
-
displayTime = format(date, "HH:mm");
|
|
686
|
-
try {
|
|
687
|
-
acronym = new Intl.DateTimeFormat("en-US", { timeZoneName: "short" }).formatToParts(date).find((part) => part.type === "timeZoneName")?.value || "";
|
|
688
|
-
} catch (e) {
|
|
689
|
-
acronym = "LOC";
|
|
690
|
-
}
|
|
691
|
-
} else {
|
|
692
|
-
try {
|
|
693
|
-
const zDate = toZonedTime(date, tz);
|
|
694
|
-
displayTime = format(zDate, "HH:mm");
|
|
695
|
-
acronym = new Intl.DateTimeFormat("en-US", { timeZone: tz, timeZoneName: "short" }).formatToParts(date).find((part) => part.type === "timeZoneName")?.value || "";
|
|
696
|
-
} catch (e) {
|
|
697
|
-
displayTime = format(date, "HH:mm");
|
|
698
|
-
acronym = tz;
|
|
699
|
-
}
|
|
1131
|
+
const offsetHour = 4;
|
|
1132
|
+
scrollContainerRef.current.scrollTop = (toDisplay(/* @__PURE__ */ new Date(), timezone).getHours() - offsetHour) * HOUR_HEIGHT;
|
|
700
1133
|
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
const
|
|
704
|
-
const
|
|
705
|
-
const
|
|
706
|
-
const
|
|
707
|
-
|
|
1134
|
+
}, [timezone]);
|
|
1135
|
+
const segmentsByDay = segmentsByDisplayDay(events, timezone, visibleRange);
|
|
1136
|
+
const today = displayToday(timezone, now);
|
|
1137
|
+
const zonedNow = toDisplay(now, timezone);
|
|
1138
|
+
const nowTop = minutesSinceDisplayMidnight(now, timezone) / 60 * HOUR_HEIGHT;
|
|
1139
|
+
const showsToday = weekDays.some((day) => isSameDay4(day, today));
|
|
1140
|
+
const zone = resolveTimeZone(timezone);
|
|
1141
|
+
const zoneLabel = timeZoneAbbreviation(now, zone) || zone || "LOC";
|
|
1142
|
+
const formats = timeFormats(locale);
|
|
1143
|
+
return /* @__PURE__ */ React9.createElement("div", { className: "flex flex-col h-full bg-background border-[0.5px] border-border/50 rounded-2xl overflow-hidden min-w-[800px] md:min-w-0 shadow-sm" }, /* @__PURE__ */ React9.createElement(
|
|
708
1144
|
"div",
|
|
709
1145
|
{
|
|
710
1146
|
ref: scrollContainerRef,
|
|
711
1147
|
className: "flex-1 overflow-y-auto scrollbar-hide relative bg-background scroll-smooth"
|
|
712
1148
|
},
|
|
713
|
-
/* @__PURE__ */
|
|
1149
|
+
/* @__PURE__ */ React9.createElement("div", { className: "flex border-b-[0.5px] border-border/50 bg-gradient-to-r from-muted/20 via-background to-muted/20 z-20 sticky top-0 backdrop-blur-sm" }, /* @__PURE__ */ React9.createElement("div", { className: "flex-none p-3 text-center text-xs font-semibold text-muted-foreground w-16 flex items-center justify-center border-r-[0.5px] border-border/30 bg-muted/10" }, /* @__PURE__ */ React9.createElement("div", { className: "flex flex-col items-center justify-center leading-tight" }, /* @__PURE__ */ React9.createElement("span", null, format8(zonedNow, "HH:mm")), /* @__PURE__ */ React9.createElement("span", { className: "text-[10px] opacity-75" }, "(", zoneLabel, ")"))), /* @__PURE__ */ React9.createElement("div", { className: "flex-1 grid grid-cols-7" }, weekDays.map((day, index) => /* @__PURE__ */ React9.createElement("div", { key: day.toISOString(), className: cn("py-3 px-2 text-center", index > 0 && "border-l-[0.5px] border-border/30") }, /* @__PURE__ */ React9.createElement("div", { className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-wider mb-1.5" }, format8(day, "EEE", { locale })), /* @__PURE__ */ React9.createElement("div", { className: cn(
|
|
714
1150
|
"w-9 h-9 flex items-center justify-center rounded-xl mx-auto text-sm font-semibold transition-all duration-200",
|
|
715
|
-
|
|
716
|
-
) },
|
|
717
|
-
/* @__PURE__ */
|
|
1151
|
+
isSameDay4(day, today) ? "bg-primary text-primary-foreground shadow-lg shadow-primary/30 scale-110" : "text-foreground hover:bg-accent/80"
|
|
1152
|
+
) }, format8(day, "d", { locale })))))),
|
|
1153
|
+
/* @__PURE__ */ React9.createElement(
|
|
718
1154
|
"div",
|
|
719
1155
|
{
|
|
720
1156
|
className: "flex min-w-full relative",
|
|
721
|
-
style: { height:
|
|
1157
|
+
style: { height: HOURS.length * HOUR_HEIGHT }
|
|
722
1158
|
},
|
|
723
|
-
/* @__PURE__ */
|
|
1159
|
+
/* @__PURE__ */ React9.createElement("div", { className: "flex-none w-16 border-r-[0.5px] border-border/30 relative bg-muted/5" }, HOURS.map((hour) => /* @__PURE__ */ React9.createElement(
|
|
724
1160
|
"div",
|
|
725
1161
|
{
|
|
726
1162
|
key: hour,
|
|
727
1163
|
className: "relative w-full text-[11px] text-muted-foreground/80 text-right pr-3 font-medium tabular-nums box-border",
|
|
728
|
-
style: { height:
|
|
1164
|
+
style: { height: HOUR_HEIGHT }
|
|
729
1165
|
},
|
|
730
|
-
/* @__PURE__ */
|
|
731
|
-
))
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
1166
|
+
/* @__PURE__ */ React9.createElement("span", { className: "block -translate-y-1/2" }, hour !== 0 && format8(slotDate(currentDate, hour, 0), formats.hour, { locale }))
|
|
1167
|
+
)), showsToday && /* @__PURE__ */ React9.createElement(
|
|
1168
|
+
"div",
|
|
1169
|
+
{
|
|
1170
|
+
className: "absolute left-0 w-full pointer-events-none z-30 flex justify-end pr-2",
|
|
1171
|
+
style: { top: `${nowTop}px` }
|
|
1172
|
+
},
|
|
1173
|
+
/* @__PURE__ */ React9.createElement("span", { className: "text-[10px] font-bold text-primary-foreground bg-primary px-1.5 py-0.5 rounded-md shadow-md -translate-y-1/2 backdrop-blur-none" }, format8(zonedNow, formats.compact, { locale }))
|
|
1174
|
+
)),
|
|
1175
|
+
/* @__PURE__ */ React9.createElement("div", { className: "flex-1 grid grid-cols-7 relative" }, weekDays.map((day, dayIndex) => {
|
|
1176
|
+
const dayKey2 = format8(day, "yyyy-MM-dd");
|
|
1177
|
+
const positioned = layoutDayEvents(segmentsByDay.get(dayKey2) ?? NO_SEGMENTS);
|
|
1178
|
+
return /* @__PURE__ */ React9.createElement("div", { key: dayKey2, className: cn("relative h-full", dayIndex > 0 && "border-l-[0.5px] border-border/30") }, HOURS.map((hour) => /* @__PURE__ */ React9.createElement(
|
|
1179
|
+
"div",
|
|
1180
|
+
{
|
|
1181
|
+
key: hour,
|
|
1182
|
+
className: "w-full border-b-[0.5px] border-dashed border-border/20 box-border relative hover:bg-accent/5 transition-colors",
|
|
1183
|
+
style: { height: HOUR_HEIGHT }
|
|
1184
|
+
},
|
|
1185
|
+
QUARTERS.map((minute) => {
|
|
1186
|
+
const cellDate = slotDate(day, hour, minute);
|
|
1187
|
+
return /* @__PURE__ */ React9.createElement(
|
|
1188
|
+
DroppableCell,
|
|
1189
|
+
{
|
|
1190
|
+
key: minute,
|
|
1191
|
+
id: cellDate.toISOString(),
|
|
1192
|
+
date: cellDate,
|
|
1193
|
+
className: "w-full absolute left-0 right-0 z-0 transition-colors",
|
|
1194
|
+
style: {
|
|
1195
|
+
height: "25%",
|
|
1196
|
+
top: `${minute / 60 * 100}%`
|
|
760
1197
|
},
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
);
|
|
771
|
-
|
|
772
|
-
const
|
|
773
|
-
|
|
774
|
-
const s1 = getZonedDate(event.start).getTime();
|
|
775
|
-
const e1 = getZonedDate(event.end).getTime();
|
|
776
|
-
const s2 = getZonedDate(e.start).getTime();
|
|
777
|
-
const e2 = getZonedDate(e.end).getTime();
|
|
778
|
-
return s1 < e2 && e1 > s2;
|
|
779
|
-
});
|
|
780
|
-
const group = [event, ...overlappingEvents].sort(
|
|
781
|
-
(a, b) => getZonedDate(a.start).getTime() - getZonedDate(b.start).getTime() || (a.id > b.id ? 1 : -1)
|
|
782
|
-
// stable sort
|
|
783
|
-
);
|
|
784
|
-
const index = group.findIndex((e) => e.id === event.id);
|
|
785
|
-
const count = group.length;
|
|
786
|
-
const widthPercent = 100 / count;
|
|
787
|
-
const leftPercent = index * widthPercent;
|
|
788
|
-
const zonedEventStart = getZonedDate(event.start);
|
|
789
|
-
const zonedEventEnd = getZonedDate(event.end);
|
|
790
|
-
const startMinutes = zonedEventStart.getHours() * 60 + zonedEventStart.getMinutes();
|
|
791
|
-
const durationMinutes = differenceInMinutes(zonedEventEnd, zonedEventStart);
|
|
792
|
-
const top = startMinutes / 60 * hourHeight;
|
|
793
|
-
const height = durationMinutes / 60 * hourHeight;
|
|
1198
|
+
onSlotClick: onTimeSlotClick
|
|
1199
|
+
}
|
|
1200
|
+
);
|
|
1201
|
+
})
|
|
1202
|
+
)), positioned.map(({ event, start, end, column, columns }) => {
|
|
1203
|
+
const widthPercent = 100 / columns;
|
|
1204
|
+
const leftPercent = column * widthPercent;
|
|
1205
|
+
const zonedEventStart = toDisplay(event.start, timezone);
|
|
1206
|
+
const zonedEventEnd = toDisplay(event.end, timezone);
|
|
1207
|
+
const startMinutes = start.getHours() * 60 + start.getMinutes();
|
|
1208
|
+
const durationMinutes = differenceInMinutes(end, start);
|
|
1209
|
+
const top = startMinutes / 60 * HOUR_HEIGHT;
|
|
1210
|
+
const height = durationMinutes / 60 * HOUR_HEIGHT;
|
|
794
1211
|
const isShortEvent = durationMinutes < 60;
|
|
795
|
-
|
|
1212
|
+
const isLastSegment = end.getTime() === zonedEventEnd.getTime();
|
|
1213
|
+
return /* @__PURE__ */ React9.createElement(
|
|
796
1214
|
DraggableEvent,
|
|
797
1215
|
{
|
|
798
|
-
key: `${event.id}-${
|
|
1216
|
+
key: `${event.id}-${dayKey2}`,
|
|
1217
|
+
dragId: `${event.id}@${dayKey2}`,
|
|
1218
|
+
grabOffsetMs: start.getTime() - zonedEventStart.getTime(),
|
|
799
1219
|
event,
|
|
800
1220
|
className: `absolute z-10 transition-all ${readonly ? "cursor-default" : ""}`,
|
|
801
1221
|
style: {
|
|
@@ -804,20 +1224,20 @@ var WeekView = ({
|
|
|
804
1224
|
left: `${leftPercent}%`,
|
|
805
1225
|
width: `${widthPercent}%`,
|
|
806
1226
|
// Add minimal spacing between overlapping events
|
|
807
|
-
paddingRight:
|
|
1227
|
+
paddingRight: columns > 1 ? "2px" : "0"
|
|
808
1228
|
}
|
|
809
1229
|
},
|
|
810
|
-
/* @__PURE__ */
|
|
1230
|
+
/* @__PURE__ */ React9.createElement(
|
|
811
1231
|
ResizableEvent,
|
|
812
1232
|
{
|
|
813
|
-
readonly,
|
|
1233
|
+
readonly: readonly || !isLastSegment,
|
|
814
1234
|
event,
|
|
815
|
-
hourHeight,
|
|
1235
|
+
hourHeight: HOUR_HEIGHT,
|
|
816
1236
|
onResize: onEventResize,
|
|
817
1237
|
className: "h-full",
|
|
818
1238
|
style: { height: "100%" }
|
|
819
1239
|
},
|
|
820
|
-
/* @__PURE__ */
|
|
1240
|
+
/* @__PURE__ */ React9.createElement(
|
|
821
1241
|
"div",
|
|
822
1242
|
{
|
|
823
1243
|
className: cn(
|
|
@@ -827,7 +1247,7 @@ var WeekView = ({
|
|
|
827
1247
|
!event.color && "border-primary/20 bg-primary/10",
|
|
828
1248
|
isShortEvent ? "px-1 flex items-center justify-center" : "p-2",
|
|
829
1249
|
// Add active border for overlapped events to distinguish them
|
|
830
|
-
|
|
1250
|
+
columns > 1 && "border-l-4 border-l-primary/50"
|
|
831
1251
|
),
|
|
832
1252
|
style: {
|
|
833
1253
|
height: "100%",
|
|
@@ -840,41 +1260,43 @@ var WeekView = ({
|
|
|
840
1260
|
e.stopPropagation();
|
|
841
1261
|
onEventClick?.(event);
|
|
842
1262
|
},
|
|
843
|
-
title:
|
|
1263
|
+
title: columns > 1 ? `${event.title} (${column + 1}/${columns})` : void 0
|
|
844
1264
|
},
|
|
845
|
-
/* @__PURE__ */
|
|
1265
|
+
/* @__PURE__ */ React9.createElement("div", { className: "flex flex-col h-full overflow-hidden w-full" }, /* @__PURE__ */ React9.createElement("div", { className: cn(
|
|
846
1266
|
"font-semibold truncate text-foreground/90 leading-tight",
|
|
847
1267
|
isShortEvent ? "text-xs text-center" : "text-xs"
|
|
848
|
-
) }, event.title), !isShortEvent && /* @__PURE__ */
|
|
1268
|
+
) }, event.title), !isShortEvent && /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement("div", { className: "text-[10px] text-muted-foreground truncate mt-0.5 font-medium leading-tight" }, format8(zonedEventStart, formats.time, { locale }), " - ", format8(zonedEventEnd, formats.time, { locale })), event.description && height > 50 && /* @__PURE__ */ React9.createElement("div", { className: "text-[10px] text-muted-foreground/80 truncate mt-1 leading-tight opacity-80" }, event.description)), columns > 1 && !isShortEvent && /* @__PURE__ */ React9.createElement("div", { className: "absolute top-1 right-1 bg-background/80 backdrop-blur-sm rounded-full w-4 h-4 flex items-center justify-center text-[9px] font-bold text-muted-foreground border border-border shadow-sm" }, columns))
|
|
849
1269
|
)
|
|
850
1270
|
)
|
|
851
1271
|
);
|
|
852
|
-
}),
|
|
1272
|
+
}), isSameDay4(day, today) && /* @__PURE__ */ React9.createElement(
|
|
853
1273
|
"div",
|
|
854
1274
|
{
|
|
855
1275
|
className: "absolute left-0 right-0 z-20 pointer-events-none flex items-center",
|
|
856
|
-
style: {
|
|
857
|
-
top: `${(zonedNow.getHours() * 60 + zonedNow.getMinutes()) / 60 * hourHeight}px`
|
|
858
|
-
}
|
|
1276
|
+
style: { top: `${nowTop}px` }
|
|
859
1277
|
},
|
|
860
|
-
/* @__PURE__ */
|
|
861
|
-
/* @__PURE__ */
|
|
1278
|
+
/* @__PURE__ */ React9.createElement("div", { className: "h-[2px] w-full bg-gradient-to-r from-primary via-primary to-primary/50" }),
|
|
1279
|
+
/* @__PURE__ */ React9.createElement("div", { className: "absolute -left-1.5 w-3 h-3 bg-primary rounded-full shadow-lg shadow-primary/40 ring-2 ring-background animate-pulse" })
|
|
862
1280
|
));
|
|
863
1281
|
}))
|
|
864
|
-
),
|
|
865
|
-
/* @__PURE__ */ React15__default.createElement(
|
|
866
|
-
"div",
|
|
867
|
-
{
|
|
868
|
-
className: "absolute left-0 w-16 pointer-events-none z-30 flex justify-end pr-2",
|
|
869
|
-
style: {
|
|
870
|
-
top: `${(zonedNow.getHours() * 60 + zonedNow.getMinutes()) / 60 * hourHeight + 80}px`
|
|
871
|
-
}
|
|
872
|
-
},
|
|
873
|
-
/* @__PURE__ */ React15__default.createElement("span", { className: "text-[10px] font-bold text-primary-foreground bg-primary px-1.5 py-0.5 rounded-md shadow-md -translate-y-1/2 backdrop-blur-none" }, format(zonedNow, nowFormat, { locale }))
|
|
874
1282
|
)
|
|
875
1283
|
));
|
|
876
1284
|
};
|
|
877
|
-
var
|
|
1285
|
+
var WeekView = React9.memo(WeekViewImpl);
|
|
1286
|
+
|
|
1287
|
+
// src/views/DayView.tsx
|
|
1288
|
+
import React10 from "react";
|
|
1289
|
+
import { addDays as addDays3, format as format9, differenceInMinutes as differenceInMinutes2, isSameDay as isSameDay5, startOfDay as startOfDay3 } from "date-fns";
|
|
1290
|
+
var HOUR_HEIGHT2 = 80;
|
|
1291
|
+
var HOURS2 = Array.from({ length: 24 }, (_, i) => i);
|
|
1292
|
+
var QUARTERS2 = [0, 15, 30, 45];
|
|
1293
|
+
var NO_SEGMENTS2 = [];
|
|
1294
|
+
var slotDate2 = (day, hour, minute) => {
|
|
1295
|
+
const date = new Date(day);
|
|
1296
|
+
date.setHours(hour, minute, 0, 0);
|
|
1297
|
+
return date;
|
|
1298
|
+
};
|
|
1299
|
+
var DayViewImpl = ({
|
|
878
1300
|
currentDate,
|
|
879
1301
|
events,
|
|
880
1302
|
onEventClick,
|
|
@@ -884,114 +1306,84 @@ var DayView = ({
|
|
|
884
1306
|
locale,
|
|
885
1307
|
readonly
|
|
886
1308
|
}) => {
|
|
887
|
-
const
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1309
|
+
const visibleRange = React10.useMemo(() => {
|
|
1310
|
+
const start = startOfDay3(currentDate);
|
|
1311
|
+
return { start, end: addDays3(start, 1) };
|
|
1312
|
+
}, [currentDate]);
|
|
1313
|
+
const scrollContainerRef = React10.useRef(null);
|
|
1314
|
+
const [now, setNow] = React10.useState(() => /* @__PURE__ */ new Date());
|
|
1315
|
+
React10.useEffect(() => {
|
|
892
1316
|
const interval = setInterval(() => setNow(/* @__PURE__ */ new Date()), 6e4);
|
|
893
1317
|
return () => clearInterval(interval);
|
|
894
1318
|
}, []);
|
|
895
|
-
|
|
1319
|
+
React10.useEffect(() => {
|
|
896
1320
|
if (scrollContainerRef.current) {
|
|
897
|
-
const
|
|
898
|
-
|
|
899
|
-
const offsetHour = 3;
|
|
900
|
-
return (currentHour - offsetHour) * hourHeight;
|
|
901
|
-
};
|
|
902
|
-
scrollContainerRef.current.scrollTop = scrollToActualTime();
|
|
1321
|
+
const offsetHour = 3;
|
|
1322
|
+
scrollContainerRef.current.scrollTop = (toDisplay(/* @__PURE__ */ new Date(), timezone).getHours() - offsetHour) * HOUR_HEIGHT2;
|
|
903
1323
|
}
|
|
904
|
-
}, []);
|
|
905
|
-
const
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
const zonedNow =
|
|
909
|
-
const
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
});
|
|
913
|
-
const is24h = uses24HourClock(locale);
|
|
914
|
-
const timeFormat = is24h ? "H:mm" : "h a";
|
|
915
|
-
const eventTimeFormat = is24h ? "H:mm" : "h:mm a";
|
|
916
|
-
const nowFormat = is24h ? "H:mm" : "h:mm a";
|
|
917
|
-
return /* @__PURE__ */ React15__default.createElement("div", { className: "flex flex-col h-full bg-background border-[0.5px] border-border/50 rounded-2xl overflow-hidden shadow-sm" }, /* @__PURE__ */ React15__default.createElement("div", { className: "px-6 py-4 border-b-[0.5px] border-border/50 bg-gradient-to-r from-muted/20 via-background to-muted/20 text-center shrink-0" }, /* @__PURE__ */ React15__default.createElement("div", { className: "flex items-center justify-center gap-3" }, /* @__PURE__ */ React15__default.createElement("h2", { className: "text-xl font-semibold capitalize text-foreground" }, format(currentDate, "EEEE, MMMM d, yyyy", { locale })), isToday(currentDate) && /* @__PURE__ */ React15__default.createElement("span", { className: "text-xs font-semibold bg-primary text-primary-foreground px-3 py-1 rounded-full shadow-md shadow-primary/20" }, "Today"))), /* @__PURE__ */ React15__default.createElement("div", { ref: scrollContainerRef, className: "flex-1 overflow-y-auto relative" }, /* @__PURE__ */ React15__default.createElement("div", { className: "flex relative", style: { height: hours.length * hourHeight } }, /* @__PURE__ */ React15__default.createElement("div", { className: "w-20 bg-muted/5 border-r-[0.5px] border-border/30 relative" }, hours.map((hour) => /* @__PURE__ */ React15__default.createElement(
|
|
1324
|
+
}, [timezone]);
|
|
1325
|
+
const dayKey2 = format9(currentDate, "yyyy-MM-dd");
|
|
1326
|
+
const positioned = layoutDayEvents(segmentsByDisplayDay(events, timezone, visibleRange).get(dayKey2) ?? NO_SEGMENTS2);
|
|
1327
|
+
const isToday2 = isSameDay5(currentDate, displayToday(timezone, now));
|
|
1328
|
+
const zonedNow = toDisplay(now, timezone);
|
|
1329
|
+
const nowTop = minutesSinceDisplayMidnight(now, timezone) / 60 * HOUR_HEIGHT2;
|
|
1330
|
+
const formats = timeFormats(locale);
|
|
1331
|
+
return /* @__PURE__ */ React10.createElement("div", { className: "flex flex-col h-full bg-background border-[0.5px] border-border/50 rounded-2xl overflow-hidden shadow-sm" }, /* @__PURE__ */ React10.createElement("div", { className: "px-6 py-4 border-b-[0.5px] border-border/50 bg-gradient-to-r from-muted/20 via-background to-muted/20 text-center shrink-0" }, /* @__PURE__ */ React10.createElement("div", { className: "flex items-center justify-center gap-3" }, /* @__PURE__ */ React10.createElement("h2", { className: "text-xl font-semibold capitalize text-foreground" }, format9(currentDate, "EEEE, MMMM d, yyyy", { locale })), isToday2 && /* @__PURE__ */ React10.createElement("span", { className: "text-xs font-semibold bg-primary text-primary-foreground px-3 py-1 rounded-full shadow-md shadow-primary/20" }, "Today"))), /* @__PURE__ */ React10.createElement("div", { ref: scrollContainerRef, className: "flex-1 overflow-y-auto relative" }, /* @__PURE__ */ React10.createElement("div", { className: "flex relative", style: { height: HOURS2.length * HOUR_HEIGHT2 } }, /* @__PURE__ */ React10.createElement("div", { className: "w-20 bg-muted/5 border-r-[0.5px] border-border/30 relative" }, HOURS2.map((hour) => /* @__PURE__ */ React10.createElement(
|
|
918
1332
|
"div",
|
|
919
1333
|
{
|
|
920
1334
|
key: hour,
|
|
921
1335
|
className: "relative w-full",
|
|
922
|
-
style: { height:
|
|
1336
|
+
style: { height: HOUR_HEIGHT2 }
|
|
923
1337
|
},
|
|
924
|
-
hour !== 0 && /* @__PURE__ */
|
|
925
|
-
)),
|
|
1338
|
+
hour !== 0 && /* @__PURE__ */ React10.createElement("span", { className: "absolute w-full text-center -top-3 left-1/2 -translate-x-1/2 text-[11px] text-muted-foreground/80 font-medium tabular-nums bg-background px-1.5 py-0.5 rounded-md" }, format9(slotDate2(currentDate, hour, 0), formats.hour, { locale }))
|
|
1339
|
+
)), isToday2 && /* @__PURE__ */ React10.createElement(
|
|
926
1340
|
"div",
|
|
927
1341
|
{
|
|
928
1342
|
className: "absolute left-0 w-full pointer-events-none z-30 flex justify-end pr-2",
|
|
929
|
-
style: {
|
|
930
|
-
top: `${(zonedNow.getHours() * 60 + zonedNow.getMinutes()) / 60 * hourHeight}px`
|
|
931
|
-
}
|
|
1343
|
+
style: { top: `${nowTop}px` }
|
|
932
1344
|
},
|
|
933
|
-
/* @__PURE__ */
|
|
934
|
-
)), /* @__PURE__ */
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
style: {
|
|
954
|
-
height: "25%",
|
|
955
|
-
top: `${minute / 60 * 100}%`
|
|
956
|
-
}
|
|
1345
|
+
/* @__PURE__ */ React10.createElement("span", { className: "text-[10px] font-bold text-white bg-primary px-2 py-1 rounded-lg shadow-md shadow-primary/30 -translate-y-1/2" }, format9(zonedNow, formats.time, { locale }))
|
|
1346
|
+
)), /* @__PURE__ */ React10.createElement("div", { className: "flex-1 relative" }, HOURS2.map((hour) => /* @__PURE__ */ React10.createElement(
|
|
1347
|
+
"div",
|
|
1348
|
+
{
|
|
1349
|
+
key: hour,
|
|
1350
|
+
className: "border-b-[0.5px] border-dashed border-border/20 box-border relative hover:bg-accent/5 transition-colors",
|
|
1351
|
+
style: { height: HOUR_HEIGHT2 }
|
|
1352
|
+
},
|
|
1353
|
+
QUARTERS2.map((minute) => {
|
|
1354
|
+
const cellDate = slotDate2(currentDate, hour, minute);
|
|
1355
|
+
return /* @__PURE__ */ React10.createElement(
|
|
1356
|
+
DroppableCell,
|
|
1357
|
+
{
|
|
1358
|
+
key: minute,
|
|
1359
|
+
id: cellDate.toISOString(),
|
|
1360
|
+
date: cellDate,
|
|
1361
|
+
className: "w-full absolute left-0 right-0 z-0 transition-colors",
|
|
1362
|
+
style: {
|
|
1363
|
+
height: "25%",
|
|
1364
|
+
top: `${minute / 60 * 100}%`
|
|
957
1365
|
},
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
);
|
|
968
|
-
|
|
969
|
-
const
|
|
970
|
-
|
|
971
|
-
const s1 = getZonedDate(event.start).getTime();
|
|
972
|
-
const e1 = getZonedDate(event.end).getTime();
|
|
973
|
-
const s2 = getZonedDate(e.start).getTime();
|
|
974
|
-
const e2 = getZonedDate(e.end).getTime();
|
|
975
|
-
return s1 < e2 && e1 > s2;
|
|
976
|
-
});
|
|
977
|
-
const group = [event, ...overlappingEvents].sort(
|
|
978
|
-
(a, b) => getZonedDate(a.start).getTime() - getZonedDate(b.start).getTime() || (a.id > b.id ? 1 : -1)
|
|
979
|
-
);
|
|
980
|
-
const index = group.findIndex((e) => e.id === event.id);
|
|
981
|
-
const count = group.length;
|
|
982
|
-
const widthPercent = 100 / count;
|
|
983
|
-
const leftPercent = index * widthPercent;
|
|
984
|
-
const zonedStart = getZonedDate(event.start);
|
|
985
|
-
const zonedEnd = getZonedDate(event.end);
|
|
986
|
-
const startMinutes = zonedStart.getHours() * 60 + zonedStart.getMinutes();
|
|
987
|
-
const durationMinutes = differenceInMinutes(zonedEnd, zonedStart);
|
|
988
|
-
const top = startMinutes / 60 * hourHeight;
|
|
989
|
-
const height = durationMinutes / 60 * hourHeight;
|
|
1366
|
+
onSlotClick: onTimeSlotClick
|
|
1367
|
+
}
|
|
1368
|
+
);
|
|
1369
|
+
})
|
|
1370
|
+
)), positioned.map(({ event, start, end, column, columns }) => {
|
|
1371
|
+
const widthPercent = 100 / columns;
|
|
1372
|
+
const leftPercent = column * widthPercent;
|
|
1373
|
+
const zonedStart = toDisplay(event.start, timezone);
|
|
1374
|
+
const zonedEnd = toDisplay(event.end, timezone);
|
|
1375
|
+
const startMinutes = start.getHours() * 60 + start.getMinutes();
|
|
1376
|
+
const durationMinutes = differenceInMinutes2(end, start);
|
|
1377
|
+
const top = startMinutes / 60 * HOUR_HEIGHT2;
|
|
1378
|
+
const height = durationMinutes / 60 * HOUR_HEIGHT2;
|
|
990
1379
|
const isShortEvent = durationMinutes < 45;
|
|
991
|
-
|
|
1380
|
+
const isLastSegment = end.getTime() === zonedEnd.getTime();
|
|
1381
|
+
return /* @__PURE__ */ React10.createElement(
|
|
992
1382
|
DraggableEvent,
|
|
993
1383
|
{
|
|
994
|
-
key: `${event.id}-${
|
|
1384
|
+
key: `${event.id}-${dayKey2}`,
|
|
1385
|
+
dragId: `${event.id}@${dayKey2}`,
|
|
1386
|
+
grabOffsetMs: start.getTime() - zonedStart.getTime(),
|
|
995
1387
|
event,
|
|
996
1388
|
className: `absolute z-10 transition-all ${readonly ? "cursor-default" : ""}`,
|
|
997
1389
|
style: {
|
|
@@ -999,20 +1391,20 @@ var DayView = ({
|
|
|
999
1391
|
height: `${Math.max(height, 28)}px`,
|
|
1000
1392
|
left: `calc(${leftPercent}% + 4px)`,
|
|
1001
1393
|
width: `calc(${widthPercent}% - 8px)`,
|
|
1002
|
-
paddingRight:
|
|
1394
|
+
paddingRight: columns > 1 ? "2px" : "0"
|
|
1003
1395
|
}
|
|
1004
1396
|
},
|
|
1005
|
-
/* @__PURE__ */
|
|
1397
|
+
/* @__PURE__ */ React10.createElement(
|
|
1006
1398
|
ResizableEvent,
|
|
1007
1399
|
{
|
|
1008
|
-
readonly,
|
|
1400
|
+
readonly: readonly || !isLastSegment,
|
|
1009
1401
|
event,
|
|
1010
|
-
hourHeight,
|
|
1402
|
+
hourHeight: HOUR_HEIGHT2,
|
|
1011
1403
|
onResize: onEventResize,
|
|
1012
1404
|
className: "h-full",
|
|
1013
1405
|
style: { height: "100%" }
|
|
1014
1406
|
},
|
|
1015
|
-
/* @__PURE__ */
|
|
1407
|
+
/* @__PURE__ */ React10.createElement(
|
|
1016
1408
|
"div",
|
|
1017
1409
|
{
|
|
1018
1410
|
className: cn(
|
|
@@ -1021,61 +1413,72 @@ var DayView = ({
|
|
|
1021
1413
|
readonly || isEventDragDisabled(event) ? "cursor-default" : "cursor-grab active:cursor-grabbing",
|
|
1022
1414
|
!event.color && "bg-primary/10 border-primary/20",
|
|
1023
1415
|
isShortEvent ? "px-2 flex items-center" : "px-3 py-2",
|
|
1024
|
-
|
|
1416
|
+
columns > 1 && "border-l-4"
|
|
1025
1417
|
),
|
|
1026
1418
|
style: {
|
|
1027
1419
|
backgroundColor: event.color ? `${event.color}15` : void 0,
|
|
1028
1420
|
borderColor: event.color ? `${event.color}30` : void 0,
|
|
1029
1421
|
borderLeftColor: event.color || "var(--primary)",
|
|
1030
|
-
borderLeftWidth:
|
|
1422
|
+
borderLeftWidth: columns > 1 ? "4px" : "3px"
|
|
1031
1423
|
},
|
|
1032
1424
|
onClick: (e) => {
|
|
1033
1425
|
e.stopPropagation();
|
|
1034
1426
|
onEventClick?.(event);
|
|
1035
1427
|
},
|
|
1036
|
-
title:
|
|
1428
|
+
title: columns > 1 ? `${event.title} (${column + 1}/${columns})` : void 0
|
|
1037
1429
|
},
|
|
1038
|
-
/* @__PURE__ */
|
|
1430
|
+
/* @__PURE__ */ React10.createElement("div", { className: "flex flex-col h-full overflow-hidden w-full" }, /* @__PURE__ */ React10.createElement("div", { className: cn(
|
|
1039
1431
|
"font-semibold truncate leading-tight",
|
|
1040
1432
|
isShortEvent ? "text-xs" : "text-sm",
|
|
1041
1433
|
event.color ? "text-foreground" : "text-foreground/90"
|
|
1042
|
-
) }, event.title), !isShortEvent && /* @__PURE__ */
|
|
1434
|
+
) }, event.title), !isShortEvent && /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement("div", { className: "text-xs text-muted-foreground mt-0.5 font-medium" }, format9(zonedStart, formats.time, { locale }), " - ", format9(zonedEnd, formats.time, { locale })), event.description && height > 60 && /* @__PURE__ */ React10.createElement("div", { className: "text-xs text-muted-foreground/80 mt-1 line-clamp-2" }, event.description)), columns > 1 && !isShortEvent && /* @__PURE__ */ React10.createElement("div", { className: "absolute top-1.5 right-1.5 bg-background/90 backdrop-blur-sm rounded-full w-5 h-5 flex items-center justify-center text-[10px] font-bold text-muted-foreground border border-border shadow-sm" }, columns))
|
|
1043
1435
|
)
|
|
1044
1436
|
)
|
|
1045
1437
|
);
|
|
1046
|
-
}),
|
|
1438
|
+
}), isToday2 && /* @__PURE__ */ React10.createElement(
|
|
1047
1439
|
"div",
|
|
1048
1440
|
{
|
|
1049
1441
|
className: "absolute left-0 right-0 z-20 pointer-events-none flex items-center",
|
|
1050
|
-
style: {
|
|
1051
|
-
top: `${(zonedNow.getHours() * 60 + zonedNow.getMinutes()) / 60 * hourHeight}px`
|
|
1052
|
-
}
|
|
1442
|
+
style: { top: `${nowTop}px` }
|
|
1053
1443
|
},
|
|
1054
|
-
/* @__PURE__ */
|
|
1055
|
-
/* @__PURE__ */
|
|
1444
|
+
/* @__PURE__ */ React10.createElement("div", { className: "h-[2px] w-full bg-gradient-to-r from-primary via-primary to-primary/50" }),
|
|
1445
|
+
/* @__PURE__ */ React10.createElement("div", { className: "absolute -left-1.5 w-3 h-3 bg-primary rounded-full shadow-lg shadow-primary/40 ring-2 ring-background animate-pulse" })
|
|
1056
1446
|
)))));
|
|
1057
1447
|
};
|
|
1058
|
-
var
|
|
1448
|
+
var DayView = React10.memo(DayViewImpl);
|
|
1449
|
+
|
|
1450
|
+
// src/views/AgendaView.tsx
|
|
1451
|
+
import React12, { useMemo as useMemo2 } from "react";
|
|
1452
|
+
import { format as format10, isSameDay as isSameDay6, addDays as addDays4, startOfDay as startOfDay4, differenceInMinutes as differenceInMinutes3 } from "date-fns";
|
|
1453
|
+
|
|
1454
|
+
// src/components/EmptyState.tsx
|
|
1455
|
+
import React11 from "react";
|
|
1456
|
+
import { Plus as Plus2 } from "lucide-react";
|
|
1457
|
+
var AgendaEmptyState = ({ onCreateEvent, translations }) => /* @__PURE__ */ React11.createElement("div", { className: "flex flex-col items-center justify-center h-full min-h-[400px] py-12" }, /* @__PURE__ */ React11.createElement("div", { className: "relative mb-8" }, /* @__PURE__ */ React11.createElement("div", { className: "p-8 bg-gradient-to-br from-green-50 to-blue-50 dark:from-green-900/10 dark:to-blue-900/10 rounded-3xl border border-border/30" }, /* @__PURE__ */ React11.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ React11.createElement("div", { className: "w-12 h-12 rounded-2xl bg-green-100 dark:bg-green-900/30 flex items-center justify-center" }, /* @__PURE__ */ React11.createElement("svg", { className: "w-6 h-6 text-green-600 dark:text-green-400", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2 }, /* @__PURE__ */ React11.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" }))), /* @__PURE__ */ React11.createElement("div", { className: "text-left" }, /* @__PURE__ */ React11.createElement("div", { className: "font-medium text-foreground" }, translations?.allCaughtUp || "All caught up!"), /* @__PURE__ */ React11.createElement("div", { className: "text-sm text-muted-foreground" }, translations?.noEventsScheduled || "No events scheduled"))))), /* @__PURE__ */ React11.createElement("div", { className: "text-center max-w-sm" }, /* @__PURE__ */ React11.createElement("h3", { className: "text-lg font-semibold text-foreground mb-2" }, translations?.noUpcoming || "Your schedule is clear"), /* @__PURE__ */ React11.createElement("p", { className: "text-sm text-muted-foreground mb-6" }, translations?.noUpcomingHint || "No upcoming events to show. Create a new event to start planning."), onCreateEvent && /* @__PURE__ */ React11.createElement(
|
|
1059
1458
|
"button",
|
|
1060
1459
|
{
|
|
1061
1460
|
onClick: onCreateEvent,
|
|
1062
1461
|
className: "inline-flex items-center gap-2 px-5 py-2.5 bg-primary text-primary-foreground rounded-xl font-medium text-sm shadow-lg shadow-primary/20 hover:shadow-xl hover:shadow-primary/30 hover:scale-105 active:scale-95 transition-all duration-200"
|
|
1063
1462
|
},
|
|
1064
|
-
/* @__PURE__ */
|
|
1463
|
+
/* @__PURE__ */ React11.createElement(Plus2, { className: "w-4 h-4" }),
|
|
1065
1464
|
translations?.createEvent || "Create Event"
|
|
1066
1465
|
)));
|
|
1466
|
+
|
|
1467
|
+
// src/views/AgendaView.tsx
|
|
1468
|
+
import { motion } from "framer-motion";
|
|
1469
|
+
import { Clock, Users, Paperclip, Bell } from "lucide-react";
|
|
1067
1470
|
var formatDuration = (start, end) => {
|
|
1068
|
-
const minutes =
|
|
1471
|
+
const minutes = differenceInMinutes3(end, start);
|
|
1069
1472
|
if (minutes < 60) return `${minutes}m`;
|
|
1070
1473
|
const hours = Math.floor(minutes / 60);
|
|
1071
1474
|
const remainingMinutes = minutes % 60;
|
|
1072
1475
|
if (remainingMinutes === 0) return `${hours}h`;
|
|
1073
1476
|
return `${hours}h ${remainingMinutes}m`;
|
|
1074
1477
|
};
|
|
1075
|
-
var getDateLabel = (date, locale, translations) => {
|
|
1076
|
-
if (
|
|
1077
|
-
if (
|
|
1078
|
-
return
|
|
1478
|
+
var getDateLabel = (date, today, locale, translations) => {
|
|
1479
|
+
if (isSameDay6(date, today)) return translations?.today || "Today";
|
|
1480
|
+
if (isSameDay6(date, addDays4(today, 1))) return translations?.tomorrow || "Tomorrow";
|
|
1481
|
+
return format10(date, "EEEE", { locale });
|
|
1079
1482
|
};
|
|
1080
1483
|
var AgendaView = ({
|
|
1081
1484
|
currentDate,
|
|
@@ -1083,24 +1486,24 @@ var AgendaView = ({
|
|
|
1083
1486
|
onEventClick,
|
|
1084
1487
|
onCreateEvent,
|
|
1085
1488
|
locale,
|
|
1086
|
-
translations
|
|
1489
|
+
translations,
|
|
1490
|
+
timezone
|
|
1087
1491
|
}) => {
|
|
1088
1492
|
const is24h = uses24HourClock(locale);
|
|
1089
|
-
const
|
|
1090
|
-
const
|
|
1091
|
-
const groupedEvents =
|
|
1092
|
-
const
|
|
1493
|
+
const { time: timePattern, compact: compactTimePattern } = timeFormats(locale);
|
|
1494
|
+
const today = displayToday(timezone);
|
|
1495
|
+
const groupedEvents = useMemo2(() => {
|
|
1496
|
+
const byDay = eventsByStartDay(events, timezone);
|
|
1093
1497
|
const groups = [];
|
|
1094
|
-
const sortedEvents = [...events].sort((a, b) => a.start.getTime() - b.start.getTime());
|
|
1095
1498
|
for (let i = 0; i < 30; i++) {
|
|
1096
|
-
const day =
|
|
1097
|
-
const dayEvents =
|
|
1098
|
-
if (dayEvents
|
|
1099
|
-
groups.push({ date: day, events: dayEvents });
|
|
1499
|
+
const day = addDays4(startOfDay4(currentDate), i);
|
|
1500
|
+
const dayEvents = byDay.get(format10(day, "yyyy-MM-dd"));
|
|
1501
|
+
if (dayEvents?.length) {
|
|
1502
|
+
groups.push({ date: day, events: [...dayEvents].sort((a, b) => a.start.getTime() - b.start.getTime()) });
|
|
1100
1503
|
}
|
|
1101
1504
|
}
|
|
1102
1505
|
return groups;
|
|
1103
|
-
}, [currentDate, events]);
|
|
1506
|
+
}, [currentDate, events, timezone]);
|
|
1104
1507
|
const container = {
|
|
1105
1508
|
hidden: { opacity: 0 },
|
|
1106
1509
|
show: {
|
|
@@ -1114,7 +1517,7 @@ var AgendaView = ({
|
|
|
1114
1517
|
hidden: { opacity: 0, x: -20 },
|
|
1115
1518
|
show: { opacity: 1, x: 0 }
|
|
1116
1519
|
};
|
|
1117
|
-
return /* @__PURE__ */
|
|
1520
|
+
return /* @__PURE__ */ React12.createElement("div", { className: "flex flex-col h-full bg-background overflow-y-auto" }, groupedEvents.length === 0 ? /* @__PURE__ */ React12.createElement(AgendaEmptyState, { onCreateEvent, translations }) : /* @__PURE__ */ React12.createElement(
|
|
1118
1521
|
motion.div,
|
|
1119
1522
|
{
|
|
1120
1523
|
className: "max-w-3xl mx-auto w-full pb-10 px-4 md:px-6",
|
|
@@ -1122,21 +1525,21 @@ var AgendaView = ({
|
|
|
1122
1525
|
initial: "hidden",
|
|
1123
1526
|
animate: "show"
|
|
1124
1527
|
},
|
|
1125
|
-
groupedEvents.map((group
|
|
1528
|
+
groupedEvents.map((group) => /* @__PURE__ */ React12.createElement(
|
|
1126
1529
|
motion.div,
|
|
1127
1530
|
{
|
|
1128
1531
|
key: group.date.toISOString(),
|
|
1129
1532
|
className: "relative",
|
|
1130
1533
|
variants: item
|
|
1131
1534
|
},
|
|
1132
|
-
/* @__PURE__ */
|
|
1535
|
+
/* @__PURE__ */ React12.createElement("div", { className: "sticky top-0 bg-background/95 backdrop-blur-md py-4 z-10 border-b border-border/50" }, /* @__PURE__ */ React12.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ React12.createElement("div", { className: cn(
|
|
1133
1536
|
"flex flex-col items-center justify-center w-16 h-16 rounded-2xl transition-all",
|
|
1134
|
-
|
|
1135
|
-
) }, /* @__PURE__ */
|
|
1537
|
+
isSameDay6(group.date, today) ? "bg-primary text-primary-foreground shadow-lg shadow-primary/30" : "bg-muted/50 text-foreground"
|
|
1538
|
+
) }, /* @__PURE__ */ React12.createElement("span", { className: "text-2xl font-bold leading-none" }, format10(group.date, "d")), /* @__PURE__ */ React12.createElement("span", { className: "text-xs font-medium uppercase tracking-wide opacity-80" }, format10(group.date, "MMM", { locale }))), /* @__PURE__ */ React12.createElement("div", { className: "flex flex-col" }, /* @__PURE__ */ React12.createElement("span", { className: cn(
|
|
1136
1539
|
"text-lg font-semibold",
|
|
1137
|
-
|
|
1138
|
-
) }, getDateLabel(group.date, locale, translations)), /* @__PURE__ */
|
|
1139
|
-
/* @__PURE__ */
|
|
1540
|
+
isSameDay6(group.date, today) && "text-primary"
|
|
1541
|
+
) }, getDateLabel(group.date, today, locale, translations)), /* @__PURE__ */ React12.createElement("span", { className: "text-sm text-muted-foreground" }, group.events.length, " ", group.events.length !== 1 ? translations?.eventPlural || "events" : translations?.eventSingular || "event")))),
|
|
1542
|
+
/* @__PURE__ */ React12.createElement("div", { className: "py-4 space-y-3" }, group.events.map((event) => /* @__PURE__ */ React12.createElement(
|
|
1140
1543
|
motion.div,
|
|
1141
1544
|
{
|
|
1142
1545
|
key: event.id,
|
|
@@ -1150,44 +1553,49 @@ var AgendaView = ({
|
|
|
1150
1553
|
whileHover: { scale: 1.01, y: -2 },
|
|
1151
1554
|
transition: { duration: 0.2 }
|
|
1152
1555
|
},
|
|
1153
|
-
/* @__PURE__ */
|
|
1556
|
+
/* @__PURE__ */ React12.createElement(
|
|
1154
1557
|
"div",
|
|
1155
1558
|
{
|
|
1156
1559
|
className: "absolute left-0 top-3 bottom-3 w-1 rounded-full",
|
|
1157
1560
|
style: { backgroundColor: event.color || "var(--primary)" }
|
|
1158
1561
|
}
|
|
1159
1562
|
),
|
|
1160
|
-
/* @__PURE__ */
|
|
1161
|
-
/* @__PURE__ */
|
|
1563
|
+
/* @__PURE__ */ React12.createElement("div", { className: "flex flex-col items-center min-w-[70px] pl-2" }, event.allDay ? /* @__PURE__ */ React12.createElement("div", { className: "flex flex-col items-center" }, /* @__PURE__ */ React12.createElement("span", { className: "text-xs font-semibold text-muted-foreground bg-muted/80 px-2.5 py-1 rounded-full" }, translations?.allDay || "All Day")) : /* @__PURE__ */ React12.createElement("div", { className: "flex flex-col items-center" }, /* @__PURE__ */ React12.createElement("span", { className: "text-base font-semibold text-foreground" }, format10(toDisplay(event.start, timezone), compactTimePattern, { locale })), !is24h && /* @__PURE__ */ React12.createElement("span", { className: "text-xs text-muted-foreground uppercase" }, format10(toDisplay(event.start, timezone), "a")), /* @__PURE__ */ React12.createElement("div", { className: "w-px h-3 bg-border my-1" }), /* @__PURE__ */ React12.createElement("span", { className: "text-xs text-muted-foreground/70 font-medium" }, formatDuration(event.start, event.end)))),
|
|
1564
|
+
/* @__PURE__ */ React12.createElement("div", { className: "flex-1 min-w-0 space-y-2" }, /* @__PURE__ */ React12.createElement("div", { className: "flex items-start justify-between gap-2" }, /* @__PURE__ */ React12.createElement("h4", { className: "text-base font-semibold text-foreground group-hover:text-primary transition-colors line-clamp-1" }, event.title), /* @__PURE__ */ React12.createElement(
|
|
1162
1565
|
"div",
|
|
1163
1566
|
{
|
|
1164
1567
|
className: "w-3 h-3 rounded-full shrink-0 mt-1.5",
|
|
1165
1568
|
style: { backgroundColor: event.color || "var(--primary)" }
|
|
1166
1569
|
}
|
|
1167
|
-
)), event.description && /* @__PURE__ */
|
|
1168
|
-
/* @__PURE__ */
|
|
1570
|
+
)), event.description && /* @__PURE__ */ React12.createElement("p", { className: "text-sm text-muted-foreground line-clamp-2 leading-relaxed" }, event.description), /* @__PURE__ */ React12.createElement("div", { className: "flex flex-wrap items-center gap-3 pt-1" }, !event.allDay && /* @__PURE__ */ React12.createElement("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground" }, /* @__PURE__ */ React12.createElement(Clock, { className: "w-3.5 h-3.5" }), /* @__PURE__ */ React12.createElement("span", null, format10(toDisplay(event.start, timezone), timePattern, { locale }), " - ", format10(toDisplay(event.end, timezone), timePattern, { locale }))), event.guests && event.guests.length > 0 && /* @__PURE__ */ React12.createElement("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground" }, /* @__PURE__ */ React12.createElement(Users, { className: "w-3.5 h-3.5" }), /* @__PURE__ */ React12.createElement("span", null, event.guests.length, " ", event.guests.length !== 1 ? translations?.guestsCount || "guests" : translations?.guestCount || "guest")), event.attachments && event.attachments.length > 0 && /* @__PURE__ */ React12.createElement("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground" }, /* @__PURE__ */ React12.createElement(Paperclip, { className: "w-3.5 h-3.5" }), /* @__PURE__ */ React12.createElement("span", null, event.attachments.length)), event.reminders && event.reminders.length > 0 && /* @__PURE__ */ React12.createElement("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground" }, /* @__PURE__ */ React12.createElement(Bell, { className: "w-3.5 h-3.5" }), /* @__PURE__ */ React12.createElement("span", null, event.reminders.length)))),
|
|
1571
|
+
/* @__PURE__ */ React12.createElement("div", { className: "absolute right-4 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity" }, /* @__PURE__ */ React12.createElement("svg", { className: "w-5 h-5 text-muted-foreground", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" }, /* @__PURE__ */ React12.createElement("path", { d: "M9 18l6-6-6-6" })))
|
|
1169
1572
|
)))
|
|
1170
1573
|
))
|
|
1171
1574
|
));
|
|
1172
1575
|
};
|
|
1576
|
+
|
|
1577
|
+
// src/views/ResourceView.tsx
|
|
1578
|
+
import React13, { useRef as useRef2 } from "react";
|
|
1579
|
+
import { format as format11, startOfDay as startOfDay5, differenceInMinutes as differenceInMinutes4 } from "date-fns";
|
|
1173
1580
|
var ResourceView = ({
|
|
1174
1581
|
currentDate,
|
|
1175
1582
|
events,
|
|
1176
1583
|
resources,
|
|
1177
1584
|
onEventClick,
|
|
1178
1585
|
onTimeSlotClick,
|
|
1179
|
-
locale
|
|
1586
|
+
locale,
|
|
1587
|
+
timezone
|
|
1180
1588
|
}) => {
|
|
1181
|
-
const containerRef =
|
|
1589
|
+
const containerRef = useRef2(null);
|
|
1182
1590
|
const hours = Array.from({ length: 24 }, (_, i) => i);
|
|
1183
1591
|
const hourWidth = 100;
|
|
1184
|
-
const timeFormat =
|
|
1592
|
+
const timeFormat = timeFormats(locale).hour;
|
|
1185
1593
|
const getEventStyle = (event) => {
|
|
1186
|
-
const start =
|
|
1187
|
-
const end =
|
|
1188
|
-
const dayStart =
|
|
1189
|
-
const startMinutes =
|
|
1190
|
-
const durationMinutes =
|
|
1594
|
+
const start = toDisplay(event.start, timezone);
|
|
1595
|
+
const end = toDisplay(event.end, timezone);
|
|
1596
|
+
const dayStart = startOfDay5(currentDate);
|
|
1597
|
+
const startMinutes = differenceInMinutes4(start, dayStart);
|
|
1598
|
+
const durationMinutes = differenceInMinutes4(end, start);
|
|
1191
1599
|
const left = startMinutes / 60 * hourWidth;
|
|
1192
1600
|
const width = durationMinutes / 60 * hourWidth;
|
|
1193
1601
|
return {
|
|
@@ -1196,19 +1604,19 @@ var ResourceView = ({
|
|
|
1196
1604
|
// Min width for visibility
|
|
1197
1605
|
};
|
|
1198
1606
|
};
|
|
1199
|
-
return /* @__PURE__ */
|
|
1607
|
+
return /* @__PURE__ */ React13.createElement("div", { className: "flex flex-col h-full bg-background overflow-hidden" }, /* @__PURE__ */ React13.createElement("div", { className: "flex border-b border-border bg-muted/20" }, /* @__PURE__ */ React13.createElement("div", { className: "w-48 shrink-0 border-r border-border p-4 font-semibold text-sm bg-background sticky left-0 z-20" }, "Resources"), /* @__PURE__ */ React13.createElement("div", { className: "flex overflow-hidden" }, /* @__PURE__ */ React13.createElement("div", { className: "flex relative", style: { width: hours.length * hourWidth } }, hours.map((hour) => /* @__PURE__ */ React13.createElement(
|
|
1200
1608
|
"div",
|
|
1201
1609
|
{
|
|
1202
1610
|
key: hour,
|
|
1203
1611
|
className: "border-r border-border/50 text-xs text-muted-foreground p-2 font-medium shrink-0",
|
|
1204
1612
|
style: { width: hourWidth }
|
|
1205
1613
|
},
|
|
1206
|
-
|
|
1207
|
-
))))), /* @__PURE__ */
|
|
1614
|
+
format11((/* @__PURE__ */ new Date()).setHours(hour, 0, 0, 0), timeFormat, { locale })
|
|
1615
|
+
))))), /* @__PURE__ */ React13.createElement("div", { className: "flex-1 overflow-auto relative", ref: containerRef }, /* @__PURE__ */ React13.createElement("div", { className: "min-w-fit" }, resources.map((resource) => {
|
|
1208
1616
|
const resourceEvents = events.filter(
|
|
1209
|
-
(e) => e.resourceId === resource.id &&
|
|
1617
|
+
(e) => e.resourceId === resource.id && isSameDisplayDay(e.start, currentDate, timezone)
|
|
1210
1618
|
);
|
|
1211
|
-
return /* @__PURE__ */
|
|
1619
|
+
return /* @__PURE__ */ React13.createElement("div", { key: resource.id, className: "flex border-b border-border min-h-[100px]" }, /* @__PURE__ */ React13.createElement("div", { className: "w-48 shrink-0 border-r border-border p-4 bg-background sticky left-0 z-10 flex items-center gap-3" }, resource.avatar ? /* @__PURE__ */ React13.createElement("img", { src: resource.avatar, alt: resource.label, className: "w-8 h-8 rounded-full object-cover" }) : /* @__PURE__ */ React13.createElement("div", { className: "w-8 h-8 rounded-full bg-primary/10 flex items-center justify-center text-xs font-bold text-primary" }, resource.label.substring(0, 2).toUpperCase()), /* @__PURE__ */ React13.createElement("div", { className: "flex flex-col" }, /* @__PURE__ */ React13.createElement("span", { className: "text-sm font-medium" }, resource.label), /* @__PURE__ */ React13.createElement("span", { className: "text-xs text-muted-foreground" }, "ID: ", resource.id))), /* @__PURE__ */ React13.createElement("div", { className: "relative flex", style: { width: hours.length * hourWidth } }, hours.map((hour) => /* @__PURE__ */ React13.createElement(
|
|
1212
1620
|
"div",
|
|
1213
1621
|
{
|
|
1214
1622
|
key: hour,
|
|
@@ -1216,24 +1624,22 @@ var ResourceView = ({
|
|
|
1216
1624
|
style: { width: hourWidth }
|
|
1217
1625
|
},
|
|
1218
1626
|
[0, 15, 30, 45].map((minute) => {
|
|
1219
|
-
const
|
|
1220
|
-
|
|
1221
|
-
const slotId = `${resource.id}-${
|
|
1222
|
-
return /* @__PURE__ */
|
|
1627
|
+
const slotDate3 = new Date(currentDate);
|
|
1628
|
+
slotDate3.setHours(hour, minute, 0, 0);
|
|
1629
|
+
const slotId = `${resource.id}-${slotDate3.toISOString()}`;
|
|
1630
|
+
return /* @__PURE__ */ React13.createElement(
|
|
1223
1631
|
DroppableCell,
|
|
1224
1632
|
{
|
|
1225
1633
|
key: minute,
|
|
1226
1634
|
id: slotId,
|
|
1227
|
-
date:
|
|
1635
|
+
date: slotDate3,
|
|
1228
1636
|
resourceId: resource.id,
|
|
1229
1637
|
className: "h-full flex-1 border-r border-border/10 last:border-border/30 hover:bg-accent/10 transition-colors",
|
|
1230
|
-
|
|
1231
|
-
onTimeSlotClick?.(slotDate, resource.id);
|
|
1232
|
-
}
|
|
1638
|
+
onSlotClick: onTimeSlotClick
|
|
1233
1639
|
}
|
|
1234
1640
|
);
|
|
1235
1641
|
})
|
|
1236
|
-
)), resourceEvents.map((event) => /* @__PURE__ */
|
|
1642
|
+
)), resourceEvents.map((event) => /* @__PURE__ */ React13.createElement(
|
|
1237
1643
|
DraggableEvent,
|
|
1238
1644
|
{
|
|
1239
1645
|
key: event.id,
|
|
@@ -1250,10 +1656,17 @@ var ResourceView = ({
|
|
|
1250
1656
|
color: "#fff"
|
|
1251
1657
|
}
|
|
1252
1658
|
},
|
|
1253
|
-
/* @__PURE__ */
|
|
1659
|
+
/* @__PURE__ */ React13.createElement("div", { className: "truncate" }, event.title)
|
|
1254
1660
|
))));
|
|
1255
1661
|
}))));
|
|
1256
1662
|
};
|
|
1663
|
+
|
|
1664
|
+
// src/components/EventModal.tsx
|
|
1665
|
+
import React15, { useState as useState3, useEffect as useEffect3 } from "react";
|
|
1666
|
+
|
|
1667
|
+
// src/components/ui/Modal.tsx
|
|
1668
|
+
import React14, { useEffect as useEffect2, useRef as useRef3 } from "react";
|
|
1669
|
+
import { X } from "lucide-react";
|
|
1257
1670
|
var Modal = ({
|
|
1258
1671
|
isOpen,
|
|
1259
1672
|
onClose,
|
|
@@ -1262,8 +1675,8 @@ var Modal = ({
|
|
|
1262
1675
|
className,
|
|
1263
1676
|
hideHeader = false
|
|
1264
1677
|
}) => {
|
|
1265
|
-
const overlayRef =
|
|
1266
|
-
|
|
1678
|
+
const overlayRef = useRef3(null);
|
|
1679
|
+
useEffect2(() => {
|
|
1267
1680
|
const handleEscape = (e) => {
|
|
1268
1681
|
if (e.key === "Escape") {
|
|
1269
1682
|
onClose();
|
|
@@ -1277,14 +1690,14 @@ var Modal = ({
|
|
|
1277
1690
|
};
|
|
1278
1691
|
}, [isOpen, onClose]);
|
|
1279
1692
|
if (!isOpen) return null;
|
|
1280
|
-
return /* @__PURE__ */
|
|
1693
|
+
return /* @__PURE__ */ React14.createElement("div", { className: "absolute inset-0 z-50 flex items-center justify-center p-4 bg-black/40 backdrop-blur-md animate-in fade-in duration-300" }, /* @__PURE__ */ React14.createElement(
|
|
1281
1694
|
"div",
|
|
1282
1695
|
{
|
|
1283
1696
|
ref: overlayRef,
|
|
1284
1697
|
className: "absolute inset-0",
|
|
1285
1698
|
onClick: onClose
|
|
1286
1699
|
}
|
|
1287
|
-
), /* @__PURE__ */
|
|
1700
|
+
), /* @__PURE__ */ React14.createElement(
|
|
1288
1701
|
"div",
|
|
1289
1702
|
{
|
|
1290
1703
|
className: cn(
|
|
@@ -1292,7 +1705,7 @@ var Modal = ({
|
|
|
1292
1705
|
className
|
|
1293
1706
|
)
|
|
1294
1707
|
},
|
|
1295
|
-
!hideHeader && /* @__PURE__ */
|
|
1708
|
+
!hideHeader && /* @__PURE__ */ React14.createElement("div", { className: "flex items-center justify-between px-6 py-4 border-b border-border/50 shrink-0 bg-gradient-to-r from-muted/20 to-background" }, /* @__PURE__ */ React14.createElement("h3", { className: "text-lg font-semibold text-foreground" }, title), /* @__PURE__ */ React14.createElement(
|
|
1296
1709
|
Button,
|
|
1297
1710
|
{
|
|
1298
1711
|
variant: "ghost",
|
|
@@ -1300,12 +1713,37 @@ var Modal = ({
|
|
|
1300
1713
|
onClick: onClose,
|
|
1301
1714
|
className: "h-9 w-9 p-0 rounded-xl hover:bg-accent/80 transition-all duration-200"
|
|
1302
1715
|
},
|
|
1303
|
-
/* @__PURE__ */
|
|
1304
|
-
/* @__PURE__ */
|
|
1716
|
+
/* @__PURE__ */ React14.createElement(X, { className: "h-4 w-4" }),
|
|
1717
|
+
/* @__PURE__ */ React14.createElement("span", { className: "sr-only" }, "Close")
|
|
1305
1718
|
)),
|
|
1306
|
-
/* @__PURE__ */
|
|
1719
|
+
/* @__PURE__ */ React14.createElement("div", { className: cn("overflow-y-auto flex-1", !hideHeader && "p-6") }, children)
|
|
1307
1720
|
));
|
|
1308
1721
|
};
|
|
1722
|
+
|
|
1723
|
+
// src/components/EventModal.tsx
|
|
1724
|
+
import { format as format12, differenceInMinutes as differenceInMinutes5 } from "date-fns";
|
|
1725
|
+
import { motion as motion2, AnimatePresence } from "framer-motion";
|
|
1726
|
+
import {
|
|
1727
|
+
Clock as Clock2,
|
|
1728
|
+
MapPin,
|
|
1729
|
+
AlignLeft,
|
|
1730
|
+
Trash2,
|
|
1731
|
+
X as X2,
|
|
1732
|
+
Edit2,
|
|
1733
|
+
Users as Users2,
|
|
1734
|
+
Paperclip as Paperclip2,
|
|
1735
|
+
File,
|
|
1736
|
+
Calendar as Calendar2,
|
|
1737
|
+
ChevronDown as ChevronDown2,
|
|
1738
|
+
Check,
|
|
1739
|
+
Download,
|
|
1740
|
+
Bell as Bell2,
|
|
1741
|
+
BellRing,
|
|
1742
|
+
Plus as Plus3,
|
|
1743
|
+
Repeat,
|
|
1744
|
+
Tag,
|
|
1745
|
+
ExternalLink
|
|
1746
|
+
} from "lucide-react";
|
|
1309
1747
|
var REMINDER_OPTIONS = [
|
|
1310
1748
|
{ value: 0, label: "At time of event" },
|
|
1311
1749
|
{ value: 5, label: "5 minutes before" },
|
|
@@ -1347,13 +1785,10 @@ var EventModal = ({
|
|
|
1347
1785
|
initialDate,
|
|
1348
1786
|
onSave,
|
|
1349
1787
|
onDelete,
|
|
1350
|
-
calendars
|
|
1351
|
-
eventTypes,
|
|
1352
|
-
translations
|
|
1788
|
+
calendars
|
|
1353
1789
|
}) => {
|
|
1354
|
-
const [mode, setMode] =
|
|
1355
|
-
const [
|
|
1356
|
-
const [formData, setFormData] = useState({
|
|
1790
|
+
const [mode, setMode] = useState3("create");
|
|
1791
|
+
const [formData, setFormData] = useState3({
|
|
1357
1792
|
title: "",
|
|
1358
1793
|
description: "",
|
|
1359
1794
|
location: "",
|
|
@@ -1367,10 +1802,10 @@ var EventModal = ({
|
|
|
1367
1802
|
attachments: [],
|
|
1368
1803
|
reminders: [{ id: "1", type: "notification", time: 30, label: "30 minutes before" }]
|
|
1369
1804
|
});
|
|
1370
|
-
const [isCalendarDropdownOpen, setIsCalendarDropdownOpen] =
|
|
1371
|
-
const [isColorPickerOpen, setIsColorPickerOpen] =
|
|
1372
|
-
const [isReminderDropdownOpen, setIsReminderDropdownOpen] =
|
|
1373
|
-
const [isGuestsDropdownOpen, setIsGuestsDropdownOpen] =
|
|
1805
|
+
const [isCalendarDropdownOpen, setIsCalendarDropdownOpen] = useState3(false);
|
|
1806
|
+
const [isColorPickerOpen, setIsColorPickerOpen] = useState3(false);
|
|
1807
|
+
const [isReminderDropdownOpen, setIsReminderDropdownOpen] = useState3(false);
|
|
1808
|
+
const [isGuestsDropdownOpen, setIsGuestsDropdownOpen] = useState3(false);
|
|
1374
1809
|
const fakeGuests = [
|
|
1375
1810
|
"alice@example.com",
|
|
1376
1811
|
"bob@example.com",
|
|
@@ -1378,9 +1813,8 @@ var EventModal = ({
|
|
|
1378
1813
|
"diana@example.com",
|
|
1379
1814
|
"evan@example.com"
|
|
1380
1815
|
];
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
useEffect(() => {
|
|
1816
|
+
const [selectedGuests, setSelectedGuests] = useState3([]);
|
|
1817
|
+
useEffect3(() => {
|
|
1384
1818
|
if (isOpen) {
|
|
1385
1819
|
if (event) {
|
|
1386
1820
|
setMode("view");
|
|
@@ -1410,7 +1844,6 @@ var EventModal = ({
|
|
|
1410
1844
|
reminders: [{ id: "1", type: "notification", time: 30, label: "30 minutes before" }]
|
|
1411
1845
|
});
|
|
1412
1846
|
setSelectedGuests([]);
|
|
1413
|
-
setActiveTab("details");
|
|
1414
1847
|
}
|
|
1415
1848
|
}
|
|
1416
1849
|
}, [isOpen, event, initialDate, calendars]);
|
|
@@ -1445,7 +1878,7 @@ var EventModal = ({
|
|
|
1445
1878
|
};
|
|
1446
1879
|
const formatDateForInput = (date) => {
|
|
1447
1880
|
if (!date) return "";
|
|
1448
|
-
return
|
|
1881
|
+
return format12(date, "yyyy-MM-dd'T'HH:mm");
|
|
1449
1882
|
};
|
|
1450
1883
|
const handleDateChange = (field, value) => {
|
|
1451
1884
|
const date = new Date(value);
|
|
@@ -1495,14 +1928,14 @@ var EventModal = ({
|
|
|
1495
1928
|
};
|
|
1496
1929
|
const getDurationText = () => {
|
|
1497
1930
|
if (!formData.start || !formData.end) return "";
|
|
1498
|
-
const mins =
|
|
1931
|
+
const mins = differenceInMinutes5(formData.end, formData.start);
|
|
1499
1932
|
if (mins < 60) return `${mins} min`;
|
|
1500
1933
|
const hours = Math.floor(mins / 60);
|
|
1501
1934
|
const remainingMins = mins % 60;
|
|
1502
1935
|
if (remainingMins === 0) return `${hours} hr`;
|
|
1503
1936
|
return `${hours} hr ${remainingMins} min`;
|
|
1504
1937
|
};
|
|
1505
|
-
const renderViewMode = () => /* @__PURE__ */
|
|
1938
|
+
const renderViewMode = () => /* @__PURE__ */ React15.createElement("div", { className: "flex flex-col bg-background overflow-hidden w-full max-h-[90vh]" }, /* @__PURE__ */ React15.createElement(
|
|
1506
1939
|
"div",
|
|
1507
1940
|
{
|
|
1508
1941
|
className: "relative px-6 pt-6 pb-8",
|
|
@@ -1510,65 +1943,65 @@ var EventModal = ({
|
|
|
1510
1943
|
background: `linear-gradient(135deg, ${event?.color || "#3b82f6"}15 0%, transparent 100%)`
|
|
1511
1944
|
}
|
|
1512
1945
|
},
|
|
1513
|
-
/* @__PURE__ */
|
|
1946
|
+
/* @__PURE__ */ React15.createElement("div", { className: "flex items-center justify-between mb-6" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(
|
|
1514
1947
|
"div",
|
|
1515
1948
|
{
|
|
1516
1949
|
className: "w-3 h-3 rounded-full",
|
|
1517
1950
|
style: { backgroundColor: event?.color || "#3b82f6" }
|
|
1518
1951
|
}
|
|
1519
|
-
), /* @__PURE__ */
|
|
1952
|
+
), /* @__PURE__ */ React15.createElement("span", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider" }, calendars?.find((c) => c.id === event?.calendarId)?.label || "Event")), /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React15.createElement(
|
|
1520
1953
|
"button",
|
|
1521
1954
|
{
|
|
1522
1955
|
onClick: () => setMode("edit"),
|
|
1523
1956
|
className: "p-2 hover:bg-background/80 rounded-lg transition-all text-muted-foreground hover:text-foreground",
|
|
1524
1957
|
title: "Edit"
|
|
1525
1958
|
},
|
|
1526
|
-
/* @__PURE__ */
|
|
1527
|
-
), /* @__PURE__ */
|
|
1959
|
+
/* @__PURE__ */ React15.createElement(Edit2, { className: "w-4 h-4" })
|
|
1960
|
+
), /* @__PURE__ */ React15.createElement(
|
|
1528
1961
|
"button",
|
|
1529
1962
|
{
|
|
1530
1963
|
onClick: handleDelete,
|
|
1531
1964
|
className: "p-2 hover:bg-destructive/10 rounded-lg transition-all text-muted-foreground hover:text-destructive",
|
|
1532
1965
|
title: "Delete"
|
|
1533
1966
|
},
|
|
1534
|
-
/* @__PURE__ */
|
|
1535
|
-
), /* @__PURE__ */
|
|
1967
|
+
/* @__PURE__ */ React15.createElement(Trash2, { className: "w-4 h-4" })
|
|
1968
|
+
), /* @__PURE__ */ React15.createElement(
|
|
1536
1969
|
"button",
|
|
1537
1970
|
{
|
|
1538
1971
|
onClick: onClose,
|
|
1539
1972
|
className: "p-2 hover:bg-background/80 rounded-lg transition-all text-muted-foreground hover:text-foreground",
|
|
1540
1973
|
title: "Close"
|
|
1541
1974
|
},
|
|
1542
|
-
/* @__PURE__ */
|
|
1975
|
+
/* @__PURE__ */ React15.createElement(X2, { className: "w-4 h-4" })
|
|
1543
1976
|
))),
|
|
1544
|
-
/* @__PURE__ */
|
|
1545
|
-
/* @__PURE__ */
|
|
1546
|
-
), /* @__PURE__ */
|
|
1547
|
-
|
|
1977
|
+
/* @__PURE__ */ React15.createElement("h1", { className: "text-2xl font-bold text-foreground mb-3 leading-tight" }, event?.title || "(No title)"),
|
|
1978
|
+
/* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-3 text-sm" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2 px-3 py-1.5 bg-background/60 backdrop-blur-sm rounded-lg border border-border/30" }, /* @__PURE__ */ React15.createElement(Calendar2, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "font-medium" }, event?.start && format12(new Date(event.start), "EEE, MMM d"))), /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2 px-3 py-1.5 bg-background/60 backdrop-blur-sm rounded-lg border border-border/30" }, /* @__PURE__ */ React15.createElement(Clock2, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "font-medium" }, event?.start && format12(new Date(event.start), "h:mm a"), " \u2013 ", event?.end && format12(new Date(event.end), "h:mm a"))))
|
|
1979
|
+
), /* @__PURE__ */ React15.createElement("div", { className: "px-6 pb-6 space-y-4 overflow-y-auto flex-1" }, event?.location && /* @__PURE__ */ React15.createElement(
|
|
1980
|
+
motion2.div,
|
|
1548
1981
|
{
|
|
1549
1982
|
initial: { opacity: 0, y: 10 },
|
|
1550
1983
|
animate: { opacity: 1, y: 0 },
|
|
1551
1984
|
className: "flex items-start gap-3 p-4 rounded-2xl bg-muted/30 border border-border/30 group hover:bg-muted/50 transition-all cursor-pointer"
|
|
1552
1985
|
},
|
|
1553
|
-
/* @__PURE__ */
|
|
1554
|
-
/* @__PURE__ */
|
|
1555
|
-
/* @__PURE__ */
|
|
1556
|
-
), event?.guests && event.guests.length > 0 && /* @__PURE__ */
|
|
1557
|
-
|
|
1986
|
+
/* @__PURE__ */ React15.createElement("div", { className: "p-2 bg-primary/10 rounded-xl" }, /* @__PURE__ */ React15.createElement(MapPin, { className: "w-5 h-5 text-primary" })),
|
|
1987
|
+
/* @__PURE__ */ React15.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React15.createElement("p", { className: "text-sm font-medium text-foreground" }, event.location), /* @__PURE__ */ React15.createElement("p", { className: "text-xs text-muted-foreground mt-0.5" }, "Click to open in maps")),
|
|
1988
|
+
/* @__PURE__ */ React15.createElement(ExternalLink, { className: "w-4 h-4 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" })
|
|
1989
|
+
), event?.guests && event.guests.length > 0 && /* @__PURE__ */ React15.createElement(
|
|
1990
|
+
motion2.div,
|
|
1558
1991
|
{
|
|
1559
1992
|
initial: { opacity: 0, y: 10 },
|
|
1560
1993
|
animate: { opacity: 1, y: 0 },
|
|
1561
1994
|
transition: { delay: 0.05 },
|
|
1562
1995
|
className: "space-y-3"
|
|
1563
1996
|
},
|
|
1564
|
-
/* @__PURE__ */
|
|
1565
|
-
/* @__PURE__ */
|
|
1997
|
+
/* @__PURE__ */ React15.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(Users2, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "text-sm font-medium text-foreground" }, event.guests.length, " ", event.guests.length > 1 ? "Guests" : "Guest"))),
|
|
1998
|
+
/* @__PURE__ */ React15.createElement("div", { className: "flex flex-wrap gap-2" }, event.guests.map((email, index) => /* @__PURE__ */ React15.createElement(
|
|
1566
1999
|
"div",
|
|
1567
2000
|
{
|
|
1568
2001
|
key: index,
|
|
1569
2002
|
className: "flex items-center gap-2 px-3 py-2 rounded-xl bg-muted/30 border border-border/30 hover:bg-muted/50 transition-all cursor-pointer group"
|
|
1570
2003
|
},
|
|
1571
|
-
/* @__PURE__ */
|
|
2004
|
+
/* @__PURE__ */ React15.createElement(
|
|
1572
2005
|
"div",
|
|
1573
2006
|
{
|
|
1574
2007
|
className: "w-8 h-8 rounded-full flex items-center justify-center text-sm font-semibold text-white",
|
|
@@ -1576,18 +2009,18 @@ var EventModal = ({
|
|
|
1576
2009
|
},
|
|
1577
2010
|
email[0].toUpperCase()
|
|
1578
2011
|
),
|
|
1579
|
-
/* @__PURE__ */
|
|
2012
|
+
/* @__PURE__ */ React15.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React15.createElement("p", { className: "text-sm font-medium text-foreground" }, email.split("@")[0]), /* @__PURE__ */ React15.createElement("p", { className: "text-xs text-muted-foreground truncate" }, email))
|
|
1580
2013
|
)))
|
|
1581
|
-
), event?.reminders && event.reminders.length > 0 && /* @__PURE__ */
|
|
1582
|
-
|
|
2014
|
+
), event?.reminders && event.reminders.length > 0 && /* @__PURE__ */ React15.createElement(
|
|
2015
|
+
motion2.div,
|
|
1583
2016
|
{
|
|
1584
2017
|
initial: { opacity: 0, y: 10 },
|
|
1585
2018
|
animate: { opacity: 1, y: 0 },
|
|
1586
2019
|
transition: { delay: 0.1 },
|
|
1587
2020
|
className: "flex items-center gap-3"
|
|
1588
2021
|
},
|
|
1589
|
-
/* @__PURE__ */
|
|
1590
|
-
/* @__PURE__ */
|
|
2022
|
+
/* @__PURE__ */ React15.createElement("div", { className: "p-2 bg-amber-500/10 rounded-xl" }, /* @__PURE__ */ React15.createElement(Bell2, { className: "w-4 h-4 text-amber-500" })),
|
|
2023
|
+
/* @__PURE__ */ React15.createElement("div", { className: "flex flex-wrap gap-2" }, event.reminders.map((reminder) => /* @__PURE__ */ React15.createElement(
|
|
1591
2024
|
"span",
|
|
1592
2025
|
{
|
|
1593
2026
|
key: reminder.id,
|
|
@@ -1595,45 +2028,45 @@ var EventModal = ({
|
|
|
1595
2028
|
},
|
|
1596
2029
|
reminder.label || `${reminder.time} min before`
|
|
1597
2030
|
)))
|
|
1598
|
-
), event?.description && /* @__PURE__ */
|
|
1599
|
-
|
|
2031
|
+
), event?.description && /* @__PURE__ */ React15.createElement(
|
|
2032
|
+
motion2.div,
|
|
1600
2033
|
{
|
|
1601
2034
|
initial: { opacity: 0, y: 10 },
|
|
1602
2035
|
animate: { opacity: 1, y: 0 },
|
|
1603
2036
|
transition: { delay: 0.15 },
|
|
1604
2037
|
className: "space-y-2"
|
|
1605
2038
|
},
|
|
1606
|
-
/* @__PURE__ */
|
|
1607
|
-
/* @__PURE__ */
|
|
1608
|
-
), event?.attachments && event.attachments.length > 0 && /* @__PURE__ */
|
|
1609
|
-
|
|
2039
|
+
/* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(AlignLeft, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "text-sm font-medium text-foreground" }, "Notes")),
|
|
2040
|
+
/* @__PURE__ */ React15.createElement("div", { className: "text-sm text-foreground/80 whitespace-pre-wrap leading-relaxed pl-6 p-3 rounded-xl bg-muted/20 border border-border/30" }, event.description)
|
|
2041
|
+
), event?.attachments && event.attachments.length > 0 && /* @__PURE__ */ React15.createElement(
|
|
2042
|
+
motion2.div,
|
|
1610
2043
|
{
|
|
1611
2044
|
initial: { opacity: 0, y: 10 },
|
|
1612
2045
|
animate: { opacity: 1, y: 0 },
|
|
1613
2046
|
transition: { delay: 0.2 },
|
|
1614
2047
|
className: "space-y-2"
|
|
1615
2048
|
},
|
|
1616
|
-
/* @__PURE__ */
|
|
1617
|
-
/* @__PURE__ */
|
|
2049
|
+
/* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(Paperclip2, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "text-sm font-medium text-foreground" }, "Attachments")),
|
|
2050
|
+
/* @__PURE__ */ React15.createElement("div", { className: "grid gap-2" }, event.attachments.map((att) => /* @__PURE__ */ React15.createElement(
|
|
1618
2051
|
"div",
|
|
1619
2052
|
{
|
|
1620
2053
|
key: att.id,
|
|
1621
2054
|
onClick: () => handleDownload(att),
|
|
1622
2055
|
className: "flex items-center gap-3 p-3 rounded-xl border border-border/30 bg-muted/20 hover:bg-muted/40 transition-all cursor-pointer group"
|
|
1623
2056
|
},
|
|
1624
|
-
/* @__PURE__ */
|
|
1625
|
-
/* @__PURE__ */
|
|
1626
|
-
/* @__PURE__ */
|
|
2057
|
+
/* @__PURE__ */ React15.createElement("div", { className: "p-2 bg-primary/10 rounded-lg" }, /* @__PURE__ */ React15.createElement(File, { className: "w-4 h-4 text-primary" })),
|
|
2058
|
+
/* @__PURE__ */ React15.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React15.createElement("p", { className: "text-sm font-medium text-foreground truncate" }, att.name), /* @__PURE__ */ React15.createElement("p", { className: "text-xs text-muted-foreground" }, att.size)),
|
|
2059
|
+
/* @__PURE__ */ React15.createElement(Download, { className: "w-4 h-4 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" })
|
|
1627
2060
|
)))
|
|
1628
2061
|
)));
|
|
1629
|
-
const renderEditMode = () => /* @__PURE__ */
|
|
2062
|
+
const renderEditMode = () => /* @__PURE__ */ React15.createElement("form", { onSubmit: handleSubmit, className: "flex flex-col bg-background overflow-hidden w-full max-h-[85vh]" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center justify-between px-5 py-4 border-b border-border/50 shrink-0" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ React15.createElement(
|
|
1630
2063
|
"button",
|
|
1631
2064
|
{
|
|
1632
2065
|
type: "button",
|
|
1633
2066
|
onClick: () => setIsColorPickerOpen(!isColorPickerOpen),
|
|
1634
2067
|
className: "relative p-1"
|
|
1635
2068
|
},
|
|
1636
|
-
/* @__PURE__ */
|
|
2069
|
+
/* @__PURE__ */ React15.createElement(
|
|
1637
2070
|
"div",
|
|
1638
2071
|
{
|
|
1639
2072
|
className: "w-5 h-5 rounded-full cursor-pointer hover:scale-110 transition-transform",
|
|
@@ -1643,7 +2076,7 @@ var EventModal = ({
|
|
|
1643
2076
|
}
|
|
1644
2077
|
}
|
|
1645
2078
|
),
|
|
1646
|
-
isColorPickerOpen && /* @__PURE__ */
|
|
2079
|
+
isColorPickerOpen && /* @__PURE__ */ React15.createElement("div", { className: "absolute top-full left-0 mt-2 p-2 bg-background border border-border rounded-xl shadow-xl z-50 flex flex-wrap gap-1.5 w-[180px]" }, COLOR_PALETTE.map((color) => /* @__PURE__ */ React15.createElement(
|
|
1647
2080
|
"button",
|
|
1648
2081
|
{
|
|
1649
2082
|
key: color,
|
|
@@ -1659,15 +2092,15 @@ var EventModal = ({
|
|
|
1659
2092
|
style: { backgroundColor: color, color }
|
|
1660
2093
|
}
|
|
1661
2094
|
)))
|
|
1662
|
-
), /* @__PURE__ */
|
|
2095
|
+
), /* @__PURE__ */ React15.createElement("h2", { className: "text-lg font-semibold text-foreground" }, mode === "edit" ? "Edit Event" : "New Event")), /* @__PURE__ */ React15.createElement(
|
|
1663
2096
|
"button",
|
|
1664
2097
|
{
|
|
1665
2098
|
type: "button",
|
|
1666
2099
|
onClick: onClose,
|
|
1667
2100
|
className: "p-2 hover:bg-accent rounded-xl transition-all text-muted-foreground hover:text-foreground"
|
|
1668
2101
|
},
|
|
1669
|
-
/* @__PURE__ */
|
|
1670
|
-
)), /* @__PURE__ */
|
|
2102
|
+
/* @__PURE__ */ React15.createElement(X2, { className: "w-4 h-4" })
|
|
2103
|
+
)), /* @__PURE__ */ React15.createElement("div", { className: "flex-1 overflow-y-auto" }, /* @__PURE__ */ React15.createElement("div", { className: "px-5 py-4 space-y-5" }, /* @__PURE__ */ React15.createElement("div", null, /* @__PURE__ */ React15.createElement(
|
|
1671
2104
|
"input",
|
|
1672
2105
|
{
|
|
1673
2106
|
type: "text",
|
|
@@ -1678,7 +2111,7 @@ var EventModal = ({
|
|
|
1678
2111
|
onChange: (e) => setFormData({ ...formData, title: e.target.value }),
|
|
1679
2112
|
placeholder: "Add title"
|
|
1680
2113
|
}
|
|
1681
|
-
)), /* @__PURE__ */
|
|
2114
|
+
)), /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-3 p-4 rounded-2xl bg-muted/20 border border-border/30" }, /* @__PURE__ */ React15.createElement("div", { className: "p-2 bg-primary/10 rounded-xl shrink-0" }, /* @__PURE__ */ React15.createElement(Clock2, { className: "w-5 h-5 text-primary" })), /* @__PURE__ */ React15.createElement("div", { className: "flex-1 grid grid-cols-2 gap-3" }, /* @__PURE__ */ React15.createElement("div", null, /* @__PURE__ */ React15.createElement("label", { className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-wider mb-1 block" }, "Start"), /* @__PURE__ */ React15.createElement(
|
|
1682
2115
|
"input",
|
|
1683
2116
|
{
|
|
1684
2117
|
type: "datetime-local",
|
|
@@ -1686,7 +2119,7 @@ var EventModal = ({
|
|
|
1686
2119
|
value: formatDateForInput(formData.start),
|
|
1687
2120
|
onChange: (e) => handleDateChange("start", e.target.value)
|
|
1688
2121
|
}
|
|
1689
|
-
)), /* @__PURE__ */
|
|
2122
|
+
)), /* @__PURE__ */ React15.createElement("div", null, /* @__PURE__ */ React15.createElement("label", { className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-wider mb-1 block" }, "End"), /* @__PURE__ */ React15.createElement(
|
|
1690
2123
|
"input",
|
|
1691
2124
|
{
|
|
1692
2125
|
type: "datetime-local",
|
|
@@ -1694,16 +2127,16 @@ var EventModal = ({
|
|
|
1694
2127
|
value: formatDateForInput(formData.end),
|
|
1695
2128
|
onChange: (e) => handleDateChange("end", e.target.value)
|
|
1696
2129
|
}
|
|
1697
|
-
)))), getDurationText() && /* @__PURE__ */
|
|
2130
|
+
)))), getDurationText() && /* @__PURE__ */ React15.createElement("div", { className: "flex justify-center" }, /* @__PURE__ */ React15.createElement("span", { className: "text-xs font-medium px-3 py-1 rounded-full bg-primary/10 text-primary" }, getDurationText())), /* @__PURE__ */ React15.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(Bell2, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "text-sm font-medium text-foreground" }, "Reminders")), /* @__PURE__ */ React15.createElement("div", { className: "relative" }, /* @__PURE__ */ React15.createElement(
|
|
1698
2131
|
"button",
|
|
1699
2132
|
{
|
|
1700
2133
|
type: "button",
|
|
1701
2134
|
onClick: () => setIsReminderDropdownOpen(!isReminderDropdownOpen),
|
|
1702
2135
|
className: "flex items-center gap-1 text-xs font-medium text-primary hover:text-primary/80 transition-colors"
|
|
1703
2136
|
},
|
|
1704
|
-
/* @__PURE__ */
|
|
2137
|
+
/* @__PURE__ */ React15.createElement(Plus3, { className: "w-3.5 h-3.5" }),
|
|
1705
2138
|
"Add"
|
|
1706
|
-
), isReminderDropdownOpen && /* @__PURE__ */
|
|
2139
|
+
), isReminderDropdownOpen && /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement("div", { className: "fixed inset-0 z-40", onClick: () => setIsReminderDropdownOpen(false) }), /* @__PURE__ */ React15.createElement("div", { className: "absolute right-0 top-full mt-1 w-48 bg-background border border-border rounded-xl shadow-xl z-50 py-1 max-h-60 overflow-y-auto" }, REMINDER_OPTIONS.map((option) => /* @__PURE__ */ React15.createElement(
|
|
1707
2140
|
"button",
|
|
1708
2141
|
{
|
|
1709
2142
|
key: option.value,
|
|
@@ -1712,58 +2145,58 @@ var EventModal = ({
|
|
|
1712
2145
|
className: "w-full px-3 py-2 text-left text-sm hover:bg-accent transition-colors"
|
|
1713
2146
|
},
|
|
1714
2147
|
option.label
|
|
1715
|
-
)))))), formData.reminders && formData.reminders.length > 0 && /* @__PURE__ */
|
|
2148
|
+
)))))), formData.reminders && formData.reminders.length > 0 && /* @__PURE__ */ React15.createElement("div", { className: "flex flex-wrap gap-2" }, formData.reminders.map((reminder) => /* @__PURE__ */ React15.createElement(
|
|
1716
2149
|
"div",
|
|
1717
2150
|
{
|
|
1718
2151
|
key: reminder.id,
|
|
1719
2152
|
className: "flex items-center gap-2 px-3 py-1.5 rounded-full bg-amber-500/10 border border-amber-500/20"
|
|
1720
2153
|
},
|
|
1721
|
-
/* @__PURE__ */
|
|
1722
|
-
/* @__PURE__ */
|
|
1723
|
-
/* @__PURE__ */
|
|
2154
|
+
/* @__PURE__ */ React15.createElement(BellRing, { className: "w-3.5 h-3.5 text-amber-500" }),
|
|
2155
|
+
/* @__PURE__ */ React15.createElement("span", { className: "text-xs font-medium text-amber-600 dark:text-amber-400" }, reminder.label),
|
|
2156
|
+
/* @__PURE__ */ React15.createElement(
|
|
1724
2157
|
"button",
|
|
1725
2158
|
{
|
|
1726
2159
|
type: "button",
|
|
1727
2160
|
onClick: () => removeReminder(reminder.id),
|
|
1728
2161
|
className: "p-0.5 hover:bg-amber-500/20 rounded-full transition-colors"
|
|
1729
2162
|
},
|
|
1730
|
-
/* @__PURE__ */
|
|
2163
|
+
/* @__PURE__ */ React15.createElement(X2, { className: "w-3 h-3 text-amber-500" })
|
|
1731
2164
|
)
|
|
1732
|
-
)))), /* @__PURE__ */
|
|
2165
|
+
)))), /* @__PURE__ */ React15.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(Users2, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "text-sm font-medium text-foreground" }, "Guests")), /* @__PURE__ */ React15.createElement("div", { className: "relative" }, /* @__PURE__ */ React15.createElement(
|
|
1733
2166
|
"button",
|
|
1734
2167
|
{
|
|
1735
2168
|
type: "button",
|
|
1736
2169
|
onClick: () => setIsGuestsDropdownOpen(!isGuestsDropdownOpen),
|
|
1737
2170
|
className: "w-full flex items-center justify-between px-4 py-3 bg-muted/20 border border-border/30 rounded-xl text-sm text-left hover:bg-muted/30 transition-all"
|
|
1738
2171
|
},
|
|
1739
|
-
/* @__PURE__ */
|
|
1740
|
-
/* @__PURE__ */
|
|
1741
|
-
), isGuestsDropdownOpen && /* @__PURE__ */
|
|
2172
|
+
/* @__PURE__ */ React15.createElement("span", { className: selectedGuests.length > 0 ? "text-foreground font-medium" : "text-muted-foreground" }, selectedGuests.length > 0 ? `${selectedGuests.length} guest${selectedGuests.length > 1 ? "s" : ""} added` : "Add guests"),
|
|
2173
|
+
/* @__PURE__ */ React15.createElement(ChevronDown2, { className: cn("w-4 h-4 text-muted-foreground transition-transform", isGuestsDropdownOpen && "rotate-180") })
|
|
2174
|
+
), isGuestsDropdownOpen && /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement("div", { className: "fixed inset-0 z-40", onClick: () => setIsGuestsDropdownOpen(false) }), /* @__PURE__ */ React15.createElement("div", { className: "absolute z-50 w-full mt-2 bg-background border border-border rounded-xl shadow-xl max-h-48 overflow-y-auto" }, fakeGuests.map((email) => /* @__PURE__ */ React15.createElement(
|
|
1742
2175
|
"div",
|
|
1743
2176
|
{
|
|
1744
2177
|
key: email,
|
|
1745
2178
|
className: "px-4 py-3 hover:bg-accent cursor-pointer flex items-center gap-3 transition-colors",
|
|
1746
2179
|
onClick: () => toggleGuest(email)
|
|
1747
2180
|
},
|
|
1748
|
-
/* @__PURE__ */
|
|
2181
|
+
/* @__PURE__ */ React15.createElement("div", { className: cn(
|
|
1749
2182
|
"w-5 h-5 rounded-md border-2 flex items-center justify-center transition-all",
|
|
1750
2183
|
selectedGuests.includes(email) ? "bg-primary border-primary" : "border-border"
|
|
1751
|
-
) }, selectedGuests.includes(email) && /* @__PURE__ */
|
|
1752
|
-
/* @__PURE__ */
|
|
2184
|
+
) }, selectedGuests.includes(email) && /* @__PURE__ */ React15.createElement(Check, { className: "w-3 h-3 text-primary-foreground" })),
|
|
2185
|
+
/* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(
|
|
1753
2186
|
"div",
|
|
1754
2187
|
{
|
|
1755
2188
|
className: "w-7 h-7 rounded-full flex items-center justify-center text-xs font-semibold text-white",
|
|
1756
2189
|
style: { backgroundColor: `hsl(${fakeGuests.indexOf(email) * 60}, 70%, 50%)` }
|
|
1757
2190
|
},
|
|
1758
2191
|
email[0].toUpperCase()
|
|
1759
|
-
), /* @__PURE__ */
|
|
1760
|
-
))))), selectedGuests.length > 0 && /* @__PURE__ */
|
|
2192
|
+
), /* @__PURE__ */ React15.createElement("span", { className: "text-sm" }, email))
|
|
2193
|
+
))))), selectedGuests.length > 0 && /* @__PURE__ */ React15.createElement("div", { className: "flex flex-wrap gap-2 mt-2" }, selectedGuests.map((email) => /* @__PURE__ */ React15.createElement(
|
|
1761
2194
|
"div",
|
|
1762
2195
|
{
|
|
1763
2196
|
key: email,
|
|
1764
2197
|
className: "flex items-center gap-2 pl-1 pr-2 py-1 bg-muted/30 border border-border/30 rounded-full"
|
|
1765
2198
|
},
|
|
1766
|
-
/* @__PURE__ */
|
|
2199
|
+
/* @__PURE__ */ React15.createElement(
|
|
1767
2200
|
"div",
|
|
1768
2201
|
{
|
|
1769
2202
|
className: "w-6 h-6 rounded-full flex items-center justify-center text-xs font-semibold text-white",
|
|
@@ -1771,17 +2204,17 @@ var EventModal = ({
|
|
|
1771
2204
|
},
|
|
1772
2205
|
email[0].toUpperCase()
|
|
1773
2206
|
),
|
|
1774
|
-
/* @__PURE__ */
|
|
1775
|
-
/* @__PURE__ */
|
|
2207
|
+
/* @__PURE__ */ React15.createElement("span", { className: "text-xs font-medium text-foreground" }, email.split("@")[0]),
|
|
2208
|
+
/* @__PURE__ */ React15.createElement(
|
|
1776
2209
|
"button",
|
|
1777
2210
|
{
|
|
1778
2211
|
type: "button",
|
|
1779
2212
|
onClick: () => toggleGuest(email),
|
|
1780
2213
|
className: "p-0.5 hover:bg-destructive/10 rounded-full transition-colors"
|
|
1781
2214
|
},
|
|
1782
|
-
/* @__PURE__ */
|
|
2215
|
+
/* @__PURE__ */ React15.createElement(X2, { className: "w-3 h-3 text-muted-foreground" })
|
|
1783
2216
|
)
|
|
1784
|
-
)))), /* @__PURE__ */
|
|
2217
|
+
)))), /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-3 p-3 rounded-xl bg-muted/20 border border-border/30" }, /* @__PURE__ */ React15.createElement(MapPin, { className: "w-5 h-5 text-muted-foreground shrink-0" }), /* @__PURE__ */ React15.createElement(
|
|
1785
2218
|
"input",
|
|
1786
2219
|
{
|
|
1787
2220
|
type: "text",
|
|
@@ -1790,7 +2223,7 @@ var EventModal = ({
|
|
|
1790
2223
|
value: formData.location || "",
|
|
1791
2224
|
onChange: (e) => setFormData({ ...formData, location: e.target.value })
|
|
1792
2225
|
}
|
|
1793
|
-
)), /* @__PURE__ */
|
|
2226
|
+
)), /* @__PURE__ */ React15.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(AlignLeft, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "text-sm font-medium text-foreground" }, "Notes")), /* @__PURE__ */ React15.createElement(
|
|
1794
2227
|
"textarea",
|
|
1795
2228
|
{
|
|
1796
2229
|
className: "w-full bg-muted/20 border border-border/30 rounded-xl px-4 py-3 text-sm min-h-[100px] resize-none text-foreground placeholder-muted-foreground focus:border-primary focus:ring-2 focus:ring-primary/20 transition-all",
|
|
@@ -1798,7 +2231,7 @@ var EventModal = ({
|
|
|
1798
2231
|
value: formData.description || "",
|
|
1799
2232
|
onChange: (e) => setFormData({ ...formData, description: e.target.value })
|
|
1800
2233
|
}
|
|
1801
|
-
)), /* @__PURE__ */
|
|
2234
|
+
)), /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-3 p-3 rounded-xl bg-muted/20 border border-border/30" }, /* @__PURE__ */ React15.createElement(Repeat, { className: "w-5 h-5 text-muted-foreground shrink-0" }), /* @__PURE__ */ React15.createElement(
|
|
1802
2235
|
"select",
|
|
1803
2236
|
{
|
|
1804
2237
|
className: "flex-1 bg-transparent text-sm text-foreground focus:outline-none cursor-pointer",
|
|
@@ -1817,28 +2250,28 @@ var EventModal = ({
|
|
|
1817
2250
|
}
|
|
1818
2251
|
}
|
|
1819
2252
|
},
|
|
1820
|
-
/* @__PURE__ */
|
|
1821
|
-
/* @__PURE__ */
|
|
1822
|
-
/* @__PURE__ */
|
|
1823
|
-
/* @__PURE__ */
|
|
1824
|
-
/* @__PURE__ */
|
|
1825
|
-
)), /* @__PURE__ */
|
|
2253
|
+
/* @__PURE__ */ React15.createElement("option", { value: "" }, "Does not repeat"),
|
|
2254
|
+
/* @__PURE__ */ React15.createElement("option", { value: "DAILY" }, "Daily"),
|
|
2255
|
+
/* @__PURE__ */ React15.createElement("option", { value: "WEEKLY" }, "Weekly"),
|
|
2256
|
+
/* @__PURE__ */ React15.createElement("option", { value: "MONTHLY" }, "Monthly"),
|
|
2257
|
+
/* @__PURE__ */ React15.createElement("option", { value: "YEARLY" }, "Yearly")
|
|
2258
|
+
)), /* @__PURE__ */ React15.createElement("div", { className: "flex items-center justify-between p-3 rounded-xl bg-muted/20 border border-border/30" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(Tag, { className: "w-5 h-5 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "text-sm text-foreground" }, "Calendar")), /* @__PURE__ */ React15.createElement("div", { className: "relative" }, /* @__PURE__ */ React15.createElement(
|
|
1826
2259
|
"button",
|
|
1827
2260
|
{
|
|
1828
2261
|
type: "button",
|
|
1829
2262
|
onClick: () => setIsCalendarDropdownOpen(!isCalendarDropdownOpen),
|
|
1830
2263
|
className: "flex items-center gap-2 px-3 py-1.5 bg-background border border-border/50 rounded-lg text-sm hover:bg-accent/50 transition-all"
|
|
1831
2264
|
},
|
|
1832
|
-
/* @__PURE__ */
|
|
2265
|
+
/* @__PURE__ */ React15.createElement(
|
|
1833
2266
|
"div",
|
|
1834
2267
|
{
|
|
1835
2268
|
className: "w-3 h-3 rounded-full",
|
|
1836
2269
|
style: { backgroundColor: calendars?.find((c) => c.id === formData.calendarId)?.color || "#3b82f6" }
|
|
1837
2270
|
}
|
|
1838
2271
|
),
|
|
1839
|
-
/* @__PURE__ */
|
|
1840
|
-
/* @__PURE__ */
|
|
1841
|
-
), isCalendarDropdownOpen && /* @__PURE__ */
|
|
2272
|
+
/* @__PURE__ */ React15.createElement("span", { className: "font-medium" }, calendars?.find((c) => c.id === formData.calendarId)?.label || "Select"),
|
|
2273
|
+
/* @__PURE__ */ React15.createElement(ChevronDown2, { className: "w-3.5 h-3.5 text-muted-foreground" })
|
|
2274
|
+
), isCalendarDropdownOpen && /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement("div", { className: "fixed inset-0 z-40", onClick: () => setIsCalendarDropdownOpen(false) }), /* @__PURE__ */ React15.createElement("div", { className: "absolute right-0 bottom-full mb-2 w-48 bg-background border border-border rounded-xl shadow-xl z-50 py-1" }, calendars?.map((cal) => /* @__PURE__ */ React15.createElement(
|
|
1842
2275
|
"button",
|
|
1843
2276
|
{
|
|
1844
2277
|
key: cal.id,
|
|
@@ -1853,19 +2286,19 @@ var EventModal = ({
|
|
|
1853
2286
|
setIsCalendarDropdownOpen(false);
|
|
1854
2287
|
}
|
|
1855
2288
|
},
|
|
1856
|
-
/* @__PURE__ */
|
|
1857
|
-
/* @__PURE__ */
|
|
1858
|
-
formData.calendarId === cal.id && /* @__PURE__ */
|
|
1859
|
-
)))))))), /* @__PURE__ */
|
|
2289
|
+
/* @__PURE__ */ React15.createElement("div", { className: "w-3 h-3 rounded-full", style: { backgroundColor: cal.color } }),
|
|
2290
|
+
/* @__PURE__ */ React15.createElement("span", { className: "flex-1 text-sm" }, cal.label),
|
|
2291
|
+
formData.calendarId === cal.id && /* @__PURE__ */ React15.createElement(Check, { className: "w-4 h-4 text-primary" })
|
|
2292
|
+
)))))))), /* @__PURE__ */ React15.createElement("div", { className: "px-5 py-4 border-t border-border/50 flex items-center justify-between bg-muted/10 shrink-0" }, mode === "edit" && /* @__PURE__ */ React15.createElement(
|
|
1860
2293
|
"button",
|
|
1861
2294
|
{
|
|
1862
2295
|
type: "button",
|
|
1863
2296
|
onClick: handleDelete,
|
|
1864
2297
|
className: "flex items-center gap-2 px-4 py-2 text-destructive hover:bg-destructive/10 rounded-xl transition-all text-sm font-medium"
|
|
1865
2298
|
},
|
|
1866
|
-
/* @__PURE__ */
|
|
2299
|
+
/* @__PURE__ */ React15.createElement(Trash2, { className: "w-4 h-4" }),
|
|
1867
2300
|
"Delete"
|
|
1868
|
-
), mode === "create" && /* @__PURE__ */
|
|
2301
|
+
), mode === "create" && /* @__PURE__ */ React15.createElement("div", null), /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(
|
|
1869
2302
|
"button",
|
|
1870
2303
|
{
|
|
1871
2304
|
type: "button",
|
|
@@ -1873,7 +2306,7 @@ var EventModal = ({
|
|
|
1873
2306
|
className: "px-4 py-2.5 text-muted-foreground hover:text-foreground font-medium text-sm rounded-xl transition-all hover:bg-accent"
|
|
1874
2307
|
},
|
|
1875
2308
|
"Cancel"
|
|
1876
|
-
), /* @__PURE__ */
|
|
2309
|
+
), /* @__PURE__ */ React15.createElement(
|
|
1877
2310
|
"button",
|
|
1878
2311
|
{
|
|
1879
2312
|
type: "submit",
|
|
@@ -1881,8 +2314,8 @@ var EventModal = ({
|
|
|
1881
2314
|
},
|
|
1882
2315
|
mode === "edit" ? "Save Changes" : "Create Event"
|
|
1883
2316
|
))));
|
|
1884
|
-
return /* @__PURE__ */
|
|
1885
|
-
|
|
2317
|
+
return /* @__PURE__ */ React15.createElement(Modal, { isOpen, onClose, hideHeader: true, className: "p-0 overflow-hidden max-w-lg rounded-2xl shadow-2xl" }, /* @__PURE__ */ React15.createElement(AnimatePresence, { mode: "wait" }, mode === "view" && event ? /* @__PURE__ */ React15.createElement(
|
|
2318
|
+
motion2.div,
|
|
1886
2319
|
{
|
|
1887
2320
|
key: "view",
|
|
1888
2321
|
initial: { opacity: 0 },
|
|
@@ -1891,8 +2324,8 @@ var EventModal = ({
|
|
|
1891
2324
|
transition: { duration: 0.15 }
|
|
1892
2325
|
},
|
|
1893
2326
|
renderViewMode()
|
|
1894
|
-
) : /* @__PURE__ */
|
|
1895
|
-
|
|
2327
|
+
) : /* @__PURE__ */ React15.createElement(
|
|
2328
|
+
motion2.div,
|
|
1896
2329
|
{
|
|
1897
2330
|
key: "edit",
|
|
1898
2331
|
initial: { opacity: 0 },
|
|
@@ -1903,9 +2336,12 @@ var EventModal = ({
|
|
|
1903
2336
|
renderEditMode()
|
|
1904
2337
|
)));
|
|
1905
2338
|
};
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
2339
|
+
|
|
2340
|
+
// src/components/Skeleton.tsx
|
|
2341
|
+
import React16 from "react";
|
|
2342
|
+
var Skeleton = ({ className, style }) => /* @__PURE__ */ React16.createElement("div", { className: cn("animate-pulse bg-muted/40 rounded", className), style });
|
|
2343
|
+
var MonthViewSkeleton = () => /* @__PURE__ */ React16.createElement("div", { className: "h-full bg-background border border-border/50 rounded-2xl overflow-hidden" }, /* @__PURE__ */ React16.createElement("div", { className: "grid grid-cols-7 border-b border-border/50 bg-muted/10" }, Array.from({ length: 7 }).map((_, i) => /* @__PURE__ */ React16.createElement("div", { key: i, className: "py-3 px-2 text-center border-r border-border/30 last:border-r-0" }, /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-4 w-8 mx-auto" })))), /* @__PURE__ */ React16.createElement("div", { className: "grid grid-cols-7 flex-1" }, Array.from({ length: 35 }).map((_, i) => /* @__PURE__ */ React16.createElement("div", { key: i, className: "min-h-[120px] border-r border-b border-border/30 p-2" }, /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-4 w-6 mb-2" }), /* @__PURE__ */ React16.createElement("div", { className: "space-y-1" }, Math.random() > 0.5 && /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-5 w-full rounded-md" }), Math.random() > 0.7 && /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-5 w-3/4 rounded-md" }))))));
|
|
2344
|
+
var WeekViewSkeleton = () => /* @__PURE__ */ React16.createElement("div", { className: "h-full bg-background border border-border/50 rounded-2xl overflow-hidden" }, /* @__PURE__ */ React16.createElement("div", { className: "flex border-b border-border/50 bg-muted/10" }, /* @__PURE__ */ React16.createElement("div", { className: "w-16 p-3 border-r border-border/30" }, /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-8 w-10 mx-auto" })), /* @__PURE__ */ React16.createElement("div", { className: "flex-1 grid grid-cols-7" }, Array.from({ length: 7 }).map((_, i) => /* @__PURE__ */ React16.createElement("div", { key: i, className: "py-3 px-2 text-center border-r border-border/30 last:border-r-0" }, /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-3 w-8 mx-auto mb-1" }), /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-8 w-8 mx-auto rounded-xl" }))))), /* @__PURE__ */ React16.createElement("div", { className: "flex flex-1", style: { height: "600px" } }, /* @__PURE__ */ React16.createElement("div", { className: "w-16 border-r border-border/30" }, Array.from({ length: 10 }).map((_, i) => /* @__PURE__ */ React16.createElement("div", { key: i, className: "h-[60px] relative" }, /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-4 w-10 absolute right-2 -translate-y-1/2" })))), /* @__PURE__ */ React16.createElement("div", { className: "flex-1 grid grid-cols-7 relative" }, Array.from({ length: 7 }).map((_, colIdx) => /* @__PURE__ */ React16.createElement("div", { key: colIdx, className: "relative border-r border-border/30 last:border-r-0" }, Array.from({ length: 10 }).map((_2, rowIdx) => /* @__PURE__ */ React16.createElement("div", { key: rowIdx, className: "h-[60px] border-b border-dashed border-border/20" })), Math.random() > 0.3 && /* @__PURE__ */ React16.createElement(
|
|
1909
2345
|
Skeleton,
|
|
1910
2346
|
{
|
|
1911
2347
|
className: "absolute rounded-md",
|
|
@@ -1917,20 +2353,25 @@ var WeekViewSkeleton = () => /* @__PURE__ */ React15__default.createElement("div
|
|
|
1917
2353
|
}
|
|
1918
2354
|
}
|
|
1919
2355
|
))))));
|
|
1920
|
-
var DayViewSkeleton = () => /* @__PURE__ */
|
|
2356
|
+
var DayViewSkeleton = () => /* @__PURE__ */ React16.createElement("div", { className: "h-full bg-background border border-border/50 rounded-2xl overflow-hidden" }, /* @__PURE__ */ React16.createElement("div", { className: "px-6 py-4 border-b border-border/50 bg-muted/10 text-center" }, /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-7 w-64 mx-auto" })), /* @__PURE__ */ React16.createElement("div", { className: "flex flex-1", style: { height: "600px" } }, /* @__PURE__ */ React16.createElement("div", { className: "w-20 border-r border-border/30 bg-muted/5" }, Array.from({ length: 10 }).map((_, i) => /* @__PURE__ */ React16.createElement("div", { key: i, className: "h-[80px] relative" }, /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-4 w-12 absolute left-1/2 -translate-x-1/2 -translate-y-1/2" })))), /* @__PURE__ */ React16.createElement("div", { className: "flex-1 relative" }, Array.from({ length: 10 }).map((_, i) => /* @__PURE__ */ React16.createElement("div", { key: i, className: "h-[80px] border-b border-dashed border-border/20" })), /* @__PURE__ */ React16.createElement(
|
|
1921
2357
|
Skeleton,
|
|
1922
2358
|
{
|
|
1923
2359
|
className: "absolute rounded-lg left-4 right-4",
|
|
1924
2360
|
style: { top: "160px", height: "120px" }
|
|
1925
2361
|
}
|
|
1926
|
-
), /* @__PURE__ */
|
|
2362
|
+
), /* @__PURE__ */ React16.createElement(
|
|
1927
2363
|
Skeleton,
|
|
1928
2364
|
{
|
|
1929
2365
|
className: "absolute rounded-lg left-4 right-4",
|
|
1930
2366
|
style: { top: "400px", height: "80px" }
|
|
1931
2367
|
}
|
|
1932
2368
|
))));
|
|
1933
|
-
var AgendaViewSkeleton = () => /* @__PURE__ */
|
|
2369
|
+
var AgendaViewSkeleton = () => /* @__PURE__ */ React16.createElement("div", { className: "h-full bg-background border border-border/50 rounded-2xl overflow-hidden p-6" }, /* @__PURE__ */ React16.createElement("div", { className: "space-y-6" }, Array.from({ length: 4 }).map((_, dayIdx) => /* @__PURE__ */ React16.createElement("div", { key: dayIdx }, /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-5 w-40 mb-4" }), /* @__PURE__ */ React16.createElement("div", { className: "space-y-3" }, Array.from({ length: 2 + Math.floor(Math.random() * 3) }).map((_2, eventIdx) => /* @__PURE__ */ React16.createElement("div", { key: eventIdx, className: "flex items-center gap-4 p-3 rounded-xl bg-muted/10" }, /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-10 w-10 rounded-lg" }), /* @__PURE__ */ React16.createElement("div", { className: "flex-1 space-y-2" }, /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-4 w-3/4" }), /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-3 w-1/2" })))))))));
|
|
2370
|
+
|
|
2371
|
+
// src/components/ContextMenu.tsx
|
|
2372
|
+
import React17, { useState as useState4, useCallback as useCallback2, useEffect as useEffect4, useRef as useRef4 } from "react";
|
|
2373
|
+
import { Edit3, Trash2 as Trash22, Copy } from "lucide-react";
|
|
2374
|
+
import { AnimatePresence as AnimatePresence2, motion as motion3 } from "framer-motion";
|
|
1934
2375
|
var EventContextMenu = ({
|
|
1935
2376
|
event,
|
|
1936
2377
|
position,
|
|
@@ -1941,8 +2382,8 @@ var EventContextMenu = ({
|
|
|
1941
2382
|
customActions = [],
|
|
1942
2383
|
translations
|
|
1943
2384
|
}) => {
|
|
1944
|
-
const menuRef =
|
|
1945
|
-
|
|
2385
|
+
const menuRef = useRef4(null);
|
|
2386
|
+
useEffect4(() => {
|
|
1946
2387
|
const handleKeyDown = (e) => {
|
|
1947
2388
|
if (e.key === "Escape") {
|
|
1948
2389
|
onClose();
|
|
@@ -1953,7 +2394,7 @@ var EventContextMenu = ({
|
|
|
1953
2394
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
1954
2395
|
}
|
|
1955
2396
|
}, [position, onClose]);
|
|
1956
|
-
|
|
2397
|
+
useEffect4(() => {
|
|
1957
2398
|
const handleClickOutside = (e) => {
|
|
1958
2399
|
if (menuRef.current && !menuRef.current.contains(e.target)) {
|
|
1959
2400
|
onClose();
|
|
@@ -1971,7 +2412,7 @@ var EventContextMenu = ({
|
|
|
1971
2412
|
...onEdit ? [{
|
|
1972
2413
|
id: "edit",
|
|
1973
2414
|
label: translations?.edit || "Edit",
|
|
1974
|
-
icon: /* @__PURE__ */
|
|
2415
|
+
icon: /* @__PURE__ */ React17.createElement(Edit3, { className: "w-4 h-4" }),
|
|
1975
2416
|
onClick: () => {
|
|
1976
2417
|
onEdit(event);
|
|
1977
2418
|
onClose();
|
|
@@ -1980,7 +2421,7 @@ var EventContextMenu = ({
|
|
|
1980
2421
|
...onDuplicate ? [{
|
|
1981
2422
|
id: "duplicate",
|
|
1982
2423
|
label: translations?.duplicate || "Duplicate",
|
|
1983
|
-
icon: /* @__PURE__ */
|
|
2424
|
+
icon: /* @__PURE__ */ React17.createElement(Copy, { className: "w-4 h-4" }),
|
|
1984
2425
|
onClick: () => {
|
|
1985
2426
|
onDuplicate(event);
|
|
1986
2427
|
onClose();
|
|
@@ -1990,7 +2431,7 @@ var EventContextMenu = ({
|
|
|
1990
2431
|
...onDelete ? [{
|
|
1991
2432
|
id: "delete",
|
|
1992
2433
|
label: translations?.delete || "Delete",
|
|
1993
|
-
icon: /* @__PURE__ */
|
|
2434
|
+
icon: /* @__PURE__ */ React17.createElement(Trash22, { className: "w-4 h-4" }),
|
|
1994
2435
|
onClick: () => {
|
|
1995
2436
|
onDelete(event.id);
|
|
1996
2437
|
onClose();
|
|
@@ -2002,8 +2443,8 @@ var EventContextMenu = ({
|
|
|
2002
2443
|
x: Math.min(position.x, window.innerWidth - 200),
|
|
2003
2444
|
y: Math.min(position.y, window.innerHeight - (actions.length * 44 + 80))
|
|
2004
2445
|
};
|
|
2005
|
-
return /* @__PURE__ */
|
|
2006
|
-
|
|
2446
|
+
return /* @__PURE__ */ React17.createElement(AnimatePresence2, null, /* @__PURE__ */ React17.createElement(
|
|
2447
|
+
motion3.div,
|
|
2007
2448
|
{
|
|
2008
2449
|
ref: menuRef,
|
|
2009
2450
|
initial: { opacity: 0, scale: 0.95, y: -5 },
|
|
@@ -2016,14 +2457,14 @@ var EventContextMenu = ({
|
|
|
2016
2457
|
top: adjustedPosition.y
|
|
2017
2458
|
}
|
|
2018
2459
|
},
|
|
2019
|
-
/* @__PURE__ */
|
|
2460
|
+
/* @__PURE__ */ React17.createElement("div", { className: "px-3 py-2 border-b-[0.5px] border-border bg-muted/30" }, /* @__PURE__ */ React17.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React17.createElement(
|
|
2020
2461
|
"div",
|
|
2021
2462
|
{
|
|
2022
2463
|
className: "w-2.5 h-2.5 rounded-full shrink-0",
|
|
2023
2464
|
style: { backgroundColor: event.color || "var(--primary)" }
|
|
2024
2465
|
}
|
|
2025
|
-
), /* @__PURE__ */
|
|
2026
|
-
/* @__PURE__ */
|
|
2466
|
+
), /* @__PURE__ */ React17.createElement("span", { className: "text-sm font-medium truncate text-foreground" }, event.title))),
|
|
2467
|
+
/* @__PURE__ */ React17.createElement("div", { className: "py-1" }, actions.map((action, index) => /* @__PURE__ */ React17.createElement(React17.Fragment, { key: action.id }, index > 0 && action.variant === "danger" && /* @__PURE__ */ React17.createElement("div", { className: "h-px bg-border my-1" }), /* @__PURE__ */ React17.createElement(
|
|
2027
2468
|
"button",
|
|
2028
2469
|
{
|
|
2029
2470
|
onClick: action.onClick,
|
|
@@ -2035,13 +2476,13 @@ var EventContextMenu = ({
|
|
|
2035
2476
|
)
|
|
2036
2477
|
},
|
|
2037
2478
|
action.icon,
|
|
2038
|
-
/* @__PURE__ */
|
|
2479
|
+
/* @__PURE__ */ React17.createElement("span", null, action.label)
|
|
2039
2480
|
))))
|
|
2040
2481
|
));
|
|
2041
2482
|
};
|
|
2042
2483
|
var useEventContextMenu = () => {
|
|
2043
|
-
const [contextMenu, setContextMenu] =
|
|
2044
|
-
const openContextMenu =
|
|
2484
|
+
const [contextMenu, setContextMenu] = useState4({ event: null, position: null });
|
|
2485
|
+
const openContextMenu = useCallback2((event, e) => {
|
|
2045
2486
|
e.preventDefault();
|
|
2046
2487
|
e.stopPropagation();
|
|
2047
2488
|
setContextMenu({
|
|
@@ -2049,7 +2490,7 @@ var useEventContextMenu = () => {
|
|
|
2049
2490
|
position: { x: e.clientX, y: e.clientY }
|
|
2050
2491
|
});
|
|
2051
2492
|
}, []);
|
|
2052
|
-
const closeContextMenu =
|
|
2493
|
+
const closeContextMenu = useCallback2(() => {
|
|
2053
2494
|
setContextMenu({ event: null, position: null });
|
|
2054
2495
|
}, []);
|
|
2055
2496
|
return {
|
|
@@ -2060,146 +2501,6 @@ var useEventContextMenu = () => {
|
|
|
2060
2501
|
};
|
|
2061
2502
|
};
|
|
2062
2503
|
|
|
2063
|
-
// src/locales/index.ts
|
|
2064
|
-
var en = {
|
|
2065
|
-
today: "Today",
|
|
2066
|
-
month: "Month",
|
|
2067
|
-
week: "Week",
|
|
2068
|
-
day: "Day",
|
|
2069
|
-
agenda: "Agenda",
|
|
2070
|
-
resource: "Resources",
|
|
2071
|
-
createEvent: "Create Event",
|
|
2072
|
-
editEvent: "Edit Event",
|
|
2073
|
-
delete: "Delete",
|
|
2074
|
-
save: "Save",
|
|
2075
|
-
cancel: "Cancel",
|
|
2076
|
-
title: "Add title",
|
|
2077
|
-
start: "Start",
|
|
2078
|
-
end: "End",
|
|
2079
|
-
allDay: "All Day",
|
|
2080
|
-
description: "Description",
|
|
2081
|
-
repeat: "Repeat",
|
|
2082
|
-
noRepeat: "Does not repeat",
|
|
2083
|
-
selectCalendar: "Select Calendar",
|
|
2084
|
-
selectType: "Select Type",
|
|
2085
|
-
daily: "Daily",
|
|
2086
|
-
weekly: "Weekly",
|
|
2087
|
-
monthly: "Monthly",
|
|
2088
|
-
yearly: "Yearly",
|
|
2089
|
-
search: "Search events...",
|
|
2090
|
-
allTypes: "All Types",
|
|
2091
|
-
timezone: "Time zone",
|
|
2092
|
-
localTime: "Local Time",
|
|
2093
|
-
create: "Create",
|
|
2094
|
-
calendars: "Calendars",
|
|
2095
|
-
event: "Event",
|
|
2096
|
-
task: "Task",
|
|
2097
|
-
appointmentSchedule: "Appointment schedule",
|
|
2098
|
-
new: "New",
|
|
2099
|
-
dateAndTime: "Date & Time",
|
|
2100
|
-
whosJoining: "Who's joining?",
|
|
2101
|
-
suggestedTimes: "Suggested times for guests",
|
|
2102
|
-
viewSuggestions: "View suggestions",
|
|
2103
|
-
whereWillItBe: "Where will it be?",
|
|
2104
|
-
location: "Enter address",
|
|
2105
|
-
descriptionAndAttachments: "Description",
|
|
2106
|
-
dragAndDrop: "Drag & drop files or",
|
|
2107
|
-
guests: "Guests",
|
|
2108
|
-
addGuests: "Add guests",
|
|
2109
|
-
addAttachment: "Upload files",
|
|
2110
|
-
moreOptions: "More options",
|
|
2111
|
-
doesNotRepeat: "Does not repeat",
|
|
2112
|
-
locationHelpText: 'Enter a physical address (e.g. "123 Main St") to generate the map.',
|
|
2113
|
-
notes: "Notes...",
|
|
2114
|
-
guestCount: "guest",
|
|
2115
|
-
guestsCount: "guests",
|
|
2116
|
-
repeatFor: "Repeat for",
|
|
2117
|
-
times: "times",
|
|
2118
|
-
until: "Until",
|
|
2119
|
-
endRepeat: "End repeat",
|
|
2120
|
-
never: "Never",
|
|
2121
|
-
afterOccurrences: "After occurrences",
|
|
2122
|
-
onDate: "On date",
|
|
2123
|
-
tomorrow: "Tomorrow",
|
|
2124
|
-
edit: "Edit",
|
|
2125
|
-
duplicate: "Duplicate",
|
|
2126
|
-
eventSingular: "event",
|
|
2127
|
-
eventPlural: "events",
|
|
2128
|
-
allCaughtUp: "All caught up!",
|
|
2129
|
-
noEventsScheduled: "No events scheduled",
|
|
2130
|
-
noUpcoming: "Your schedule is clear",
|
|
2131
|
-
noUpcomingHint: "No upcoming events to show. Create a new event to start planning."
|
|
2132
|
-
};
|
|
2133
|
-
var fr = {
|
|
2134
|
-
today: "Aujourd'hui",
|
|
2135
|
-
month: "Mois",
|
|
2136
|
-
week: "Semaine",
|
|
2137
|
-
day: "Jour",
|
|
2138
|
-
agenda: "Agenda",
|
|
2139
|
-
resource: "Ressources",
|
|
2140
|
-
createEvent: "Cr\xE9er un \xE9v\xE9nement",
|
|
2141
|
-
editEvent: "Modifier l'\xE9v\xE9nement",
|
|
2142
|
-
delete: "Supprimer",
|
|
2143
|
-
save: "Enregistrer",
|
|
2144
|
-
cancel: "Annuler",
|
|
2145
|
-
title: "Ajouter un titre",
|
|
2146
|
-
start: "D\xE9but",
|
|
2147
|
-
end: "Fin",
|
|
2148
|
-
allDay: "Toute la journ\xE9e",
|
|
2149
|
-
description: "Description",
|
|
2150
|
-
repeat: "R\xE9p\xE9ter",
|
|
2151
|
-
noRepeat: "Ne se r\xE9p\xE8te pas",
|
|
2152
|
-
selectCalendar: "S\xE9lectionner un calendrier",
|
|
2153
|
-
selectType: "S\xE9lectionner un type",
|
|
2154
|
-
daily: "Quotidien",
|
|
2155
|
-
weekly: "Hebdomadaire",
|
|
2156
|
-
monthly: "Mensuel",
|
|
2157
|
-
yearly: "Annuel",
|
|
2158
|
-
search: "Rechercher...",
|
|
2159
|
-
allTypes: "Tous les types",
|
|
2160
|
-
timezone: "Fuseau horaire",
|
|
2161
|
-
localTime: "Heure locale",
|
|
2162
|
-
create: "Cr\xE9er",
|
|
2163
|
-
calendars: "Calendriers",
|
|
2164
|
-
event: "\xC9v\xE9nement",
|
|
2165
|
-
task: "T\xE2che",
|
|
2166
|
-
appointmentSchedule: "Planning de rendez-vous",
|
|
2167
|
-
new: "Nouveau",
|
|
2168
|
-
dateAndTime: "Date et heure",
|
|
2169
|
-
whosJoining: "Qui participe ?",
|
|
2170
|
-
suggestedTimes: "Horaires sugg\xE9r\xE9s",
|
|
2171
|
-
viewSuggestions: "Voir les suggestions",
|
|
2172
|
-
whereWillItBe: "O\xF9 aura lieu ?",
|
|
2173
|
-
location: "Entrer une adresse",
|
|
2174
|
-
descriptionAndAttachments: "Description",
|
|
2175
|
-
dragAndDrop: "Glisser-d\xE9poser des fichiers ou",
|
|
2176
|
-
guests: "Invit\xE9s",
|
|
2177
|
-
addGuests: "Ajouter des invit\xE9s",
|
|
2178
|
-
addAttachment: "T\xE9l\xE9charger des fichiers",
|
|
2179
|
-
moreOptions: "Plus d'options",
|
|
2180
|
-
doesNotRepeat: "Ne se r\xE9p\xE8te pas",
|
|
2181
|
-
locationHelpText: 'Entrez une adresse physique (ex: "123 Rue Principale") pour g\xE9n\xE9rer la carte.',
|
|
2182
|
-
notes: "Notes...",
|
|
2183
|
-
guestCount: "invit\xE9",
|
|
2184
|
-
guestsCount: "invit\xE9s",
|
|
2185
|
-
repeatFor: "R\xE9p\xE9ter pendant",
|
|
2186
|
-
times: "fois",
|
|
2187
|
-
until: "Jusqu'au",
|
|
2188
|
-
endRepeat: "Fin de r\xE9p\xE9tition",
|
|
2189
|
-
never: "Jamais",
|
|
2190
|
-
afterOccurrences: "Apr\xE8s occurrences",
|
|
2191
|
-
onDate: "\xC0 la date",
|
|
2192
|
-
tomorrow: "Demain",
|
|
2193
|
-
edit: "Modifier",
|
|
2194
|
-
duplicate: "Dupliquer",
|
|
2195
|
-
eventSingular: "\xE9v\xE9nement",
|
|
2196
|
-
eventPlural: "\xE9v\xE9nements",
|
|
2197
|
-
allCaughtUp: "Tout est \xE0 jour !",
|
|
2198
|
-
noEventsScheduled: "Aucun \xE9v\xE9nement pr\xE9vu",
|
|
2199
|
-
noUpcoming: "Votre planning est libre",
|
|
2200
|
-
noUpcomingHint: "Aucun \xE9v\xE9nement \xE0 venir. Cr\xE9ez un nouvel \xE9v\xE9nement pour commencer \xE0 planifier."
|
|
2201
|
-
};
|
|
2202
|
-
|
|
2203
2504
|
// src/lib/theme.ts
|
|
2204
2505
|
function colorToHsl(color) {
|
|
2205
2506
|
let r = 0, g = 0, b = 0;
|
|
@@ -2225,7 +2526,7 @@ function colorToHsl(color) {
|
|
|
2225
2526
|
const min = Math.min(r, g, b);
|
|
2226
2527
|
let h = 0;
|
|
2227
2528
|
let s = 0;
|
|
2228
|
-
|
|
2529
|
+
const l = (max + min) / 2;
|
|
2229
2530
|
if (max !== min) {
|
|
2230
2531
|
const d = max - min;
|
|
2231
2532
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
@@ -2278,6 +2579,162 @@ function getThemeStyles(theme) {
|
|
|
2278
2579
|
}
|
|
2279
2580
|
return styles;
|
|
2280
2581
|
}
|
|
2582
|
+
|
|
2583
|
+
// src/hooks/useCalendarLogic.ts
|
|
2584
|
+
import { useMemo as useMemo3, useState as useState5 } from "react";
|
|
2585
|
+
import {
|
|
2586
|
+
addDays as addDays5,
|
|
2587
|
+
addMonths as addMonths3,
|
|
2588
|
+
addWeeks,
|
|
2589
|
+
endOfMonth as endOfMonth2,
|
|
2590
|
+
format as format13,
|
|
2591
|
+
startOfMonth as startOfMonth3,
|
|
2592
|
+
startOfWeek as startOfWeek2,
|
|
2593
|
+
subDays,
|
|
2594
|
+
subMonths as subMonths2,
|
|
2595
|
+
subWeeks
|
|
2596
|
+
} from "date-fns";
|
|
2597
|
+
|
|
2598
|
+
// src/core/recurrence.ts
|
|
2599
|
+
import { addMonths as addMonths2, startOfMonth as startOfMonth2 } from "date-fns";
|
|
2600
|
+
import { RRule } from "rrule";
|
|
2601
|
+
var WEEKDAYS = [RRule.MO, RRule.TU, RRule.WE, RRule.TH, RRule.FR, RRule.SA, RRule.SU];
|
|
2602
|
+
var toFloating = (instant, tz) => {
|
|
2603
|
+
const w = toDisplay(instant, tz);
|
|
2604
|
+
return new Date(Date.UTC(w.getFullYear(), w.getMonth(), w.getDate(), w.getHours(), w.getMinutes(), w.getSeconds(), w.getMilliseconds()));
|
|
2605
|
+
};
|
|
2606
|
+
var fromFloating = (floating, tz) => fromDisplay(
|
|
2607
|
+
new Date(
|
|
2608
|
+
floating.getUTCFullYear(),
|
|
2609
|
+
floating.getUTCMonth(),
|
|
2610
|
+
floating.getUTCDate(),
|
|
2611
|
+
floating.getUTCHours(),
|
|
2612
|
+
floating.getUTCMinutes(),
|
|
2613
|
+
floating.getUTCSeconds(),
|
|
2614
|
+
floating.getUTCMilliseconds()
|
|
2615
|
+
),
|
|
2616
|
+
tz
|
|
2617
|
+
);
|
|
2618
|
+
var minuteKey = (date) => Math.floor(date.getTime() / 6e4);
|
|
2619
|
+
var FREQUENCIES = { DAILY: RRule.DAILY, WEEKLY: RRule.WEEKLY, MONTHLY: RRule.MONTHLY, YEARLY: RRule.YEARLY };
|
|
2620
|
+
function buildRule(event, tz) {
|
|
2621
|
+
const r = event.recurrence;
|
|
2622
|
+
const freq = FREQUENCIES[r.freq];
|
|
2623
|
+
if (freq === void 0) throw new Error(`unsupported frequency "${r.freq}"`);
|
|
2624
|
+
return new RRule({
|
|
2625
|
+
freq,
|
|
2626
|
+
interval: r.interval || 1,
|
|
2627
|
+
dtstart: toFloating(event.start, tz),
|
|
2628
|
+
until: r.until ? toFloating(r.until, tz) : void 0,
|
|
2629
|
+
count: r.count,
|
|
2630
|
+
byweekday: r.byweekday?.length ? r.byweekday.map((d) => WEEKDAYS[d]) : void 0
|
|
2631
|
+
});
|
|
2632
|
+
}
|
|
2633
|
+
function occurrencesInMonth(event, monthStart, tz) {
|
|
2634
|
+
const rule = buildRule(event, tz);
|
|
2635
|
+
const excluded = new Set((event.recurrence.exdates ?? []).map(minuteKey));
|
|
2636
|
+
const duration = event.end.getTime() - event.start.getTime();
|
|
2637
|
+
const from = toFloating(monthStart, tz);
|
|
2638
|
+
const to = toFloating(addMonths2(monthStart, 1), tz);
|
|
2639
|
+
const result = [];
|
|
2640
|
+
for (const floating of rule.between(from, to, true)) {
|
|
2641
|
+
if (floating.getTime() === to.getTime()) continue;
|
|
2642
|
+
const start = fromFloating(floating, tz);
|
|
2643
|
+
if (excluded.has(minuteKey(start))) continue;
|
|
2644
|
+
result.push({
|
|
2645
|
+
...event,
|
|
2646
|
+
id: `${event.id}-${start.getTime()}`,
|
|
2647
|
+
originalEventId: event.id,
|
|
2648
|
+
occurrenceStart: start,
|
|
2649
|
+
start,
|
|
2650
|
+
end: new Date(start.getTime() + duration)
|
|
2651
|
+
});
|
|
2652
|
+
}
|
|
2653
|
+
return result;
|
|
2654
|
+
}
|
|
2655
|
+
var MAX_MONTHS_PER_EVENT = 12;
|
|
2656
|
+
var occurrenceCache = /* @__PURE__ */ new WeakMap();
|
|
2657
|
+
function cachedMonth(event, monthStart, tz) {
|
|
2658
|
+
let months = occurrenceCache.get(event);
|
|
2659
|
+
if (!months) {
|
|
2660
|
+
months = /* @__PURE__ */ new Map();
|
|
2661
|
+
occurrenceCache.set(event, months);
|
|
2662
|
+
}
|
|
2663
|
+
const key = `${monthStart.getFullYear()}-${monthStart.getMonth()}|${tz ?? ""}`;
|
|
2664
|
+
let occurrences = months.get(key);
|
|
2665
|
+
if (occurrences) {
|
|
2666
|
+
months.delete(key);
|
|
2667
|
+
} else {
|
|
2668
|
+
occurrences = occurrencesInMonth(event, monthStart, tz);
|
|
2669
|
+
if (months.size >= MAX_MONTHS_PER_EVENT) months.delete(months.keys().next().value);
|
|
2670
|
+
}
|
|
2671
|
+
months.set(key, occurrences);
|
|
2672
|
+
return occurrences;
|
|
2673
|
+
}
|
|
2674
|
+
function expandRecurrences(events, range, anchorTz) {
|
|
2675
|
+
const result = [];
|
|
2676
|
+
const firstMonth = startOfMonth2(toDisplay(range.start, anchorTz));
|
|
2677
|
+
const lastMonth = startOfMonth2(toDisplay(range.end, anchorTz));
|
|
2678
|
+
for (const event of events) {
|
|
2679
|
+
if (!event.recurrence) {
|
|
2680
|
+
result.push(event);
|
|
2681
|
+
continue;
|
|
2682
|
+
}
|
|
2683
|
+
try {
|
|
2684
|
+
for (let month = firstMonth; month <= lastMonth; month = addMonths2(month, 1)) {
|
|
2685
|
+
for (const occurrence of cachedMonth(event, fromDisplay(month, anchorTz), anchorTz)) {
|
|
2686
|
+
if (occurrence.end >= range.start && occurrence.start <= range.end) result.push(occurrence);
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
} catch (error) {
|
|
2690
|
+
warnOnce(`invalid-recurrence:${event.id}`, `Event "${event.id}" has an invalid recurrence rule: ${String(error)}`);
|
|
2691
|
+
result.push(event);
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
return result;
|
|
2695
|
+
}
|
|
2696
|
+
function stripOccurrenceFields(event) {
|
|
2697
|
+
const { originalEventId: _originalEventId, occurrenceStart: _occurrenceStart, ...rest } = event;
|
|
2698
|
+
return rest;
|
|
2699
|
+
}
|
|
2700
|
+
var shiftFloating = (instant, deltaMs, tz) => fromFloating(new Date(toFloating(instant, tz).getTime() + deltaMs), tz);
|
|
2701
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
2702
|
+
var floatingDay = (floating) => Math.floor(floating.getTime() / DAY_MS);
|
|
2703
|
+
function shiftSeries(master, occurrence, newStart, newEnd, anchorTz) {
|
|
2704
|
+
const fromFloat = toFloating(occurrence.start, anchorTz);
|
|
2705
|
+
const toFloat = toFloating(newStart, anchorTz);
|
|
2706
|
+
const deltaMs = toFloat.getTime() - fromFloat.getTime();
|
|
2707
|
+
const dayDelta = floatingDay(toFloat) - floatingDay(fromFloat);
|
|
2708
|
+
const start = shiftFloating(master.start, deltaMs, anchorTz);
|
|
2709
|
+
const end = new Date(start.getTime() + (newEnd.getTime() - newStart.getTime()));
|
|
2710
|
+
const recurrence = master.recurrence && {
|
|
2711
|
+
...master.recurrence,
|
|
2712
|
+
byweekday: master.recurrence.byweekday && dayDelta % 7 !== 0 ? master.recurrence.byweekday.map((d) => ((d + dayDelta) % 7 + 7) % 7) : master.recurrence.byweekday,
|
|
2713
|
+
exdates: master.recurrence.exdates?.map((d) => shiftFloating(d, deltaMs, anchorTz))
|
|
2714
|
+
};
|
|
2715
|
+
return stripOccurrenceFields({ ...master, start, end, recurrence });
|
|
2716
|
+
}
|
|
2717
|
+
function excludeOccurrence(master, occurrenceStart) {
|
|
2718
|
+
const recurrence = master.recurrence ?? { freq: "DAILY" };
|
|
2719
|
+
return { ...master, recurrence: { ...recurrence, exdates: [...recurrence.exdates ?? [], occurrenceStart] } };
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2722
|
+
// src/hooks/useStableCallback.ts
|
|
2723
|
+
import { useCallback as useCallback3, useRef as useRef5 } from "react";
|
|
2724
|
+
function useStableCallback(fn) {
|
|
2725
|
+
const ref = useRef5(fn);
|
|
2726
|
+
ref.current = fn;
|
|
2727
|
+
return useCallback3((...args) => ref.current(...args), []);
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2730
|
+
// src/hooks/useCalendarLogic.ts
|
|
2731
|
+
function expansionRange(monthStart) {
|
|
2732
|
+
return {
|
|
2733
|
+
start: startOfWeek2(startOfMonth3(subMonths2(monthStart, 1))),
|
|
2734
|
+
end: addDays5(endOfMonth2(addMonths3(monthStart, 1)), 37)
|
|
2735
|
+
};
|
|
2736
|
+
}
|
|
2737
|
+
var DAY_MS2 = 24 * 60 * 60 * 1e3;
|
|
2281
2738
|
var useCalendarLogic = ({
|
|
2282
2739
|
events,
|
|
2283
2740
|
view: controlledView,
|
|
@@ -2288,72 +2745,47 @@ var useCalendarLogic = ({
|
|
|
2288
2745
|
onEventUpdate,
|
|
2289
2746
|
onEventCreate,
|
|
2290
2747
|
onEventDelete,
|
|
2748
|
+
onEventDrop,
|
|
2749
|
+
onEventResize,
|
|
2750
|
+
onOccurrenceDelete,
|
|
2291
2751
|
readOnly,
|
|
2292
2752
|
timezone
|
|
2293
2753
|
}) => {
|
|
2294
|
-
const [internalView, setInternalView] =
|
|
2295
|
-
const [internalDate, setInternalDate] =
|
|
2296
|
-
const [isSidebarOpen, setIsSidebarOpen] =
|
|
2297
|
-
const [isModalOpen, setIsModalOpen] =
|
|
2298
|
-
const [selectedEvent, setSelectedEvent] =
|
|
2299
|
-
const [modalInitialDate, setModalInitialDate] =
|
|
2754
|
+
const [internalView, setInternalView] = useState5("week");
|
|
2755
|
+
const [internalDate, setInternalDate] = useState5(() => /* @__PURE__ */ new Date());
|
|
2756
|
+
const [isSidebarOpen, setIsSidebarOpen] = useState5(true);
|
|
2757
|
+
const [isModalOpen, setIsModalOpen] = useState5(false);
|
|
2758
|
+
const [selectedEvent, setSelectedEvent] = useState5(null);
|
|
2759
|
+
const [modalInitialDate, setModalInitialDate] = useState5(void 0);
|
|
2760
|
+
const [modalInitialResourceId, setModalInitialResourceId] = useState5(void 0);
|
|
2300
2761
|
const view = controlledView ?? internalView;
|
|
2301
2762
|
const currentDate = controlledDate ?? internalDate;
|
|
2302
|
-
const
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
interval: event.recurrence.interval || 1,
|
|
2312
|
-
dtstart: new Date(event.start),
|
|
2313
|
-
until: event.recurrence.until ? new Date(event.recurrence.until) : void 0,
|
|
2314
|
-
count: event.recurrence.count
|
|
2315
|
-
});
|
|
2316
|
-
const dates = rule.between(rangeStart, rangeEnd);
|
|
2317
|
-
dates.forEach((date) => {
|
|
2318
|
-
const duration = differenceInMilliseconds(new Date(event.end), new Date(event.start));
|
|
2319
|
-
allEvents.push({
|
|
2320
|
-
...event,
|
|
2321
|
-
id: `${event.id}-${date.getTime()}`,
|
|
2322
|
-
// Unique ID for each instance
|
|
2323
|
-
originalEventId: event.id,
|
|
2324
|
-
// Reference to original
|
|
2325
|
-
start: date,
|
|
2326
|
-
end: new Date(date.getTime() + duration)
|
|
2327
|
-
});
|
|
2328
|
-
});
|
|
2329
|
-
} catch (e) {
|
|
2330
|
-
console.error("Error parsing recurrence rule", e);
|
|
2331
|
-
allEvents.push(event);
|
|
2332
|
-
}
|
|
2333
|
-
} else {
|
|
2334
|
-
allEvents.push(event);
|
|
2335
|
-
}
|
|
2336
|
-
});
|
|
2337
|
-
return allEvents;
|
|
2338
|
-
}, [events, currentDate]);
|
|
2339
|
-
const handleViewChange = (newView) => {
|
|
2763
|
+
const normalizedEvents = useMemo3(() => normalizeEvents(events), [events]);
|
|
2764
|
+
const monthKey = format13(currentDate, "yyyy-MM");
|
|
2765
|
+
const range = useMemo3(() => expansionRange(/* @__PURE__ */ new Date(`${monthKey}-01T00:00:00`)), [monthKey]);
|
|
2766
|
+
const expandedEvents = useMemo3(
|
|
2767
|
+
() => expandRecurrences(normalizedEvents, range, timezone),
|
|
2768
|
+
[normalizedEvents, range, timezone]
|
|
2769
|
+
);
|
|
2770
|
+
const masterOf = (occurrence) => normalizedEvents.find((e) => e.id === occurrence.originalEventId);
|
|
2771
|
+
const handleViewChange = useStableCallback((newView) => {
|
|
2340
2772
|
if (controlledOnViewChange) {
|
|
2341
2773
|
controlledOnViewChange(newView);
|
|
2342
2774
|
} else {
|
|
2343
2775
|
setInternalView(newView);
|
|
2344
2776
|
}
|
|
2345
|
-
};
|
|
2346
|
-
const handleDateChange = (newDate) => {
|
|
2777
|
+
});
|
|
2778
|
+
const handleDateChange = useStableCallback((newDate) => {
|
|
2347
2779
|
if (controlledOnDateChange) {
|
|
2348
2780
|
controlledOnDateChange(newDate);
|
|
2349
2781
|
} else {
|
|
2350
2782
|
setInternalDate(newDate);
|
|
2351
2783
|
}
|
|
2352
|
-
};
|
|
2353
|
-
const handlePrev = () => {
|
|
2784
|
+
});
|
|
2785
|
+
const handlePrev = useStableCallback(() => {
|
|
2354
2786
|
switch (view) {
|
|
2355
2787
|
case "month":
|
|
2356
|
-
handleDateChange(
|
|
2788
|
+
handleDateChange(subMonths2(currentDate, 1));
|
|
2357
2789
|
break;
|
|
2358
2790
|
case "week":
|
|
2359
2791
|
handleDateChange(subWeeks(currentDate, 1));
|
|
@@ -2366,133 +2798,139 @@ var useCalendarLogic = ({
|
|
|
2366
2798
|
handleDateChange(subDays(currentDate, 7));
|
|
2367
2799
|
break;
|
|
2368
2800
|
}
|
|
2369
|
-
};
|
|
2370
|
-
const handleNext = () => {
|
|
2801
|
+
});
|
|
2802
|
+
const handleNext = useStableCallback(() => {
|
|
2371
2803
|
switch (view) {
|
|
2372
2804
|
case "month":
|
|
2373
|
-
handleDateChange(
|
|
2805
|
+
handleDateChange(addMonths3(currentDate, 1));
|
|
2374
2806
|
break;
|
|
2375
2807
|
case "week":
|
|
2376
2808
|
handleDateChange(addWeeks(currentDate, 1));
|
|
2377
2809
|
break;
|
|
2378
2810
|
case "day":
|
|
2379
2811
|
case "resource":
|
|
2380
|
-
handleDateChange(
|
|
2812
|
+
handleDateChange(addDays5(currentDate, 1));
|
|
2381
2813
|
break;
|
|
2382
2814
|
case "agenda":
|
|
2383
|
-
handleDateChange(
|
|
2815
|
+
handleDateChange(addDays5(currentDate, 7));
|
|
2384
2816
|
break;
|
|
2385
2817
|
}
|
|
2386
|
-
};
|
|
2387
|
-
const handleToday = () => {
|
|
2818
|
+
});
|
|
2819
|
+
const handleToday = useStableCallback(() => {
|
|
2388
2820
|
handleDateChange(/* @__PURE__ */ new Date());
|
|
2389
|
-
};
|
|
2390
|
-
const handleDateClick = (date) => {
|
|
2821
|
+
});
|
|
2822
|
+
const handleDateClick = useStableCallback((date) => {
|
|
2391
2823
|
handleDateChange(date);
|
|
2392
2824
|
handleViewChange("day");
|
|
2393
|
-
};
|
|
2394
|
-
const handleTimeSlotClick = (date) => {
|
|
2825
|
+
});
|
|
2826
|
+
const handleTimeSlotClick = useStableCallback((date, resourceId) => {
|
|
2395
2827
|
if (readOnly) return;
|
|
2396
2828
|
setSelectedEvent(null);
|
|
2397
2829
|
setModalInitialDate(date);
|
|
2830
|
+
setModalInitialResourceId(resourceId);
|
|
2398
2831
|
setIsModalOpen(true);
|
|
2399
|
-
};
|
|
2400
|
-
const handleEventClickInternal = (event) => {
|
|
2401
|
-
|
|
2402
|
-
onEventClick(event);
|
|
2403
|
-
}
|
|
2832
|
+
});
|
|
2833
|
+
const handleEventClickInternal = useStableCallback((event) => {
|
|
2834
|
+
onEventClick?.(event);
|
|
2404
2835
|
if (!readOnly) {
|
|
2405
2836
|
setSelectedEvent(event);
|
|
2406
2837
|
setModalInitialDate(void 0);
|
|
2838
|
+
setModalInitialResourceId(void 0);
|
|
2407
2839
|
setIsModalOpen(true);
|
|
2408
2840
|
}
|
|
2409
|
-
};
|
|
2410
|
-
const handleCreateEvent = () => {
|
|
2841
|
+
});
|
|
2842
|
+
const handleCreateEvent = useStableCallback(() => {
|
|
2411
2843
|
if (readOnly) return;
|
|
2412
2844
|
setSelectedEvent(null);
|
|
2413
2845
|
setModalInitialDate(/* @__PURE__ */ new Date());
|
|
2846
|
+
setModalInitialResourceId(void 0);
|
|
2414
2847
|
setIsModalOpen(true);
|
|
2415
|
-
};
|
|
2416
|
-
const handleModalSave = (eventData) => {
|
|
2417
|
-
let effectiveEventId = selectedEvent?.id;
|
|
2848
|
+
});
|
|
2849
|
+
const handleModalSave = useStableCallback((eventData) => {
|
|
2418
2850
|
if (selectedEvent?.originalEventId) {
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
}
|
|
2851
|
+
const master = masterOf(selectedEvent);
|
|
2852
|
+
if (!master) return;
|
|
2853
|
+
const edited = {
|
|
2854
|
+
...master,
|
|
2855
|
+
...stripOccurrenceFields(eventData),
|
|
2856
|
+
id: master.id,
|
|
2857
|
+
start: master.start,
|
|
2858
|
+
end: master.end
|
|
2859
|
+
};
|
|
2860
|
+
onEventUpdate?.(
|
|
2861
|
+
shiftSeries(edited, selectedEvent, eventData.start ?? selectedEvent.start, eventData.end ?? selectedEvent.end, timezone)
|
|
2862
|
+
);
|
|
2863
|
+
} else if (selectedEvent) {
|
|
2864
|
+
onEventUpdate?.({ ...eventData, id: selectedEvent.id });
|
|
2428
2865
|
} else {
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
}
|
|
2433
|
-
};
|
|
2434
|
-
const handleModalDelete = (eventId) => {
|
|
2435
|
-
let effectiveEventId = eventId;
|
|
2436
|
-
if (selectedEvent?.originalEventId && selectedEvent.id === eventId) {
|
|
2437
|
-
effectiveEventId = selectedEvent.originalEventId;
|
|
2866
|
+
onEventCreate?.(
|
|
2867
|
+
modalInitialResourceId && eventData.resourceId === void 0 ? { ...eventData, resourceId: modalInitialResourceId } : eventData
|
|
2868
|
+
);
|
|
2438
2869
|
}
|
|
2439
|
-
|
|
2440
|
-
|
|
2870
|
+
});
|
|
2871
|
+
const handleEventDelete = useStableCallback((event) => {
|
|
2872
|
+
if (!event.originalEventId) {
|
|
2873
|
+
onEventDelete?.(event.id);
|
|
2874
|
+
return;
|
|
2441
2875
|
}
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
if (!over) return;
|
|
2446
|
-
const activeEvent = active.data.current?.event;
|
|
2447
|
-
const overDate = over.data.current?.date;
|
|
2448
|
-
if (!activeEvent || !overDate) return;
|
|
2449
|
-
const originalStart = new Date(activeEvent.start);
|
|
2450
|
-
const originalEnd = new Date(activeEvent.end);
|
|
2451
|
-
const duration = differenceInMilliseconds(originalEnd, originalStart);
|
|
2452
|
-
let newStart;
|
|
2453
|
-
if (view === "month") {
|
|
2454
|
-
if (timezone) {
|
|
2455
|
-
const zonedOriginal = toZonedTime(originalStart, timezone);
|
|
2456
|
-
const zonedNew = new Date(overDate);
|
|
2457
|
-
zonedNew.setHours(zonedOriginal.getHours());
|
|
2458
|
-
zonedNew.setMinutes(zonedOriginal.getMinutes());
|
|
2459
|
-
zonedNew.setSeconds(zonedOriginal.getSeconds());
|
|
2460
|
-
zonedNew.setMilliseconds(zonedOriginal.getMilliseconds());
|
|
2461
|
-
newStart = fromZonedTime(zonedNew, timezone);
|
|
2462
|
-
} else {
|
|
2463
|
-
newStart = new Date(overDate);
|
|
2464
|
-
newStart.setHours(originalStart.getHours());
|
|
2465
|
-
newStart.setMinutes(originalStart.getMinutes());
|
|
2466
|
-
newStart.setSeconds(originalStart.getSeconds());
|
|
2467
|
-
newStart.setMilliseconds(originalStart.getMilliseconds());
|
|
2468
|
-
}
|
|
2876
|
+
const master = masterOf(event);
|
|
2877
|
+
if (onOccurrenceDelete && master) {
|
|
2878
|
+
onOccurrenceDelete(event, excludeOccurrence(master, event.occurrenceStart ?? event.start));
|
|
2469
2879
|
} else {
|
|
2470
|
-
|
|
2471
|
-
newStart = fromZonedTime(overDate, timezone);
|
|
2472
|
-
} else {
|
|
2473
|
-
newStart = new Date(overDate);
|
|
2474
|
-
}
|
|
2475
|
-
newStart.setSeconds(0);
|
|
2476
|
-
newStart.setMilliseconds(0);
|
|
2477
|
-
}
|
|
2478
|
-
const newEnd = new Date(newStart.getTime() + duration);
|
|
2479
|
-
let newResourceId = activeEvent.resourceId;
|
|
2480
|
-
const overResourceId = over.data.current?.resourceId;
|
|
2481
|
-
if (overResourceId) {
|
|
2482
|
-
newResourceId = overResourceId;
|
|
2483
|
-
}
|
|
2484
|
-
if (newStart.getTime() === originalStart.getTime() && newEnd.getTime() === originalEnd.getTime() && newResourceId === activeEvent.resourceId) {
|
|
2485
|
-
return;
|
|
2880
|
+
onEventDelete?.(event.originalEventId);
|
|
2486
2881
|
}
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
});
|
|
2882
|
+
});
|
|
2883
|
+
const handleModalDelete = useStableCallback((eventId) => {
|
|
2884
|
+
if (selectedEvent && selectedEvent.id === eventId) {
|
|
2885
|
+
handleEventDelete(selectedEvent);
|
|
2886
|
+
} else {
|
|
2887
|
+
onEventDelete?.(eventId);
|
|
2494
2888
|
}
|
|
2889
|
+
});
|
|
2890
|
+
const handleEventDuplicate = useStableCallback((event) => {
|
|
2891
|
+
const { recurrence: _recurrence, ...copy } = stripOccurrenceFields(event);
|
|
2892
|
+
onEventCreate?.({
|
|
2893
|
+
...copy,
|
|
2894
|
+
id: `${event.originalEventId ?? event.id}-copy-${Date.now()}`,
|
|
2895
|
+
title: `${event.title} (Copy)`,
|
|
2896
|
+
start: new Date(event.start.getTime() + DAY_MS2),
|
|
2897
|
+
end: new Date(event.end.getTime() + DAY_MS2)
|
|
2898
|
+
});
|
|
2899
|
+
});
|
|
2900
|
+
const applyChange = (event, start, end) => {
|
|
2901
|
+
if (!event.originalEventId) return { original: event, updated: { ...event, start, end } };
|
|
2902
|
+
const master = masterOf(event);
|
|
2903
|
+
if (!master) return null;
|
|
2904
|
+
return { original: master, updated: shiftSeries(master, event, start, end, timezone) };
|
|
2495
2905
|
};
|
|
2906
|
+
const handleDragEnd = useStableCallback((dragEvent) => {
|
|
2907
|
+
const { active, over } = dragEvent;
|
|
2908
|
+
if (!over) return;
|
|
2909
|
+
const dragged = active.data.current?.event;
|
|
2910
|
+
const overDate = over.data.current?.date;
|
|
2911
|
+
if (!dragged || !overDate) return;
|
|
2912
|
+
const drop = computeDrop({
|
|
2913
|
+
event: dragged,
|
|
2914
|
+
overDate,
|
|
2915
|
+
overResourceId: over.data.current?.resourceId,
|
|
2916
|
+
grabOffsetMs: active.data.current?.grabOffsetMs,
|
|
2917
|
+
view,
|
|
2918
|
+
tz: timezone
|
|
2919
|
+
});
|
|
2920
|
+
if (!drop) return;
|
|
2921
|
+
const change = applyChange(dragged, drop.start, drop.end);
|
|
2922
|
+
if (!change) return;
|
|
2923
|
+
const updated = { ...change.updated, resourceId: drop.resourceId };
|
|
2924
|
+
onEventDrop?.(change.original, updated.start, updated.end);
|
|
2925
|
+
onEventUpdate?.(updated);
|
|
2926
|
+
});
|
|
2927
|
+
const handleEventResize = useStableCallback((event, newEnd) => {
|
|
2928
|
+
if (readOnly) return;
|
|
2929
|
+
const change = applyChange(event, event.start, newEnd);
|
|
2930
|
+
if (!change) return;
|
|
2931
|
+
onEventResize?.(change.original, change.updated.start, change.updated.end);
|
|
2932
|
+
onEventUpdate?.(change.updated);
|
|
2933
|
+
});
|
|
2496
2934
|
return {
|
|
2497
2935
|
view,
|
|
2498
2936
|
currentDate,
|
|
@@ -2514,9 +2952,18 @@ var useCalendarLogic = ({
|
|
|
2514
2952
|
handleCreateEvent,
|
|
2515
2953
|
handleModalSave,
|
|
2516
2954
|
handleModalDelete,
|
|
2955
|
+
handleEventDelete,
|
|
2956
|
+
handleEventDuplicate,
|
|
2957
|
+
handleEventResize,
|
|
2517
2958
|
handleDragEnd
|
|
2518
2959
|
};
|
|
2519
2960
|
};
|
|
2961
|
+
|
|
2962
|
+
// src/index.tsx
|
|
2963
|
+
import { differenceInMinutes as differenceInMinutes6, format as format14 } from "date-fns";
|
|
2964
|
+
|
|
2965
|
+
// src/hooks/useSwipeGesture.ts
|
|
2966
|
+
import { useRef as useRef6, useCallback as useCallback4, useEffect as useEffect5 } from "react";
|
|
2520
2967
|
var useSwipeGesture = (options) => {
|
|
2521
2968
|
const {
|
|
2522
2969
|
onSwipeLeft,
|
|
@@ -2528,9 +2975,9 @@ var useSwipeGesture = (options) => {
|
|
|
2528
2975
|
allowedTime = 500,
|
|
2529
2976
|
enabled = true
|
|
2530
2977
|
} = options;
|
|
2531
|
-
const touchInfoRef =
|
|
2532
|
-
const elementRef =
|
|
2533
|
-
const handleTouchStart =
|
|
2978
|
+
const touchInfoRef = useRef6(null);
|
|
2979
|
+
const elementRef = useRef6(null);
|
|
2980
|
+
const handleTouchStart = useCallback4((e) => {
|
|
2534
2981
|
if (!enabled) return;
|
|
2535
2982
|
const touch = e.touches[0];
|
|
2536
2983
|
touchInfoRef.current = {
|
|
@@ -2539,7 +2986,7 @@ var useSwipeGesture = (options) => {
|
|
|
2539
2986
|
startTime: Date.now()
|
|
2540
2987
|
};
|
|
2541
2988
|
}, [enabled]);
|
|
2542
|
-
const handleTouchEnd =
|
|
2989
|
+
const handleTouchEnd = useCallback4((e) => {
|
|
2543
2990
|
if (!enabled || !touchInfoRef.current) return;
|
|
2544
2991
|
const touch = e.changedTouches[0];
|
|
2545
2992
|
const { startX, startY, startTime } = touchInfoRef.current;
|
|
@@ -2563,7 +3010,7 @@ var useSwipeGesture = (options) => {
|
|
|
2563
3010
|
}
|
|
2564
3011
|
touchInfoRef.current = null;
|
|
2565
3012
|
}, [enabled, threshold, restraint, allowedTime, onSwipeLeft, onSwipeRight, onSwipeUp, onSwipeDown]);
|
|
2566
|
-
|
|
3013
|
+
useEffect5(() => {
|
|
2567
3014
|
const element = elementRef.current;
|
|
2568
3015
|
if (!element) return;
|
|
2569
3016
|
element.addEventListener("touchstart", handleTouchStart, { passive: true });
|
|
@@ -2597,7 +3044,9 @@ var Scheduler = ({
|
|
|
2597
3044
|
onEventCreate,
|
|
2598
3045
|
onEventUpdate,
|
|
2599
3046
|
onEventDelete,
|
|
3047
|
+
onEventDrop,
|
|
2600
3048
|
onEventResize,
|
|
3049
|
+
onOccurrenceDelete,
|
|
2601
3050
|
timezone,
|
|
2602
3051
|
onTimezoneChange,
|
|
2603
3052
|
className,
|
|
@@ -2619,7 +3068,7 @@ var Scheduler = ({
|
|
|
2619
3068
|
locale
|
|
2620
3069
|
// Date-fns locale
|
|
2621
3070
|
}) => {
|
|
2622
|
-
const [activeDragEvent, setActiveDragEvent] =
|
|
3071
|
+
const [activeDragEvent, setActiveDragEvent] = useState6(null);
|
|
2623
3072
|
const {
|
|
2624
3073
|
contextMenuEvent,
|
|
2625
3074
|
contextMenuPosition,
|
|
@@ -2645,9 +3094,11 @@ var Scheduler = ({
|
|
|
2645
3094
|
handleCreateEvent,
|
|
2646
3095
|
handleModalSave,
|
|
2647
3096
|
handleModalDelete,
|
|
3097
|
+
handleEventDelete,
|
|
3098
|
+
handleEventDuplicate,
|
|
3099
|
+
handleEventResize,
|
|
2648
3100
|
handleDragEnd,
|
|
2649
3101
|
expandedEvents
|
|
2650
|
-
// New export
|
|
2651
3102
|
} = useCalendarLogic({
|
|
2652
3103
|
events,
|
|
2653
3104
|
view: controlledView,
|
|
@@ -2658,6 +3109,9 @@ var Scheduler = ({
|
|
|
2658
3109
|
onEventUpdate,
|
|
2659
3110
|
onEventCreate,
|
|
2660
3111
|
onEventDelete,
|
|
3112
|
+
onEventDrop,
|
|
3113
|
+
onEventResize,
|
|
3114
|
+
onOccurrenceDelete,
|
|
2661
3115
|
readOnly,
|
|
2662
3116
|
timezone
|
|
2663
3117
|
});
|
|
@@ -2672,57 +3126,40 @@ var Scheduler = ({
|
|
|
2672
3126
|
const snapToGrid = createSnapModifier(gridSize);
|
|
2673
3127
|
const modifiers = [snapToGrid, restrictToWindowEdges];
|
|
2674
3128
|
const dndSensors = readOnly ? [] : sensors;
|
|
2675
|
-
const handleEventResize = (event, newEnd) => {
|
|
2676
|
-
if (readOnly) return;
|
|
2677
|
-
if (onEventResize) {
|
|
2678
|
-
onEventResize(event, event.start, newEnd);
|
|
2679
|
-
}
|
|
2680
|
-
if (onEventUpdate) {
|
|
2681
|
-
onEventUpdate({
|
|
2682
|
-
...event,
|
|
2683
|
-
end: newEnd
|
|
2684
|
-
});
|
|
2685
|
-
}
|
|
2686
|
-
};
|
|
2687
3129
|
const id = useId();
|
|
2688
3130
|
const swipeRef = useViewSwipe(handlePrev, handleNext, true);
|
|
2689
|
-
const
|
|
2690
|
-
const
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
};
|
|
2694
|
-
const overlayTimeFormat = uses24HourClock(locale) ? "H:mm" : "h:mm a";
|
|
2695
|
-
const handleDragStart = (event) => {
|
|
2696
|
-
const { active } = event;
|
|
2697
|
-
const draggedEvent = expandedEvents.find((e) => e.id === active.id);
|
|
3131
|
+
const t = useMemo4(() => resolveTranslations(language, translations), [language, translations]);
|
|
3132
|
+
const overlayTimeFormat = timeFormats(locale).time;
|
|
3133
|
+
const handleDragStart = useCallback5((event) => {
|
|
3134
|
+
const draggedEvent = event.active.data.current?.event;
|
|
2698
3135
|
if (draggedEvent) {
|
|
2699
3136
|
setActiveDragEvent(draggedEvent);
|
|
2700
3137
|
}
|
|
2701
|
-
};
|
|
2702
|
-
const onDragEndWrapper = (event) => {
|
|
3138
|
+
}, []);
|
|
3139
|
+
const onDragEndWrapper = useCallback5((event) => {
|
|
2703
3140
|
setActiveDragEvent(null);
|
|
2704
3141
|
handleDragEnd(event);
|
|
2705
|
-
};
|
|
3142
|
+
}, [handleDragEnd]);
|
|
2706
3143
|
const getDragHeight = () => {
|
|
2707
3144
|
if (!activeDragEvent) return void 0;
|
|
2708
3145
|
if (view === "resource") {
|
|
2709
3146
|
return 80;
|
|
2710
3147
|
}
|
|
2711
3148
|
if (view !== "week" && view !== "day") return void 0;
|
|
2712
|
-
const duration =
|
|
3149
|
+
const duration = differenceInMinutes6(activeDragEvent.end, activeDragEvent.start);
|
|
2713
3150
|
const hourHeight = view === "day" ? 80 : 60;
|
|
2714
3151
|
return duration / 60 * hourHeight;
|
|
2715
3152
|
};
|
|
2716
3153
|
const getDragWidth = () => {
|
|
2717
3154
|
if (view === "month") return "100%";
|
|
2718
3155
|
if (view === "resource" && activeDragEvent) {
|
|
2719
|
-
const duration =
|
|
3156
|
+
const duration = differenceInMinutes6(activeDragEvent.end, activeDragEvent.start);
|
|
2720
3157
|
const width = duration / 60 * 100;
|
|
2721
3158
|
return `${Math.max(width, 4)}px`;
|
|
2722
3159
|
}
|
|
2723
3160
|
return "150px";
|
|
2724
3161
|
};
|
|
2725
|
-
const filteredEvents =
|
|
3162
|
+
const filteredEvents = useMemo4(() => {
|
|
2726
3163
|
if (!calendars) return expandedEvents;
|
|
2727
3164
|
const activeCalendarIds = calendars.filter((c) => c.active !== false).map((c) => c.id);
|
|
2728
3165
|
return expandedEvents.filter((e) => {
|
|
@@ -2730,7 +3167,7 @@ var Scheduler = ({
|
|
|
2730
3167
|
return activeCalendarIds.includes(e.calendarId);
|
|
2731
3168
|
});
|
|
2732
3169
|
}, [expandedEvents, calendars]);
|
|
2733
|
-
return /* @__PURE__ */
|
|
3170
|
+
return /* @__PURE__ */ React18.createElement(
|
|
2734
3171
|
DndContext,
|
|
2735
3172
|
{
|
|
2736
3173
|
id,
|
|
@@ -2739,13 +3176,13 @@ var Scheduler = ({
|
|
|
2739
3176
|
onDragEnd: onDragEndWrapper,
|
|
2740
3177
|
modifiers
|
|
2741
3178
|
},
|
|
2742
|
-
/* @__PURE__ */
|
|
3179
|
+
/* @__PURE__ */ React18.createElement(
|
|
2743
3180
|
"div",
|
|
2744
3181
|
{
|
|
2745
3182
|
className: cn("flex flex-col h-full bg-background text-foreground relative", className),
|
|
2746
3183
|
style: getThemeStyles(theme)
|
|
2747
3184
|
},
|
|
2748
|
-
/* @__PURE__ */
|
|
3185
|
+
/* @__PURE__ */ React18.createElement(
|
|
2749
3186
|
CalendarHeader,
|
|
2750
3187
|
{
|
|
2751
3188
|
currentDate,
|
|
@@ -2764,8 +3201,8 @@ var Scheduler = ({
|
|
|
2764
3201
|
locale
|
|
2765
3202
|
}
|
|
2766
3203
|
),
|
|
2767
|
-
/* @__PURE__ */
|
|
2768
|
-
|
|
3204
|
+
/* @__PURE__ */ React18.createElement("div", { className: "flex flex-1 overflow-hidden" }, /* @__PURE__ */ React18.createElement(
|
|
3205
|
+
motion4.div,
|
|
2769
3206
|
{
|
|
2770
3207
|
className: cn(
|
|
2771
3208
|
"hidden md:block overflow-hidden"
|
|
@@ -2780,7 +3217,7 @@ var Scheduler = ({
|
|
|
2780
3217
|
ease: [0.4, 0, 0.2, 1]
|
|
2781
3218
|
}
|
|
2782
3219
|
},
|
|
2783
|
-
/* @__PURE__ */
|
|
3220
|
+
/* @__PURE__ */ React18.createElement(
|
|
2784
3221
|
Sidebar,
|
|
2785
3222
|
{
|
|
2786
3223
|
currentDate,
|
|
@@ -2797,8 +3234,8 @@ var Scheduler = ({
|
|
|
2797
3234
|
locale
|
|
2798
3235
|
}
|
|
2799
3236
|
)
|
|
2800
|
-
), /* @__PURE__ */
|
|
2801
|
-
|
|
3237
|
+
), /* @__PURE__ */ React18.createElement("div", { className: "flex-1 flex flex-col overflow-hidden relative" }, isLoading ? /* @__PURE__ */ React18.createElement("div", { className: "flex-1 overflow-auto p-0 md:p-4" }, /* @__PURE__ */ React18.createElement("div", { className: "h-full min-w-full" }, view === "month" && /* @__PURE__ */ React18.createElement(MonthViewSkeleton, null), view === "week" && /* @__PURE__ */ React18.createElement(WeekViewSkeleton, null), view === "day" && /* @__PURE__ */ React18.createElement(DayViewSkeleton, null), view === "agenda" && /* @__PURE__ */ React18.createElement(AgendaViewSkeleton, null), view === "resource" && /* @__PURE__ */ React18.createElement(WeekViewSkeleton, null))) : /* @__PURE__ */ React18.createElement("div", { ref: swipeRef, className: "flex-1 overflow-auto p-0 md:p-4 touch-pan-y" }, /* @__PURE__ */ React18.createElement("div", { className: "h-full min-w-full" }, /* @__PURE__ */ React18.createElement(AnimatePresence3, { mode: "wait", initial: false }, /* @__PURE__ */ React18.createElement(
|
|
3238
|
+
motion4.div,
|
|
2802
3239
|
{
|
|
2803
3240
|
key: `${view}-${currentDate.toISOString()}-${timezone || "local"}`,
|
|
2804
3241
|
initial: { opacity: 0, scale: 0.98, y: 15 },
|
|
@@ -2822,7 +3259,7 @@ var Scheduler = ({
|
|
|
2822
3259
|
},
|
|
2823
3260
|
className: "h-full"
|
|
2824
3261
|
},
|
|
2825
|
-
view === "month" && /* @__PURE__ */
|
|
3262
|
+
view === "month" && /* @__PURE__ */ React18.createElement(
|
|
2826
3263
|
MonthView,
|
|
2827
3264
|
{
|
|
2828
3265
|
currentDate,
|
|
@@ -2833,7 +3270,7 @@ var Scheduler = ({
|
|
|
2833
3270
|
locale
|
|
2834
3271
|
}
|
|
2835
3272
|
),
|
|
2836
|
-
view === "week" && /* @__PURE__ */
|
|
3273
|
+
view === "week" && /* @__PURE__ */ React18.createElement(
|
|
2837
3274
|
WeekView,
|
|
2838
3275
|
{
|
|
2839
3276
|
currentDate,
|
|
@@ -2846,7 +3283,7 @@ var Scheduler = ({
|
|
|
2846
3283
|
readonly: readOnly
|
|
2847
3284
|
}
|
|
2848
3285
|
),
|
|
2849
|
-
view === "day" && /* @__PURE__ */
|
|
3286
|
+
view === "day" && /* @__PURE__ */ React18.createElement(
|
|
2850
3287
|
DayView,
|
|
2851
3288
|
{
|
|
2852
3289
|
currentDate,
|
|
@@ -2859,7 +3296,7 @@ var Scheduler = ({
|
|
|
2859
3296
|
readonly: readOnly
|
|
2860
3297
|
}
|
|
2861
3298
|
),
|
|
2862
|
-
view === "agenda" && /* @__PURE__ */
|
|
3299
|
+
view === "agenda" && /* @__PURE__ */ React18.createElement(
|
|
2863
3300
|
AgendaView,
|
|
2864
3301
|
{
|
|
2865
3302
|
currentDate,
|
|
@@ -2867,30 +3304,29 @@ var Scheduler = ({
|
|
|
2867
3304
|
onEventClick: handleEventClickInternal,
|
|
2868
3305
|
onCreateEvent: handleCreateEvent,
|
|
2869
3306
|
locale,
|
|
2870
|
-
translations: t
|
|
3307
|
+
translations: t,
|
|
3308
|
+
timezone
|
|
2871
3309
|
}
|
|
2872
3310
|
),
|
|
2873
|
-
view === "resource" && resources && /* @__PURE__ */
|
|
3311
|
+
view === "resource" && resources && /* @__PURE__ */ React18.createElement(
|
|
2874
3312
|
ResourceView,
|
|
2875
3313
|
{
|
|
2876
3314
|
currentDate,
|
|
2877
3315
|
events: filteredEvents,
|
|
2878
3316
|
resources,
|
|
2879
3317
|
onEventClick: handleEventClickInternal,
|
|
2880
|
-
onTimeSlotClick:
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
},
|
|
2884
|
-
locale
|
|
3318
|
+
onTimeSlotClick: handleTimeSlotClick,
|
|
3319
|
+
locale,
|
|
3320
|
+
timezone
|
|
2885
3321
|
}
|
|
2886
3322
|
)
|
|
2887
|
-
)))), /* @__PURE__ */
|
|
3323
|
+
)))), /* @__PURE__ */ React18.createElement("div", { className: "md:hidden absolute bottom-6 right-6 z-50" }, /* @__PURE__ */ React18.createElement(
|
|
2888
3324
|
"button",
|
|
2889
3325
|
{
|
|
2890
3326
|
onClick: handleCreateEvent,
|
|
2891
3327
|
className: "w-14 h-14 bg-blue-600 rounded-full shadow-lg flex items-center justify-center text-white active:scale-90 transition-transform"
|
|
2892
3328
|
},
|
|
2893
|
-
/* @__PURE__ */
|
|
3329
|
+
/* @__PURE__ */ React18.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React18.createElement("path", { d: "M5 12h14" }), /* @__PURE__ */ React18.createElement("path", { d: "M12 5v14" }))
|
|
2894
3330
|
)))),
|
|
2895
3331
|
renderEventForm ? renderEventForm({
|
|
2896
3332
|
isOpen: isModalOpen,
|
|
@@ -2899,7 +3335,7 @@ var Scheduler = ({
|
|
|
2899
3335
|
initialDate: modalInitialDate,
|
|
2900
3336
|
onSave: handleModalSave,
|
|
2901
3337
|
onDelete: handleModalDelete
|
|
2902
|
-
}) : /* @__PURE__ */
|
|
3338
|
+
}) : /* @__PURE__ */ React18.createElement(
|
|
2903
3339
|
EventModal,
|
|
2904
3340
|
{
|
|
2905
3341
|
isOpen: isModalOpen,
|
|
@@ -2913,7 +3349,7 @@ var Scheduler = ({
|
|
|
2913
3349
|
translations: t
|
|
2914
3350
|
}
|
|
2915
3351
|
),
|
|
2916
|
-
/* @__PURE__ */
|
|
3352
|
+
/* @__PURE__ */ React18.createElement(
|
|
2917
3353
|
EventContextMenu,
|
|
2918
3354
|
{
|
|
2919
3355
|
event: contextMenuEvent,
|
|
@@ -2923,20 +3359,12 @@ var Scheduler = ({
|
|
|
2923
3359
|
handleEventClickInternal(event);
|
|
2924
3360
|
closeContextMenu();
|
|
2925
3361
|
},
|
|
2926
|
-
onDelete: (
|
|
2927
|
-
|
|
3362
|
+
onDelete: () => {
|
|
3363
|
+
if (contextMenuEvent) handleEventDelete(contextMenuEvent);
|
|
2928
3364
|
closeContextMenu();
|
|
2929
3365
|
},
|
|
2930
3366
|
onDuplicate: (event) => {
|
|
2931
|
-
|
|
2932
|
-
...event,
|
|
2933
|
-
id: `${event.id}-copy-${Date.now()}`,
|
|
2934
|
-
title: `${event.title} (Copy)`,
|
|
2935
|
-
start: new Date(event.start.getTime() + 24 * 60 * 60 * 1e3),
|
|
2936
|
-
// +1 day
|
|
2937
|
-
end: new Date(event.end.getTime() + 24 * 60 * 60 * 1e3)
|
|
2938
|
-
};
|
|
2939
|
-
onEventCreate?.(duplicatedEvent);
|
|
3367
|
+
handleEventDuplicate(event);
|
|
2940
3368
|
closeContextMenu();
|
|
2941
3369
|
},
|
|
2942
3370
|
translations: {
|
|
@@ -2946,7 +3374,7 @@ var Scheduler = ({
|
|
|
2946
3374
|
}
|
|
2947
3375
|
}
|
|
2948
3376
|
),
|
|
2949
|
-
/* @__PURE__ */
|
|
3377
|
+
/* @__PURE__ */ React18.createElement(DragOverlay, { dropAnimation: null }, activeDragEvent ? /* @__PURE__ */ React18.createElement(
|
|
2950
3378
|
"div",
|
|
2951
3379
|
{
|
|
2952
3380
|
className: cn(
|
|
@@ -2964,18 +3392,18 @@ var Scheduler = ({
|
|
|
2964
3392
|
transform: "rotate(-2deg) scale(1.02)"
|
|
2965
3393
|
}
|
|
2966
3394
|
},
|
|
2967
|
-
/* @__PURE__ */
|
|
3395
|
+
/* @__PURE__ */ React18.createElement("div", { className: "p-2.5 h-full flex flex-col" }, /* @__PURE__ */ React18.createElement(
|
|
2968
3396
|
"div",
|
|
2969
3397
|
{
|
|
2970
3398
|
className: "absolute left-0 top-0 bottom-0 w-1 rounded-l-lg",
|
|
2971
3399
|
style: { backgroundColor: activeDragEvent.color || "var(--primary)" }
|
|
2972
3400
|
}
|
|
2973
|
-
), /* @__PURE__ */
|
|
3401
|
+
), /* @__PURE__ */ React18.createElement("div", { className: "pl-2" }, /* @__PURE__ */ React18.createElement("div", { className: "font-semibold truncate text-sm" }, activeDragEvent.title), (view === "week" || view === "day") && getDragHeight() && getDragHeight() > 40 && /* @__PURE__ */ React18.createElement("div", { className: "text-xs opacity-80 mt-0.5 flex items-center gap-1" }, /* @__PURE__ */ React18.createElement("svg", { className: "w-3 h-3", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" }, /* @__PURE__ */ React18.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ React18.createElement("path", { d: "M12 6v6l4 2" })), format14(activeDragEvent.start, overlayTimeFormat, { locale }), " - ", format14(activeDragEvent.end, overlayTimeFormat, { locale }))), /* @__PURE__ */ React18.createElement("div", { className: "absolute bottom-1.5 right-1.5 opacity-60" }, /* @__PURE__ */ React18.createElement("svg", { className: "w-4 h-4", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React18.createElement("circle", { cx: "9", cy: "5", r: "1.5" }), /* @__PURE__ */ React18.createElement("circle", { cx: "15", cy: "5", r: "1.5" }), /* @__PURE__ */ React18.createElement("circle", { cx: "9", cy: "12", r: "1.5" }), /* @__PURE__ */ React18.createElement("circle", { cx: "15", cy: "12", r: "1.5" }), /* @__PURE__ */ React18.createElement("circle", { cx: "9", cy: "19", r: "1.5" }), /* @__PURE__ */ React18.createElement("circle", { cx: "15", cy: "19", r: "1.5" }))))
|
|
2974
3402
|
) : null)
|
|
2975
3403
|
)
|
|
2976
3404
|
);
|
|
2977
3405
|
};
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
3406
|
+
export {
|
|
3407
|
+
Scheduler,
|
|
3408
|
+
cn
|
|
3409
|
+
};
|