calendarkit-pro 1.2.1 → 1.3.1
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 +28 -2
- package/dist/index.d.mts +50 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +1461 -977
- package/dist/index.mjs +1371 -884
- package/package.json +2 -1
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/index.tsx
|
|
4
|
-
import React18, { useId, useState as useState6, useMemo as useMemo4 } from "react";
|
|
4
|
+
import React18, { useCallback as useCallback5, useId, useState as useState6, useMemo as useMemo4 } from "react";
|
|
5
5
|
import { DndContext, useSensor, useSensors, PointerSensor, DragOverlay } from "@dnd-kit/core";
|
|
6
6
|
import { createSnapModifier, restrictToWindowEdges } from "@dnd-kit/modifiers";
|
|
7
7
|
import { AnimatePresence as AnimatePresence3, motion as motion4 } from "framer-motion";
|
|
@@ -94,7 +94,8 @@ var CalendarHeader = ({
|
|
|
94
94
|
variant: "ghost",
|
|
95
95
|
size: "icon",
|
|
96
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",
|
|
97
|
-
onClick: onMenuClick
|
|
97
|
+
onClick: onMenuClick,
|
|
98
|
+
"aria-label": translations.toggleSidebar
|
|
98
99
|
},
|
|
99
100
|
/* @__PURE__ */ React2.createElement(Menu, { className: "h-5 w-5" })
|
|
100
101
|
), /* @__PURE__ */ React2.createElement(
|
|
@@ -111,7 +112,8 @@ var CalendarHeader = ({
|
|
|
111
112
|
variant: "ghost",
|
|
112
113
|
size: "icon",
|
|
113
114
|
onClick: onPrev,
|
|
114
|
-
className: "rounded-lg h-8 w-8 hover:bg-background/80 transition-all duration-200"
|
|
115
|
+
className: "rounded-lg h-8 w-8 hover:bg-background/80 transition-all duration-200",
|
|
116
|
+
"aria-label": translations.previous
|
|
115
117
|
},
|
|
116
118
|
/* @__PURE__ */ React2.createElement(ChevronLeft, { className: "h-4 w-4 text-muted-foreground" })
|
|
117
119
|
), /* @__PURE__ */ React2.createElement(
|
|
@@ -120,7 +122,8 @@ var CalendarHeader = ({
|
|
|
120
122
|
variant: "ghost",
|
|
121
123
|
size: "icon",
|
|
122
124
|
onClick: onNext,
|
|
123
|
-
className: "rounded-lg h-8 w-8 hover:bg-background/80 transition-all duration-200"
|
|
125
|
+
className: "rounded-lg h-8 w-8 hover:bg-background/80 transition-all duration-200",
|
|
126
|
+
"aria-label": translations.next
|
|
124
127
|
},
|
|
125
128
|
/* @__PURE__ */ React2.createElement(ChevronRight, { className: "h-4 w-4 text-muted-foreground" })
|
|
126
129
|
)), /* @__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(
|
|
@@ -129,6 +132,7 @@ var CalendarHeader = ({
|
|
|
129
132
|
variant: "ghost",
|
|
130
133
|
size: "sm",
|
|
131
134
|
onClick: () => onLanguageChange(language === "en" ? "fr" : "en"),
|
|
135
|
+
"aria-label": translations.switchLanguage,
|
|
132
136
|
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"
|
|
133
137
|
},
|
|
134
138
|
language
|
|
@@ -138,7 +142,8 @@ var CalendarHeader = ({
|
|
|
138
142
|
variant: "ghost",
|
|
139
143
|
size: "icon",
|
|
140
144
|
className: "rounded-xl h-9 w-9 hover:bg-accent/80 transition-all duration-200",
|
|
141
|
-
onClick: onThemeToggle
|
|
145
|
+
onClick: onThemeToggle,
|
|
146
|
+
"aria-label": translations.toggleTheme
|
|
142
147
|
},
|
|
143
148
|
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
149
|
), !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(
|
|
@@ -148,6 +153,8 @@ var CalendarHeader = ({
|
|
|
148
153
|
variant: "ghost",
|
|
149
154
|
size: "sm",
|
|
150
155
|
onClick: () => onViewChange(key),
|
|
156
|
+
"aria-pressed": view === key,
|
|
157
|
+
"aria-label": translations[key],
|
|
151
158
|
className: cn(
|
|
152
159
|
"h-8 px-3 text-xs rounded-lg transition-all duration-200 gap-1.5",
|
|
153
160
|
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"
|
|
@@ -164,21 +171,26 @@ import { Plus, ChevronDown, Globe } from "lucide-react";
|
|
|
164
171
|
|
|
165
172
|
// src/components/MiniCalendar.tsx
|
|
166
173
|
import React3 from "react";
|
|
167
|
-
import { format as
|
|
174
|
+
import { format as format2, isSameMonth, isSameDay, isToday, addMonths, subMonths } from "date-fns";
|
|
168
175
|
|
|
169
|
-
// src/
|
|
170
|
-
import {
|
|
171
|
-
|
|
176
|
+
// src/core/week.ts
|
|
177
|
+
import { eachDayOfInterval, endOfMonth, endOfWeek, startOfMonth, startOfWeek } from "date-fns";
|
|
178
|
+
function resolveWeekStartsOn(locale, fallback) {
|
|
179
|
+
return locale?.options?.weekStartsOn ?? fallback;
|
|
180
|
+
}
|
|
181
|
+
function getWeekDays(date, weekStartsOn) {
|
|
182
|
+
return eachDayOfInterval({
|
|
183
|
+
start: startOfWeek(date, { weekStartsOn }),
|
|
184
|
+
end: endOfWeek(date, { weekStartsOn })
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
function getMonthGrid(date, weekStartsOn) {
|
|
172
188
|
const monthStart = startOfMonth(date);
|
|
173
189
|
return eachDayOfInterval({
|
|
174
|
-
start: startOfWeek(monthStart, { weekStartsOn
|
|
175
|
-
end: endOfWeek(endOfMonth(monthStart), { weekStartsOn
|
|
190
|
+
start: startOfWeek(monthStart, { weekStartsOn }),
|
|
191
|
+
end: endOfWeek(endOfMonth(monthStart), { weekStartsOn })
|
|
176
192
|
});
|
|
177
|
-
}
|
|
178
|
-
var uses24HourClock = (locale) => {
|
|
179
|
-
if (!locale) return false;
|
|
180
|
-
return !/[ap]\.?m/i.test(format2(new Date(2e3, 0, 1, 13, 0), "p", { locale }));
|
|
181
|
-
};
|
|
193
|
+
}
|
|
182
194
|
|
|
183
195
|
// src/components/MiniCalendar.tsx
|
|
184
196
|
import { ChevronLeft as ChevronLeft2, ChevronRight as ChevronRight2 } from "lucide-react";
|
|
@@ -187,13 +199,14 @@ var MiniCalendar = ({
|
|
|
187
199
|
onDateChange,
|
|
188
200
|
onViewChange,
|
|
189
201
|
className,
|
|
190
|
-
locale
|
|
202
|
+
locale,
|
|
203
|
+
labels
|
|
191
204
|
}) => {
|
|
192
205
|
const [viewDate, setViewDate] = React3.useState(currentDate);
|
|
193
206
|
React3.useEffect(() => {
|
|
194
207
|
setViewDate(currentDate);
|
|
195
208
|
}, [currentDate]);
|
|
196
|
-
const weekStartsOn = locale
|
|
209
|
+
const weekStartsOn = resolveWeekStartsOn(locale, 0);
|
|
197
210
|
const days = React3.useMemo(
|
|
198
211
|
() => getMonthGrid(viewDate, weekStartsOn),
|
|
199
212
|
[viewDate, weekStartsOn]
|
|
@@ -218,14 +231,14 @@ var MiniCalendar = ({
|
|
|
218
231
|
onDateChange(day);
|
|
219
232
|
if (onViewChange) onViewChange("day");
|
|
220
233
|
};
|
|
221
|
-
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" },
|
|
234
|
+
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(
|
|
222
235
|
Button,
|
|
223
236
|
{
|
|
224
237
|
variant: "ghost",
|
|
225
238
|
size: "icon",
|
|
226
239
|
className: "h-7 w-7 rounded-md hover:bg-background/80 transition-all",
|
|
227
240
|
onClick: handlePrev,
|
|
228
|
-
"aria-label": "Previous month"
|
|
241
|
+
"aria-label": labels?.previousMonth || "Previous month"
|
|
229
242
|
},
|
|
230
243
|
/* @__PURE__ */ React3.createElement(ChevronLeft2, { className: "h-4 w-4" })
|
|
231
244
|
), /* @__PURE__ */ React3.createElement(
|
|
@@ -235,7 +248,7 @@ var MiniCalendar = ({
|
|
|
235
248
|
size: "icon",
|
|
236
249
|
className: "h-7 w-7 rounded-md hover:bg-background/80 transition-all",
|
|
237
250
|
onClick: handleNext,
|
|
238
|
-
"aria-label": "Next month"
|
|
251
|
+
"aria-label": labels?.nextMonth || "Next month"
|
|
239
252
|
},
|
|
240
253
|
/* @__PURE__ */ React3.createElement(ChevronRight2, { className: "h-4 w-4" })
|
|
241
254
|
))), /* @__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) => {
|
|
@@ -255,7 +268,7 @@ var MiniCalendar = ({
|
|
|
255
268
|
!isSelected && isTodayDate && "bg-primary/10 text-primary ring-1 ring-primary/30"
|
|
256
269
|
)
|
|
257
270
|
},
|
|
258
|
-
|
|
271
|
+
format2(day, "d")
|
|
259
272
|
);
|
|
260
273
|
})));
|
|
261
274
|
};
|
|
@@ -263,6 +276,281 @@ var MiniCalendar = ({
|
|
|
263
276
|
// src/components/Sidebar.tsx
|
|
264
277
|
import { toZonedTime } from "date-fns-tz";
|
|
265
278
|
import { format as format4 } from "date-fns";
|
|
279
|
+
|
|
280
|
+
// src/core/format.ts
|
|
281
|
+
import { format as format3 } from "date-fns";
|
|
282
|
+
|
|
283
|
+
// src/locales/index.ts
|
|
284
|
+
var en = {
|
|
285
|
+
today: "Today",
|
|
286
|
+
month: "Month",
|
|
287
|
+
week: "Week",
|
|
288
|
+
day: "Day",
|
|
289
|
+
agenda: "Agenda",
|
|
290
|
+
resource: "Resources",
|
|
291
|
+
createEvent: "Create Event",
|
|
292
|
+
editEvent: "Edit Event",
|
|
293
|
+
delete: "Delete",
|
|
294
|
+
save: "Save",
|
|
295
|
+
cancel: "Cancel",
|
|
296
|
+
title: "Add title",
|
|
297
|
+
start: "Start",
|
|
298
|
+
end: "End",
|
|
299
|
+
allDay: "All Day",
|
|
300
|
+
description: "Description",
|
|
301
|
+
repeat: "Repeat",
|
|
302
|
+
noRepeat: "Does not repeat",
|
|
303
|
+
selectCalendar: "Select Calendar",
|
|
304
|
+
selectType: "Select Type",
|
|
305
|
+
daily: "Daily",
|
|
306
|
+
weekly: "Weekly",
|
|
307
|
+
monthly: "Monthly",
|
|
308
|
+
yearly: "Yearly",
|
|
309
|
+
search: "Search events...",
|
|
310
|
+
allTypes: "All Types",
|
|
311
|
+
timezone: "Time zone",
|
|
312
|
+
localTime: "Local Time",
|
|
313
|
+
create: "Create",
|
|
314
|
+
calendars: "Calendars",
|
|
315
|
+
event: "Event",
|
|
316
|
+
task: "Task",
|
|
317
|
+
appointmentSchedule: "Appointment schedule",
|
|
318
|
+
new: "New",
|
|
319
|
+
dateAndTime: "Date & Time",
|
|
320
|
+
whosJoining: "Who's joining?",
|
|
321
|
+
suggestedTimes: "Suggested times for guests",
|
|
322
|
+
viewSuggestions: "View suggestions",
|
|
323
|
+
whereWillItBe: "Where will it be?",
|
|
324
|
+
location: "Enter address",
|
|
325
|
+
descriptionAndAttachments: "Description",
|
|
326
|
+
dragAndDrop: "Drag & drop files or",
|
|
327
|
+
guests: "Guests",
|
|
328
|
+
addGuests: "Add guests",
|
|
329
|
+
addAttachment: "Upload files",
|
|
330
|
+
moreOptions: "More options",
|
|
331
|
+
doesNotRepeat: "Does not repeat",
|
|
332
|
+
locationHelpText: 'Enter a physical address (e.g. "123 Main St") to generate the map.',
|
|
333
|
+
notes: "Notes...",
|
|
334
|
+
guestCount: "guest",
|
|
335
|
+
guestsCount: "guests",
|
|
336
|
+
repeatFor: "Repeat for",
|
|
337
|
+
times: "times",
|
|
338
|
+
until: "Until",
|
|
339
|
+
endRepeat: "End repeat",
|
|
340
|
+
never: "Never",
|
|
341
|
+
afterOccurrences: "After occurrences",
|
|
342
|
+
onDate: "On date",
|
|
343
|
+
tomorrow: "Tomorrow",
|
|
344
|
+
edit: "Edit",
|
|
345
|
+
duplicate: "Duplicate",
|
|
346
|
+
eventSingular: "event",
|
|
347
|
+
eventPlural: "events",
|
|
348
|
+
allCaughtUp: "All caught up!",
|
|
349
|
+
noEventsScheduled: "No events scheduled",
|
|
350
|
+
noUpcoming: "Your schedule is clear",
|
|
351
|
+
noUpcomingHint: "No upcoming events to show. Create a new event to start planning.",
|
|
352
|
+
newEvent: "New Event",
|
|
353
|
+
saveChanges: "Save Changes",
|
|
354
|
+
close: "Close",
|
|
355
|
+
noTitle: "(No title)",
|
|
356
|
+
openInMaps: "Open in maps",
|
|
357
|
+
notesLabel: "Notes",
|
|
358
|
+
addNotes: "Add description or notes...",
|
|
359
|
+
attachments: "Attachments",
|
|
360
|
+
reminders: "Reminders",
|
|
361
|
+
add: "Add",
|
|
362
|
+
calendar: "Calendar",
|
|
363
|
+
select: "Select",
|
|
364
|
+
addLocation: "Add location",
|
|
365
|
+
guestSingular: "Guest",
|
|
366
|
+
addGuestPlaceholder: "Add a guest email and press Enter",
|
|
367
|
+
invalidEmail: "Enter a valid email address",
|
|
368
|
+
removeGuest: "Remove {email}",
|
|
369
|
+
reminderAtTime: "At time of event",
|
|
370
|
+
reminderMinutes: "{n} minutes before",
|
|
371
|
+
reminderHour: "1 hour before",
|
|
372
|
+
reminderHours: "{n} hours before",
|
|
373
|
+
reminderDay: "1 day before",
|
|
374
|
+
reminderDays: "{n} days before",
|
|
375
|
+
reminderWeek: "1 week before",
|
|
376
|
+
removeReminder: "Remove reminder",
|
|
377
|
+
durationMinutes: "{n} min",
|
|
378
|
+
durationHours: "{n} hr",
|
|
379
|
+
durationHoursMinutes: "{h} hr {m} min",
|
|
380
|
+
moreEvents: "+{n} more",
|
|
381
|
+
idLabel: "ID: {id}",
|
|
382
|
+
previous: "Previous",
|
|
383
|
+
next: "Next",
|
|
384
|
+
previousMonth: "Previous month",
|
|
385
|
+
nextMonth: "Next month",
|
|
386
|
+
toggleSidebar: "Toggle sidebar",
|
|
387
|
+
toggleTheme: "Toggle dark mode",
|
|
388
|
+
switchLanguage: "Switch language",
|
|
389
|
+
eventColor: "Event color"
|
|
390
|
+
};
|
|
391
|
+
var fr = {
|
|
392
|
+
today: "Aujourd'hui",
|
|
393
|
+
month: "Mois",
|
|
394
|
+
week: "Semaine",
|
|
395
|
+
day: "Jour",
|
|
396
|
+
agenda: "Agenda",
|
|
397
|
+
resource: "Ressources",
|
|
398
|
+
createEvent: "Cr\xE9er un \xE9v\xE9nement",
|
|
399
|
+
editEvent: "Modifier l'\xE9v\xE9nement",
|
|
400
|
+
delete: "Supprimer",
|
|
401
|
+
save: "Enregistrer",
|
|
402
|
+
cancel: "Annuler",
|
|
403
|
+
title: "Ajouter un titre",
|
|
404
|
+
start: "D\xE9but",
|
|
405
|
+
end: "Fin",
|
|
406
|
+
allDay: "Toute la journ\xE9e",
|
|
407
|
+
description: "Description",
|
|
408
|
+
repeat: "R\xE9p\xE9ter",
|
|
409
|
+
noRepeat: "Ne se r\xE9p\xE8te pas",
|
|
410
|
+
selectCalendar: "S\xE9lectionner un calendrier",
|
|
411
|
+
selectType: "S\xE9lectionner un type",
|
|
412
|
+
daily: "Quotidien",
|
|
413
|
+
weekly: "Hebdomadaire",
|
|
414
|
+
monthly: "Mensuel",
|
|
415
|
+
yearly: "Annuel",
|
|
416
|
+
search: "Rechercher...",
|
|
417
|
+
allTypes: "Tous les types",
|
|
418
|
+
timezone: "Fuseau horaire",
|
|
419
|
+
localTime: "Heure locale",
|
|
420
|
+
create: "Cr\xE9er",
|
|
421
|
+
calendars: "Calendriers",
|
|
422
|
+
event: "\xC9v\xE9nement",
|
|
423
|
+
task: "T\xE2che",
|
|
424
|
+
appointmentSchedule: "Planning de rendez-vous",
|
|
425
|
+
new: "Nouveau",
|
|
426
|
+
dateAndTime: "Date et heure",
|
|
427
|
+
whosJoining: "Qui participe ?",
|
|
428
|
+
suggestedTimes: "Horaires sugg\xE9r\xE9s",
|
|
429
|
+
viewSuggestions: "Voir les suggestions",
|
|
430
|
+
whereWillItBe: "O\xF9 aura lieu ?",
|
|
431
|
+
location: "Entrer une adresse",
|
|
432
|
+
descriptionAndAttachments: "Description",
|
|
433
|
+
dragAndDrop: "Glisser-d\xE9poser des fichiers ou",
|
|
434
|
+
guests: "Invit\xE9s",
|
|
435
|
+
addGuests: "Ajouter des invit\xE9s",
|
|
436
|
+
addAttachment: "T\xE9l\xE9charger des fichiers",
|
|
437
|
+
moreOptions: "Plus d'options",
|
|
438
|
+
doesNotRepeat: "Ne se r\xE9p\xE8te pas",
|
|
439
|
+
locationHelpText: 'Entrez une adresse physique (ex: "123 Rue Principale") pour g\xE9n\xE9rer la carte.',
|
|
440
|
+
notes: "Notes...",
|
|
441
|
+
guestCount: "invit\xE9",
|
|
442
|
+
guestsCount: "invit\xE9s",
|
|
443
|
+
repeatFor: "R\xE9p\xE9ter pendant",
|
|
444
|
+
times: "fois",
|
|
445
|
+
until: "Jusqu'au",
|
|
446
|
+
endRepeat: "Fin de r\xE9p\xE9tition",
|
|
447
|
+
never: "Jamais",
|
|
448
|
+
afterOccurrences: "Apr\xE8s occurrences",
|
|
449
|
+
onDate: "\xC0 la date",
|
|
450
|
+
tomorrow: "Demain",
|
|
451
|
+
edit: "Modifier",
|
|
452
|
+
duplicate: "Dupliquer",
|
|
453
|
+
eventSingular: "\xE9v\xE9nement",
|
|
454
|
+
eventPlural: "\xE9v\xE9nements",
|
|
455
|
+
allCaughtUp: "Tout est \xE0 jour !",
|
|
456
|
+
noEventsScheduled: "Aucun \xE9v\xE9nement pr\xE9vu",
|
|
457
|
+
noUpcoming: "Votre planning est libre",
|
|
458
|
+
noUpcomingHint: "Aucun \xE9v\xE9nement \xE0 venir. Cr\xE9ez un nouvel \xE9v\xE9nement pour commencer \xE0 planifier.",
|
|
459
|
+
newEvent: "Nouvel \xE9v\xE9nement",
|
|
460
|
+
saveChanges: "Enregistrer",
|
|
461
|
+
close: "Fermer",
|
|
462
|
+
noTitle: "(Sans titre)",
|
|
463
|
+
openInMaps: "Ouvrir dans Maps",
|
|
464
|
+
notesLabel: "Notes",
|
|
465
|
+
addNotes: "Ajouter une description ou des notes\u2026",
|
|
466
|
+
attachments: "Pi\xE8ces jointes",
|
|
467
|
+
reminders: "Rappels",
|
|
468
|
+
add: "Ajouter",
|
|
469
|
+
calendar: "Calendrier",
|
|
470
|
+
select: "Choisir",
|
|
471
|
+
addLocation: "Ajouter un lieu",
|
|
472
|
+
guestSingular: "Invit\xE9",
|
|
473
|
+
addGuestPlaceholder: "Ajoutez l'e-mail d'un invit\xE9 puis Entr\xE9e",
|
|
474
|
+
invalidEmail: "Saisissez une adresse e-mail valide",
|
|
475
|
+
removeGuest: "Retirer {email}",
|
|
476
|
+
reminderAtTime: "Au moment de l'\xE9v\xE9nement",
|
|
477
|
+
reminderMinutes: "{n} minutes avant",
|
|
478
|
+
reminderHour: "1 heure avant",
|
|
479
|
+
reminderHours: "{n} heures avant",
|
|
480
|
+
reminderDay: "1 jour avant",
|
|
481
|
+
reminderDays: "{n} jours avant",
|
|
482
|
+
reminderWeek: "1 semaine avant",
|
|
483
|
+
removeReminder: "Supprimer le rappel",
|
|
484
|
+
durationMinutes: "{n} min",
|
|
485
|
+
durationHours: "{n} h",
|
|
486
|
+
durationHoursMinutes: "{h} h {m} min",
|
|
487
|
+
moreEvents: "+{n} de plus",
|
|
488
|
+
idLabel: "ID : {id}",
|
|
489
|
+
previous: "Pr\xE9c\xE9dent",
|
|
490
|
+
next: "Suivant",
|
|
491
|
+
previousMonth: "Mois pr\xE9c\xE9dent",
|
|
492
|
+
nextMonth: "Mois suivant",
|
|
493
|
+
toggleSidebar: "Afficher ou masquer la barre lat\xE9rale",
|
|
494
|
+
toggleTheme: "Basculer le th\xE8me sombre",
|
|
495
|
+
switchLanguage: "Changer de langue",
|
|
496
|
+
eventColor: "Couleur de l'\xE9v\xE9nement"
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
// src/core/format.ts
|
|
500
|
+
var clockCache = /* @__PURE__ */ new WeakMap();
|
|
501
|
+
function uses24HourClock(locale) {
|
|
502
|
+
if (!locale) return false;
|
|
503
|
+
let is24 = clockCache.get(locale);
|
|
504
|
+
if (is24 === void 0) {
|
|
505
|
+
is24 = !/[ap]\.?m/i.test(format3(new Date(2e3, 0, 1, 13, 0), "p", { locale }));
|
|
506
|
+
clockCache.set(locale, is24);
|
|
507
|
+
}
|
|
508
|
+
return is24;
|
|
509
|
+
}
|
|
510
|
+
var H24 = { hour: "H:mm", time: "H:mm", compact: "H:mm" };
|
|
511
|
+
var H12 = { hour: "h a", time: "h:mm a", compact: "h:mm" };
|
|
512
|
+
function timeFormats(locale) {
|
|
513
|
+
return uses24HourClock(locale) ? H24 : H12;
|
|
514
|
+
}
|
|
515
|
+
var MAX_FORMATTERS = 50;
|
|
516
|
+
var formatters = /* @__PURE__ */ new Map();
|
|
517
|
+
function getDateTimeFormat(locale, timeZone, options) {
|
|
518
|
+
const key = `${locale}|${timeZone ?? ""}|${JSON.stringify(options)}`;
|
|
519
|
+
let formatter = formatters.get(key);
|
|
520
|
+
if (formatter) {
|
|
521
|
+
formatters.delete(key);
|
|
522
|
+
} else {
|
|
523
|
+
formatter = new Intl.DateTimeFormat(locale, { ...options, timeZone });
|
|
524
|
+
if (formatters.size >= MAX_FORMATTERS) {
|
|
525
|
+
formatters.delete(formatters.keys().next().value);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
formatters.set(key, formatter);
|
|
529
|
+
return formatter;
|
|
530
|
+
}
|
|
531
|
+
function timeZoneAbbreviation(date, timeZone) {
|
|
532
|
+
try {
|
|
533
|
+
return getDateTimeFormat("en-US", timeZone, { timeZoneName: "short" }).formatToParts(date).find((part) => part.type === "timeZoneName")?.value ?? "";
|
|
534
|
+
} catch {
|
|
535
|
+
return "";
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
function resolveTranslations(language, overrides) {
|
|
539
|
+
const defined = Object.fromEntries(Object.entries(overrides ?? {}).filter(([, value]) => value !== void 0));
|
|
540
|
+
return { ...language === "fr" ? fr : en, ...defined };
|
|
541
|
+
}
|
|
542
|
+
function interpolate(template, values) {
|
|
543
|
+
return template.replace(/\{(\w+)\}/g, (match, key) => key in values ? String(values[key]) : match);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// src/core/color.ts
|
|
547
|
+
var SAFE_COLOR = /^[#\w\s%.,/()+-]+$/;
|
|
548
|
+
var FORBIDDEN = /url\(|expression\(/i;
|
|
549
|
+
function safeColor(value, fallback) {
|
|
550
|
+
return value && SAFE_COLOR.test(value.trim()) && !FORBIDDEN.test(value) ? value.trim() : fallback;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// src/components/Sidebar.tsx
|
|
266
554
|
var Sidebar = ({
|
|
267
555
|
currentDate,
|
|
268
556
|
onDateChange,
|
|
@@ -287,20 +575,7 @@ var Sidebar = ({
|
|
|
287
575
|
const timer = setInterval(() => setNow(/* @__PURE__ */ new Date()), 6e4);
|
|
288
576
|
return () => clearInterval(timer);
|
|
289
577
|
}, []);
|
|
290
|
-
const
|
|
291
|
-
{ id: "1", label: "My Calendar", color: "#3b82f6", active: true },
|
|
292
|
-
{ id: "2", label: "Birthdays", color: "#10b981", active: true },
|
|
293
|
-
{ id: "3", label: "Tasks", color: "#6366f1", active: true }
|
|
294
|
-
];
|
|
295
|
-
const displayCalendars = calendars || defaultCalendars;
|
|
296
|
-
const getAcronym = (tz) => {
|
|
297
|
-
if (!tz || !now) return "LOC";
|
|
298
|
-
try {
|
|
299
|
-
return new Intl.DateTimeFormat("en-US", { timeZone: tz, timeZoneName: "short" }).formatToParts(now).find((part) => part.type === "timeZoneName")?.value || "";
|
|
300
|
-
} catch {
|
|
301
|
-
return "";
|
|
302
|
-
}
|
|
303
|
-
};
|
|
578
|
+
const getAcronym = (tz) => !tz || !now ? "LOC" : timeZoneAbbreviation(now, tz);
|
|
304
579
|
const timezones = [
|
|
305
580
|
{ value: "", label: translations?.localTime || "Local Time", acronym: "LOC" },
|
|
306
581
|
{ value: "UTC", label: "UTC", acronym: "UTC" },
|
|
@@ -325,10 +600,7 @@ var Sidebar = ({
|
|
|
325
600
|
try {
|
|
326
601
|
if (!tz.value) {
|
|
327
602
|
time = format4(now, "HH:mm");
|
|
328
|
-
|
|
329
|
-
acronym = new Intl.DateTimeFormat("en-US", { timeZoneName: "short" }).formatToParts(now).find((part) => part.type === "timeZoneName")?.value || "LOC";
|
|
330
|
-
} catch {
|
|
331
|
-
}
|
|
603
|
+
acronym = timeZoneAbbreviation(now) || "LOC";
|
|
332
604
|
} else {
|
|
333
605
|
const zDate = toZonedTime(now, tz.value);
|
|
334
606
|
time = format4(zDate, "HH:mm");
|
|
@@ -352,7 +624,16 @@ var Sidebar = ({
|
|
|
352
624
|
},
|
|
353
625
|
/* @__PURE__ */ React4.createElement(Plus, { className: "w-5 h-5" }),
|
|
354
626
|
/* @__PURE__ */ React4.createElement("span", { className: "text-sm" }, translations?.create || "Create")
|
|
355
|
-
)), /* @__PURE__ */ React4.createElement(
|
|
627
|
+
)), /* @__PURE__ */ React4.createElement(
|
|
628
|
+
MiniCalendar,
|
|
629
|
+
{
|
|
630
|
+
currentDate,
|
|
631
|
+
onDateChange,
|
|
632
|
+
onViewChange,
|
|
633
|
+
locale,
|
|
634
|
+
labels: { previousMonth: translations?.previousMonth, nextMonth: translations?.nextMonth }
|
|
635
|
+
}
|
|
636
|
+
), /* @__PURE__ */ React4.createElement("div", { className: "flex-1 px-4 space-y-5 mt-5" }, calendars && calendars.length > 0 && /* @__PURE__ */ React4.createElement("div", { className: "bg-muted/20 rounded-2xl p-3 border-[0px] border-border/30" }, /* @__PURE__ */ React4.createElement(
|
|
356
637
|
"div",
|
|
357
638
|
{
|
|
358
639
|
className: "flex items-center justify-between cursor-pointer hover:bg-accent/50 p-2 -m-1 rounded-xl mb-2 transition-all duration-200",
|
|
@@ -360,41 +641,40 @@ var Sidebar = ({
|
|
|
360
641
|
},
|
|
361
642
|
/* @__PURE__ */ React4.createElement("span", { className: "text-sm font-semibold text-foreground" }, translations?.calendars || "Calendars"),
|
|
362
643
|
/* @__PURE__ */ React4.createElement(ChevronDown, { className: cn("w-4 h-4 text-muted-foreground transition-transform duration-200", calendarsOpen && "rotate-180") })
|
|
363
|
-
), calendarsOpen && /* @__PURE__ */ React4.createElement("div", { className: "space-y-1" },
|
|
644
|
+
), calendarsOpen && /* @__PURE__ */ React4.createElement("div", { className: "space-y-1" }, calendars.map((cal) => /* @__PURE__ */ React4.createElement(
|
|
364
645
|
"div",
|
|
365
646
|
{
|
|
366
647
|
key: cal.id,
|
|
367
648
|
className: "flex items-center gap-3 py-2 px-2 hover:bg-accent/60 rounded-xl cursor-pointer group transition-all duration-200",
|
|
368
649
|
onClick: () => onCalendarToggle?.(cal.id, !(cal.active ?? true))
|
|
369
650
|
},
|
|
370
|
-
/* @__PURE__ */ React4.createElement(
|
|
371
|
-
"
|
|
651
|
+
/* @__PURE__ */ React4.createElement(
|
|
652
|
+
"div",
|
|
372
653
|
{
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
`)),
|
|
654
|
+
className: "relative flex items-center justify-center",
|
|
655
|
+
style: { "--cal-color": safeColor(cal.color, "hsl(var(--primary))") }
|
|
656
|
+
},
|
|
657
|
+
/* @__PURE__ */ React4.createElement(
|
|
658
|
+
"input",
|
|
659
|
+
{
|
|
660
|
+
type: "checkbox",
|
|
661
|
+
checked: cal.active ?? true,
|
|
662
|
+
onChange: (e) => {
|
|
663
|
+
e.stopPropagation();
|
|
664
|
+
onCalendarToggle?.(cal.id, e.target.checked);
|
|
665
|
+
},
|
|
666
|
+
"aria-label": cal.label,
|
|
667
|
+
className: "peer h-5 w-5 rounded-md border-2 border-border/60 cursor-pointer appearance-none checked:border-transparent checked:bg-[var(--cal-color)] focus:ring-2 focus:ring-[var(--cal-color)] transition-all duration-200"
|
|
668
|
+
}
|
|
669
|
+
),
|
|
670
|
+
/* @__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" })))
|
|
671
|
+
),
|
|
392
672
|
/* @__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)),
|
|
393
673
|
/* @__PURE__ */ React4.createElement(
|
|
394
674
|
"div",
|
|
395
675
|
{
|
|
396
676
|
className: "w-2 h-2 rounded-full opacity-60 group-hover:opacity-100 transition-opacity",
|
|
397
|
-
style: { backgroundColor: cal.color }
|
|
677
|
+
style: { backgroundColor: safeColor(cal.color, "hsl(var(--primary))") }
|
|
398
678
|
}
|
|
399
679
|
)
|
|
400
680
|
))))), /* @__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(
|
|
@@ -423,32 +703,198 @@ var Sidebar = ({
|
|
|
423
703
|
};
|
|
424
704
|
|
|
425
705
|
// src/views/MonthView.tsx
|
|
426
|
-
import React7, { useMemo
|
|
427
|
-
import { format as
|
|
428
|
-
|
|
706
|
+
import React7, { useMemo } from "react";
|
|
707
|
+
import { format as format7, isSameDay as isSameDay3, isSameMonth as isSameMonth2 } from "date-fns";
|
|
708
|
+
|
|
709
|
+
// src/core/events.ts
|
|
710
|
+
import { addDays, format as format6, startOfDay as startOfDay2 } from "date-fns";
|
|
711
|
+
|
|
712
|
+
// src/core/timezone.ts
|
|
713
|
+
import { format as format5, isSameDay as isSameDay2, startOfDay } from "date-fns";
|
|
714
|
+
import { fromZonedTime, toZonedTime as toZonedTime2 } from "date-fns-tz";
|
|
715
|
+
|
|
716
|
+
// src/core/warn.ts
|
|
717
|
+
var warned = /* @__PURE__ */ new Set();
|
|
718
|
+
function isProductionBuild() {
|
|
719
|
+
try {
|
|
720
|
+
return process.env.NODE_ENV === "production";
|
|
721
|
+
} catch {
|
|
722
|
+
return false;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
function warnOnce(key, message) {
|
|
726
|
+
if (isProductionBuild() || warned.has(key)) return;
|
|
727
|
+
warned.add(key);
|
|
728
|
+
console.warn(`[calendarkit-pro] ${message}`);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// src/core/timezone.ts
|
|
732
|
+
var validity = /* @__PURE__ */ new Map();
|
|
733
|
+
function resolveTimeZone(tz) {
|
|
734
|
+
if (!tz) return void 0;
|
|
735
|
+
let valid = validity.get(tz);
|
|
736
|
+
if (valid === void 0) {
|
|
737
|
+
try {
|
|
738
|
+
new Intl.DateTimeFormat("en-US", { timeZone: tz });
|
|
739
|
+
valid = true;
|
|
740
|
+
} catch {
|
|
741
|
+
valid = false;
|
|
742
|
+
}
|
|
743
|
+
validity.set(tz, valid);
|
|
744
|
+
}
|
|
745
|
+
if (!valid) {
|
|
746
|
+
warnOnce(`invalid-timezone:${tz}`, `Unknown timezone "${tz}", falling back to local time.`);
|
|
747
|
+
return void 0;
|
|
748
|
+
}
|
|
749
|
+
return tz;
|
|
750
|
+
}
|
|
751
|
+
function toDisplay(date, tz) {
|
|
752
|
+
const zone = resolveTimeZone(tz);
|
|
753
|
+
return zone ? toZonedTime2(date, zone) : date;
|
|
754
|
+
}
|
|
755
|
+
function fromDisplay(wall, tz) {
|
|
756
|
+
const zone = resolveTimeZone(tz);
|
|
757
|
+
return zone ? fromZonedTime(wall, zone) : new Date(wall);
|
|
758
|
+
}
|
|
759
|
+
function minutesSinceDisplayMidnight(date, tz) {
|
|
760
|
+
const wall = toDisplay(date, tz);
|
|
761
|
+
return wall.getHours() * 60 + wall.getMinutes();
|
|
762
|
+
}
|
|
763
|
+
function isSameDisplayDay(date, day, tz) {
|
|
764
|
+
return isSameDay2(toDisplay(date, tz), day);
|
|
765
|
+
}
|
|
766
|
+
function displayToday(tz, now = /* @__PURE__ */ new Date()) {
|
|
767
|
+
return startOfDay(toDisplay(now, tz));
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// src/core/events.ts
|
|
771
|
+
var toDate = (value) => value === void 0 || value === null ? void 0 : value instanceof Date ? value : new Date(value);
|
|
772
|
+
var isValidDate = (date) => !!date && !Number.isNaN(date.getTime());
|
|
773
|
+
var normalizedEvent = /* @__PURE__ */ new WeakMap();
|
|
774
|
+
var normalizedList = /* @__PURE__ */ new WeakMap();
|
|
775
|
+
function normalizeEvent(event) {
|
|
776
|
+
const cached = normalizedEvent.get(event);
|
|
777
|
+
if (cached !== void 0) return cached;
|
|
778
|
+
const start = toDate(event.start);
|
|
779
|
+
const end = toDate(event.end);
|
|
780
|
+
let result = null;
|
|
781
|
+
if (!isValidDate(start) || !isValidDate(end)) {
|
|
782
|
+
warnOnce(`invalid-date:${event.id}`, `Event "${event.id}" has an invalid start or end date and was skipped.`);
|
|
783
|
+
} else if (end.getTime() < start.getTime()) {
|
|
784
|
+
warnOnce(`end-before-start:${event.id}`, `Event "${event.id}" ends before it starts and was skipped.`);
|
|
785
|
+
} else {
|
|
786
|
+
const needsCopy = start !== event.start || end !== event.end || needsRecurrenceCopy(event);
|
|
787
|
+
result = needsCopy ? {
|
|
788
|
+
...event,
|
|
789
|
+
start,
|
|
790
|
+
end,
|
|
791
|
+
recurrence: event.recurrence && {
|
|
792
|
+
...event.recurrence,
|
|
793
|
+
until: toDate(event.recurrence.until),
|
|
794
|
+
exdates: event.recurrence.exdates?.map((d) => toDate(d))
|
|
795
|
+
}
|
|
796
|
+
} : event;
|
|
797
|
+
}
|
|
798
|
+
normalizedEvent.set(event, result);
|
|
799
|
+
return result;
|
|
800
|
+
}
|
|
801
|
+
function needsRecurrenceCopy(event) {
|
|
802
|
+
const r = event.recurrence;
|
|
803
|
+
if (!r) return false;
|
|
804
|
+
return r.until !== void 0 && !(r.until instanceof Date) || !!r.exdates?.some((d) => !(d instanceof Date));
|
|
805
|
+
}
|
|
806
|
+
function normalizeEvents(events) {
|
|
807
|
+
const cached = normalizedList.get(events);
|
|
808
|
+
if (cached) return cached;
|
|
809
|
+
const seen = /* @__PURE__ */ new Set();
|
|
810
|
+
const result = [];
|
|
811
|
+
for (const event of events) {
|
|
812
|
+
const normalized = normalizeEvent(event);
|
|
813
|
+
if (!normalized) continue;
|
|
814
|
+
if (seen.has(normalized.id)) {
|
|
815
|
+
warnOnce(`duplicate-id:${normalized.id}`, `Duplicate event id "${normalized.id}": ids must be unique.`);
|
|
816
|
+
}
|
|
817
|
+
seen.add(normalized.id);
|
|
818
|
+
result.push(normalized);
|
|
819
|
+
}
|
|
820
|
+
normalizedList.set(events, result);
|
|
821
|
+
return result;
|
|
822
|
+
}
|
|
823
|
+
var dayKey = (wall) => format6(wall, "yyyy-MM-dd");
|
|
824
|
+
function memoByZone(cache, events, tz, compute) {
|
|
825
|
+
let byZone = cache.get(events);
|
|
826
|
+
if (!byZone) {
|
|
827
|
+
byZone = /* @__PURE__ */ new Map();
|
|
828
|
+
cache.set(events, byZone);
|
|
829
|
+
}
|
|
830
|
+
const key = tz ?? "";
|
|
831
|
+
let value = byZone.get(key);
|
|
832
|
+
if (value === void 0) {
|
|
833
|
+
value = compute();
|
|
834
|
+
byZone.set(key, value);
|
|
835
|
+
}
|
|
836
|
+
return value;
|
|
837
|
+
}
|
|
838
|
+
var startDayCache = /* @__PURE__ */ new WeakMap();
|
|
839
|
+
function eventsByStartDay(events, tz) {
|
|
840
|
+
return memoByZone(startDayCache, events, tz, () => {
|
|
841
|
+
const map = /* @__PURE__ */ new Map();
|
|
842
|
+
for (const event of events) {
|
|
843
|
+
const key = dayKey(toDisplay(event.start, tz));
|
|
844
|
+
const bucket = map.get(key);
|
|
845
|
+
if (bucket) bucket.push(event);
|
|
846
|
+
else map.set(key, [event]);
|
|
847
|
+
}
|
|
848
|
+
return map;
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
var segmentCache = /* @__PURE__ */ new WeakMap();
|
|
852
|
+
function segmentsByDisplayDay(events, tz, range) {
|
|
853
|
+
const cacheKey = range ? `${tz ?? ""}|${range.start.getTime()}|${range.end.getTime()}` : tz;
|
|
854
|
+
return memoByZone(segmentCache, events, cacheKey, () => {
|
|
855
|
+
const map = /* @__PURE__ */ new Map();
|
|
856
|
+
for (const event of events) {
|
|
857
|
+
const wallStart = toDisplay(event.start, tz);
|
|
858
|
+
const rawEnd = toDisplay(event.end, tz);
|
|
859
|
+
const wallEnd = rawEnd < wallStart ? wallStart : rawEnd;
|
|
860
|
+
if (range && (wallEnd < range.start || wallStart >= range.end)) continue;
|
|
861
|
+
let day = startOfDay2(range && wallStart < range.start ? range.start : wallStart);
|
|
862
|
+
const stop = range && wallEnd > range.end ? range.end : wallEnd;
|
|
863
|
+
do {
|
|
864
|
+
const next = addDays(day, 1);
|
|
865
|
+
const start = wallStart > day ? wallStart : day;
|
|
866
|
+
const end = wallEnd < next ? wallEnd : next;
|
|
867
|
+
if (end > start || wallStart.getTime() === wallEnd.getTime()) {
|
|
868
|
+
const key = dayKey(day);
|
|
869
|
+
const bucket = map.get(key);
|
|
870
|
+
const segment = { event, start, end };
|
|
871
|
+
if (bucket) bucket.push(segment);
|
|
872
|
+
else map.set(key, [segment]);
|
|
873
|
+
}
|
|
874
|
+
day = next;
|
|
875
|
+
} while (day < stop);
|
|
876
|
+
}
|
|
877
|
+
return map;
|
|
878
|
+
});
|
|
879
|
+
}
|
|
429
880
|
|
|
430
881
|
// src/components/dnd/DraggableEvent.tsx
|
|
431
882
|
import React5 from "react";
|
|
432
883
|
import { useDraggable } from "@dnd-kit/core";
|
|
433
884
|
import { CSS } from "@dnd-kit/utilities";
|
|
434
|
-
var
|
|
885
|
+
var DraggableEventImpl = ({ event, dragId, grabOffsetMs, children, className, style: propStyle, ...props }) => {
|
|
435
886
|
const dragDisabled = isEventDragDisabled(event);
|
|
436
887
|
const { attributes, listeners, setNodeRef, transform, isDragging } = useDraggable({
|
|
437
|
-
id: event.id,
|
|
438
|
-
data: { event },
|
|
888
|
+
id: dragId ?? event.id,
|
|
889
|
+
data: { event, grabOffsetMs },
|
|
439
890
|
disabled: dragDisabled
|
|
440
891
|
});
|
|
441
892
|
const style = {
|
|
442
893
|
...propStyle,
|
|
443
894
|
transform: CSS.Translate.toString(transform),
|
|
444
895
|
zIndex: isDragging ? 100 : propStyle?.zIndex,
|
|
445
|
-
//
|
|
446
|
-
// so that the DragOverlay (the "ghost") is the only thing the user focuses on.
|
|
447
|
-
// If we set opacity: 0, it disappears. If 0.1, it's a faint placeholder.
|
|
448
|
-
// The user asked for "Make the entire dragged item transparent".
|
|
449
|
-
// Usually this means the original item should be faded out while the drag overlay is moving.
|
|
896
|
+
// Hide the original while dragging: the DragOverlay ghost is what the user follows.
|
|
450
897
|
opacity: isDragging ? 0 : propStyle?.opacity
|
|
451
|
-
// Hiding original element completely
|
|
452
898
|
};
|
|
453
899
|
return /* @__PURE__ */ React5.createElement(
|
|
454
900
|
"div",
|
|
@@ -463,36 +909,59 @@ var DraggableEvent = ({ event, children, className, style: propStyle, ...props }
|
|
|
463
909
|
children
|
|
464
910
|
);
|
|
465
911
|
};
|
|
912
|
+
var DraggableEvent = React5.memo(DraggableEventImpl);
|
|
466
913
|
|
|
467
914
|
// src/components/dnd/DroppableCell.tsx
|
|
468
915
|
import React6 from "react";
|
|
469
916
|
import { useDroppable } from "@dnd-kit/core";
|
|
470
|
-
var
|
|
917
|
+
var QUARTER_HOVER = [
|
|
918
|
+
"hover:bg-blue-50/50 dark:hover:bg-blue-900/10",
|
|
919
|
+
"hover:bg-blue-50/80 dark:hover:bg-blue-900/20",
|
|
920
|
+
"hover:bg-blue-100/50 dark:hover:bg-blue-900/30",
|
|
921
|
+
"hover:bg-blue-100/80 dark:hover:bg-blue-900/40"
|
|
922
|
+
];
|
|
923
|
+
var QUARTER_ACTIVE = [
|
|
924
|
+
"bg-blue-50/50 dark:bg-blue-900/10 ring-2 ring-primary ring-inset",
|
|
925
|
+
"bg-blue-50/80 dark:bg-blue-900/20 ring-2 ring-primary ring-inset",
|
|
926
|
+
"bg-blue-100/50 dark:bg-blue-900/30 ring-2 ring-primary ring-inset",
|
|
927
|
+
"bg-blue-100/80 dark:bg-blue-900/40 ring-2 ring-primary ring-inset"
|
|
928
|
+
];
|
|
929
|
+
var quarterOf = (date) => Math.min(3, Math.floor(date.getMinutes() / 15));
|
|
930
|
+
var DroppableCellImpl = ({ id, date, resourceId, children, className, style, onClick, onSlotClick }) => {
|
|
471
931
|
const { isOver, setNodeRef } = useDroppable({
|
|
472
932
|
id,
|
|
473
933
|
data: { date, resourceId }
|
|
474
934
|
});
|
|
475
|
-
const
|
|
476
|
-
const quarterClass = minutes === 0 ? "hover:bg-blue-50/50 dark:hover:bg-blue-900/10" : minutes === 15 ? "hover:bg-blue-50/80 dark:hover:bg-blue-900/20" : minutes === 30 ? "hover:bg-blue-100/50 dark:hover:bg-blue-900/30" : "hover:bg-blue-100/80 dark:hover:bg-blue-900/40";
|
|
477
|
-
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";
|
|
935
|
+
const quarter = quarterOf(date);
|
|
478
936
|
return /* @__PURE__ */ React6.createElement(
|
|
479
937
|
"div",
|
|
480
938
|
{
|
|
481
939
|
ref: setNodeRef,
|
|
482
940
|
className: cn(
|
|
483
941
|
className,
|
|
484
|
-
|
|
485
|
-
isOver &&
|
|
942
|
+
QUARTER_HOVER[quarter],
|
|
943
|
+
isOver && QUARTER_ACTIVE[quarter],
|
|
944
|
+
onSlotClick && "cursor-pointer"
|
|
486
945
|
),
|
|
487
946
|
style,
|
|
488
|
-
onClick
|
|
947
|
+
onClick: onSlotClick ? () => onSlotClick(date, resourceId) : onClick
|
|
489
948
|
},
|
|
490
949
|
children
|
|
491
950
|
);
|
|
492
951
|
};
|
|
952
|
+
var shallowEqualStyle = (a, b) => {
|
|
953
|
+
if (a === b) return true;
|
|
954
|
+
if (!a || !b) return false;
|
|
955
|
+
const keys = Object.keys(a);
|
|
956
|
+
return keys.length === Object.keys(b).length && keys.every((key) => a[key] === b[key]);
|
|
957
|
+
};
|
|
958
|
+
var DroppableCell = React6.memo(
|
|
959
|
+
DroppableCellImpl,
|
|
960
|
+
(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)
|
|
961
|
+
);
|
|
493
962
|
|
|
494
963
|
// src/views/MonthView.tsx
|
|
495
|
-
var EventItem = React7.memo(({ event, onEventClick }) => /* @__PURE__ */ React7.createElement(DraggableEvent, { event }, /* @__PURE__ */ React7.createElement(
|
|
964
|
+
var EventItem = React7.memo(({ event, onEventClick, onEventContextMenu }) => /* @__PURE__ */ React7.createElement(DraggableEvent, { event }, /* @__PURE__ */ React7.createElement(
|
|
496
965
|
"div",
|
|
497
966
|
{
|
|
498
967
|
className: cn(
|
|
@@ -508,7 +977,8 @@ var EventItem = React7.memo(({ event, onEventClick }) => /* @__PURE__ */ React7.
|
|
|
508
977
|
onClick: (e) => {
|
|
509
978
|
e.stopPropagation();
|
|
510
979
|
onEventClick?.(event);
|
|
511
|
-
}
|
|
980
|
+
},
|
|
981
|
+
onContextMenu: onEventContextMenu ? (e) => onEventContextMenu(event, e) : void 0
|
|
512
982
|
},
|
|
513
983
|
/* @__PURE__ */ React7.createElement("span", { className: "font-medium" }, event.title)
|
|
514
984
|
)));
|
|
@@ -519,43 +989,25 @@ var MonthView = ({
|
|
|
519
989
|
onEventClick,
|
|
520
990
|
onDateClick,
|
|
521
991
|
timezone,
|
|
522
|
-
locale
|
|
992
|
+
locale,
|
|
993
|
+
translations,
|
|
994
|
+
onEventContextMenu
|
|
523
995
|
}) => {
|
|
524
|
-
const weekStartsOn = locale
|
|
525
|
-
const days = useMemo(
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
);
|
|
529
|
-
const weekDays = useMemo(() => {
|
|
530
|
-
const start = startOfWeek2(currentDate, { weekStartsOn });
|
|
531
|
-
const end = endOfWeek2(currentDate, { weekStartsOn });
|
|
532
|
-
return eachDayOfInterval2({ start, end });
|
|
533
|
-
}, [currentDate, weekStartsOn]);
|
|
534
|
-
const getZonedDate = useCallback((date) => {
|
|
535
|
-
return timezone ? toZonedTime2(date, timezone) : date;
|
|
536
|
-
}, [timezone]);
|
|
537
|
-
const eventsByDay = useMemo(() => {
|
|
538
|
-
const map = /* @__PURE__ */ new Map();
|
|
539
|
-
events.forEach((event) => {
|
|
540
|
-
const zonedStart = getZonedDate(event.start);
|
|
541
|
-
const key = format5(zonedStart, "yyyy-MM-dd");
|
|
542
|
-
if (!map.has(key)) {
|
|
543
|
-
map.set(key, []);
|
|
544
|
-
}
|
|
545
|
-
map.get(key).push(event);
|
|
546
|
-
});
|
|
547
|
-
return map;
|
|
548
|
-
}, [events, getZonedDate]);
|
|
996
|
+
const weekStartsOn = resolveWeekStartsOn(locale, 1);
|
|
997
|
+
const days = useMemo(() => getMonthGrid(currentDate, weekStartsOn), [currentDate, weekStartsOn]);
|
|
998
|
+
const weekDays = useMemo(() => getWeekDays(currentDate, weekStartsOn), [currentDate, weekStartsOn]);
|
|
999
|
+
const eventsByDay = eventsByStartDay(events, timezone);
|
|
1000
|
+
const today = displayToday(timezone);
|
|
549
1001
|
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(
|
|
550
1002
|
"div",
|
|
551
1003
|
{
|
|
552
1004
|
key: day.toISOString(),
|
|
553
1005
|
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"
|
|
554
1006
|
},
|
|
555
|
-
|
|
1007
|
+
format7(day, "EEE", { locale })
|
|
556
1008
|
))), /* @__PURE__ */ React7.createElement("div", { className: "grid grid-cols-7", style: { gridAutoRows: "130px" } }, days.map((day) => {
|
|
557
|
-
const
|
|
558
|
-
const dayEvents = eventsByDay.get(
|
|
1009
|
+
const dayKey2 = format7(day, "yyyy-MM-dd");
|
|
1010
|
+
const dayEvents = eventsByDay.get(dayKey2) || [];
|
|
559
1011
|
const isCurrentMonth = isSameMonth2(day, currentDate);
|
|
560
1012
|
const cellId = day.toISOString();
|
|
561
1013
|
return /* @__PURE__ */ React7.createElement(
|
|
@@ -567,26 +1019,99 @@ var MonthView = ({
|
|
|
567
1019
|
className: cn(
|
|
568
1020
|
"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",
|
|
569
1021
|
!isCurrentMonth && "bg-muted/5 text-muted-foreground/60",
|
|
570
|
-
|
|
1022
|
+
isSameDay3(day, today) && "bg-primary/5 ring-1 ring-inset ring-primary/20"
|
|
571
1023
|
),
|
|
572
1024
|
onClick: () => onDateClick?.(day)
|
|
573
1025
|
},
|
|
574
1026
|
/* @__PURE__ */ React7.createElement("div", { className: "flex justify-between items-start" }, /* @__PURE__ */ React7.createElement("div", { className: cn(
|
|
575
1027
|
"text-sm font-semibold w-7 h-7 flex items-center justify-center rounded-full transition-all duration-200",
|
|
576
|
-
|
|
577
|
-
) },
|
|
578
|
-
/* @__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}-${
|
|
1028
|
+
isSameDay3(day, today) ? "bg-primary text-primary-foreground shadow-md shadow-primary/30" : "group-hover:bg-accent"
|
|
1029
|
+
) }, 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)),
|
|
1030
|
+
/* @__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, onEventContextMenu })), dayEvents.length > 4 && // U7: "+N more" opens the day instead of doing nothing.
|
|
1031
|
+
/* @__PURE__ */ React7.createElement(
|
|
1032
|
+
"button",
|
|
1033
|
+
{
|
|
1034
|
+
type: "button",
|
|
1035
|
+
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",
|
|
1036
|
+
onClick: (e) => {
|
|
1037
|
+
e.stopPropagation();
|
|
1038
|
+
onDateClick?.(day);
|
|
1039
|
+
}
|
|
1040
|
+
},
|
|
1041
|
+
interpolate(translations?.moreEvents || "+{n} more", { n: dayEvents.length - 4 })
|
|
1042
|
+
))
|
|
579
1043
|
);
|
|
580
1044
|
}))));
|
|
581
1045
|
};
|
|
582
1046
|
|
|
583
1047
|
// src/views/WeekView.tsx
|
|
584
1048
|
import React9 from "react";
|
|
585
|
-
import {
|
|
586
|
-
|
|
1049
|
+
import { addDays as addDays2, format as format8, differenceInMinutes, isSameDay as isSameDay4 } from "date-fns";
|
|
1050
|
+
|
|
1051
|
+
// src/core/layout.ts
|
|
1052
|
+
var layoutCache = /* @__PURE__ */ new WeakMap();
|
|
1053
|
+
function layoutDayEvents(segments) {
|
|
1054
|
+
const cached = layoutCache.get(segments);
|
|
1055
|
+
if (cached) return cached;
|
|
1056
|
+
const sorted = [...segments].sort(
|
|
1057
|
+
(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)
|
|
1058
|
+
);
|
|
1059
|
+
const result = [];
|
|
1060
|
+
let cluster = [];
|
|
1061
|
+
let columnEnds = [];
|
|
1062
|
+
let clusterEnd = -Infinity;
|
|
1063
|
+
const closeCluster = () => {
|
|
1064
|
+
for (const placed of cluster) placed.columns = columnEnds.length;
|
|
1065
|
+
cluster = [];
|
|
1066
|
+
columnEnds = [];
|
|
1067
|
+
};
|
|
1068
|
+
for (const segment of sorted) {
|
|
1069
|
+
const start = segment.start.getTime();
|
|
1070
|
+
const end = Math.max(segment.end.getTime(), start + 1);
|
|
1071
|
+
if (start >= clusterEnd) closeCluster();
|
|
1072
|
+
let column = columnEnds.findIndex((columnEnd) => columnEnd <= start);
|
|
1073
|
+
if (column === -1) {
|
|
1074
|
+
column = columnEnds.length;
|
|
1075
|
+
columnEnds.push(end);
|
|
1076
|
+
} else {
|
|
1077
|
+
columnEnds[column] = end;
|
|
1078
|
+
}
|
|
1079
|
+
const placed = { ...segment, column, columns: 1 };
|
|
1080
|
+
cluster.push(placed);
|
|
1081
|
+
result.push(placed);
|
|
1082
|
+
clusterEnd = Math.max(clusterEnd, end);
|
|
1083
|
+
}
|
|
1084
|
+
closeCluster();
|
|
1085
|
+
layoutCache.set(segments, result);
|
|
1086
|
+
return result;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
// src/components/dnd/ResizableEvent.tsx
|
|
1090
|
+
import React8, { useState as useState2, useRef, useCallback } from "react";
|
|
1091
|
+
|
|
1092
|
+
// src/core/dnd.ts
|
|
1093
|
+
function computeDrop({ event, overDate, overResourceId, grabOffsetMs = 0, view, tz }) {
|
|
1094
|
+
let wallStart;
|
|
1095
|
+
if (view === "month") {
|
|
1096
|
+
const original = toDisplay(event.start, tz);
|
|
1097
|
+
wallStart = new Date(overDate);
|
|
1098
|
+
wallStart.setHours(original.getHours(), original.getMinutes(), original.getSeconds(), original.getMilliseconds());
|
|
1099
|
+
} else {
|
|
1100
|
+
wallStart = new Date(overDate.getTime() - grabOffsetMs);
|
|
1101
|
+
wallStart.setSeconds(0, 0);
|
|
1102
|
+
}
|
|
1103
|
+
const start = fromDisplay(wallStart, tz);
|
|
1104
|
+
const end = new Date(start.getTime() + (event.end.getTime() - event.start.getTime()));
|
|
1105
|
+
const resourceId = overResourceId ?? event.resourceId;
|
|
1106
|
+
if (start.getTime() === event.start.getTime() && resourceId === event.resourceId) return null;
|
|
1107
|
+
return { start, end, resourceId };
|
|
1108
|
+
}
|
|
1109
|
+
function computeResizeEnd(event, deltaMinutes, minDuration = 15) {
|
|
1110
|
+
const minEnd = event.start.getTime() + minDuration * 6e4;
|
|
1111
|
+
return new Date(Math.max(event.end.getTime() + deltaMinutes * 6e4, minEnd));
|
|
1112
|
+
}
|
|
587
1113
|
|
|
588
1114
|
// src/components/dnd/ResizableEvent.tsx
|
|
589
|
-
import React8, { useState as useState2, useRef, useCallback as useCallback2 } from "react";
|
|
590
1115
|
var ResizableEvent = ({
|
|
591
1116
|
event,
|
|
592
1117
|
hourHeight,
|
|
@@ -604,7 +1129,7 @@ var ResizableEvent = ({
|
|
|
604
1129
|
const startYRef = useRef(0);
|
|
605
1130
|
const startHeightRef = useRef(0);
|
|
606
1131
|
const resizeHeightRef = useRef(null);
|
|
607
|
-
const handleResizeStart =
|
|
1132
|
+
const handleResizeStart = useCallback((e) => {
|
|
608
1133
|
e.preventDefault();
|
|
609
1134
|
e.stopPropagation();
|
|
610
1135
|
const clientY = "touches" in e ? e.touches[0].clientY : e.clientY;
|
|
@@ -628,14 +1153,8 @@ var ResizableEvent = ({
|
|
|
628
1153
|
setIsResizing(false);
|
|
629
1154
|
const finalHeight = resizeHeightRef.current;
|
|
630
1155
|
if (finalHeight !== null && containerRef.current && onResize) {
|
|
631
|
-
const
|
|
632
|
-
|
|
633
|
-
const minutesDiff = heightDiff / hourHeight * 60;
|
|
634
|
-
const newEnd = new Date(event.end);
|
|
635
|
-
newEnd.setMinutes(newEnd.getMinutes() + minutesDiff);
|
|
636
|
-
if (newEnd > event.start) {
|
|
637
|
-
onResize(event, newEnd);
|
|
638
|
-
}
|
|
1156
|
+
const minutesDiff = (finalHeight - startHeightRef.current) / hourHeight * 60;
|
|
1157
|
+
onResize(event, computeResizeEnd(event, minutesDiff, minDuration));
|
|
639
1158
|
}
|
|
640
1159
|
resizeHeightRef.current = null;
|
|
641
1160
|
setResizeHeight(null);
|
|
@@ -649,7 +1168,7 @@ var ResizableEvent = ({
|
|
|
649
1168
|
document.addEventListener("touchmove", handleMove, { passive: false });
|
|
650
1169
|
document.addEventListener("touchend", handleEnd);
|
|
651
1170
|
}, [event, hourHeight, minDuration, onResize]);
|
|
652
|
-
const handlePointerDown =
|
|
1171
|
+
const handlePointerDown = useCallback((e) => {
|
|
653
1172
|
e.stopPropagation();
|
|
654
1173
|
}, []);
|
|
655
1174
|
return /* @__PURE__ */ React8.createElement(
|
|
@@ -686,7 +1205,16 @@ var ResizableEvent = ({
|
|
|
686
1205
|
};
|
|
687
1206
|
|
|
688
1207
|
// src/views/WeekView.tsx
|
|
689
|
-
var
|
|
1208
|
+
var HOUR_HEIGHT = 60;
|
|
1209
|
+
var HOURS = Array.from({ length: 24 }, (_, i) => i);
|
|
1210
|
+
var QUARTERS = [0, 15, 30, 45];
|
|
1211
|
+
var NO_SEGMENTS = [];
|
|
1212
|
+
var slotDate = (day, hour, minute) => {
|
|
1213
|
+
const date = new Date(day);
|
|
1214
|
+
date.setHours(hour, minute, 0, 0);
|
|
1215
|
+
return date;
|
|
1216
|
+
};
|
|
1217
|
+
var WeekViewImpl = ({
|
|
690
1218
|
currentDate,
|
|
691
1219
|
events,
|
|
692
1220
|
onEventClick,
|
|
@@ -694,16 +1222,19 @@ var WeekView = ({
|
|
|
694
1222
|
onEventResize,
|
|
695
1223
|
timezone,
|
|
696
1224
|
locale,
|
|
697
|
-
readonly
|
|
1225
|
+
readonly,
|
|
1226
|
+
onEventContextMenu
|
|
698
1227
|
}) => {
|
|
699
|
-
const
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
const
|
|
704
|
-
|
|
1228
|
+
const weekDays = React9.useMemo(
|
|
1229
|
+
() => getWeekDays(currentDate, resolveWeekStartsOn(locale, 1)),
|
|
1230
|
+
[currentDate, locale]
|
|
1231
|
+
);
|
|
1232
|
+
const visibleRange = React9.useMemo(
|
|
1233
|
+
() => ({ start: weekDays[0], end: addDays2(weekDays[weekDays.length - 1], 1) }),
|
|
1234
|
+
[weekDays]
|
|
1235
|
+
);
|
|
705
1236
|
const scrollContainerRef = React9.useRef(null);
|
|
706
|
-
const [now, setNow] = React9.useState(/* @__PURE__ */ new Date());
|
|
1237
|
+
const [now, setNow] = React9.useState(() => /* @__PURE__ */ new Date());
|
|
707
1238
|
React9.useEffect(() => {
|
|
708
1239
|
const interval = setInterval(() => setNow(/* @__PURE__ */ new Date()), 6e4);
|
|
709
1240
|
return () => clearInterval(interval);
|
|
@@ -711,132 +1242,93 @@ var WeekView = ({
|
|
|
711
1242
|
React9.useEffect(() => {
|
|
712
1243
|
if (scrollContainerRef.current) {
|
|
713
1244
|
const offsetHour = 4;
|
|
714
|
-
scrollContainerRef.current.scrollTop = ((/* @__PURE__ */ new Date()).getHours() - offsetHour) *
|
|
715
|
-
}
|
|
716
|
-
}, []);
|
|
717
|
-
const getZonedDate = (date) => {
|
|
718
|
-
return timezone ? toZonedTime3(date, timezone) : date;
|
|
719
|
-
};
|
|
720
|
-
const zonedNow = getZonedDate(/* @__PURE__ */ new Date());
|
|
721
|
-
const getTimezoneDisplay = (tz) => {
|
|
722
|
-
const date = now;
|
|
723
|
-
let displayTime = "";
|
|
724
|
-
let acronym = "";
|
|
725
|
-
if (!tz) {
|
|
726
|
-
displayTime = format6(date, "HH:mm");
|
|
727
|
-
try {
|
|
728
|
-
acronym = new Intl.DateTimeFormat("en-US", { timeZoneName: "short" }).formatToParts(date).find((part) => part.type === "timeZoneName")?.value || "";
|
|
729
|
-
} catch {
|
|
730
|
-
acronym = "LOC";
|
|
731
|
-
}
|
|
732
|
-
} else {
|
|
733
|
-
try {
|
|
734
|
-
const zDate = toZonedTime3(date, tz);
|
|
735
|
-
displayTime = format6(zDate, "HH:mm");
|
|
736
|
-
acronym = new Intl.DateTimeFormat("en-US", { timeZone: tz, timeZoneName: "short" }).formatToParts(date).find((part) => part.type === "timeZoneName")?.value || "";
|
|
737
|
-
} catch {
|
|
738
|
-
displayTime = format6(date, "HH:mm");
|
|
739
|
-
acronym = tz;
|
|
740
|
-
}
|
|
1245
|
+
scrollContainerRef.current.scrollTop = (toDisplay(/* @__PURE__ */ new Date(), timezone).getHours() - offsetHour) * HOUR_HEIGHT;
|
|
741
1246
|
}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
const
|
|
745
|
-
const
|
|
746
|
-
const
|
|
747
|
-
const
|
|
1247
|
+
}, [timezone]);
|
|
1248
|
+
const segmentsByDay = segmentsByDisplayDay(events, timezone, visibleRange);
|
|
1249
|
+
const today = displayToday(timezone, now);
|
|
1250
|
+
const zonedNow = toDisplay(now, timezone);
|
|
1251
|
+
const nowTop = minutesSinceDisplayMidnight(now, timezone) / 60 * HOUR_HEIGHT;
|
|
1252
|
+
const showsToday = weekDays.some((day) => isSameDay4(day, today));
|
|
1253
|
+
const zone = resolveTimeZone(timezone);
|
|
1254
|
+
const zoneLabel = timeZoneAbbreviation(now, zone) || zone || "LOC";
|
|
1255
|
+
const formats = timeFormats(locale);
|
|
748
1256
|
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(
|
|
749
1257
|
"div",
|
|
750
1258
|
{
|
|
751
1259
|
ref: scrollContainerRef,
|
|
752
1260
|
className: "flex-1 overflow-y-auto scrollbar-hide relative bg-background scroll-smooth"
|
|
753
1261
|
},
|
|
754
|
-
/* @__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" },
|
|
1262
|
+
/* @__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(
|
|
755
1263
|
"w-9 h-9 flex items-center justify-center rounded-xl mx-auto text-sm font-semibold transition-all duration-200",
|
|
756
|
-
|
|
757
|
-
) },
|
|
1264
|
+
isSameDay4(day, today) ? "bg-primary text-primary-foreground shadow-lg shadow-primary/30 scale-110" : "text-foreground hover:bg-accent/80"
|
|
1265
|
+
) }, format8(day, "d", { locale })))))),
|
|
758
1266
|
/* @__PURE__ */ React9.createElement(
|
|
759
1267
|
"div",
|
|
760
1268
|
{
|
|
761
1269
|
className: "flex min-w-full relative",
|
|
762
|
-
style: { height:
|
|
1270
|
+
style: { height: HOURS.length * HOUR_HEIGHT }
|
|
763
1271
|
},
|
|
764
|
-
/* @__PURE__ */ React9.createElement("div", { className: "flex-none w-16 border-r-[0.5px] border-border/30 relative bg-muted/5" },
|
|
1272
|
+
/* @__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(
|
|
765
1273
|
"div",
|
|
766
1274
|
{
|
|
767
1275
|
key: hour,
|
|
768
1276
|
className: "relative w-full text-[11px] text-muted-foreground/80 text-right pr-3 font-medium tabular-nums box-border",
|
|
769
|
-
style: { height:
|
|
1277
|
+
style: { height: HOUR_HEIGHT }
|
|
1278
|
+
},
|
|
1279
|
+
/* @__PURE__ */ React9.createElement("span", { className: "block -translate-y-1/2" }, hour !== 0 && format8(slotDate(currentDate, hour, 0), formats.hour, { locale }))
|
|
1280
|
+
)), showsToday && /* @__PURE__ */ React9.createElement(
|
|
1281
|
+
"div",
|
|
1282
|
+
{
|
|
1283
|
+
className: "absolute left-0 w-full pointer-events-none z-30 flex justify-end pr-2",
|
|
1284
|
+
style: { top: `${nowTop}px` }
|
|
770
1285
|
},
|
|
771
|
-
/* @__PURE__ */ React9.createElement("span", { className: "
|
|
772
|
-
))
|
|
1286
|
+
/* @__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 }))
|
|
1287
|
+
)),
|
|
773
1288
|
/* @__PURE__ */ React9.createElement("div", { className: "flex-1 grid grid-cols-7 relative" }, weekDays.map((day, dayIndex) => {
|
|
774
|
-
const
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
"
|
|
781
|
-
{
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
{
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
date: cellDate,
|
|
796
|
-
className: "w-full absolute left-0 right-0 z-0 transition-colors",
|
|
797
|
-
style: {
|
|
798
|
-
height: "25%",
|
|
799
|
-
top: `${minute / 60 * 100}%`
|
|
800
|
-
}
|
|
1289
|
+
const dayKey2 = format8(day, "yyyy-MM-dd");
|
|
1290
|
+
const positioned = layoutDayEvents(segmentsByDay.get(dayKey2) ?? NO_SEGMENTS);
|
|
1291
|
+
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(
|
|
1292
|
+
"div",
|
|
1293
|
+
{
|
|
1294
|
+
key: hour,
|
|
1295
|
+
className: "w-full border-b-[0.5px] border-dashed border-border/20 box-border relative hover:bg-accent/5 transition-colors",
|
|
1296
|
+
style: { height: HOUR_HEIGHT }
|
|
1297
|
+
},
|
|
1298
|
+
QUARTERS.map((minute) => {
|
|
1299
|
+
const cellDate = slotDate(day, hour, minute);
|
|
1300
|
+
return /* @__PURE__ */ React9.createElement(
|
|
1301
|
+
DroppableCell,
|
|
1302
|
+
{
|
|
1303
|
+
key: minute,
|
|
1304
|
+
id: cellDate.toISOString(),
|
|
1305
|
+
date: cellDate,
|
|
1306
|
+
className: "w-full absolute left-0 right-0 z-0 transition-colors",
|
|
1307
|
+
style: {
|
|
1308
|
+
height: "25%",
|
|
1309
|
+
top: `${minute / 60 * 100}%`
|
|
801
1310
|
},
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
);
|
|
812
|
-
|
|
813
|
-
const
|
|
814
|
-
|
|
815
|
-
const s1 = getZonedDate(event.start).getTime();
|
|
816
|
-
const e1 = getZonedDate(event.end).getTime();
|
|
817
|
-
const s2 = getZonedDate(e.start).getTime();
|
|
818
|
-
const e2 = getZonedDate(e.end).getTime();
|
|
819
|
-
return s1 < e2 && e1 > s2;
|
|
820
|
-
});
|
|
821
|
-
const group = [event, ...overlappingEvents].sort(
|
|
822
|
-
(a, b) => getZonedDate(a.start).getTime() - getZonedDate(b.start).getTime() || (a.id > b.id ? 1 : -1)
|
|
823
|
-
// stable sort
|
|
824
|
-
);
|
|
825
|
-
const index = group.findIndex((e) => e.id === event.id);
|
|
826
|
-
const count = group.length;
|
|
827
|
-
const widthPercent = 100 / count;
|
|
828
|
-
const leftPercent = index * widthPercent;
|
|
829
|
-
const zonedEventStart = getZonedDate(event.start);
|
|
830
|
-
const zonedEventEnd = getZonedDate(event.end);
|
|
831
|
-
const startMinutes = zonedEventStart.getHours() * 60 + zonedEventStart.getMinutes();
|
|
832
|
-
const durationMinutes = differenceInMinutes(zonedEventEnd, zonedEventStart);
|
|
833
|
-
const top = startMinutes / 60 * hourHeight;
|
|
834
|
-
const height = durationMinutes / 60 * hourHeight;
|
|
1311
|
+
onSlotClick: onTimeSlotClick
|
|
1312
|
+
}
|
|
1313
|
+
);
|
|
1314
|
+
})
|
|
1315
|
+
)), positioned.map(({ event, start, end, column, columns }) => {
|
|
1316
|
+
const widthPercent = 100 / columns;
|
|
1317
|
+
const leftPercent = column * widthPercent;
|
|
1318
|
+
const zonedEventStart = toDisplay(event.start, timezone);
|
|
1319
|
+
const zonedEventEnd = toDisplay(event.end, timezone);
|
|
1320
|
+
const startMinutes = start.getHours() * 60 + start.getMinutes();
|
|
1321
|
+
const durationMinutes = differenceInMinutes(end, start);
|
|
1322
|
+
const top = startMinutes / 60 * HOUR_HEIGHT;
|
|
1323
|
+
const height = durationMinutes / 60 * HOUR_HEIGHT;
|
|
835
1324
|
const isShortEvent = durationMinutes < 60;
|
|
1325
|
+
const isLastSegment = end.getTime() === zonedEventEnd.getTime();
|
|
836
1326
|
return /* @__PURE__ */ React9.createElement(
|
|
837
1327
|
DraggableEvent,
|
|
838
1328
|
{
|
|
839
|
-
key: `${event.id}-${
|
|
1329
|
+
key: `${event.id}-${dayKey2}`,
|
|
1330
|
+
dragId: `${event.id}@${dayKey2}`,
|
|
1331
|
+
grabOffsetMs: start.getTime() - zonedEventStart.getTime(),
|
|
840
1332
|
event,
|
|
841
1333
|
className: `absolute z-10 transition-all ${readonly ? "cursor-default" : ""}`,
|
|
842
1334
|
style: {
|
|
@@ -845,15 +1337,15 @@ var WeekView = ({
|
|
|
845
1337
|
left: `${leftPercent}%`,
|
|
846
1338
|
width: `${widthPercent}%`,
|
|
847
1339
|
// Add minimal spacing between overlapping events
|
|
848
|
-
paddingRight:
|
|
1340
|
+
paddingRight: columns > 1 ? "2px" : "0"
|
|
849
1341
|
}
|
|
850
1342
|
},
|
|
851
1343
|
/* @__PURE__ */ React9.createElement(
|
|
852
1344
|
ResizableEvent,
|
|
853
1345
|
{
|
|
854
|
-
readonly,
|
|
1346
|
+
readonly: readonly || !isLastSegment,
|
|
855
1347
|
event,
|
|
856
|
-
hourHeight,
|
|
1348
|
+
hourHeight: HOUR_HEIGHT,
|
|
857
1349
|
onResize: onEventResize,
|
|
858
1350
|
className: "h-full",
|
|
859
1351
|
style: { height: "100%" }
|
|
@@ -868,7 +1360,7 @@ var WeekView = ({
|
|
|
868
1360
|
!event.color && "border-primary/20 bg-primary/10",
|
|
869
1361
|
isShortEvent ? "px-1 flex items-center justify-center" : "p-2",
|
|
870
1362
|
// Add active border for overlapped events to distinguish them
|
|
871
|
-
|
|
1363
|
+
columns > 1 && "border-l-4 border-l-primary/50"
|
|
872
1364
|
),
|
|
873
1365
|
style: {
|
|
874
1366
|
height: "100%",
|
|
@@ -881,46 +1373,44 @@ var WeekView = ({
|
|
|
881
1373
|
e.stopPropagation();
|
|
882
1374
|
onEventClick?.(event);
|
|
883
1375
|
},
|
|
884
|
-
|
|
1376
|
+
onContextMenu: onEventContextMenu ? (e) => onEventContextMenu(event, e) : void 0,
|
|
1377
|
+
title: columns > 1 ? `${event.title} (${column + 1}/${columns})` : void 0
|
|
885
1378
|
},
|
|
886
1379
|
/* @__PURE__ */ React9.createElement("div", { className: "flex flex-col h-full overflow-hidden w-full" }, /* @__PURE__ */ React9.createElement("div", { className: cn(
|
|
887
1380
|
"font-semibold truncate text-foreground/90 leading-tight",
|
|
888
1381
|
isShortEvent ? "text-xs text-center" : "text-xs"
|
|
889
|
-
) }, 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" },
|
|
1382
|
+
) }, 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))
|
|
890
1383
|
)
|
|
891
1384
|
)
|
|
892
1385
|
);
|
|
893
|
-
}),
|
|
1386
|
+
}), isSameDay4(day, today) && /* @__PURE__ */ React9.createElement(
|
|
894
1387
|
"div",
|
|
895
1388
|
{
|
|
896
1389
|
className: "absolute left-0 right-0 z-20 pointer-events-none flex items-center",
|
|
897
|
-
style: {
|
|
898
|
-
top: `${(zonedNow.getHours() * 60 + zonedNow.getMinutes()) / 60 * hourHeight}px`
|
|
899
|
-
}
|
|
1390
|
+
style: { top: `${nowTop}px` }
|
|
900
1391
|
},
|
|
901
1392
|
/* @__PURE__ */ React9.createElement("div", { className: "h-[2px] w-full bg-gradient-to-r from-primary via-primary to-primary/50" }),
|
|
902
1393
|
/* @__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" })
|
|
903
1394
|
));
|
|
904
1395
|
}))
|
|
905
|
-
),
|
|
906
|
-
/* @__PURE__ */ React9.createElement(
|
|
907
|
-
"div",
|
|
908
|
-
{
|
|
909
|
-
className: "absolute left-0 w-16 pointer-events-none z-30 flex justify-end pr-2",
|
|
910
|
-
style: {
|
|
911
|
-
top: `${(zonedNow.getHours() * 60 + zonedNow.getMinutes()) / 60 * hourHeight + 80}px`
|
|
912
|
-
}
|
|
913
|
-
},
|
|
914
|
-
/* @__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" }, format6(zonedNow, nowFormat, { locale }))
|
|
915
1396
|
)
|
|
916
1397
|
));
|
|
917
1398
|
};
|
|
1399
|
+
var WeekView = React9.memo(WeekViewImpl);
|
|
918
1400
|
|
|
919
1401
|
// src/views/DayView.tsx
|
|
920
1402
|
import React10 from "react";
|
|
921
|
-
import { format as
|
|
922
|
-
|
|
923
|
-
var
|
|
1403
|
+
import { addDays as addDays3, format as format9, differenceInMinutes as differenceInMinutes2, isSameDay as isSameDay5, startOfDay as startOfDay3 } from "date-fns";
|
|
1404
|
+
var HOUR_HEIGHT2 = 80;
|
|
1405
|
+
var HOURS2 = Array.from({ length: 24 }, (_, i) => i);
|
|
1406
|
+
var QUARTERS2 = [0, 15, 30, 45];
|
|
1407
|
+
var NO_SEGMENTS2 = [];
|
|
1408
|
+
var slotDate2 = (day, hour, minute) => {
|
|
1409
|
+
const date = new Date(day);
|
|
1410
|
+
date.setHours(hour, minute, 0, 0);
|
|
1411
|
+
return date;
|
|
1412
|
+
};
|
|
1413
|
+
var DayViewImpl = ({
|
|
924
1414
|
currentDate,
|
|
925
1415
|
events,
|
|
926
1416
|
onEventClick,
|
|
@@ -928,12 +1418,16 @@ var DayView = ({
|
|
|
928
1418
|
onEventResize,
|
|
929
1419
|
timezone,
|
|
930
1420
|
locale,
|
|
931
|
-
readonly
|
|
1421
|
+
readonly,
|
|
1422
|
+
onEventContextMenu,
|
|
1423
|
+
translations
|
|
932
1424
|
}) => {
|
|
933
|
-
const
|
|
934
|
-
|
|
1425
|
+
const visibleRange = React10.useMemo(() => {
|
|
1426
|
+
const start = startOfDay3(currentDate);
|
|
1427
|
+
return { start, end: addDays3(start, 1) };
|
|
1428
|
+
}, [currentDate]);
|
|
935
1429
|
const scrollContainerRef = React10.useRef(null);
|
|
936
|
-
const [now, setNow] = React10.useState(/* @__PURE__ */ new Date());
|
|
1430
|
+
const [now, setNow] = React10.useState(() => /* @__PURE__ */ new Date());
|
|
937
1431
|
React10.useEffect(() => {
|
|
938
1432
|
const interval = setInterval(() => setNow(/* @__PURE__ */ new Date()), 6e4);
|
|
939
1433
|
return () => clearInterval(interval);
|
|
@@ -941,99 +1435,71 @@ var DayView = ({
|
|
|
941
1435
|
React10.useEffect(() => {
|
|
942
1436
|
if (scrollContainerRef.current) {
|
|
943
1437
|
const offsetHour = 3;
|
|
944
|
-
scrollContainerRef.current.scrollTop = ((/* @__PURE__ */ new Date()).getHours() - offsetHour) *
|
|
1438
|
+
scrollContainerRef.current.scrollTop = (toDisplay(/* @__PURE__ */ new Date(), timezone).getHours() - offsetHour) * HOUR_HEIGHT2;
|
|
945
1439
|
}
|
|
946
|
-
}, []);
|
|
947
|
-
const
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
const zonedNow =
|
|
951
|
-
const
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
});
|
|
955
|
-
const is24h = uses24HourClock(locale);
|
|
956
|
-
const timeFormat = is24h ? "H:mm" : "h a";
|
|
957
|
-
const eventTimeFormat = is24h ? "H:mm" : "h:mm a";
|
|
958
|
-
const nowFormat = is24h ? "H:mm" : "h:mm a";
|
|
959
|
-
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" }, format7(currentDate, "EEEE, MMMM d, yyyy", { locale })), isToday4(currentDate) && /* @__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: hours.length * hourHeight } }, /* @__PURE__ */ React10.createElement("div", { className: "w-20 bg-muted/5 border-r-[0.5px] border-border/30 relative" }, hours.map((hour) => /* @__PURE__ */ React10.createElement(
|
|
1440
|
+
}, [timezone]);
|
|
1441
|
+
const dayKey2 = format9(currentDate, "yyyy-MM-dd");
|
|
1442
|
+
const positioned = layoutDayEvents(segmentsByDisplayDay(events, timezone, visibleRange).get(dayKey2) ?? NO_SEGMENTS2);
|
|
1443
|
+
const isToday2 = isSameDay5(currentDate, displayToday(timezone, now));
|
|
1444
|
+
const zonedNow = toDisplay(now, timezone);
|
|
1445
|
+
const nowTop = minutesSinceDisplayMidnight(now, timezone) / 60 * HOUR_HEIGHT2;
|
|
1446
|
+
const formats = timeFormats(locale);
|
|
1447
|
+
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" }, translations?.today || "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(
|
|
960
1448
|
"div",
|
|
961
1449
|
{
|
|
962
1450
|
key: hour,
|
|
963
1451
|
className: "relative w-full",
|
|
964
|
-
style: { height:
|
|
1452
|
+
style: { height: HOUR_HEIGHT2 }
|
|
965
1453
|
},
|
|
966
|
-
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" },
|
|
967
|
-
)),
|
|
1454
|
+
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 }))
|
|
1455
|
+
)), isToday2 && /* @__PURE__ */ React10.createElement(
|
|
968
1456
|
"div",
|
|
969
1457
|
{
|
|
970
1458
|
className: "absolute left-0 w-full pointer-events-none z-30 flex justify-end pr-2",
|
|
971
|
-
style: {
|
|
972
|
-
top: `${(zonedNow.getHours() * 60 + zonedNow.getMinutes()) / 60 * hourHeight}px`
|
|
973
|
-
}
|
|
1459
|
+
style: { top: `${nowTop}px` }
|
|
974
1460
|
},
|
|
975
|
-
/* @__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" },
|
|
976
|
-
)), /* @__PURE__ */ React10.createElement("div", { className: "flex-1 relative" },
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
style: {
|
|
996
|
-
height: "25%",
|
|
997
|
-
top: `${minute / 60 * 100}%`
|
|
998
|
-
}
|
|
1461
|
+
/* @__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 }))
|
|
1462
|
+
)), /* @__PURE__ */ React10.createElement("div", { className: "flex-1 relative" }, HOURS2.map((hour) => /* @__PURE__ */ React10.createElement(
|
|
1463
|
+
"div",
|
|
1464
|
+
{
|
|
1465
|
+
key: hour,
|
|
1466
|
+
className: "border-b-[0.5px] border-dashed border-border/20 box-border relative hover:bg-accent/5 transition-colors",
|
|
1467
|
+
style: { height: HOUR_HEIGHT2 }
|
|
1468
|
+
},
|
|
1469
|
+
QUARTERS2.map((minute) => {
|
|
1470
|
+
const cellDate = slotDate2(currentDate, hour, minute);
|
|
1471
|
+
return /* @__PURE__ */ React10.createElement(
|
|
1472
|
+
DroppableCell,
|
|
1473
|
+
{
|
|
1474
|
+
key: minute,
|
|
1475
|
+
id: cellDate.toISOString(),
|
|
1476
|
+
date: cellDate,
|
|
1477
|
+
className: "w-full absolute left-0 right-0 z-0 transition-colors",
|
|
1478
|
+
style: {
|
|
1479
|
+
height: "25%",
|
|
1480
|
+
top: `${minute / 60 * 100}%`
|
|
999
1481
|
},
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
);
|
|
1010
|
-
|
|
1011
|
-
const
|
|
1012
|
-
|
|
1013
|
-
const s1 = getZonedDate(event.start).getTime();
|
|
1014
|
-
const e1 = getZonedDate(event.end).getTime();
|
|
1015
|
-
const s2 = getZonedDate(e.start).getTime();
|
|
1016
|
-
const e2 = getZonedDate(e.end).getTime();
|
|
1017
|
-
return s1 < e2 && e1 > s2;
|
|
1018
|
-
});
|
|
1019
|
-
const group = [event, ...overlappingEvents].sort(
|
|
1020
|
-
(a, b) => getZonedDate(a.start).getTime() - getZonedDate(b.start).getTime() || (a.id > b.id ? 1 : -1)
|
|
1021
|
-
);
|
|
1022
|
-
const index = group.findIndex((e) => e.id === event.id);
|
|
1023
|
-
const count = group.length;
|
|
1024
|
-
const widthPercent = 100 / count;
|
|
1025
|
-
const leftPercent = index * widthPercent;
|
|
1026
|
-
const zonedStart = getZonedDate(event.start);
|
|
1027
|
-
const zonedEnd = getZonedDate(event.end);
|
|
1028
|
-
const startMinutes = zonedStart.getHours() * 60 + zonedStart.getMinutes();
|
|
1029
|
-
const durationMinutes = differenceInMinutes2(zonedEnd, zonedStart);
|
|
1030
|
-
const top = startMinutes / 60 * hourHeight;
|
|
1031
|
-
const height = durationMinutes / 60 * hourHeight;
|
|
1482
|
+
onSlotClick: onTimeSlotClick
|
|
1483
|
+
}
|
|
1484
|
+
);
|
|
1485
|
+
})
|
|
1486
|
+
)), positioned.map(({ event, start, end, column, columns }) => {
|
|
1487
|
+
const widthPercent = 100 / columns;
|
|
1488
|
+
const leftPercent = column * widthPercent;
|
|
1489
|
+
const zonedStart = toDisplay(event.start, timezone);
|
|
1490
|
+
const zonedEnd = toDisplay(event.end, timezone);
|
|
1491
|
+
const startMinutes = start.getHours() * 60 + start.getMinutes();
|
|
1492
|
+
const durationMinutes = differenceInMinutes2(end, start);
|
|
1493
|
+
const top = startMinutes / 60 * HOUR_HEIGHT2;
|
|
1494
|
+
const height = durationMinutes / 60 * HOUR_HEIGHT2;
|
|
1032
1495
|
const isShortEvent = durationMinutes < 45;
|
|
1496
|
+
const isLastSegment = end.getTime() === zonedEnd.getTime();
|
|
1033
1497
|
return /* @__PURE__ */ React10.createElement(
|
|
1034
1498
|
DraggableEvent,
|
|
1035
1499
|
{
|
|
1036
|
-
key: `${event.id}-${
|
|
1500
|
+
key: `${event.id}-${dayKey2}`,
|
|
1501
|
+
dragId: `${event.id}@${dayKey2}`,
|
|
1502
|
+
grabOffsetMs: start.getTime() - zonedStart.getTime(),
|
|
1037
1503
|
event,
|
|
1038
1504
|
className: `absolute z-10 transition-all ${readonly ? "cursor-default" : ""}`,
|
|
1039
1505
|
style: {
|
|
@@ -1041,15 +1507,15 @@ var DayView = ({
|
|
|
1041
1507
|
height: `${Math.max(height, 28)}px`,
|
|
1042
1508
|
left: `calc(${leftPercent}% + 4px)`,
|
|
1043
1509
|
width: `calc(${widthPercent}% - 8px)`,
|
|
1044
|
-
paddingRight:
|
|
1510
|
+
paddingRight: columns > 1 ? "2px" : "0"
|
|
1045
1511
|
}
|
|
1046
1512
|
},
|
|
1047
1513
|
/* @__PURE__ */ React10.createElement(
|
|
1048
1514
|
ResizableEvent,
|
|
1049
1515
|
{
|
|
1050
|
-
readonly,
|
|
1516
|
+
readonly: readonly || !isLastSegment,
|
|
1051
1517
|
event,
|
|
1052
|
-
hourHeight,
|
|
1518
|
+
hourHeight: HOUR_HEIGHT2,
|
|
1053
1519
|
onResize: onEventResize,
|
|
1054
1520
|
className: "h-full",
|
|
1055
1521
|
style: { height: "100%" }
|
|
@@ -1063,44 +1529,44 @@ var DayView = ({
|
|
|
1063
1529
|
readonly || isEventDragDisabled(event) ? "cursor-default" : "cursor-grab active:cursor-grabbing",
|
|
1064
1530
|
!event.color && "bg-primary/10 border-primary/20",
|
|
1065
1531
|
isShortEvent ? "px-2 flex items-center" : "px-3 py-2",
|
|
1066
|
-
|
|
1532
|
+
columns > 1 && "border-l-4"
|
|
1067
1533
|
),
|
|
1068
1534
|
style: {
|
|
1069
1535
|
backgroundColor: event.color ? `${event.color}15` : void 0,
|
|
1070
1536
|
borderColor: event.color ? `${event.color}30` : void 0,
|
|
1071
1537
|
borderLeftColor: event.color || "var(--primary)",
|
|
1072
|
-
borderLeftWidth:
|
|
1538
|
+
borderLeftWidth: columns > 1 ? "4px" : "3px"
|
|
1073
1539
|
},
|
|
1074
1540
|
onClick: (e) => {
|
|
1075
1541
|
e.stopPropagation();
|
|
1076
1542
|
onEventClick?.(event);
|
|
1077
1543
|
},
|
|
1078
|
-
|
|
1544
|
+
onContextMenu: onEventContextMenu ? (e) => onEventContextMenu(event, e) : void 0,
|
|
1545
|
+
title: columns > 1 ? `${event.title} (${column + 1}/${columns})` : void 0
|
|
1079
1546
|
},
|
|
1080
1547
|
/* @__PURE__ */ React10.createElement("div", { className: "flex flex-col h-full overflow-hidden w-full" }, /* @__PURE__ */ React10.createElement("div", { className: cn(
|
|
1081
1548
|
"font-semibold truncate leading-tight",
|
|
1082
1549
|
isShortEvent ? "text-xs" : "text-sm",
|
|
1083
1550
|
event.color ? "text-foreground" : "text-foreground/90"
|
|
1084
|
-
) }, event.title), !isShortEvent && /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement("div", { className: "text-xs text-muted-foreground mt-0.5 font-medium" },
|
|
1551
|
+
) }, 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))
|
|
1085
1552
|
)
|
|
1086
1553
|
)
|
|
1087
1554
|
);
|
|
1088
|
-
}),
|
|
1555
|
+
}), isToday2 && /* @__PURE__ */ React10.createElement(
|
|
1089
1556
|
"div",
|
|
1090
1557
|
{
|
|
1091
1558
|
className: "absolute left-0 right-0 z-20 pointer-events-none flex items-center",
|
|
1092
|
-
style: {
|
|
1093
|
-
top: `${(zonedNow.getHours() * 60 + zonedNow.getMinutes()) / 60 * hourHeight}px`
|
|
1094
|
-
}
|
|
1559
|
+
style: { top: `${nowTop}px` }
|
|
1095
1560
|
},
|
|
1096
1561
|
/* @__PURE__ */ React10.createElement("div", { className: "h-[2px] w-full bg-gradient-to-r from-primary via-primary to-primary/50" }),
|
|
1097
1562
|
/* @__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" })
|
|
1098
1563
|
)))));
|
|
1099
1564
|
};
|
|
1565
|
+
var DayView = React10.memo(DayViewImpl);
|
|
1100
1566
|
|
|
1101
1567
|
// src/views/AgendaView.tsx
|
|
1102
1568
|
import React12, { useMemo as useMemo2 } from "react";
|
|
1103
|
-
import { format as
|
|
1569
|
+
import { format as format10, isSameDay as isSameDay6, addDays as addDays4, startOfDay as startOfDay4, differenceInMinutes as differenceInMinutes3 } from "date-fns";
|
|
1104
1570
|
|
|
1105
1571
|
// src/components/EmptyState.tsx
|
|
1106
1572
|
import React11 from "react";
|
|
@@ -1126,10 +1592,10 @@ var formatDuration = (start, end) => {
|
|
|
1126
1592
|
if (remainingMinutes === 0) return `${hours}h`;
|
|
1127
1593
|
return `${hours}h ${remainingMinutes}m`;
|
|
1128
1594
|
};
|
|
1129
|
-
var getDateLabel = (date, locale, translations) => {
|
|
1130
|
-
if (
|
|
1131
|
-
if (
|
|
1132
|
-
return
|
|
1595
|
+
var getDateLabel = (date, today, locale, translations) => {
|
|
1596
|
+
if (isSameDay6(date, today)) return translations?.today || "Today";
|
|
1597
|
+
if (isSameDay6(date, addDays4(today, 1))) return translations?.tomorrow || "Tomorrow";
|
|
1598
|
+
return format10(date, "EEEE", { locale });
|
|
1133
1599
|
};
|
|
1134
1600
|
var AgendaView = ({
|
|
1135
1601
|
currentDate,
|
|
@@ -1137,24 +1603,25 @@ var AgendaView = ({
|
|
|
1137
1603
|
onEventClick,
|
|
1138
1604
|
onCreateEvent,
|
|
1139
1605
|
locale,
|
|
1140
|
-
translations
|
|
1606
|
+
translations,
|
|
1607
|
+
timezone,
|
|
1608
|
+
onEventContextMenu
|
|
1141
1609
|
}) => {
|
|
1142
1610
|
const is24h = uses24HourClock(locale);
|
|
1143
|
-
const
|
|
1144
|
-
const
|
|
1611
|
+
const { time: timePattern, compact: compactTimePattern } = timeFormats(locale);
|
|
1612
|
+
const today = displayToday(timezone);
|
|
1145
1613
|
const groupedEvents = useMemo2(() => {
|
|
1146
|
-
const
|
|
1614
|
+
const byDay = eventsByStartDay(events, timezone);
|
|
1147
1615
|
const groups = [];
|
|
1148
|
-
const sortedEvents = [...events].sort((a, b) => a.start.getTime() - b.start.getTime());
|
|
1149
1616
|
for (let i = 0; i < 30; i++) {
|
|
1150
|
-
const day =
|
|
1151
|
-
const dayEvents =
|
|
1152
|
-
if (dayEvents
|
|
1153
|
-
groups.push({ date: day, events: dayEvents });
|
|
1617
|
+
const day = addDays4(startOfDay4(currentDate), i);
|
|
1618
|
+
const dayEvents = byDay.get(format10(day, "yyyy-MM-dd"));
|
|
1619
|
+
if (dayEvents?.length) {
|
|
1620
|
+
groups.push({ date: day, events: [...dayEvents].sort((a, b) => a.start.getTime() - b.start.getTime()) });
|
|
1154
1621
|
}
|
|
1155
1622
|
}
|
|
1156
1623
|
return groups;
|
|
1157
|
-
}, [currentDate, events]);
|
|
1624
|
+
}, [currentDate, events, timezone]);
|
|
1158
1625
|
const container = {
|
|
1159
1626
|
hidden: { opacity: 0 },
|
|
1160
1627
|
show: {
|
|
@@ -1185,16 +1652,17 @@ var AgendaView = ({
|
|
|
1185
1652
|
},
|
|
1186
1653
|
/* @__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(
|
|
1187
1654
|
"flex flex-col items-center justify-center w-16 h-16 rounded-2xl transition-all",
|
|
1188
|
-
|
|
1189
|
-
) }, /* @__PURE__ */ React12.createElement("span", { className: "text-2xl font-bold leading-none" },
|
|
1655
|
+
isSameDay6(group.date, today) ? "bg-primary text-primary-foreground shadow-lg shadow-primary/30" : "bg-muted/50 text-foreground"
|
|
1656
|
+
) }, /* @__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(
|
|
1190
1657
|
"text-lg font-semibold",
|
|
1191
|
-
|
|
1192
|
-
) }, getDateLabel(group.date, 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")))),
|
|
1658
|
+
isSameDay6(group.date, today) && "text-primary"
|
|
1659
|
+
) }, 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")))),
|
|
1193
1660
|
/* @__PURE__ */ React12.createElement("div", { className: "py-4 space-y-3" }, group.events.map((event) => /* @__PURE__ */ React12.createElement(
|
|
1194
1661
|
motion.div,
|
|
1195
1662
|
{
|
|
1196
1663
|
key: event.id,
|
|
1197
1664
|
onClick: () => onEventClick?.(event),
|
|
1665
|
+
onContextMenu: onEventContextMenu ? (e) => onEventContextMenu(event, e) : void 0,
|
|
1198
1666
|
className: cn(
|
|
1199
1667
|
"group relative flex gap-4 p-4 rounded-2xl border border-border/40",
|
|
1200
1668
|
"hover:border-primary/40 hover:shadow-lg hover:shadow-primary/5",
|
|
@@ -1211,14 +1679,14 @@ var AgendaView = ({
|
|
|
1211
1679
|
style: { backgroundColor: event.color || "var(--primary)" }
|
|
1212
1680
|
}
|
|
1213
1681
|
),
|
|
1214
|
-
/* @__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" },
|
|
1682
|
+
/* @__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)))),
|
|
1215
1683
|
/* @__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(
|
|
1216
1684
|
"div",
|
|
1217
1685
|
{
|
|
1218
1686
|
className: "w-3 h-3 rounded-full shrink-0 mt-1.5",
|
|
1219
1687
|
style: { backgroundColor: event.color || "var(--primary)" }
|
|
1220
1688
|
}
|
|
1221
|
-
)), 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,
|
|
1689
|
+
)), 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)))),
|
|
1222
1690
|
/* @__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" })))
|
|
1223
1691
|
)))
|
|
1224
1692
|
))
|
|
@@ -1227,23 +1695,26 @@ var AgendaView = ({
|
|
|
1227
1695
|
|
|
1228
1696
|
// src/views/ResourceView.tsx
|
|
1229
1697
|
import React13, { useRef as useRef2 } from "react";
|
|
1230
|
-
import { format as
|
|
1698
|
+
import { format as format11, startOfDay as startOfDay5, differenceInMinutes as differenceInMinutes4 } from "date-fns";
|
|
1231
1699
|
var ResourceView = ({
|
|
1232
1700
|
currentDate,
|
|
1233
1701
|
events,
|
|
1234
1702
|
resources,
|
|
1235
1703
|
onEventClick,
|
|
1236
1704
|
onTimeSlotClick,
|
|
1237
|
-
locale
|
|
1705
|
+
locale,
|
|
1706
|
+
timezone,
|
|
1707
|
+
translations,
|
|
1708
|
+
onEventContextMenu
|
|
1238
1709
|
}) => {
|
|
1239
1710
|
const containerRef = useRef2(null);
|
|
1240
1711
|
const hours = Array.from({ length: 24 }, (_, i) => i);
|
|
1241
1712
|
const hourWidth = 100;
|
|
1242
|
-
const timeFormat =
|
|
1713
|
+
const timeFormat = timeFormats(locale).hour;
|
|
1243
1714
|
const getEventStyle = (event) => {
|
|
1244
|
-
const start =
|
|
1245
|
-
const end =
|
|
1246
|
-
const dayStart =
|
|
1715
|
+
const start = toDisplay(event.start, timezone);
|
|
1716
|
+
const end = toDisplay(event.end, timezone);
|
|
1717
|
+
const dayStart = startOfDay5(currentDate);
|
|
1247
1718
|
const startMinutes = differenceInMinutes4(start, dayStart);
|
|
1248
1719
|
const durationMinutes = differenceInMinutes4(end, start);
|
|
1249
1720
|
const left = startMinutes / 60 * hourWidth;
|
|
@@ -1254,19 +1725,19 @@ var ResourceView = ({
|
|
|
1254
1725
|
// Min width for visibility
|
|
1255
1726
|
};
|
|
1256
1727
|
};
|
|
1257
|
-
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(
|
|
1728
|
+
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" }, translations?.resource || "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(
|
|
1258
1729
|
"div",
|
|
1259
1730
|
{
|
|
1260
1731
|
key: hour,
|
|
1261
1732
|
className: "border-r border-border/50 text-xs text-muted-foreground p-2 font-medium shrink-0",
|
|
1262
1733
|
style: { width: hourWidth }
|
|
1263
1734
|
},
|
|
1264
|
-
|
|
1735
|
+
format11((/* @__PURE__ */ new Date()).setHours(hour, 0, 0, 0), timeFormat, { locale })
|
|
1265
1736
|
))))), /* @__PURE__ */ React13.createElement("div", { className: "flex-1 overflow-auto relative", ref: containerRef }, /* @__PURE__ */ React13.createElement("div", { className: "min-w-fit" }, resources.map((resource) => {
|
|
1266
1737
|
const resourceEvents = events.filter(
|
|
1267
|
-
(e) => e.resourceId === resource.id &&
|
|
1738
|
+
(e) => e.resourceId === resource.id && isSameDisplayDay(e.start, currentDate, timezone)
|
|
1268
1739
|
);
|
|
1269
|
-
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(
|
|
1740
|
+
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" }, interpolate(translations?.idLabel || "ID: {id}", { id: resource.id })))), /* @__PURE__ */ React13.createElement("div", { className: "relative flex", style: { width: hours.length * hourWidth } }, hours.map((hour) => /* @__PURE__ */ React13.createElement(
|
|
1270
1741
|
"div",
|
|
1271
1742
|
{
|
|
1272
1743
|
key: hour,
|
|
@@ -1274,20 +1745,18 @@ var ResourceView = ({
|
|
|
1274
1745
|
style: { width: hourWidth }
|
|
1275
1746
|
},
|
|
1276
1747
|
[0, 15, 30, 45].map((minute) => {
|
|
1277
|
-
const
|
|
1278
|
-
|
|
1279
|
-
const slotId = `${resource.id}-${
|
|
1748
|
+
const slotDate3 = new Date(currentDate);
|
|
1749
|
+
slotDate3.setHours(hour, minute, 0, 0);
|
|
1750
|
+
const slotId = `${resource.id}-${slotDate3.toISOString()}`;
|
|
1280
1751
|
return /* @__PURE__ */ React13.createElement(
|
|
1281
1752
|
DroppableCell,
|
|
1282
1753
|
{
|
|
1283
1754
|
key: minute,
|
|
1284
1755
|
id: slotId,
|
|
1285
|
-
date:
|
|
1756
|
+
date: slotDate3,
|
|
1286
1757
|
resourceId: resource.id,
|
|
1287
1758
|
className: "h-full flex-1 border-r border-border/10 last:border-border/30 hover:bg-accent/10 transition-colors",
|
|
1288
|
-
|
|
1289
|
-
onTimeSlotClick?.(slotDate, resource.id);
|
|
1290
|
-
}
|
|
1759
|
+
onSlotClick: onTimeSlotClick
|
|
1291
1760
|
}
|
|
1292
1761
|
);
|
|
1293
1762
|
})
|
|
@@ -1300,6 +1769,7 @@ var ResourceView = ({
|
|
|
1300
1769
|
e.stopPropagation();
|
|
1301
1770
|
onEventClick?.(event);
|
|
1302
1771
|
},
|
|
1772
|
+
onContextMenu: onEventContextMenu ? (e) => onEventContextMenu(event, e) : void 0,
|
|
1303
1773
|
className: "absolute top-2 bottom-2 rounded-md px-2 py-1 text-xs font-medium border shadow-sm cursor-pointer overflow-hidden hover:brightness-95 transition-all z-10",
|
|
1304
1774
|
style: {
|
|
1305
1775
|
...getEventStyle(event),
|
|
@@ -1314,7 +1784,7 @@ var ResourceView = ({
|
|
|
1314
1784
|
};
|
|
1315
1785
|
|
|
1316
1786
|
// src/components/EventModal.tsx
|
|
1317
|
-
import React15, { useState as useState3, useEffect as useEffect3 } from "react";
|
|
1787
|
+
import React15, { useState as useState3, useEffect as useEffect3, useInsertionEffect, useRef as useRef4 } from "react";
|
|
1318
1788
|
|
|
1319
1789
|
// src/components/ui/Modal.tsx
|
|
1320
1790
|
import React14, { useEffect as useEffect2, useRef as useRef3 } from "react";
|
|
@@ -1325,7 +1795,8 @@ var Modal = ({
|
|
|
1325
1795
|
title,
|
|
1326
1796
|
children,
|
|
1327
1797
|
className,
|
|
1328
|
-
hideHeader = false
|
|
1798
|
+
hideHeader = false,
|
|
1799
|
+
closeLabel = "Close"
|
|
1329
1800
|
}) => {
|
|
1330
1801
|
const overlayRef = useRef3(null);
|
|
1331
1802
|
useEffect2(() => {
|
|
@@ -1366,14 +1837,14 @@ var Modal = ({
|
|
|
1366
1837
|
className: "h-9 w-9 p-0 rounded-xl hover:bg-accent/80 transition-all duration-200"
|
|
1367
1838
|
},
|
|
1368
1839
|
/* @__PURE__ */ React14.createElement(X, { className: "h-4 w-4" }),
|
|
1369
|
-
/* @__PURE__ */ React14.createElement("span", { className: "sr-only" },
|
|
1840
|
+
/* @__PURE__ */ React14.createElement("span", { className: "sr-only" }, closeLabel)
|
|
1370
1841
|
)),
|
|
1371
1842
|
/* @__PURE__ */ React14.createElement("div", { className: cn("overflow-y-auto flex-1", !hideHeader && "p-6") }, children)
|
|
1372
1843
|
));
|
|
1373
1844
|
};
|
|
1374
1845
|
|
|
1375
1846
|
// src/components/EventModal.tsx
|
|
1376
|
-
import { format as
|
|
1847
|
+
import { format as format12, differenceInMinutes as differenceInMinutes5 } from "date-fns";
|
|
1377
1848
|
import { motion as motion2, AnimatePresence } from "framer-motion";
|
|
1378
1849
|
import {
|
|
1379
1850
|
Clock as Clock2,
|
|
@@ -1396,18 +1867,7 @@ import {
|
|
|
1396
1867
|
Tag,
|
|
1397
1868
|
ExternalLink
|
|
1398
1869
|
} from "lucide-react";
|
|
1399
|
-
var REMINDER_OPTIONS = [
|
|
1400
|
-
{ value: 0, label: "At time of event" },
|
|
1401
|
-
{ value: 5, label: "5 minutes before" },
|
|
1402
|
-
{ value: 10, label: "10 minutes before" },
|
|
1403
|
-
{ value: 15, label: "15 minutes before" },
|
|
1404
|
-
{ value: 30, label: "30 minutes before" },
|
|
1405
|
-
{ value: 60, label: "1 hour before" },
|
|
1406
|
-
{ value: 120, label: "2 hours before" },
|
|
1407
|
-
{ value: 1440, label: "1 day before" },
|
|
1408
|
-
{ value: 2880, label: "2 days before" },
|
|
1409
|
-
{ value: 10080, label: "1 week before" }
|
|
1410
|
-
];
|
|
1870
|
+
var REMINDER_OPTIONS = [0, 5, 10, 15, 30, 60, 120, 1440, 2880, 10080];
|
|
1411
1871
|
var COLOR_PALETTE = [
|
|
1412
1872
|
"#3b82f6",
|
|
1413
1873
|
// Blue
|
|
@@ -1430,6 +1890,30 @@ var COLOR_PALETTE = [
|
|
|
1430
1890
|
"#6366f1"
|
|
1431
1891
|
// Indigo
|
|
1432
1892
|
];
|
|
1893
|
+
var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1894
|
+
var mergeGuests = (current, added) => {
|
|
1895
|
+
const seen = new Set(current.map((email) => email.toLowerCase()));
|
|
1896
|
+
const merged = [...current];
|
|
1897
|
+
for (const email of added) {
|
|
1898
|
+
if (!seen.has(email.toLowerCase())) {
|
|
1899
|
+
seen.add(email.toLowerCase());
|
|
1900
|
+
merged.push(email);
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
return merged;
|
|
1904
|
+
};
|
|
1905
|
+
var reminderLabel = (minutes, t) => {
|
|
1906
|
+
if (minutes === 0) return t.reminderAtTime;
|
|
1907
|
+
if (minutes === 10080) return t.reminderWeek;
|
|
1908
|
+
if (minutes === 1440) return t.reminderDay;
|
|
1909
|
+
if (minutes % 1440 === 0) return interpolate(t.reminderDays, { n: minutes / 1440 });
|
|
1910
|
+
if (minutes === 60) return t.reminderHour;
|
|
1911
|
+
if (minutes % 60 === 0) return interpolate(t.reminderHours, { n: minutes / 60 });
|
|
1912
|
+
return interpolate(t.reminderMinutes, { n: minutes });
|
|
1913
|
+
};
|
|
1914
|
+
var avatarHue = (email) => [...email].reduce((hash, char) => (hash * 31 + char.charCodeAt(0)) % 360, 7);
|
|
1915
|
+
var mapsUrl = (location) => `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(location)}`;
|
|
1916
|
+
var isSafeUrl = (url) => /^(https?:|mailto:|blob:|\/|\.\/|\.\.\/|[^:]*$)/i.test(url.trim());
|
|
1433
1917
|
var EventModal = ({
|
|
1434
1918
|
isOpen,
|
|
1435
1919
|
onClose,
|
|
@@ -1437,9 +1921,15 @@ var EventModal = ({
|
|
|
1437
1921
|
initialDate,
|
|
1438
1922
|
onSave,
|
|
1439
1923
|
onDelete,
|
|
1440
|
-
calendars
|
|
1924
|
+
calendars,
|
|
1925
|
+
translations: t,
|
|
1926
|
+
locale
|
|
1441
1927
|
}) => {
|
|
1442
1928
|
const [mode, setMode] = useState3("create");
|
|
1929
|
+
const translationsRef = useRef4(t);
|
|
1930
|
+
useInsertionEffect(() => {
|
|
1931
|
+
translationsRef.current = t;
|
|
1932
|
+
});
|
|
1443
1933
|
const [formData, setFormData] = useState3({
|
|
1444
1934
|
title: "",
|
|
1445
1935
|
description: "",
|
|
@@ -1452,22 +1942,18 @@ var EventModal = ({
|
|
|
1452
1942
|
type: void 0,
|
|
1453
1943
|
recurrence: void 0,
|
|
1454
1944
|
attachments: [],
|
|
1455
|
-
reminders: [
|
|
1945
|
+
reminders: []
|
|
1456
1946
|
});
|
|
1457
1947
|
const [isCalendarDropdownOpen, setIsCalendarDropdownOpen] = useState3(false);
|
|
1458
1948
|
const [isColorPickerOpen, setIsColorPickerOpen] = useState3(false);
|
|
1459
1949
|
const [isReminderDropdownOpen, setIsReminderDropdownOpen] = useState3(false);
|
|
1460
|
-
const [
|
|
1461
|
-
const
|
|
1462
|
-
"alice@example.com",
|
|
1463
|
-
"bob@example.com",
|
|
1464
|
-
"charlie@example.com",
|
|
1465
|
-
"diana@example.com",
|
|
1466
|
-
"evan@example.com"
|
|
1467
|
-
];
|
|
1468
|
-
const [selectedGuests, setSelectedGuests] = useState3([]);
|
|
1950
|
+
const [guestInput, setGuestInput] = useState3("");
|
|
1951
|
+
const [guestError, setGuestError] = useState3(false);
|
|
1469
1952
|
useEffect3(() => {
|
|
1953
|
+
const defaultReminders = () => [{ id: "1", type: "notification", time: 30, label: reminderLabel(30, translationsRef.current) }];
|
|
1470
1954
|
if (isOpen) {
|
|
1955
|
+
setGuestInput("");
|
|
1956
|
+
setGuestError(false);
|
|
1471
1957
|
if (event) {
|
|
1472
1958
|
setMode("view");
|
|
1473
1959
|
setFormData({
|
|
@@ -1475,9 +1961,9 @@ var EventModal = ({
|
|
|
1475
1961
|
start: new Date(event.start),
|
|
1476
1962
|
end: new Date(event.end),
|
|
1477
1963
|
attachments: event.attachments || [],
|
|
1478
|
-
reminders: event.reminders ||
|
|
1964
|
+
reminders: event.reminders || defaultReminders(),
|
|
1965
|
+
guests: event.guests || []
|
|
1479
1966
|
});
|
|
1480
|
-
setSelectedGuests(event.guests || []);
|
|
1481
1967
|
} else {
|
|
1482
1968
|
setMode("create");
|
|
1483
1969
|
const start = initialDate || /* @__PURE__ */ new Date();
|
|
@@ -1493,21 +1979,34 @@ var EventModal = ({
|
|
|
1493
1979
|
color: "#3b82f6",
|
|
1494
1980
|
calendarId: calendars?.[0]?.id,
|
|
1495
1981
|
attachments: [],
|
|
1496
|
-
reminders:
|
|
1982
|
+
reminders: defaultReminders(),
|
|
1983
|
+
guests: []
|
|
1497
1984
|
});
|
|
1498
|
-
setSelectedGuests([]);
|
|
1499
1985
|
}
|
|
1500
1986
|
}
|
|
1501
1987
|
}, [isOpen, event, initialDate, calendars]);
|
|
1502
|
-
const
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1988
|
+
const guests = formData.guests || [];
|
|
1989
|
+
const addGuests = (raw) => {
|
|
1990
|
+
const entries = raw.split(/[\s,;]+/).filter(Boolean);
|
|
1991
|
+
const valid = entries.filter((entry) => EMAIL.test(entry));
|
|
1992
|
+
const invalid = entries.filter((entry) => !EMAIL.test(entry));
|
|
1993
|
+
if (valid.length > 0) {
|
|
1994
|
+
setFormData((prev) => ({ ...prev, guests: mergeGuests(prev.guests || [], valid) }));
|
|
1995
|
+
}
|
|
1996
|
+
setGuestInput(invalid.join(", "));
|
|
1997
|
+
setGuestError(invalid.length > 0);
|
|
1998
|
+
return invalid.length === 0;
|
|
1999
|
+
};
|
|
2000
|
+
const removeGuest = (email) => {
|
|
2001
|
+
setFormData((prev) => ({ ...prev, guests: (prev.guests || []).filter((g) => g !== email) }));
|
|
1508
2002
|
};
|
|
1509
2003
|
const handleSubmit = (e) => {
|
|
1510
2004
|
e.preventDefault();
|
|
2005
|
+
const pending = guestInput.split(/[\s,;]+/).filter(Boolean);
|
|
2006
|
+
if (pending.some((entry) => !EMAIL.test(entry))) {
|
|
2007
|
+
addGuests(guestInput);
|
|
2008
|
+
return;
|
|
2009
|
+
}
|
|
1511
2010
|
let eventColor = formData.color;
|
|
1512
2011
|
if (formData.calendarId && calendars) {
|
|
1513
2012
|
const selectedCal = calendars.find((c) => c.id === formData.calendarId);
|
|
@@ -1515,8 +2014,10 @@ var EventModal = ({
|
|
|
1515
2014
|
eventColor = selectedCal.color;
|
|
1516
2015
|
}
|
|
1517
2016
|
}
|
|
2017
|
+
const finalGuests = mergeGuests(guests, pending);
|
|
1518
2018
|
onSave({
|
|
1519
2019
|
...formData,
|
|
2020
|
+
guests: finalGuests,
|
|
1520
2021
|
color: eventColor,
|
|
1521
2022
|
id: event?.id
|
|
1522
2023
|
});
|
|
@@ -1530,7 +2031,7 @@ var EventModal = ({
|
|
|
1530
2031
|
};
|
|
1531
2032
|
const formatDateForInput = (date) => {
|
|
1532
2033
|
if (!date) return "";
|
|
1533
|
-
return
|
|
2034
|
+
return format12(date, "yyyy-MM-dd'T'HH:mm");
|
|
1534
2035
|
};
|
|
1535
2036
|
const handleDateChange = (field, value) => {
|
|
1536
2037
|
const date = new Date(value);
|
|
@@ -1550,12 +2051,12 @@ var EventModal = ({
|
|
|
1550
2051
|
}
|
|
1551
2052
|
}
|
|
1552
2053
|
};
|
|
1553
|
-
const addReminder = (minutes
|
|
2054
|
+
const addReminder = (minutes) => {
|
|
1554
2055
|
const newReminder = {
|
|
1555
|
-
id: Math.random().toString(36).
|
|
2056
|
+
id: Math.random().toString(36).slice(2, 11),
|
|
1556
2057
|
type: "notification",
|
|
1557
2058
|
time: minutes,
|
|
1558
|
-
label
|
|
2059
|
+
label: reminderLabel(minutes, t)
|
|
1559
2060
|
};
|
|
1560
2061
|
setFormData((prev) => ({
|
|
1561
2062
|
...prev,
|
|
@@ -1569,23 +2070,20 @@ var EventModal = ({
|
|
|
1569
2070
|
reminders: prev.reminders?.filter((r) => r.id !== id)
|
|
1570
2071
|
}));
|
|
1571
2072
|
};
|
|
1572
|
-
const handleDownload = (attachment) => {
|
|
1573
|
-
const element = document.createElement("a");
|
|
1574
|
-
const file = new Blob(["Dummy content for " + attachment.name], { type: "text/plain" });
|
|
1575
|
-
element.href = URL.createObjectURL(file);
|
|
1576
|
-
element.download = attachment.name;
|
|
1577
|
-
document.body.appendChild(element);
|
|
1578
|
-
element.click();
|
|
1579
|
-
document.body.removeChild(element);
|
|
1580
|
-
};
|
|
1581
2073
|
const getDurationText = () => {
|
|
1582
2074
|
if (!formData.start || !formData.end) return "";
|
|
1583
2075
|
const mins = differenceInMinutes5(formData.end, formData.start);
|
|
1584
|
-
if (mins < 60) return
|
|
2076
|
+
if (mins < 60) return interpolate(t.durationMinutes, { n: mins });
|
|
1585
2077
|
const hours = Math.floor(mins / 60);
|
|
1586
2078
|
const remainingMins = mins % 60;
|
|
1587
|
-
if (remainingMins === 0) return
|
|
1588
|
-
return
|
|
2079
|
+
if (remainingMins === 0) return interpolate(t.durationHours, { n: hours });
|
|
2080
|
+
return interpolate(t.durationHoursMinutes, { h: hours, m: remainingMins });
|
|
2081
|
+
};
|
|
2082
|
+
const { time: timeFormat } = timeFormats(locale);
|
|
2083
|
+
const renderAttachment = (att) => {
|
|
2084
|
+
const content = /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement("div", { className: "p-2 bg-primary/10 rounded-lg" }, /* @__PURE__ */ React15.createElement(File, { className: "w-4 h-4 text-primary" })), /* @__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)), att.url && /* @__PURE__ */ React15.createElement(Download, { className: "w-4 h-4 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" }));
|
|
2085
|
+
const className = "flex items-center gap-3 p-3 rounded-xl border border-border/30 bg-muted/20 transition-all group";
|
|
2086
|
+
return att.url && isSafeUrl(att.url) ? /* @__PURE__ */ React15.createElement("a", { key: att.id, href: att.url, target: "_blank", rel: "noopener noreferrer", download: att.name, className: cn(className, "hover:bg-muted/40 cursor-pointer") }, content) : /* @__PURE__ */ React15.createElement("div", { key: att.id, className }, content);
|
|
1589
2087
|
};
|
|
1590
2088
|
const renderViewMode = () => /* @__PURE__ */ React15.createElement("div", { className: "flex flex-col bg-background overflow-hidden w-full max-h-[90vh]" }, /* @__PURE__ */ React15.createElement(
|
|
1591
2089
|
"div",
|
|
@@ -1601,12 +2099,13 @@ var EventModal = ({
|
|
|
1601
2099
|
className: "w-3 h-3 rounded-full",
|
|
1602
2100
|
style: { backgroundColor: event?.color || "#3b82f6" }
|
|
1603
2101
|
}
|
|
1604
|
-
), /* @__PURE__ */ React15.createElement("span", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider" }, calendars?.find((c) => c.id === event?.calendarId)?.label ||
|
|
2102
|
+
), /* @__PURE__ */ React15.createElement("span", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider" }, calendars?.find((c) => c.id === event?.calendarId)?.label || t.event)), /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React15.createElement(
|
|
1605
2103
|
"button",
|
|
1606
2104
|
{
|
|
1607
2105
|
onClick: () => setMode("edit"),
|
|
1608
2106
|
className: "p-2 hover:bg-background/80 rounded-lg transition-all text-muted-foreground hover:text-foreground",
|
|
1609
|
-
title:
|
|
2107
|
+
title: t.edit,
|
|
2108
|
+
"aria-label": t.edit
|
|
1610
2109
|
},
|
|
1611
2110
|
/* @__PURE__ */ React15.createElement(Edit2, { className: "w-4 h-4" })
|
|
1612
2111
|
), /* @__PURE__ */ React15.createElement(
|
|
@@ -1614,7 +2113,8 @@ var EventModal = ({
|
|
|
1614
2113
|
{
|
|
1615
2114
|
onClick: handleDelete,
|
|
1616
2115
|
className: "p-2 hover:bg-destructive/10 rounded-lg transition-all text-muted-foreground hover:text-destructive",
|
|
1617
|
-
title:
|
|
2116
|
+
title: t.delete,
|
|
2117
|
+
"aria-label": t.delete
|
|
1618
2118
|
},
|
|
1619
2119
|
/* @__PURE__ */ React15.createElement(Trash2, { className: "w-4 h-4" })
|
|
1620
2120
|
), /* @__PURE__ */ React15.createElement(
|
|
@@ -1622,21 +2122,25 @@ var EventModal = ({
|
|
|
1622
2122
|
{
|
|
1623
2123
|
onClick: onClose,
|
|
1624
2124
|
className: "p-2 hover:bg-background/80 rounded-lg transition-all text-muted-foreground hover:text-foreground",
|
|
1625
|
-
title:
|
|
2125
|
+
title: t.close,
|
|
2126
|
+
"aria-label": t.close
|
|
1626
2127
|
},
|
|
1627
2128
|
/* @__PURE__ */ React15.createElement(X2, { className: "w-4 h-4" })
|
|
1628
2129
|
))),
|
|
1629
|
-
/* @__PURE__ */ React15.createElement("h1", { className: "text-2xl font-bold text-foreground mb-3 leading-tight" }, event?.title ||
|
|
1630
|
-
/* @__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 &&
|
|
2130
|
+
/* @__PURE__ */ React15.createElement("h1", { className: "text-2xl font-bold text-foreground mb-3 leading-tight" }, event?.title || t.noTitle),
|
|
2131
|
+
/* @__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", { locale }))), /* @__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), timeFormat, { locale }), " \u2013 ", event?.end && format12(new Date(event.end), timeFormat, { locale }))))
|
|
1631
2132
|
), /* @__PURE__ */ React15.createElement("div", { className: "px-6 pb-6 space-y-4 overflow-y-auto flex-1" }, event?.location && /* @__PURE__ */ React15.createElement(
|
|
1632
|
-
motion2.
|
|
2133
|
+
motion2.a,
|
|
1633
2134
|
{
|
|
2135
|
+
href: mapsUrl(event.location),
|
|
2136
|
+
target: "_blank",
|
|
2137
|
+
rel: "noopener noreferrer",
|
|
1634
2138
|
initial: { opacity: 0, y: 10 },
|
|
1635
2139
|
animate: { opacity: 1, y: 0 },
|
|
1636
2140
|
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"
|
|
1637
2141
|
},
|
|
1638
2142
|
/* @__PURE__ */ React15.createElement("div", { className: "p-2 bg-primary/10 rounded-xl" }, /* @__PURE__ */ React15.createElement(MapPin, { className: "w-5 h-5 text-primary" })),
|
|
1639
|
-
/* @__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" },
|
|
2143
|
+
/* @__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" }, t.openInMaps)),
|
|
1640
2144
|
/* @__PURE__ */ React15.createElement(ExternalLink, { className: "w-4 h-4 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" })
|
|
1641
2145
|
), event?.guests && event.guests.length > 0 && /* @__PURE__ */ React15.createElement(
|
|
1642
2146
|
motion2.div,
|
|
@@ -1646,18 +2150,18 @@ var EventModal = ({
|
|
|
1646
2150
|
transition: { delay: 0.05 },
|
|
1647
2151
|
className: "space-y-3"
|
|
1648
2152
|
},
|
|
1649
|
-
/* @__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 ?
|
|
1650
|
-
/* @__PURE__ */ React15.createElement("div", { className: "flex flex-wrap gap-2" }, event.guests.map((email
|
|
2153
|
+
/* @__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 ? t.guests : t.guestSingular))),
|
|
2154
|
+
/* @__PURE__ */ React15.createElement("div", { className: "flex flex-wrap gap-2" }, event.guests.map((email) => /* @__PURE__ */ React15.createElement(
|
|
1651
2155
|
"div",
|
|
1652
2156
|
{
|
|
1653
|
-
key:
|
|
1654
|
-
className: "flex items-center gap-2 px-3 py-2 rounded-xl bg-muted/30 border border-border/30
|
|
2157
|
+
key: email,
|
|
2158
|
+
className: "flex items-center gap-2 px-3 py-2 rounded-xl bg-muted/30 border border-border/30"
|
|
1655
2159
|
},
|
|
1656
2160
|
/* @__PURE__ */ React15.createElement(
|
|
1657
2161
|
"div",
|
|
1658
2162
|
{
|
|
1659
2163
|
className: "w-8 h-8 rounded-full flex items-center justify-center text-sm font-semibold text-white",
|
|
1660
|
-
style: { backgroundColor: `hsl(${
|
|
2164
|
+
style: { backgroundColor: `hsl(${avatarHue(email)}, 70%, 50%)` }
|
|
1661
2165
|
},
|
|
1662
2166
|
email[0].toUpperCase()
|
|
1663
2167
|
),
|
|
@@ -1678,7 +2182,7 @@ var EventModal = ({
|
|
|
1678
2182
|
key: reminder.id,
|
|
1679
2183
|
className: "text-xs font-medium px-2.5 py-1 rounded-full bg-amber-500/10 text-amber-600 dark:text-amber-400"
|
|
1680
2184
|
},
|
|
1681
|
-
reminder.label ||
|
|
2185
|
+
reminder.label || reminderLabel(reminder.time, t)
|
|
1682
2186
|
)))
|
|
1683
2187
|
), event?.description && /* @__PURE__ */ React15.createElement(
|
|
1684
2188
|
motion2.div,
|
|
@@ -1688,7 +2192,7 @@ var EventModal = ({
|
|
|
1688
2192
|
transition: { delay: 0.15 },
|
|
1689
2193
|
className: "space-y-2"
|
|
1690
2194
|
},
|
|
1691
|
-
/* @__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" },
|
|
2195
|
+
/* @__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" }, t.notesLabel)),
|
|
1692
2196
|
/* @__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)
|
|
1693
2197
|
), event?.attachments && event.attachments.length > 0 && /* @__PURE__ */ React15.createElement(
|
|
1694
2198
|
motion2.div,
|
|
@@ -1698,25 +2202,16 @@ var EventModal = ({
|
|
|
1698
2202
|
transition: { delay: 0.2 },
|
|
1699
2203
|
className: "space-y-2"
|
|
1700
2204
|
},
|
|
1701
|
-
/* @__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" },
|
|
1702
|
-
/* @__PURE__ */ React15.createElement("div", { className: "grid gap-2" }, event.attachments.map(
|
|
1703
|
-
"div",
|
|
1704
|
-
{
|
|
1705
|
-
key: att.id,
|
|
1706
|
-
onClick: () => handleDownload(att),
|
|
1707
|
-
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"
|
|
1708
|
-
},
|
|
1709
|
-
/* @__PURE__ */ React15.createElement("div", { className: "p-2 bg-primary/10 rounded-lg" }, /* @__PURE__ */ React15.createElement(File, { className: "w-4 h-4 text-primary" })),
|
|
1710
|
-
/* @__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)),
|
|
1711
|
-
/* @__PURE__ */ React15.createElement(Download, { className: "w-4 h-4 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" })
|
|
1712
|
-
)))
|
|
2205
|
+
/* @__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" }, t.attachments)),
|
|
2206
|
+
/* @__PURE__ */ React15.createElement("div", { className: "grid gap-2" }, event.attachments.map(renderAttachment))
|
|
1713
2207
|
)));
|
|
1714
2208
|
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(
|
|
1715
2209
|
"button",
|
|
1716
2210
|
{
|
|
1717
2211
|
type: "button",
|
|
1718
2212
|
onClick: () => setIsColorPickerOpen(!isColorPickerOpen),
|
|
1719
|
-
className: "relative p-1"
|
|
2213
|
+
className: "relative p-1",
|
|
2214
|
+
"aria-label": t.eventColor
|
|
1720
2215
|
},
|
|
1721
2216
|
/* @__PURE__ */ React15.createElement(
|
|
1722
2217
|
"div",
|
|
@@ -1733,6 +2228,7 @@ var EventModal = ({
|
|
|
1733
2228
|
{
|
|
1734
2229
|
key: color,
|
|
1735
2230
|
type: "button",
|
|
2231
|
+
"aria-label": color,
|
|
1736
2232
|
onClick: () => {
|
|
1737
2233
|
setFormData((prev) => ({ ...prev, color }));
|
|
1738
2234
|
setIsColorPickerOpen(false);
|
|
@@ -1744,12 +2240,13 @@ var EventModal = ({
|
|
|
1744
2240
|
style: { backgroundColor: color, color }
|
|
1745
2241
|
}
|
|
1746
2242
|
)))
|
|
1747
|
-
), /* @__PURE__ */ React15.createElement("h2", { className: "text-lg font-semibold text-foreground" }, mode === "edit" ?
|
|
2243
|
+
), /* @__PURE__ */ React15.createElement("h2", { className: "text-lg font-semibold text-foreground" }, mode === "edit" ? t.editEvent : t.newEvent)), /* @__PURE__ */ React15.createElement(
|
|
1748
2244
|
"button",
|
|
1749
2245
|
{
|
|
1750
2246
|
type: "button",
|
|
1751
2247
|
onClick: onClose,
|
|
1752
|
-
className: "p-2 hover:bg-accent rounded-xl transition-all text-muted-foreground hover:text-foreground"
|
|
2248
|
+
className: "p-2 hover:bg-accent rounded-xl transition-all text-muted-foreground hover:text-foreground",
|
|
2249
|
+
"aria-label": t.close
|
|
1753
2250
|
},
|
|
1754
2251
|
/* @__PURE__ */ React15.createElement(X2, { className: "w-4 h-4" })
|
|
1755
2252
|
)), /* @__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(
|
|
@@ -1761,25 +2258,28 @@ var EventModal = ({
|
|
|
1761
2258
|
className: "w-full text-xl font-semibold border-0 border-b-2 border-transparent focus:border-primary bg-transparent px-0 py-2 placeholder-muted-foreground/50 transition-all text-foreground focus:outline-none",
|
|
1762
2259
|
value: formData.title,
|
|
1763
2260
|
onChange: (e) => setFormData({ ...formData, title: e.target.value }),
|
|
1764
|
-
placeholder:
|
|
2261
|
+
placeholder: t.title,
|
|
2262
|
+
"aria-label": t.title
|
|
1765
2263
|
}
|
|
1766
|
-
)), /* @__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" },
|
|
2264
|
+
)), /* @__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", { htmlFor: "calendarkit-event-start", className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-wider mb-1 block" }, t.start), /* @__PURE__ */ React15.createElement(
|
|
1767
2265
|
"input",
|
|
1768
2266
|
{
|
|
2267
|
+
id: "calendarkit-event-start",
|
|
1769
2268
|
type: "datetime-local",
|
|
1770
2269
|
className: "w-full bg-background border border-border/50 rounded-xl px-3 py-2 text-sm font-medium text-foreground focus:border-primary focus:ring-2 focus:ring-primary/20 transition-all",
|
|
1771
2270
|
value: formatDateForInput(formData.start),
|
|
1772
2271
|
onChange: (e) => handleDateChange("start", e.target.value)
|
|
1773
2272
|
}
|
|
1774
|
-
)), /* @__PURE__ */ React15.createElement("div", null, /* @__PURE__ */ React15.createElement("label", { className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-wider mb-1 block" },
|
|
2273
|
+
)), /* @__PURE__ */ React15.createElement("div", null, /* @__PURE__ */ React15.createElement("label", { htmlFor: "calendarkit-event-end", className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-wider mb-1 block" }, t.end), /* @__PURE__ */ React15.createElement(
|
|
1775
2274
|
"input",
|
|
1776
2275
|
{
|
|
2276
|
+
id: "calendarkit-event-end",
|
|
1777
2277
|
type: "datetime-local",
|
|
1778
2278
|
className: "w-full bg-background border border-border/50 rounded-xl px-3 py-2 text-sm font-medium text-foreground focus:border-primary focus:ring-2 focus:ring-primary/20 transition-all",
|
|
1779
2279
|
value: formatDateForInput(formData.end),
|
|
1780
2280
|
onChange: (e) => handleDateChange("end", e.target.value)
|
|
1781
2281
|
}
|
|
1782
|
-
)))), 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" },
|
|
2282
|
+
)))), 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" }, t.reminders)), /* @__PURE__ */ React15.createElement("div", { className: "relative" }, /* @__PURE__ */ React15.createElement(
|
|
1783
2283
|
"button",
|
|
1784
2284
|
{
|
|
1785
2285
|
type: "button",
|
|
@@ -1787,16 +2287,16 @@ var EventModal = ({
|
|
|
1787
2287
|
className: "flex items-center gap-1 text-xs font-medium text-primary hover:text-primary/80 transition-colors"
|
|
1788
2288
|
},
|
|
1789
2289
|
/* @__PURE__ */ React15.createElement(Plus3, { className: "w-3.5 h-3.5" }),
|
|
1790
|
-
|
|
1791
|
-
), 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((
|
|
2290
|
+
t.add
|
|
2291
|
+
), 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((minutes) => /* @__PURE__ */ React15.createElement(
|
|
1792
2292
|
"button",
|
|
1793
2293
|
{
|
|
1794
|
-
key:
|
|
2294
|
+
key: minutes,
|
|
1795
2295
|
type: "button",
|
|
1796
|
-
onClick: () => addReminder(
|
|
2296
|
+
onClick: () => addReminder(minutes),
|
|
1797
2297
|
className: "w-full px-3 py-2 text-left text-sm hover:bg-accent transition-colors"
|
|
1798
2298
|
},
|
|
1799
|
-
|
|
2299
|
+
reminderLabel(minutes, t)
|
|
1800
2300
|
)))))), formData.reminders && formData.reminders.length > 0 && /* @__PURE__ */ React15.createElement("div", { className: "flex flex-wrap gap-2" }, formData.reminders.map((reminder) => /* @__PURE__ */ React15.createElement(
|
|
1801
2301
|
"div",
|
|
1802
2302
|
{
|
|
@@ -1804,45 +2304,44 @@ var EventModal = ({
|
|
|
1804
2304
|
className: "flex items-center gap-2 px-3 py-1.5 rounded-full bg-amber-500/10 border border-amber-500/20"
|
|
1805
2305
|
},
|
|
1806
2306
|
/* @__PURE__ */ React15.createElement(BellRing, { className: "w-3.5 h-3.5 text-amber-500" }),
|
|
1807
|
-
/* @__PURE__ */ React15.createElement("span", { className: "text-xs font-medium text-amber-600 dark:text-amber-400" }, reminder.label),
|
|
2307
|
+
/* @__PURE__ */ React15.createElement("span", { className: "text-xs font-medium text-amber-600 dark:text-amber-400" }, reminder.label || reminderLabel(reminder.time, t)),
|
|
1808
2308
|
/* @__PURE__ */ React15.createElement(
|
|
1809
2309
|
"button",
|
|
1810
2310
|
{
|
|
1811
2311
|
type: "button",
|
|
1812
2312
|
onClick: () => removeReminder(reminder.id),
|
|
1813
|
-
className: "p-0.5 hover:bg-amber-500/20 rounded-full transition-colors"
|
|
2313
|
+
className: "p-0.5 hover:bg-amber-500/20 rounded-full transition-colors",
|
|
2314
|
+
"aria-label": t.removeReminder
|
|
1814
2315
|
},
|
|
1815
2316
|
/* @__PURE__ */ React15.createElement(X2, { className: "w-3 h-3 text-amber-500" })
|
|
1816
2317
|
)
|
|
1817
|
-
)))), /* @__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("
|
|
1818
|
-
"
|
|
1819
|
-
{
|
|
1820
|
-
type: "button",
|
|
1821
|
-
onClick: () => setIsGuestsDropdownOpen(!isGuestsDropdownOpen),
|
|
1822
|
-
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"
|
|
1823
|
-
},
|
|
1824
|
-
/* @__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"),
|
|
1825
|
-
/* @__PURE__ */ React15.createElement(ChevronDown2, { className: cn("w-4 h-4 text-muted-foreground transition-transform", isGuestsDropdownOpen && "rotate-180") })
|
|
1826
|
-
), 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(
|
|
1827
|
-
"div",
|
|
2318
|
+
)))), /* @__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("label", { htmlFor: "calendarkit-event-guest", className: "text-sm font-medium text-foreground" }, t.guests)), /* @__PURE__ */ React15.createElement(
|
|
2319
|
+
"input",
|
|
1828
2320
|
{
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
/* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(
|
|
1838
|
-
"div",
|
|
1839
|
-
{
|
|
1840
|
-
className: "w-7 h-7 rounded-full flex items-center justify-center text-xs font-semibold text-white",
|
|
1841
|
-
style: { backgroundColor: `hsl(${fakeGuests.indexOf(email) * 60}, 70%, 50%)` }
|
|
2321
|
+
id: "calendarkit-event-guest",
|
|
2322
|
+
type: "text",
|
|
2323
|
+
inputMode: "email",
|
|
2324
|
+
autoComplete: "email",
|
|
2325
|
+
value: guestInput,
|
|
2326
|
+
onChange: (e) => {
|
|
2327
|
+
setGuestInput(e.target.value);
|
|
2328
|
+
setGuestError(false);
|
|
1842
2329
|
},
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
2330
|
+
onKeyDown: (e) => {
|
|
2331
|
+
if (e.key === "Enter" || e.key === ",") {
|
|
2332
|
+
e.preventDefault();
|
|
2333
|
+
addGuests(guestInput);
|
|
2334
|
+
}
|
|
2335
|
+
},
|
|
2336
|
+
onBlur: () => guestInput.trim() && addGuests(guestInput),
|
|
2337
|
+
placeholder: t.addGuestPlaceholder,
|
|
2338
|
+
"aria-invalid": guestError,
|
|
2339
|
+
className: cn(
|
|
2340
|
+
"w-full px-4 py-3 bg-muted/20 border rounded-xl text-sm text-foreground placeholder-muted-foreground focus:outline-none focus:border-primary transition-all",
|
|
2341
|
+
guestError ? "border-destructive" : "border-border/30"
|
|
2342
|
+
)
|
|
2343
|
+
}
|
|
2344
|
+
), guestError && /* @__PURE__ */ React15.createElement("p", { role: "alert", className: "text-xs text-destructive" }, t.invalidEmail), guests.length > 0 && /* @__PURE__ */ React15.createElement("div", { className: "flex flex-wrap gap-2 mt-2" }, guests.map((email) => /* @__PURE__ */ React15.createElement(
|
|
1846
2345
|
"div",
|
|
1847
2346
|
{
|
|
1848
2347
|
key: email,
|
|
@@ -1852,17 +2351,18 @@ var EventModal = ({
|
|
|
1852
2351
|
"div",
|
|
1853
2352
|
{
|
|
1854
2353
|
className: "w-6 h-6 rounded-full flex items-center justify-center text-xs font-semibold text-white",
|
|
1855
|
-
style: { backgroundColor: `hsl(${
|
|
2354
|
+
style: { backgroundColor: `hsl(${avatarHue(email)}, 70%, 50%)` }
|
|
1856
2355
|
},
|
|
1857
2356
|
email[0].toUpperCase()
|
|
1858
2357
|
),
|
|
1859
|
-
/* @__PURE__ */ React15.createElement("span", { className: "text-xs font-medium text-foreground" }, email
|
|
2358
|
+
/* @__PURE__ */ React15.createElement("span", { className: "text-xs font-medium text-foreground" }, email),
|
|
1860
2359
|
/* @__PURE__ */ React15.createElement(
|
|
1861
2360
|
"button",
|
|
1862
2361
|
{
|
|
1863
2362
|
type: "button",
|
|
1864
|
-
onClick: () =>
|
|
1865
|
-
className: "p-0.5 hover:bg-destructive/10 rounded-full transition-colors"
|
|
2363
|
+
onClick: () => removeGuest(email),
|
|
2364
|
+
className: "p-0.5 hover:bg-destructive/10 rounded-full transition-colors",
|
|
2365
|
+
"aria-label": interpolate(t.removeGuest, { email })
|
|
1866
2366
|
},
|
|
1867
2367
|
/* @__PURE__ */ React15.createElement(X2, { className: "w-3 h-3 text-muted-foreground" })
|
|
1868
2368
|
)
|
|
@@ -1871,15 +2371,17 @@ var EventModal = ({
|
|
|
1871
2371
|
{
|
|
1872
2372
|
type: "text",
|
|
1873
2373
|
className: "flex-1 bg-transparent text-sm text-foreground placeholder-muted-foreground focus:outline-none",
|
|
1874
|
-
placeholder:
|
|
2374
|
+
placeholder: t.addLocation,
|
|
2375
|
+
"aria-label": t.addLocation,
|
|
1875
2376
|
value: formData.location || "",
|
|
1876
2377
|
onChange: (e) => setFormData({ ...formData, location: e.target.value })
|
|
1877
2378
|
}
|
|
1878
|
-
)), /* @__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" },
|
|
2379
|
+
)), /* @__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" }, t.notesLabel)), /* @__PURE__ */ React15.createElement(
|
|
1879
2380
|
"textarea",
|
|
1880
2381
|
{
|
|
1881
2382
|
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",
|
|
1882
|
-
placeholder:
|
|
2383
|
+
placeholder: t.addNotes,
|
|
2384
|
+
"aria-label": t.notesLabel,
|
|
1883
2385
|
value: formData.description || "",
|
|
1884
2386
|
onChange: (e) => setFormData({ ...formData, description: e.target.value })
|
|
1885
2387
|
}
|
|
@@ -1887,6 +2389,7 @@ var EventModal = ({
|
|
|
1887
2389
|
"select",
|
|
1888
2390
|
{
|
|
1889
2391
|
className: "flex-1 bg-transparent text-sm text-foreground focus:outline-none cursor-pointer",
|
|
2392
|
+
"aria-label": t.repeat,
|
|
1890
2393
|
value: formData.recurrence?.freq || "",
|
|
1891
2394
|
onChange: (e) => {
|
|
1892
2395
|
if (e.target.value === "") {
|
|
@@ -1902,12 +2405,12 @@ var EventModal = ({
|
|
|
1902
2405
|
}
|
|
1903
2406
|
}
|
|
1904
2407
|
},
|
|
1905
|
-
/* @__PURE__ */ React15.createElement("option", { value: "" },
|
|
1906
|
-
/* @__PURE__ */ React15.createElement("option", { value: "DAILY" },
|
|
1907
|
-
/* @__PURE__ */ React15.createElement("option", { value: "WEEKLY" },
|
|
1908
|
-
/* @__PURE__ */ React15.createElement("option", { value: "MONTHLY" },
|
|
1909
|
-
/* @__PURE__ */ React15.createElement("option", { value: "YEARLY" },
|
|
1910
|
-
)), /* @__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" },
|
|
2408
|
+
/* @__PURE__ */ React15.createElement("option", { value: "" }, t.doesNotRepeat),
|
|
2409
|
+
/* @__PURE__ */ React15.createElement("option", { value: "DAILY" }, t.daily),
|
|
2410
|
+
/* @__PURE__ */ React15.createElement("option", { value: "WEEKLY" }, t.weekly),
|
|
2411
|
+
/* @__PURE__ */ React15.createElement("option", { value: "MONTHLY" }, t.monthly),
|
|
2412
|
+
/* @__PURE__ */ React15.createElement("option", { value: "YEARLY" }, t.yearly)
|
|
2413
|
+
)), calendars && calendars.length > 0 && /* @__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" }, t.calendar)), /* @__PURE__ */ React15.createElement("div", { className: "relative" }, /* @__PURE__ */ React15.createElement(
|
|
1911
2414
|
"button",
|
|
1912
2415
|
{
|
|
1913
2416
|
type: "button",
|
|
@@ -1918,12 +2421,12 @@ var EventModal = ({
|
|
|
1918
2421
|
"div",
|
|
1919
2422
|
{
|
|
1920
2423
|
className: "w-3 h-3 rounded-full",
|
|
1921
|
-
style: { backgroundColor: calendars
|
|
2424
|
+
style: { backgroundColor: calendars.find((c) => c.id === formData.calendarId)?.color || "#3b82f6" }
|
|
1922
2425
|
}
|
|
1923
2426
|
),
|
|
1924
|
-
/* @__PURE__ */ React15.createElement("span", { className: "font-medium" }, calendars
|
|
2427
|
+
/* @__PURE__ */ React15.createElement("span", { className: "font-medium" }, calendars.find((c) => c.id === formData.calendarId)?.label || t.select),
|
|
1925
2428
|
/* @__PURE__ */ React15.createElement(ChevronDown2, { className: "w-3.5 h-3.5 text-muted-foreground" })
|
|
1926
|
-
), 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
|
|
2429
|
+
), 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(
|
|
1927
2430
|
"button",
|
|
1928
2431
|
{
|
|
1929
2432
|
key: cal.id,
|
|
@@ -1949,7 +2452,7 @@ var EventModal = ({
|
|
|
1949
2452
|
className: "flex items-center gap-2 px-4 py-2 text-destructive hover:bg-destructive/10 rounded-xl transition-all text-sm font-medium"
|
|
1950
2453
|
},
|
|
1951
2454
|
/* @__PURE__ */ React15.createElement(Trash2, { className: "w-4 h-4" }),
|
|
1952
|
-
|
|
2455
|
+
t.delete
|
|
1953
2456
|
), mode === "create" && /* @__PURE__ */ React15.createElement("div", null), /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(
|
|
1954
2457
|
"button",
|
|
1955
2458
|
{
|
|
@@ -1957,14 +2460,14 @@ var EventModal = ({
|
|
|
1957
2460
|
onClick: onClose,
|
|
1958
2461
|
className: "px-4 py-2.5 text-muted-foreground hover:text-foreground font-medium text-sm rounded-xl transition-all hover:bg-accent"
|
|
1959
2462
|
},
|
|
1960
|
-
|
|
2463
|
+
t.cancel
|
|
1961
2464
|
), /* @__PURE__ */ React15.createElement(
|
|
1962
2465
|
"button",
|
|
1963
2466
|
{
|
|
1964
2467
|
type: "submit",
|
|
1965
2468
|
className: "px-6 py-2.5 bg-primary hover:bg-primary/90 text-primary-foreground font-semibold text-sm rounded-xl shadow-md transition-all hover:shadow-lg hover:shadow-primary/20 active:scale-95"
|
|
1966
2469
|
},
|
|
1967
|
-
mode === "edit" ?
|
|
2470
|
+
mode === "edit" ? t.saveChanges : t.createEvent
|
|
1968
2471
|
))));
|
|
1969
2472
|
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(
|
|
1970
2473
|
motion2.div,
|
|
@@ -1992,7 +2495,7 @@ var EventModal = ({
|
|
|
1992
2495
|
// src/components/Skeleton.tsx
|
|
1993
2496
|
import React16 from "react";
|
|
1994
2497
|
var Skeleton = ({ className, style }) => /* @__PURE__ */ React16.createElement("div", { className: cn("animate-pulse bg-muted/40 rounded", className), style });
|
|
1995
|
-
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" },
|
|
2498
|
+
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" }, i * 7 % 10 > 4 && /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-5 w-full rounded-md" }), i * 3 % 10 > 6 && /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-5 w-3/4 rounded-md" }))))));
|
|
1996
2499
|
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(
|
|
1997
2500
|
Skeleton,
|
|
1998
2501
|
{
|
|
@@ -2021,7 +2524,7 @@ var DayViewSkeleton = () => /* @__PURE__ */ React16.createElement("div", { class
|
|
|
2021
2524
|
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" })))))))));
|
|
2022
2525
|
|
|
2023
2526
|
// src/components/ContextMenu.tsx
|
|
2024
|
-
import React17, { useState as useState4, useCallback as
|
|
2527
|
+
import React17, { useState as useState4, useCallback as useCallback2, useEffect as useEffect4, useRef as useRef5 } from "react";
|
|
2025
2528
|
import { Edit3, Trash2 as Trash22, Copy } from "lucide-react";
|
|
2026
2529
|
import { AnimatePresence as AnimatePresence2, motion as motion3 } from "framer-motion";
|
|
2027
2530
|
var EventContextMenu = ({
|
|
@@ -2034,7 +2537,7 @@ var EventContextMenu = ({
|
|
|
2034
2537
|
customActions = [],
|
|
2035
2538
|
translations
|
|
2036
2539
|
}) => {
|
|
2037
|
-
const menuRef =
|
|
2540
|
+
const menuRef = useRef5(null);
|
|
2038
2541
|
useEffect4(() => {
|
|
2039
2542
|
const handleKeyDown = (e) => {
|
|
2040
2543
|
if (e.key === "Escape") {
|
|
@@ -2134,7 +2637,7 @@ var EventContextMenu = ({
|
|
|
2134
2637
|
};
|
|
2135
2638
|
var useEventContextMenu = () => {
|
|
2136
2639
|
const [contextMenu, setContextMenu] = useState4({ event: null, position: null });
|
|
2137
|
-
const openContextMenu =
|
|
2640
|
+
const openContextMenu = useCallback2((event, e) => {
|
|
2138
2641
|
e.preventDefault();
|
|
2139
2642
|
e.stopPropagation();
|
|
2140
2643
|
setContextMenu({
|
|
@@ -2142,7 +2645,7 @@ var useEventContextMenu = () => {
|
|
|
2142
2645
|
position: { x: e.clientX, y: e.clientY }
|
|
2143
2646
|
});
|
|
2144
2647
|
}, []);
|
|
2145
|
-
const closeContextMenu =
|
|
2648
|
+
const closeContextMenu = useCallback2(() => {
|
|
2146
2649
|
setContextMenu({ event: null, position: null });
|
|
2147
2650
|
}, []);
|
|
2148
2651
|
return {
|
|
@@ -2153,146 +2656,6 @@ var useEventContextMenu = () => {
|
|
|
2153
2656
|
};
|
|
2154
2657
|
};
|
|
2155
2658
|
|
|
2156
|
-
// src/locales/index.ts
|
|
2157
|
-
var en = {
|
|
2158
|
-
today: "Today",
|
|
2159
|
-
month: "Month",
|
|
2160
|
-
week: "Week",
|
|
2161
|
-
day: "Day",
|
|
2162
|
-
agenda: "Agenda",
|
|
2163
|
-
resource: "Resources",
|
|
2164
|
-
createEvent: "Create Event",
|
|
2165
|
-
editEvent: "Edit Event",
|
|
2166
|
-
delete: "Delete",
|
|
2167
|
-
save: "Save",
|
|
2168
|
-
cancel: "Cancel",
|
|
2169
|
-
title: "Add title",
|
|
2170
|
-
start: "Start",
|
|
2171
|
-
end: "End",
|
|
2172
|
-
allDay: "All Day",
|
|
2173
|
-
description: "Description",
|
|
2174
|
-
repeat: "Repeat",
|
|
2175
|
-
noRepeat: "Does not repeat",
|
|
2176
|
-
selectCalendar: "Select Calendar",
|
|
2177
|
-
selectType: "Select Type",
|
|
2178
|
-
daily: "Daily",
|
|
2179
|
-
weekly: "Weekly",
|
|
2180
|
-
monthly: "Monthly",
|
|
2181
|
-
yearly: "Yearly",
|
|
2182
|
-
search: "Search events...",
|
|
2183
|
-
allTypes: "All Types",
|
|
2184
|
-
timezone: "Time zone",
|
|
2185
|
-
localTime: "Local Time",
|
|
2186
|
-
create: "Create",
|
|
2187
|
-
calendars: "Calendars",
|
|
2188
|
-
event: "Event",
|
|
2189
|
-
task: "Task",
|
|
2190
|
-
appointmentSchedule: "Appointment schedule",
|
|
2191
|
-
new: "New",
|
|
2192
|
-
dateAndTime: "Date & Time",
|
|
2193
|
-
whosJoining: "Who's joining?",
|
|
2194
|
-
suggestedTimes: "Suggested times for guests",
|
|
2195
|
-
viewSuggestions: "View suggestions",
|
|
2196
|
-
whereWillItBe: "Where will it be?",
|
|
2197
|
-
location: "Enter address",
|
|
2198
|
-
descriptionAndAttachments: "Description",
|
|
2199
|
-
dragAndDrop: "Drag & drop files or",
|
|
2200
|
-
guests: "Guests",
|
|
2201
|
-
addGuests: "Add guests",
|
|
2202
|
-
addAttachment: "Upload files",
|
|
2203
|
-
moreOptions: "More options",
|
|
2204
|
-
doesNotRepeat: "Does not repeat",
|
|
2205
|
-
locationHelpText: 'Enter a physical address (e.g. "123 Main St") to generate the map.',
|
|
2206
|
-
notes: "Notes...",
|
|
2207
|
-
guestCount: "guest",
|
|
2208
|
-
guestsCount: "guests",
|
|
2209
|
-
repeatFor: "Repeat for",
|
|
2210
|
-
times: "times",
|
|
2211
|
-
until: "Until",
|
|
2212
|
-
endRepeat: "End repeat",
|
|
2213
|
-
never: "Never",
|
|
2214
|
-
afterOccurrences: "After occurrences",
|
|
2215
|
-
onDate: "On date",
|
|
2216
|
-
tomorrow: "Tomorrow",
|
|
2217
|
-
edit: "Edit",
|
|
2218
|
-
duplicate: "Duplicate",
|
|
2219
|
-
eventSingular: "event",
|
|
2220
|
-
eventPlural: "events",
|
|
2221
|
-
allCaughtUp: "All caught up!",
|
|
2222
|
-
noEventsScheduled: "No events scheduled",
|
|
2223
|
-
noUpcoming: "Your schedule is clear",
|
|
2224
|
-
noUpcomingHint: "No upcoming events to show. Create a new event to start planning."
|
|
2225
|
-
};
|
|
2226
|
-
var fr = {
|
|
2227
|
-
today: "Aujourd'hui",
|
|
2228
|
-
month: "Mois",
|
|
2229
|
-
week: "Semaine",
|
|
2230
|
-
day: "Jour",
|
|
2231
|
-
agenda: "Agenda",
|
|
2232
|
-
resource: "Ressources",
|
|
2233
|
-
createEvent: "Cr\xE9er un \xE9v\xE9nement",
|
|
2234
|
-
editEvent: "Modifier l'\xE9v\xE9nement",
|
|
2235
|
-
delete: "Supprimer",
|
|
2236
|
-
save: "Enregistrer",
|
|
2237
|
-
cancel: "Annuler",
|
|
2238
|
-
title: "Ajouter un titre",
|
|
2239
|
-
start: "D\xE9but",
|
|
2240
|
-
end: "Fin",
|
|
2241
|
-
allDay: "Toute la journ\xE9e",
|
|
2242
|
-
description: "Description",
|
|
2243
|
-
repeat: "R\xE9p\xE9ter",
|
|
2244
|
-
noRepeat: "Ne se r\xE9p\xE8te pas",
|
|
2245
|
-
selectCalendar: "S\xE9lectionner un calendrier",
|
|
2246
|
-
selectType: "S\xE9lectionner un type",
|
|
2247
|
-
daily: "Quotidien",
|
|
2248
|
-
weekly: "Hebdomadaire",
|
|
2249
|
-
monthly: "Mensuel",
|
|
2250
|
-
yearly: "Annuel",
|
|
2251
|
-
search: "Rechercher...",
|
|
2252
|
-
allTypes: "Tous les types",
|
|
2253
|
-
timezone: "Fuseau horaire",
|
|
2254
|
-
localTime: "Heure locale",
|
|
2255
|
-
create: "Cr\xE9er",
|
|
2256
|
-
calendars: "Calendriers",
|
|
2257
|
-
event: "\xC9v\xE9nement",
|
|
2258
|
-
task: "T\xE2che",
|
|
2259
|
-
appointmentSchedule: "Planning de rendez-vous",
|
|
2260
|
-
new: "Nouveau",
|
|
2261
|
-
dateAndTime: "Date et heure",
|
|
2262
|
-
whosJoining: "Qui participe ?",
|
|
2263
|
-
suggestedTimes: "Horaires sugg\xE9r\xE9s",
|
|
2264
|
-
viewSuggestions: "Voir les suggestions",
|
|
2265
|
-
whereWillItBe: "O\xF9 aura lieu ?",
|
|
2266
|
-
location: "Entrer une adresse",
|
|
2267
|
-
descriptionAndAttachments: "Description",
|
|
2268
|
-
dragAndDrop: "Glisser-d\xE9poser des fichiers ou",
|
|
2269
|
-
guests: "Invit\xE9s",
|
|
2270
|
-
addGuests: "Ajouter des invit\xE9s",
|
|
2271
|
-
addAttachment: "T\xE9l\xE9charger des fichiers",
|
|
2272
|
-
moreOptions: "Plus d'options",
|
|
2273
|
-
doesNotRepeat: "Ne se r\xE9p\xE8te pas",
|
|
2274
|
-
locationHelpText: 'Entrez une adresse physique (ex: "123 Rue Principale") pour g\xE9n\xE9rer la carte.',
|
|
2275
|
-
notes: "Notes...",
|
|
2276
|
-
guestCount: "invit\xE9",
|
|
2277
|
-
guestsCount: "invit\xE9s",
|
|
2278
|
-
repeatFor: "R\xE9p\xE9ter pendant",
|
|
2279
|
-
times: "fois",
|
|
2280
|
-
until: "Jusqu'au",
|
|
2281
|
-
endRepeat: "Fin de r\xE9p\xE9tition",
|
|
2282
|
-
never: "Jamais",
|
|
2283
|
-
afterOccurrences: "Apr\xE8s occurrences",
|
|
2284
|
-
onDate: "\xC0 la date",
|
|
2285
|
-
tomorrow: "Demain",
|
|
2286
|
-
edit: "Modifier",
|
|
2287
|
-
duplicate: "Dupliquer",
|
|
2288
|
-
eventSingular: "\xE9v\xE9nement",
|
|
2289
|
-
eventPlural: "\xE9v\xE9nements",
|
|
2290
|
-
allCaughtUp: "Tout est \xE0 jour !",
|
|
2291
|
-
noEventsScheduled: "Aucun \xE9v\xE9nement pr\xE9vu",
|
|
2292
|
-
noUpcoming: "Votre planning est libre",
|
|
2293
|
-
noUpcomingHint: "Aucun \xE9v\xE9nement \xE0 venir. Cr\xE9ez un nouvel \xE9v\xE9nement pour commencer \xE0 planifier."
|
|
2294
|
-
};
|
|
2295
|
-
|
|
2296
2659
|
// src/lib/theme.ts
|
|
2297
2660
|
function colorToHsl(color) {
|
|
2298
2661
|
let r = 0, g = 0, b = 0;
|
|
@@ -2373,18 +2736,164 @@ function getThemeStyles(theme) {
|
|
|
2373
2736
|
}
|
|
2374
2737
|
|
|
2375
2738
|
// src/hooks/useCalendarLogic.ts
|
|
2376
|
-
import {
|
|
2739
|
+
import { useMemo as useMemo3, useState as useState5 } from "react";
|
|
2377
2740
|
import {
|
|
2378
|
-
|
|
2379
|
-
|
|
2741
|
+
addDays as addDays5,
|
|
2742
|
+
addMonths as addMonths3,
|
|
2380
2743
|
addWeeks,
|
|
2381
|
-
|
|
2382
|
-
|
|
2744
|
+
endOfMonth as endOfMonth2,
|
|
2745
|
+
format as format13,
|
|
2746
|
+
startOfMonth as startOfMonth3,
|
|
2747
|
+
startOfWeek as startOfWeek2,
|
|
2383
2748
|
subDays,
|
|
2384
|
-
|
|
2749
|
+
subMonths as subMonths2,
|
|
2750
|
+
subWeeks
|
|
2385
2751
|
} from "date-fns";
|
|
2386
|
-
|
|
2752
|
+
|
|
2753
|
+
// src/core/recurrence.ts
|
|
2754
|
+
import { addMonths as addMonths2, startOfMonth as startOfMonth2 } from "date-fns";
|
|
2387
2755
|
import { RRule } from "rrule";
|
|
2756
|
+
var WEEKDAYS = [RRule.MO, RRule.TU, RRule.WE, RRule.TH, RRule.FR, RRule.SA, RRule.SU];
|
|
2757
|
+
var toFloating = (instant, tz) => {
|
|
2758
|
+
const w = toDisplay(instant, tz);
|
|
2759
|
+
return new Date(Date.UTC(w.getFullYear(), w.getMonth(), w.getDate(), w.getHours(), w.getMinutes(), w.getSeconds(), w.getMilliseconds()));
|
|
2760
|
+
};
|
|
2761
|
+
var fromFloating = (floating, tz) => fromDisplay(
|
|
2762
|
+
new Date(
|
|
2763
|
+
floating.getUTCFullYear(),
|
|
2764
|
+
floating.getUTCMonth(),
|
|
2765
|
+
floating.getUTCDate(),
|
|
2766
|
+
floating.getUTCHours(),
|
|
2767
|
+
floating.getUTCMinutes(),
|
|
2768
|
+
floating.getUTCSeconds(),
|
|
2769
|
+
floating.getUTCMilliseconds()
|
|
2770
|
+
),
|
|
2771
|
+
tz
|
|
2772
|
+
);
|
|
2773
|
+
var minuteKey = (date) => Math.floor(date.getTime() / 6e4);
|
|
2774
|
+
var FREQUENCIES = { DAILY: RRule.DAILY, WEEKLY: RRule.WEEKLY, MONTHLY: RRule.MONTHLY, YEARLY: RRule.YEARLY };
|
|
2775
|
+
function buildRule(event, tz) {
|
|
2776
|
+
const r = event.recurrence;
|
|
2777
|
+
const freq = FREQUENCIES[r.freq];
|
|
2778
|
+
if (freq === void 0) throw new Error(`unsupported frequency "${r.freq}"`);
|
|
2779
|
+
return new RRule({
|
|
2780
|
+
freq,
|
|
2781
|
+
interval: r.interval || 1,
|
|
2782
|
+
dtstart: toFloating(event.start, tz),
|
|
2783
|
+
until: r.until ? toFloating(r.until, tz) : void 0,
|
|
2784
|
+
count: r.count,
|
|
2785
|
+
byweekday: r.byweekday?.length ? r.byweekday.map((d) => WEEKDAYS[d]) : void 0
|
|
2786
|
+
});
|
|
2787
|
+
}
|
|
2788
|
+
function occurrencesInMonth(event, monthStart, tz) {
|
|
2789
|
+
const rule = buildRule(event, tz);
|
|
2790
|
+
const excluded = new Set((event.recurrence.exdates ?? []).map(minuteKey));
|
|
2791
|
+
const duration = event.end.getTime() - event.start.getTime();
|
|
2792
|
+
const from = toFloating(monthStart, tz);
|
|
2793
|
+
const to = toFloating(addMonths2(monthStart, 1), tz);
|
|
2794
|
+
const result = [];
|
|
2795
|
+
for (const floating of rule.between(from, to, true)) {
|
|
2796
|
+
if (floating.getTime() === to.getTime()) continue;
|
|
2797
|
+
const start = fromFloating(floating, tz);
|
|
2798
|
+
if (excluded.has(minuteKey(start))) continue;
|
|
2799
|
+
result.push({
|
|
2800
|
+
...event,
|
|
2801
|
+
id: `${event.id}-${start.getTime()}`,
|
|
2802
|
+
originalEventId: event.id,
|
|
2803
|
+
occurrenceStart: start,
|
|
2804
|
+
start,
|
|
2805
|
+
end: new Date(start.getTime() + duration)
|
|
2806
|
+
});
|
|
2807
|
+
}
|
|
2808
|
+
return result;
|
|
2809
|
+
}
|
|
2810
|
+
var MAX_MONTHS_PER_EVENT = 12;
|
|
2811
|
+
var occurrenceCache = /* @__PURE__ */ new WeakMap();
|
|
2812
|
+
function cachedMonth(event, monthStart, tz) {
|
|
2813
|
+
let months = occurrenceCache.get(event);
|
|
2814
|
+
if (!months) {
|
|
2815
|
+
months = /* @__PURE__ */ new Map();
|
|
2816
|
+
occurrenceCache.set(event, months);
|
|
2817
|
+
}
|
|
2818
|
+
const key = `${monthStart.getFullYear()}-${monthStart.getMonth()}|${tz ?? ""}`;
|
|
2819
|
+
let occurrences = months.get(key);
|
|
2820
|
+
if (occurrences) {
|
|
2821
|
+
months.delete(key);
|
|
2822
|
+
} else {
|
|
2823
|
+
occurrences = occurrencesInMonth(event, monthStart, tz);
|
|
2824
|
+
if (months.size >= MAX_MONTHS_PER_EVENT) months.delete(months.keys().next().value);
|
|
2825
|
+
}
|
|
2826
|
+
months.set(key, occurrences);
|
|
2827
|
+
return occurrences;
|
|
2828
|
+
}
|
|
2829
|
+
function expandRecurrences(events, range, anchorTz) {
|
|
2830
|
+
const result = [];
|
|
2831
|
+
const lastMonth = startOfMonth2(toDisplay(range.end, anchorTz));
|
|
2832
|
+
for (const event of events) {
|
|
2833
|
+
if (!event.recurrence) {
|
|
2834
|
+
result.push(event);
|
|
2835
|
+
continue;
|
|
2836
|
+
}
|
|
2837
|
+
const duration = event.end.getTime() - event.start.getTime();
|
|
2838
|
+
const firstMonth = startOfMonth2(toDisplay(new Date(range.start.getTime() - duration), anchorTz));
|
|
2839
|
+
try {
|
|
2840
|
+
for (let month = firstMonth; month <= lastMonth; month = addMonths2(month, 1)) {
|
|
2841
|
+
for (const occurrence of cachedMonth(event, fromDisplay(month, anchorTz), anchorTz)) {
|
|
2842
|
+
if (occurrence.end >= range.start && occurrence.start <= range.end) result.push(occurrence);
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
} catch (error) {
|
|
2846
|
+
warnOnce(`invalid-recurrence:${event.id}`, `Event "${event.id}" has an invalid recurrence rule: ${String(error)}`);
|
|
2847
|
+
result.push(event);
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
return result;
|
|
2851
|
+
}
|
|
2852
|
+
function stripOccurrenceFields(event) {
|
|
2853
|
+
const { originalEventId: _originalEventId, occurrenceStart: _occurrenceStart, ...rest } = event;
|
|
2854
|
+
return rest;
|
|
2855
|
+
}
|
|
2856
|
+
var shiftFloating = (instant, deltaMs, tz) => fromFloating(new Date(toFloating(instant, tz).getTime() + deltaMs), tz);
|
|
2857
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
2858
|
+
var floatingDay = (floating) => Math.floor(floating.getTime() / DAY_MS);
|
|
2859
|
+
function shiftSeries(master, occurrence, newStart, newEnd, anchorTz) {
|
|
2860
|
+
const fromFloat = toFloating(occurrence.start, anchorTz);
|
|
2861
|
+
const toFloat = toFloating(newStart, anchorTz);
|
|
2862
|
+
const deltaMs = toFloat.getTime() - fromFloat.getTime();
|
|
2863
|
+
const dayDelta = floatingDay(toFloat) - floatingDay(fromFloat);
|
|
2864
|
+
const start = shiftFloating(master.start, deltaMs, anchorTz);
|
|
2865
|
+
const end = new Date(start.getTime() + (newEnd.getTime() - newStart.getTime()));
|
|
2866
|
+
const recurrence = master.recurrence && {
|
|
2867
|
+
...master.recurrence,
|
|
2868
|
+
byweekday: master.recurrence.byweekday && dayDelta % 7 !== 0 ? master.recurrence.byweekday.map((d) => ((d + dayDelta) % 7 + 7) % 7) : master.recurrence.byweekday,
|
|
2869
|
+
until: master.recurrence.until && shiftFloating(master.recurrence.until, deltaMs, anchorTz),
|
|
2870
|
+
exdates: master.recurrence.exdates?.map((d) => shiftFloating(d, deltaMs, anchorTz))
|
|
2871
|
+
};
|
|
2872
|
+
return stripOccurrenceFields({ ...master, start, end, recurrence });
|
|
2873
|
+
}
|
|
2874
|
+
function excludeOccurrence(master, occurrenceStart) {
|
|
2875
|
+
const recurrence = master.recurrence ?? { freq: "DAILY" };
|
|
2876
|
+
return { ...master, recurrence: { ...recurrence, exdates: [...recurrence.exdates ?? [], occurrenceStart] } };
|
|
2877
|
+
}
|
|
2878
|
+
|
|
2879
|
+
// src/hooks/useStableCallback.ts
|
|
2880
|
+
import { useCallback as useCallback3, useInsertionEffect as useInsertionEffect2, useRef as useRef6 } from "react";
|
|
2881
|
+
function useStableCallback(fn) {
|
|
2882
|
+
const ref = useRef6(fn);
|
|
2883
|
+
useInsertionEffect2(() => {
|
|
2884
|
+
ref.current = fn;
|
|
2885
|
+
});
|
|
2886
|
+
return useCallback3((...args) => ref.current(...args), []);
|
|
2887
|
+
}
|
|
2888
|
+
|
|
2889
|
+
// src/hooks/useCalendarLogic.ts
|
|
2890
|
+
function expansionRange(monthStart) {
|
|
2891
|
+
return {
|
|
2892
|
+
start: startOfWeek2(startOfMonth3(subMonths2(monthStart, 1))),
|
|
2893
|
+
end: addDays5(endOfMonth2(addMonths3(monthStart, 1)), 37)
|
|
2894
|
+
};
|
|
2895
|
+
}
|
|
2896
|
+
var DAY_MS2 = 24 * 60 * 60 * 1e3;
|
|
2388
2897
|
var useCalendarLogic = ({
|
|
2389
2898
|
events,
|
|
2390
2899
|
view: controlledView,
|
|
@@ -2395,69 +2904,44 @@ var useCalendarLogic = ({
|
|
|
2395
2904
|
onEventUpdate,
|
|
2396
2905
|
onEventCreate,
|
|
2397
2906
|
onEventDelete,
|
|
2907
|
+
onEventDrop,
|
|
2908
|
+
onEventResize,
|
|
2909
|
+
onOccurrenceDelete,
|
|
2398
2910
|
readOnly,
|
|
2399
2911
|
timezone
|
|
2400
2912
|
}) => {
|
|
2401
2913
|
const [internalView, setInternalView] = useState5("week");
|
|
2402
|
-
const [internalDate, setInternalDate] = useState5(/* @__PURE__ */ new Date());
|
|
2914
|
+
const [internalDate, setInternalDate] = useState5(() => /* @__PURE__ */ new Date());
|
|
2403
2915
|
const [isSidebarOpen, setIsSidebarOpen] = useState5(true);
|
|
2404
2916
|
const [isModalOpen, setIsModalOpen] = useState5(false);
|
|
2405
2917
|
const [selectedEvent, setSelectedEvent] = useState5(null);
|
|
2406
2918
|
const [modalInitialDate, setModalInitialDate] = useState5(void 0);
|
|
2919
|
+
const [modalInitialResourceId, setModalInitialResourceId] = useState5(void 0);
|
|
2407
2920
|
const view = controlledView ?? internalView;
|
|
2408
2921
|
const currentDate = controlledDate ?? internalDate;
|
|
2409
|
-
const
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
interval: event.recurrence.interval || 1,
|
|
2419
|
-
dtstart: new Date(event.start),
|
|
2420
|
-
until: event.recurrence.until ? new Date(event.recurrence.until) : void 0,
|
|
2421
|
-
count: event.recurrence.count
|
|
2422
|
-
});
|
|
2423
|
-
const dates = rule.between(rangeStart, rangeEnd);
|
|
2424
|
-
dates.forEach((date) => {
|
|
2425
|
-
const duration = differenceInMilliseconds(new Date(event.end), new Date(event.start));
|
|
2426
|
-
allEvents.push({
|
|
2427
|
-
...event,
|
|
2428
|
-
id: `${event.id}-${date.getTime()}`,
|
|
2429
|
-
// Unique ID for each instance
|
|
2430
|
-
originalEventId: event.id,
|
|
2431
|
-
// Reference to original
|
|
2432
|
-
start: date,
|
|
2433
|
-
end: new Date(date.getTime() + duration)
|
|
2434
|
-
});
|
|
2435
|
-
});
|
|
2436
|
-
} catch (e) {
|
|
2437
|
-
console.error("Error parsing recurrence rule", e);
|
|
2438
|
-
allEvents.push(event);
|
|
2439
|
-
}
|
|
2440
|
-
} else {
|
|
2441
|
-
allEvents.push(event);
|
|
2442
|
-
}
|
|
2443
|
-
});
|
|
2444
|
-
return allEvents;
|
|
2445
|
-
}, [events, currentDate]);
|
|
2446
|
-
const handleViewChange = (newView) => {
|
|
2922
|
+
const normalizedEvents = useMemo3(() => normalizeEvents(events), [events]);
|
|
2923
|
+
const monthKey = format13(currentDate, "yyyy-MM");
|
|
2924
|
+
const range = useMemo3(() => expansionRange(/* @__PURE__ */ new Date(`${monthKey}-01T00:00:00`)), [monthKey]);
|
|
2925
|
+
const expandedEvents = useMemo3(
|
|
2926
|
+
() => expandRecurrences(normalizedEvents, range, timezone),
|
|
2927
|
+
[normalizedEvents, range, timezone]
|
|
2928
|
+
);
|
|
2929
|
+
const masterOf = (occurrence) => normalizedEvents.find((e) => e.id === occurrence.originalEventId);
|
|
2930
|
+
const handleViewChange = useStableCallback((newView) => {
|
|
2447
2931
|
if (controlledOnViewChange) {
|
|
2448
2932
|
controlledOnViewChange(newView);
|
|
2449
2933
|
} else {
|
|
2450
2934
|
setInternalView(newView);
|
|
2451
2935
|
}
|
|
2452
|
-
};
|
|
2453
|
-
const handleDateChange = (newDate) => {
|
|
2936
|
+
});
|
|
2937
|
+
const handleDateChange = useStableCallback((newDate) => {
|
|
2454
2938
|
if (controlledOnDateChange) {
|
|
2455
2939
|
controlledOnDateChange(newDate);
|
|
2456
2940
|
} else {
|
|
2457
2941
|
setInternalDate(newDate);
|
|
2458
2942
|
}
|
|
2459
|
-
};
|
|
2460
|
-
const handlePrev = () => {
|
|
2943
|
+
});
|
|
2944
|
+
const handlePrev = useStableCallback(() => {
|
|
2461
2945
|
switch (view) {
|
|
2462
2946
|
case "month":
|
|
2463
2947
|
handleDateChange(subMonths2(currentDate, 1));
|
|
@@ -2473,133 +2957,140 @@ var useCalendarLogic = ({
|
|
|
2473
2957
|
handleDateChange(subDays(currentDate, 7));
|
|
2474
2958
|
break;
|
|
2475
2959
|
}
|
|
2476
|
-
};
|
|
2477
|
-
const handleNext = () => {
|
|
2960
|
+
});
|
|
2961
|
+
const handleNext = useStableCallback(() => {
|
|
2478
2962
|
switch (view) {
|
|
2479
2963
|
case "month":
|
|
2480
|
-
handleDateChange(
|
|
2964
|
+
handleDateChange(addMonths3(currentDate, 1));
|
|
2481
2965
|
break;
|
|
2482
2966
|
case "week":
|
|
2483
2967
|
handleDateChange(addWeeks(currentDate, 1));
|
|
2484
2968
|
break;
|
|
2485
2969
|
case "day":
|
|
2486
2970
|
case "resource":
|
|
2487
|
-
handleDateChange(
|
|
2971
|
+
handleDateChange(addDays5(currentDate, 1));
|
|
2488
2972
|
break;
|
|
2489
2973
|
case "agenda":
|
|
2490
|
-
handleDateChange(
|
|
2974
|
+
handleDateChange(addDays5(currentDate, 7));
|
|
2491
2975
|
break;
|
|
2492
2976
|
}
|
|
2493
|
-
};
|
|
2494
|
-
const handleToday = () => {
|
|
2977
|
+
});
|
|
2978
|
+
const handleToday = useStableCallback(() => {
|
|
2495
2979
|
handleDateChange(/* @__PURE__ */ new Date());
|
|
2496
|
-
};
|
|
2497
|
-
const handleDateClick = (date) => {
|
|
2980
|
+
});
|
|
2981
|
+
const handleDateClick = useStableCallback((date) => {
|
|
2498
2982
|
handleDateChange(date);
|
|
2499
2983
|
handleViewChange("day");
|
|
2500
|
-
};
|
|
2501
|
-
const handleTimeSlotClick = (date) => {
|
|
2984
|
+
});
|
|
2985
|
+
const handleTimeSlotClick = useStableCallback((date, resourceId) => {
|
|
2502
2986
|
if (readOnly) return;
|
|
2503
2987
|
setSelectedEvent(null);
|
|
2504
2988
|
setModalInitialDate(date);
|
|
2989
|
+
setModalInitialResourceId(resourceId);
|
|
2505
2990
|
setIsModalOpen(true);
|
|
2506
|
-
};
|
|
2507
|
-
const handleEventClickInternal = (event) => {
|
|
2508
|
-
|
|
2509
|
-
onEventClick(event);
|
|
2510
|
-
}
|
|
2991
|
+
});
|
|
2992
|
+
const handleEventClickInternal = useStableCallback((event) => {
|
|
2993
|
+
onEventClick?.(event);
|
|
2511
2994
|
if (!readOnly) {
|
|
2512
2995
|
setSelectedEvent(event);
|
|
2513
2996
|
setModalInitialDate(void 0);
|
|
2997
|
+
setModalInitialResourceId(void 0);
|
|
2514
2998
|
setIsModalOpen(true);
|
|
2515
2999
|
}
|
|
2516
|
-
};
|
|
2517
|
-
const handleCreateEvent = () => {
|
|
3000
|
+
});
|
|
3001
|
+
const handleCreateEvent = useStableCallback(() => {
|
|
2518
3002
|
if (readOnly) return;
|
|
2519
3003
|
setSelectedEvent(null);
|
|
2520
3004
|
setModalInitialDate(/* @__PURE__ */ new Date());
|
|
3005
|
+
setModalInitialResourceId(void 0);
|
|
2521
3006
|
setIsModalOpen(true);
|
|
2522
|
-
};
|
|
2523
|
-
const handleModalSave = (eventData) => {
|
|
2524
|
-
let effectiveEventId = selectedEvent?.id;
|
|
3007
|
+
});
|
|
3008
|
+
const handleModalSave = useStableCallback((eventData) => {
|
|
2525
3009
|
if (selectedEvent?.originalEventId) {
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
}
|
|
3010
|
+
const master = masterOf(selectedEvent);
|
|
3011
|
+
if (!master) return;
|
|
3012
|
+
const edited = {
|
|
3013
|
+
...master,
|
|
3014
|
+
...stripOccurrenceFields(eventData),
|
|
3015
|
+
id: master.id,
|
|
3016
|
+
start: master.start,
|
|
3017
|
+
end: master.end
|
|
3018
|
+
};
|
|
3019
|
+
onEventUpdate?.(
|
|
3020
|
+
shiftSeries(edited, selectedEvent, eventData.start ?? selectedEvent.start, eventData.end ?? selectedEvent.end, timezone)
|
|
3021
|
+
);
|
|
3022
|
+
} else if (selectedEvent) {
|
|
3023
|
+
onEventUpdate?.({ ...eventData, id: selectedEvent.id });
|
|
2535
3024
|
} else {
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
}
|
|
2540
|
-
};
|
|
2541
|
-
const handleModalDelete = (eventId) => {
|
|
2542
|
-
let effectiveEventId = eventId;
|
|
2543
|
-
if (selectedEvent?.originalEventId && selectedEvent.id === eventId) {
|
|
2544
|
-
effectiveEventId = selectedEvent.originalEventId;
|
|
3025
|
+
onEventCreate?.(
|
|
3026
|
+
modalInitialResourceId && eventData.resourceId === void 0 ? { ...eventData, resourceId: modalInitialResourceId } : eventData
|
|
3027
|
+
);
|
|
2545
3028
|
}
|
|
2546
|
-
|
|
2547
|
-
|
|
3029
|
+
});
|
|
3030
|
+
const handleEventDelete = useStableCallback((event) => {
|
|
3031
|
+
if (!event.originalEventId) {
|
|
3032
|
+
onEventDelete?.(event.id);
|
|
3033
|
+
return;
|
|
2548
3034
|
}
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
if (!over) return;
|
|
2553
|
-
const activeEvent = active.data.current?.event;
|
|
2554
|
-
const overDate = over.data.current?.date;
|
|
2555
|
-
if (!activeEvent || !overDate) return;
|
|
2556
|
-
const originalStart = new Date(activeEvent.start);
|
|
2557
|
-
const originalEnd = new Date(activeEvent.end);
|
|
2558
|
-
const duration = differenceInMilliseconds(originalEnd, originalStart);
|
|
2559
|
-
let newStart;
|
|
2560
|
-
if (view === "month") {
|
|
2561
|
-
if (timezone) {
|
|
2562
|
-
const zonedOriginal = toZonedTime5(originalStart, timezone);
|
|
2563
|
-
const zonedNew = new Date(overDate);
|
|
2564
|
-
zonedNew.setHours(zonedOriginal.getHours());
|
|
2565
|
-
zonedNew.setMinutes(zonedOriginal.getMinutes());
|
|
2566
|
-
zonedNew.setSeconds(zonedOriginal.getSeconds());
|
|
2567
|
-
zonedNew.setMilliseconds(zonedOriginal.getMilliseconds());
|
|
2568
|
-
newStart = fromZonedTime(zonedNew, timezone);
|
|
2569
|
-
} else {
|
|
2570
|
-
newStart = new Date(overDate);
|
|
2571
|
-
newStart.setHours(originalStart.getHours());
|
|
2572
|
-
newStart.setMinutes(originalStart.getMinutes());
|
|
2573
|
-
newStart.setSeconds(originalStart.getSeconds());
|
|
2574
|
-
newStart.setMilliseconds(originalStart.getMilliseconds());
|
|
2575
|
-
}
|
|
3035
|
+
const master = masterOf(event);
|
|
3036
|
+
if (onOccurrenceDelete && master) {
|
|
3037
|
+
onOccurrenceDelete(event, excludeOccurrence(master, event.occurrenceStart ?? event.start));
|
|
2576
3038
|
} else {
|
|
2577
|
-
|
|
2578
|
-
newStart = fromZonedTime(overDate, timezone);
|
|
2579
|
-
} else {
|
|
2580
|
-
newStart = new Date(overDate);
|
|
2581
|
-
}
|
|
2582
|
-
newStart.setSeconds(0);
|
|
2583
|
-
newStart.setMilliseconds(0);
|
|
2584
|
-
}
|
|
2585
|
-
const newEnd = new Date(newStart.getTime() + duration);
|
|
2586
|
-
let newResourceId = activeEvent.resourceId;
|
|
2587
|
-
const overResourceId = over.data.current?.resourceId;
|
|
2588
|
-
if (overResourceId) {
|
|
2589
|
-
newResourceId = overResourceId;
|
|
2590
|
-
}
|
|
2591
|
-
if (newStart.getTime() === originalStart.getTime() && newEnd.getTime() === originalEnd.getTime() && newResourceId === activeEvent.resourceId) {
|
|
2592
|
-
return;
|
|
3039
|
+
onEventDelete?.(event.originalEventId);
|
|
2593
3040
|
}
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
});
|
|
3041
|
+
});
|
|
3042
|
+
const handleModalDelete = useStableCallback((eventId) => {
|
|
3043
|
+
if (selectedEvent && selectedEvent.id === eventId) {
|
|
3044
|
+
handleEventDelete(selectedEvent);
|
|
3045
|
+
} else {
|
|
3046
|
+
onEventDelete?.(eventId);
|
|
2601
3047
|
}
|
|
3048
|
+
});
|
|
3049
|
+
const handleEventDuplicate = useStableCallback((event) => {
|
|
3050
|
+
const { recurrence: _recurrence, ...copy } = stripOccurrenceFields(event);
|
|
3051
|
+
onEventCreate?.({
|
|
3052
|
+
...copy,
|
|
3053
|
+
id: `${event.originalEventId ?? event.id}-copy-${Date.now()}`,
|
|
3054
|
+
title: `${event.title} (Copy)`,
|
|
3055
|
+
start: new Date(event.start.getTime() + DAY_MS2),
|
|
3056
|
+
end: new Date(event.end.getTime() + DAY_MS2)
|
|
3057
|
+
});
|
|
3058
|
+
});
|
|
3059
|
+
const applyChange = (event, start, end) => {
|
|
3060
|
+
if (!event.originalEventId) return { original: event, updated: { ...event, start, end } };
|
|
3061
|
+
const master = masterOf(event);
|
|
3062
|
+
if (!master) return null;
|
|
3063
|
+
return { original: master, updated: shiftSeries(master, event, start, end, timezone) };
|
|
2602
3064
|
};
|
|
3065
|
+
const handleDragEnd = useStableCallback((dragEvent) => {
|
|
3066
|
+
if (readOnly) return;
|
|
3067
|
+
const { active, over } = dragEvent;
|
|
3068
|
+
if (!over) return;
|
|
3069
|
+
const dragged = active.data.current?.event;
|
|
3070
|
+
const overDate = over.data.current?.date;
|
|
3071
|
+
if (!dragged || !overDate) return;
|
|
3072
|
+
const drop = computeDrop({
|
|
3073
|
+
event: dragged,
|
|
3074
|
+
overDate,
|
|
3075
|
+
overResourceId: over.data.current?.resourceId,
|
|
3076
|
+
grabOffsetMs: active.data.current?.grabOffsetMs,
|
|
3077
|
+
view,
|
|
3078
|
+
tz: timezone
|
|
3079
|
+
});
|
|
3080
|
+
if (!drop) return;
|
|
3081
|
+
const change = applyChange(dragged, drop.start, drop.end);
|
|
3082
|
+
if (!change) return;
|
|
3083
|
+
const updated = { ...change.updated, resourceId: drop.resourceId };
|
|
3084
|
+
onEventDrop?.(change.original, updated.start, updated.end);
|
|
3085
|
+
onEventUpdate?.(updated);
|
|
3086
|
+
});
|
|
3087
|
+
const handleEventResize = useStableCallback((event, newEnd) => {
|
|
3088
|
+
if (readOnly) return;
|
|
3089
|
+
const change = applyChange(event, event.start, newEnd);
|
|
3090
|
+
if (!change) return;
|
|
3091
|
+
onEventResize?.(change.original, change.updated.start, change.updated.end);
|
|
3092
|
+
onEventUpdate?.(change.updated);
|
|
3093
|
+
});
|
|
2603
3094
|
return {
|
|
2604
3095
|
view,
|
|
2605
3096
|
currentDate,
|
|
@@ -2621,15 +3112,18 @@ var useCalendarLogic = ({
|
|
|
2621
3112
|
handleCreateEvent,
|
|
2622
3113
|
handleModalSave,
|
|
2623
3114
|
handleModalDelete,
|
|
3115
|
+
handleEventDelete,
|
|
3116
|
+
handleEventDuplicate,
|
|
3117
|
+
handleEventResize,
|
|
2624
3118
|
handleDragEnd
|
|
2625
3119
|
};
|
|
2626
3120
|
};
|
|
2627
3121
|
|
|
2628
3122
|
// src/index.tsx
|
|
2629
|
-
import { differenceInMinutes as differenceInMinutes6, format as
|
|
3123
|
+
import { differenceInMinutes as differenceInMinutes6, format as format14, startOfWeek as startOfWeek3 } from "date-fns";
|
|
2630
3124
|
|
|
2631
3125
|
// src/hooks/useSwipeGesture.ts
|
|
2632
|
-
import { useRef as
|
|
3126
|
+
import { useRef as useRef7, useCallback as useCallback4, useEffect as useEffect5 } from "react";
|
|
2633
3127
|
var useSwipeGesture = (options) => {
|
|
2634
3128
|
const {
|
|
2635
3129
|
onSwipeLeft,
|
|
@@ -2641,8 +3135,8 @@ var useSwipeGesture = (options) => {
|
|
|
2641
3135
|
allowedTime = 500,
|
|
2642
3136
|
enabled = true
|
|
2643
3137
|
} = options;
|
|
2644
|
-
const touchInfoRef =
|
|
2645
|
-
const elementRef =
|
|
3138
|
+
const touchInfoRef = useRef7(null);
|
|
3139
|
+
const elementRef = useRef7(null);
|
|
2646
3140
|
const handleTouchStart = useCallback4((e) => {
|
|
2647
3141
|
if (!enabled) return;
|
|
2648
3142
|
const touch = e.touches[0];
|
|
@@ -2710,7 +3204,9 @@ var Scheduler = ({
|
|
|
2710
3204
|
onEventCreate,
|
|
2711
3205
|
onEventUpdate,
|
|
2712
3206
|
onEventDelete,
|
|
3207
|
+
onEventDrop,
|
|
2713
3208
|
onEventResize,
|
|
3209
|
+
onOccurrenceDelete,
|
|
2714
3210
|
timezone,
|
|
2715
3211
|
onTimezoneChange,
|
|
2716
3212
|
className,
|
|
@@ -2736,6 +3232,7 @@ var Scheduler = ({
|
|
|
2736
3232
|
const {
|
|
2737
3233
|
contextMenuEvent,
|
|
2738
3234
|
contextMenuPosition,
|
|
3235
|
+
openContextMenu,
|
|
2739
3236
|
closeContextMenu
|
|
2740
3237
|
} = useEventContextMenu();
|
|
2741
3238
|
const {
|
|
@@ -2758,9 +3255,11 @@ var Scheduler = ({
|
|
|
2758
3255
|
handleCreateEvent,
|
|
2759
3256
|
handleModalSave,
|
|
2760
3257
|
handleModalDelete,
|
|
3258
|
+
handleEventDelete,
|
|
3259
|
+
handleEventDuplicate,
|
|
3260
|
+
handleEventResize,
|
|
2761
3261
|
handleDragEnd,
|
|
2762
3262
|
expandedEvents
|
|
2763
|
-
// New export
|
|
2764
3263
|
} = useCalendarLogic({
|
|
2765
3264
|
events,
|
|
2766
3265
|
view: controlledView,
|
|
@@ -2771,6 +3270,9 @@ var Scheduler = ({
|
|
|
2771
3270
|
onEventUpdate,
|
|
2772
3271
|
onEventCreate,
|
|
2773
3272
|
onEventDelete,
|
|
3273
|
+
onEventDrop,
|
|
3274
|
+
onEventResize,
|
|
3275
|
+
onOccurrenceDelete,
|
|
2774
3276
|
readOnly,
|
|
2775
3277
|
timezone
|
|
2776
3278
|
});
|
|
@@ -2785,37 +3287,20 @@ var Scheduler = ({
|
|
|
2785
3287
|
const snapToGrid = createSnapModifier(gridSize);
|
|
2786
3288
|
const modifiers = [snapToGrid, restrictToWindowEdges];
|
|
2787
3289
|
const dndSensors = readOnly ? [] : sensors;
|
|
2788
|
-
const handleEventResize = (event, newEnd) => {
|
|
2789
|
-
if (readOnly) return;
|
|
2790
|
-
if (onEventResize) {
|
|
2791
|
-
onEventResize(event, event.start, newEnd);
|
|
2792
|
-
}
|
|
2793
|
-
if (onEventUpdate) {
|
|
2794
|
-
onEventUpdate({
|
|
2795
|
-
...event,
|
|
2796
|
-
end: newEnd
|
|
2797
|
-
});
|
|
2798
|
-
}
|
|
2799
|
-
};
|
|
2800
3290
|
const id = useId();
|
|
2801
3291
|
const swipeRef = useViewSwipe(handlePrev, handleNext, true);
|
|
2802
|
-
const
|
|
2803
|
-
const
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
};
|
|
2807
|
-
const overlayTimeFormat = uses24HourClock(locale) ? "H:mm" : "h:mm a";
|
|
2808
|
-
const handleDragStart = (event) => {
|
|
2809
|
-
const { active } = event;
|
|
2810
|
-
const draggedEvent = expandedEvents.find((e) => e.id === active.id);
|
|
3292
|
+
const t = useMemo4(() => resolveTranslations(language, translations), [language, translations]);
|
|
3293
|
+
const overlayTimeFormat = timeFormats(locale).time;
|
|
3294
|
+
const handleDragStart = useCallback5((event) => {
|
|
3295
|
+
const draggedEvent = event.active.data.current?.event;
|
|
2811
3296
|
if (draggedEvent) {
|
|
2812
3297
|
setActiveDragEvent(draggedEvent);
|
|
2813
3298
|
}
|
|
2814
|
-
};
|
|
2815
|
-
const onDragEndWrapper = (event) => {
|
|
3299
|
+
}, []);
|
|
3300
|
+
const onDragEndWrapper = useCallback5((event) => {
|
|
2816
3301
|
setActiveDragEvent(null);
|
|
2817
3302
|
handleDragEnd(event);
|
|
2818
|
-
};
|
|
3303
|
+
}, [handleDragEnd]);
|
|
2819
3304
|
const getDragHeight = () => {
|
|
2820
3305
|
if (!activeDragEvent) return void 0;
|
|
2821
3306
|
if (view === "resource") {
|
|
@@ -2835,14 +3320,15 @@ var Scheduler = ({
|
|
|
2835
3320
|
}
|
|
2836
3321
|
return "150px";
|
|
2837
3322
|
};
|
|
3323
|
+
const activeCalendarKey = calendars ? calendars.filter((c) => c.active !== false).map((c) => c.id).join("\0") : null;
|
|
2838
3324
|
const filteredEvents = useMemo4(() => {
|
|
2839
|
-
if (
|
|
2840
|
-
const activeCalendarIds =
|
|
2841
|
-
return expandedEvents.filter((e) =>
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
3325
|
+
if (activeCalendarKey === null) return expandedEvents;
|
|
3326
|
+
const activeCalendarIds = new Set(activeCalendarKey.split("\0"));
|
|
3327
|
+
return expandedEvents.filter((e) => !e.calendarId || activeCalendarIds.has(String(e.calendarId)));
|
|
3328
|
+
}, [expandedEvents, activeCalendarKey]);
|
|
3329
|
+
const periodKey = view === "month" ? format14(currentDate, "yyyy-MM") : view === "week" ? format14(startOfWeek3(currentDate, { weekStartsOn: resolveWeekStartsOn(locale, 1) }), "yyyy-MM-dd") : format14(currentDate, "yyyy-MM-dd");
|
|
3330
|
+
const slotClickHandler = readOnly ? void 0 : handleTimeSlotClick;
|
|
3331
|
+
const contextMenuHandler = readOnly ? void 0 : openContextMenu;
|
|
2846
3332
|
return /* @__PURE__ */ React18.createElement(
|
|
2847
3333
|
DndContext,
|
|
2848
3334
|
{
|
|
@@ -2913,7 +3399,7 @@ var Scheduler = ({
|
|
|
2913
3399
|
), /* @__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(
|
|
2914
3400
|
motion4.div,
|
|
2915
3401
|
{
|
|
2916
|
-
key: `${view}-${
|
|
3402
|
+
key: `${view}-${periodKey}-${timezone || "local"}`,
|
|
2917
3403
|
initial: { opacity: 0, scale: 0.98, y: 15 },
|
|
2918
3404
|
animate: {
|
|
2919
3405
|
opacity: 1,
|
|
@@ -2943,7 +3429,9 @@ var Scheduler = ({
|
|
|
2943
3429
|
onEventClick: handleEventClickInternal,
|
|
2944
3430
|
onDateClick: handleDateClick,
|
|
2945
3431
|
timezone,
|
|
2946
|
-
locale
|
|
3432
|
+
locale,
|
|
3433
|
+
translations: t,
|
|
3434
|
+
onEventContextMenu: contextMenuHandler
|
|
2947
3435
|
}
|
|
2948
3436
|
),
|
|
2949
3437
|
view === "week" && /* @__PURE__ */ React18.createElement(
|
|
@@ -2952,11 +3440,12 @@ var Scheduler = ({
|
|
|
2952
3440
|
currentDate,
|
|
2953
3441
|
events: filteredEvents,
|
|
2954
3442
|
onEventClick: handleEventClickInternal,
|
|
2955
|
-
onTimeSlotClick:
|
|
3443
|
+
onTimeSlotClick: slotClickHandler,
|
|
2956
3444
|
onEventResize: handleEventResize,
|
|
2957
3445
|
timezone,
|
|
2958
3446
|
locale,
|
|
2959
|
-
readonly: readOnly
|
|
3447
|
+
readonly: readOnly,
|
|
3448
|
+
onEventContextMenu: contextMenuHandler
|
|
2960
3449
|
}
|
|
2961
3450
|
),
|
|
2962
3451
|
view === "day" && /* @__PURE__ */ React18.createElement(
|
|
@@ -2965,11 +3454,13 @@ var Scheduler = ({
|
|
|
2965
3454
|
currentDate,
|
|
2966
3455
|
events: filteredEvents,
|
|
2967
3456
|
onEventClick: handleEventClickInternal,
|
|
2968
|
-
onTimeSlotClick:
|
|
3457
|
+
onTimeSlotClick: slotClickHandler,
|
|
2969
3458
|
onEventResize: handleEventResize,
|
|
2970
3459
|
timezone,
|
|
2971
3460
|
locale,
|
|
2972
|
-
readonly: readOnly
|
|
3461
|
+
readonly: readOnly,
|
|
3462
|
+
translations: t,
|
|
3463
|
+
onEventContextMenu: contextMenuHandler
|
|
2973
3464
|
}
|
|
2974
3465
|
),
|
|
2975
3466
|
view === "agenda" && /* @__PURE__ */ React18.createElement(
|
|
@@ -2980,7 +3471,9 @@ var Scheduler = ({
|
|
|
2980
3471
|
onEventClick: handleEventClickInternal,
|
|
2981
3472
|
onCreateEvent: handleCreateEvent,
|
|
2982
3473
|
locale,
|
|
2983
|
-
translations: t
|
|
3474
|
+
translations: t,
|
|
3475
|
+
timezone,
|
|
3476
|
+
onEventContextMenu: contextMenuHandler
|
|
2984
3477
|
}
|
|
2985
3478
|
),
|
|
2986
3479
|
view === "resource" && resources && /* @__PURE__ */ React18.createElement(
|
|
@@ -2990,17 +3483,18 @@ var Scheduler = ({
|
|
|
2990
3483
|
events: filteredEvents,
|
|
2991
3484
|
resources,
|
|
2992
3485
|
onEventClick: handleEventClickInternal,
|
|
2993
|
-
onTimeSlotClick:
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
3486
|
+
onTimeSlotClick: slotClickHandler,
|
|
3487
|
+
locale,
|
|
3488
|
+
timezone,
|
|
3489
|
+
translations: t,
|
|
3490
|
+
onEventContextMenu: contextMenuHandler
|
|
2998
3491
|
}
|
|
2999
3492
|
)
|
|
3000
|
-
)))), /* @__PURE__ */ React18.createElement("div", { className: "md:hidden absolute bottom-6 right-6 z-50" }, /* @__PURE__ */ React18.createElement(
|
|
3493
|
+
)))), !readOnly && /* @__PURE__ */ React18.createElement("div", { className: "md:hidden absolute bottom-6 right-6 z-50" }, /* @__PURE__ */ React18.createElement(
|
|
3001
3494
|
"button",
|
|
3002
3495
|
{
|
|
3003
3496
|
onClick: handleCreateEvent,
|
|
3497
|
+
"aria-label": t.createEvent,
|
|
3004
3498
|
className: "w-14 h-14 bg-blue-600 rounded-full shadow-lg flex items-center justify-center text-white active:scale-90 transition-transform"
|
|
3005
3499
|
},
|
|
3006
3500
|
/* @__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" }))
|
|
@@ -3023,7 +3517,8 @@ var Scheduler = ({
|
|
|
3023
3517
|
onDelete: handleModalDelete,
|
|
3024
3518
|
calendars,
|
|
3025
3519
|
eventTypes,
|
|
3026
|
-
translations: t
|
|
3520
|
+
translations: t,
|
|
3521
|
+
locale
|
|
3027
3522
|
}
|
|
3028
3523
|
),
|
|
3029
3524
|
/* @__PURE__ */ React18.createElement(
|
|
@@ -3036,20 +3531,12 @@ var Scheduler = ({
|
|
|
3036
3531
|
handleEventClickInternal(event);
|
|
3037
3532
|
closeContextMenu();
|
|
3038
3533
|
},
|
|
3039
|
-
onDelete: (
|
|
3040
|
-
|
|
3534
|
+
onDelete: () => {
|
|
3535
|
+
if (contextMenuEvent) handleEventDelete(contextMenuEvent);
|
|
3041
3536
|
closeContextMenu();
|
|
3042
3537
|
},
|
|
3043
3538
|
onDuplicate: (event) => {
|
|
3044
|
-
|
|
3045
|
-
...event,
|
|
3046
|
-
id: `${event.id}-copy-${Date.now()}`,
|
|
3047
|
-
title: `${event.title} (Copy)`,
|
|
3048
|
-
start: new Date(event.start.getTime() + 24 * 60 * 60 * 1e3),
|
|
3049
|
-
// +1 day
|
|
3050
|
-
end: new Date(event.end.getTime() + 24 * 60 * 60 * 1e3)
|
|
3051
|
-
};
|
|
3052
|
-
onEventCreate?.(duplicatedEvent);
|
|
3539
|
+
handleEventDuplicate(event);
|
|
3053
3540
|
closeContextMenu();
|
|
3054
3541
|
},
|
|
3055
3542
|
translations: {
|
|
@@ -3083,7 +3570,7 @@ var Scheduler = ({
|
|
|
3083
3570
|
className: "absolute left-0 top-0 bottom-0 w-1 rounded-l-lg",
|
|
3084
3571
|
style: { backgroundColor: activeDragEvent.color || "var(--primary)" }
|
|
3085
3572
|
}
|
|
3086
|
-
), /* @__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" })),
|
|
3573
|
+
), /* @__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(toDisplay(activeDragEvent.start, timezone), overlayTimeFormat, { locale }), " - ", format14(toDisplay(activeDragEvent.end, timezone), 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" }))))
|
|
3087
3574
|
) : null)
|
|
3088
3575
|
)
|
|
3089
3576
|
);
|