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