calendarkit-pro 1.3.0 → 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/dist/index.mjs CHANGED
@@ -94,7 +94,8 @@ var CalendarHeader = ({
94
94
  variant: "ghost",
95
95
  size: "icon",
96
96
  className: "text-muted-foreground hover:text-foreground hover:bg-accent/80 rounded-xl h-10 w-10 hidden md:inline-flex transition-all duration-200",
97
- onClick: onMenuClick
97
+ onClick: onMenuClick,
98
+ "aria-label": translations.toggleSidebar
98
99
  },
99
100
  /* @__PURE__ */ React2.createElement(Menu, { className: "h-5 w-5" })
100
101
  ), /* @__PURE__ */ React2.createElement(
@@ -111,7 +112,8 @@ var CalendarHeader = ({
111
112
  variant: "ghost",
112
113
  size: "icon",
113
114
  onClick: onPrev,
114
- className: "rounded-lg h-8 w-8 hover:bg-background/80 transition-all duration-200"
115
+ className: "rounded-lg h-8 w-8 hover:bg-background/80 transition-all duration-200",
116
+ "aria-label": translations.previous
115
117
  },
116
118
  /* @__PURE__ */ React2.createElement(ChevronLeft, { className: "h-4 w-4 text-muted-foreground" })
117
119
  ), /* @__PURE__ */ React2.createElement(
@@ -120,7 +122,8 @@ var CalendarHeader = ({
120
122
  variant: "ghost",
121
123
  size: "icon",
122
124
  onClick: onNext,
123
- className: "rounded-lg h-8 w-8 hover:bg-background/80 transition-all duration-200"
125
+ className: "rounded-lg h-8 w-8 hover:bg-background/80 transition-all duration-200",
126
+ "aria-label": translations.next
124
127
  },
125
128
  /* @__PURE__ */ React2.createElement(ChevronRight, { className: "h-4 w-4 text-muted-foreground" })
126
129
  )), /* @__PURE__ */ React2.createElement("div", { className: "ml-2 md:ml-4" }, /* @__PURE__ */ React2.createElement("h2", { className: "text-lg md:text-xl font-semibold text-foreground whitespace-nowrap capitalize tracking-tight" }, format(currentDate, "MMMM yyyy", { locale }))))), /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2 md:gap-3 w-full md:w-auto justify-end" }, onLanguageChange && language && /* @__PURE__ */ React2.createElement(
@@ -129,6 +132,7 @@ var CalendarHeader = ({
129
132
  variant: "ghost",
130
133
  size: "sm",
131
134
  onClick: () => onLanguageChange(language === "en" ? "fr" : "en"),
135
+ "aria-label": translations.switchLanguage,
132
136
  className: "h-9 px-3 rounded-xl text-xs font-semibold text-muted-foreground hover:text-foreground hover:bg-accent/80 transition-all duration-200 uppercase tracking-wider"
133
137
  },
134
138
  language
@@ -138,7 +142,8 @@ var CalendarHeader = ({
138
142
  variant: "ghost",
139
143
  size: "icon",
140
144
  className: "rounded-xl h-9 w-9 hover:bg-accent/80 transition-all duration-200",
141
- onClick: onThemeToggle
145
+ onClick: onThemeToggle,
146
+ "aria-label": translations.toggleTheme
142
147
  },
143
148
  isDarkMode ? /* @__PURE__ */ React2.createElement(Sun, { className: "h-4 w-4 text-amber-500" }) : /* @__PURE__ */ React2.createElement(Moon, { className: "h-4 w-4 text-muted-foreground" })
144
149
  ), !hideViewSwitcher && /* @__PURE__ */ React2.createElement("div", { className: "flex items-center bg-muted/50 backdrop-blur-sm rounded-xl p-1" }, viewConfig.map(({ key, icon: Icon }) => /* @__PURE__ */ React2.createElement(
@@ -148,6 +153,8 @@ var CalendarHeader = ({
148
153
  variant: "ghost",
149
154
  size: "sm",
150
155
  onClick: () => onViewChange(key),
156
+ "aria-pressed": view === key,
157
+ "aria-label": translations[key],
151
158
  className: cn(
152
159
  "h-8 px-3 text-xs rounded-lg transition-all duration-200 gap-1.5",
153
160
  view === key ? "bg-background shadow-sm text-foreground font-medium border-[0.5px] border-border/50" : "text-muted-foreground hover:text-foreground hover:bg-background/50"
@@ -192,7 +199,8 @@ var MiniCalendar = ({
192
199
  onDateChange,
193
200
  onViewChange,
194
201
  className,
195
- locale
202
+ locale,
203
+ labels
196
204
  }) => {
197
205
  const [viewDate, setViewDate] = React3.useState(currentDate);
198
206
  React3.useEffect(() => {
@@ -230,7 +238,7 @@ var MiniCalendar = ({
230
238
  size: "icon",
231
239
  className: "h-7 w-7 rounded-md hover:bg-background/80 transition-all",
232
240
  onClick: handlePrev,
233
- "aria-label": "Previous month"
241
+ "aria-label": labels?.previousMonth || "Previous month"
234
242
  },
235
243
  /* @__PURE__ */ React3.createElement(ChevronLeft2, { className: "h-4 w-4" })
236
244
  ), /* @__PURE__ */ React3.createElement(
@@ -240,7 +248,7 @@ var MiniCalendar = ({
240
248
  size: "icon",
241
249
  className: "h-7 w-7 rounded-md hover:bg-background/80 transition-all",
242
250
  onClick: handleNext,
243
- "aria-label": "Next month"
251
+ "aria-label": labels?.nextMonth || "Next month"
244
252
  },
245
253
  /* @__PURE__ */ React3.createElement(ChevronRight2, { className: "h-4 w-4" })
246
254
  ))), /* @__PURE__ */ React3.createElement("div", { className: "grid grid-cols-7 gap-y-2 text-center mb-2" }, weekDays.map((day, i) => /* @__PURE__ */ React3.createElement("div", { key: `${day}-${i}`, className: "text-[10px] text-muted-foreground/70 font-semibold uppercase" }, day))), /* @__PURE__ */ React3.createElement("div", { className: "grid grid-cols-7 gap-y-1 text-center" }, days.map((day) => {
@@ -340,7 +348,45 @@ var en = {
340
348
  allCaughtUp: "All caught up!",
341
349
  noEventsScheduled: "No events scheduled",
342
350
  noUpcoming: "Your schedule is clear",
343
- noUpcomingHint: "No upcoming events to show. Create a new event to start planning."
351
+ noUpcomingHint: "No upcoming events to show. Create a new event to start planning.",
352
+ newEvent: "New Event",
353
+ saveChanges: "Save Changes",
354
+ close: "Close",
355
+ noTitle: "(No title)",
356
+ openInMaps: "Open in maps",
357
+ notesLabel: "Notes",
358
+ addNotes: "Add description or notes...",
359
+ attachments: "Attachments",
360
+ reminders: "Reminders",
361
+ add: "Add",
362
+ calendar: "Calendar",
363
+ select: "Select",
364
+ addLocation: "Add location",
365
+ guestSingular: "Guest",
366
+ addGuestPlaceholder: "Add a guest email and press Enter",
367
+ invalidEmail: "Enter a valid email address",
368
+ removeGuest: "Remove {email}",
369
+ reminderAtTime: "At time of event",
370
+ reminderMinutes: "{n} minutes before",
371
+ reminderHour: "1 hour before",
372
+ reminderHours: "{n} hours before",
373
+ reminderDay: "1 day before",
374
+ reminderDays: "{n} days before",
375
+ reminderWeek: "1 week before",
376
+ removeReminder: "Remove reminder",
377
+ durationMinutes: "{n} min",
378
+ durationHours: "{n} hr",
379
+ durationHoursMinutes: "{h} hr {m} min",
380
+ moreEvents: "+{n} more",
381
+ idLabel: "ID: {id}",
382
+ previous: "Previous",
383
+ next: "Next",
384
+ previousMonth: "Previous month",
385
+ nextMonth: "Next month",
386
+ toggleSidebar: "Toggle sidebar",
387
+ toggleTheme: "Toggle dark mode",
388
+ switchLanguage: "Switch language",
389
+ eventColor: "Event color"
344
390
  };
345
391
  var fr = {
346
392
  today: "Aujourd'hui",
@@ -409,7 +455,45 @@ var fr = {
409
455
  allCaughtUp: "Tout est \xE0 jour !",
410
456
  noEventsScheduled: "Aucun \xE9v\xE9nement pr\xE9vu",
411
457
  noUpcoming: "Votre planning est libre",
412
- noUpcomingHint: "Aucun \xE9v\xE9nement \xE0 venir. Cr\xE9ez un nouvel \xE9v\xE9nement pour commencer \xE0 planifier."
458
+ noUpcomingHint: "Aucun \xE9v\xE9nement \xE0 venir. Cr\xE9ez un nouvel \xE9v\xE9nement pour commencer \xE0 planifier.",
459
+ newEvent: "Nouvel \xE9v\xE9nement",
460
+ saveChanges: "Enregistrer",
461
+ close: "Fermer",
462
+ noTitle: "(Sans titre)",
463
+ openInMaps: "Ouvrir dans Maps",
464
+ notesLabel: "Notes",
465
+ addNotes: "Ajouter une description ou des notes\u2026",
466
+ attachments: "Pi\xE8ces jointes",
467
+ reminders: "Rappels",
468
+ add: "Ajouter",
469
+ calendar: "Calendrier",
470
+ select: "Choisir",
471
+ addLocation: "Ajouter un lieu",
472
+ guestSingular: "Invit\xE9",
473
+ addGuestPlaceholder: "Ajoutez l'e-mail d'un invit\xE9 puis Entr\xE9e",
474
+ invalidEmail: "Saisissez une adresse e-mail valide",
475
+ removeGuest: "Retirer {email}",
476
+ reminderAtTime: "Au moment de l'\xE9v\xE9nement",
477
+ reminderMinutes: "{n} minutes avant",
478
+ reminderHour: "1 heure avant",
479
+ reminderHours: "{n} heures avant",
480
+ reminderDay: "1 jour avant",
481
+ reminderDays: "{n} jours avant",
482
+ reminderWeek: "1 semaine avant",
483
+ removeReminder: "Supprimer le rappel",
484
+ durationMinutes: "{n} min",
485
+ durationHours: "{n} h",
486
+ durationHoursMinutes: "{h} h {m} min",
487
+ moreEvents: "+{n} de plus",
488
+ idLabel: "ID : {id}",
489
+ previous: "Pr\xE9c\xE9dent",
490
+ next: "Suivant",
491
+ previousMonth: "Mois pr\xE9c\xE9dent",
492
+ nextMonth: "Mois suivant",
493
+ toggleSidebar: "Afficher ou masquer la barre lat\xE9rale",
494
+ toggleTheme: "Basculer le th\xE8me sombre",
495
+ switchLanguage: "Changer de langue",
496
+ eventColor: "Couleur de l'\xE9v\xE9nement"
413
497
  };
414
498
 
415
499
  // src/core/format.ts
@@ -452,7 +536,18 @@ function timeZoneAbbreviation(date, timeZone) {
452
536
  }
453
537
  }
454
538
  function resolveTranslations(language, overrides) {
455
- return { ...language === "fr" ? fr : en, ...overrides };
539
+ const defined = Object.fromEntries(Object.entries(overrides ?? {}).filter(([, value]) => value !== void 0));
540
+ return { ...language === "fr" ? fr : en, ...defined };
541
+ }
542
+ function interpolate(template, values) {
543
+ return template.replace(/\{(\w+)\}/g, (match, key) => key in values ? String(values[key]) : match);
544
+ }
545
+
546
+ // src/core/color.ts
547
+ var SAFE_COLOR = /^[#\w\s%.,/()+-]+$/;
548
+ var FORBIDDEN = /url\(|expression\(/i;
549
+ function safeColor(value, fallback) {
550
+ return value && SAFE_COLOR.test(value.trim()) && !FORBIDDEN.test(value) ? value.trim() : fallback;
456
551
  }
457
552
 
458
553
  // src/components/Sidebar.tsx
@@ -480,12 +575,6 @@ var Sidebar = ({
480
575
  const timer = setInterval(() => setNow(/* @__PURE__ */ new Date()), 6e4);
481
576
  return () => clearInterval(timer);
482
577
  }, []);
483
- const defaultCalendars = [
484
- { id: "1", label: "My Calendar", color: "#3b82f6", active: true },
485
- { id: "2", label: "Birthdays", color: "#10b981", active: true },
486
- { id: "3", label: "Tasks", color: "#6366f1", active: true }
487
- ];
488
- const displayCalendars = calendars || defaultCalendars;
489
578
  const getAcronym = (tz) => !tz || !now ? "LOC" : timeZoneAbbreviation(now, tz);
490
579
  const timezones = [
491
580
  { value: "", label: translations?.localTime || "Local Time", acronym: "LOC" },
@@ -535,7 +624,16 @@ var Sidebar = ({
535
624
  },
536
625
  /* @__PURE__ */ React4.createElement(Plus, { className: "w-5 h-5" }),
537
626
  /* @__PURE__ */ React4.createElement("span", { className: "text-sm" }, translations?.create || "Create")
538
- )), /* @__PURE__ */ React4.createElement(MiniCalendar, { currentDate, onDateChange, onViewChange, locale }), /* @__PURE__ */ React4.createElement("div", { className: "flex-1 px-4 space-y-5 mt-5" }, /* @__PURE__ */ React4.createElement("div", { className: "bg-muted/20 rounded-2xl p-3 border-[0px] border-border/30" }, /* @__PURE__ */ React4.createElement(
627
+ )), /* @__PURE__ */ React4.createElement(
628
+ MiniCalendar,
629
+ {
630
+ currentDate,
631
+ onDateChange,
632
+ onViewChange,
633
+ locale,
634
+ labels: { previousMonth: translations?.previousMonth, nextMonth: translations?.nextMonth }
635
+ }
636
+ ), /* @__PURE__ */ React4.createElement("div", { className: "flex-1 px-4 space-y-5 mt-5" }, calendars && calendars.length > 0 && /* @__PURE__ */ React4.createElement("div", { className: "bg-muted/20 rounded-2xl p-3 border-[0px] border-border/30" }, /* @__PURE__ */ React4.createElement(
539
637
  "div",
540
638
  {
541
639
  className: "flex items-center justify-between cursor-pointer hover:bg-accent/50 p-2 -m-1 rounded-xl mb-2 transition-all duration-200",
@@ -543,41 +641,40 @@ var Sidebar = ({
543
641
  },
544
642
  /* @__PURE__ */ React4.createElement("span", { className: "text-sm font-semibold text-foreground" }, translations?.calendars || "Calendars"),
545
643
  /* @__PURE__ */ React4.createElement(ChevronDown, { className: cn("w-4 h-4 text-muted-foreground transition-transform duration-200", calendarsOpen && "rotate-180") })
546
- ), calendarsOpen && /* @__PURE__ */ React4.createElement("div", { className: "space-y-1" }, displayCalendars.map((cal) => /* @__PURE__ */ React4.createElement(
644
+ ), calendarsOpen && /* @__PURE__ */ React4.createElement("div", { className: "space-y-1" }, calendars.map((cal) => /* @__PURE__ */ React4.createElement(
547
645
  "div",
548
646
  {
549
647
  key: cal.id,
550
648
  className: "flex items-center gap-3 py-2 px-2 hover:bg-accent/60 rounded-xl cursor-pointer group transition-all duration-200",
551
649
  onClick: () => onCalendarToggle?.(cal.id, !(cal.active ?? true))
552
650
  },
553
- /* @__PURE__ */ React4.createElement("div", { className: "relative flex items-center justify-center" }, /* @__PURE__ */ React4.createElement(
554
- "input",
651
+ /* @__PURE__ */ React4.createElement(
652
+ "div",
555
653
  {
556
- type: "checkbox",
557
- checked: cal.active ?? true,
558
- onChange: (e) => {
559
- e.stopPropagation();
560
- onCalendarToggle?.(cal.id, e.target.checked);
561
- },
562
- className: "peer h-5 w-5 rounded-md border-2 border-border/60 cursor-pointer appearance-none checked:border-transparent transition-all duration-200",
563
- style: { "--primary-color": cal.color },
564
- "data-cal-id": cal.id
565
- }
566
- ), /* @__PURE__ */ React4.createElement("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none opacity-0 peer-checked:opacity-100 transition-opacity" }, /* @__PURE__ */ React4.createElement("svg", { className: "w-3.5 h-3.5 text-white", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 3 }, /* @__PURE__ */ React4.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" }))), /* @__PURE__ */ React4.createElement("style", null, `
567
- input[type="checkbox"][data-cal-id="${cal.id}"]:checked {
568
- background-color: ${cal.color} !important;
569
- border-color: ${cal.color} !important;
570
- }
571
- input[type="checkbox"][data-cal-id="${cal.id}"]:focus {
572
- --tw-ring-color: ${cal.color}40 !important;
573
- }
574
- `)),
654
+ className: "relative flex items-center justify-center",
655
+ style: { "--cal-color": safeColor(cal.color, "hsl(var(--primary))") }
656
+ },
657
+ /* @__PURE__ */ React4.createElement(
658
+ "input",
659
+ {
660
+ type: "checkbox",
661
+ checked: cal.active ?? true,
662
+ onChange: (e) => {
663
+ e.stopPropagation();
664
+ onCalendarToggle?.(cal.id, e.target.checked);
665
+ },
666
+ "aria-label": cal.label,
667
+ className: "peer h-5 w-5 rounded-md border-2 border-border/60 cursor-pointer appearance-none checked:border-transparent checked:bg-[var(--cal-color)] focus:ring-2 focus:ring-[var(--cal-color)] transition-all duration-200"
668
+ }
669
+ ),
670
+ /* @__PURE__ */ React4.createElement("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none opacity-0 peer-checked:opacity-100 transition-opacity" }, /* @__PURE__ */ React4.createElement("svg", { className: "w-3.5 h-3.5 text-white", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 3 }, /* @__PURE__ */ React4.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" })))
671
+ ),
575
672
  /* @__PURE__ */ React4.createElement("div", { className: "flex items-center gap-2 flex-1 min-w-0" }, /* @__PURE__ */ React4.createElement("span", { className: "text-sm text-foreground/90 truncate font-medium" }, cal.label)),
576
673
  /* @__PURE__ */ React4.createElement(
577
674
  "div",
578
675
  {
579
676
  className: "w-2 h-2 rounded-full opacity-60 group-hover:opacity-100 transition-opacity",
580
- style: { backgroundColor: cal.color }
677
+ style: { backgroundColor: safeColor(cal.color, "hsl(var(--primary))") }
581
678
  }
582
679
  )
583
680
  ))))), /* @__PURE__ */ React4.createElement("div", { className: "mt-auto px-4 pt-5" }, /* @__PURE__ */ React4.createElement("div", { className: "bg-muted/20 rounded-2xl p-3" }, /* @__PURE__ */ React4.createElement("div", { className: "flex items-center gap-2 mb-3" }, /* @__PURE__ */ React4.createElement("div", { className: "p-1.5 bg-primary/10 rounded-lg" }, /* @__PURE__ */ React4.createElement(Globe, { className: "w-4 h-4 text-primary" })), /* @__PURE__ */ React4.createElement("span", { className: "text-sm font-semibold text-foreground" }, translations?.timezone || "Timezone")), /* @__PURE__ */ React4.createElement("div", { className: "relative" }, /* @__PURE__ */ React4.createElement(
@@ -864,7 +961,7 @@ var DroppableCell = React6.memo(
864
961
  );
865
962
 
866
963
  // src/views/MonthView.tsx
867
- var EventItem = React7.memo(({ event, onEventClick }) => /* @__PURE__ */ React7.createElement(DraggableEvent, { event }, /* @__PURE__ */ React7.createElement(
964
+ var EventItem = React7.memo(({ event, onEventClick, onEventContextMenu }) => /* @__PURE__ */ React7.createElement(DraggableEvent, { event }, /* @__PURE__ */ React7.createElement(
868
965
  "div",
869
966
  {
870
967
  className: cn(
@@ -880,7 +977,8 @@ var EventItem = React7.memo(({ event, onEventClick }) => /* @__PURE__ */ React7.
880
977
  onClick: (e) => {
881
978
  e.stopPropagation();
882
979
  onEventClick?.(event);
883
- }
980
+ },
981
+ onContextMenu: onEventContextMenu ? (e) => onEventContextMenu(event, e) : void 0
884
982
  },
885
983
  /* @__PURE__ */ React7.createElement("span", { className: "font-medium" }, event.title)
886
984
  )));
@@ -891,7 +989,9 @@ var MonthView = ({
891
989
  onEventClick,
892
990
  onDateClick,
893
991
  timezone,
894
- locale
992
+ locale,
993
+ translations,
994
+ onEventContextMenu
895
995
  }) => {
896
996
  const weekStartsOn = resolveWeekStartsOn(locale, 1);
897
997
  const days = useMemo(() => getMonthGrid(currentDate, weekStartsOn), [currentDate, weekStartsOn]);
@@ -927,7 +1027,19 @@ var MonthView = ({
927
1027
  "text-sm font-semibold w-7 h-7 flex items-center justify-center rounded-full transition-all duration-200",
928
1028
  isSameDay3(day, today) ? "bg-primary text-primary-foreground shadow-md shadow-primary/30" : "group-hover:bg-accent"
929
1029
  ) }, format7(day, "d", { locale })), dayEvents.length > 0 && /* @__PURE__ */ React7.createElement("div", { className: "text-[10px] font-medium text-muted-foreground/60 bg-muted/50 px-1.5 py-0.5 rounded-full" }, dayEvents.length)),
930
- /* @__PURE__ */ React7.createElement("div", { className: "flex-1 flex flex-col gap-1 scrollbar-hide overflow-y-auto overflow-x-hidden" }, dayEvents.slice(0, 4).map((event) => /* @__PURE__ */ React7.createElement(EventItem, { key: `${event.id}-${dayKey2}`, event, onEventClick })), dayEvents.length > 4 && /* @__PURE__ */ React7.createElement("div", { className: "text-[10px] text-primary font-semibold text-center py-1 px-2 rounded-md bg-primary/5 hover:bg-primary/10 cursor-pointer transition-colors" }, "+", dayEvents.length - 4, " more"))
1030
+ /* @__PURE__ */ React7.createElement("div", { className: "flex-1 flex flex-col gap-1 scrollbar-hide overflow-y-auto overflow-x-hidden" }, dayEvents.slice(0, 4).map((event) => /* @__PURE__ */ React7.createElement(EventItem, { key: `${event.id}-${dayKey2}`, event, onEventClick, onEventContextMenu })), dayEvents.length > 4 && // U7: "+N more" opens the day instead of doing nothing.
1031
+ /* @__PURE__ */ React7.createElement(
1032
+ "button",
1033
+ {
1034
+ type: "button",
1035
+ className: "text-[10px] text-primary font-semibold text-center py-1 px-2 rounded-md bg-primary/5 hover:bg-primary/10 cursor-pointer transition-colors",
1036
+ onClick: (e) => {
1037
+ e.stopPropagation();
1038
+ onDateClick?.(day);
1039
+ }
1040
+ },
1041
+ interpolate(translations?.moreEvents || "+{n} more", { n: dayEvents.length - 4 })
1042
+ ))
931
1043
  );
932
1044
  }))));
933
1045
  };
@@ -1110,7 +1222,8 @@ var WeekViewImpl = ({
1110
1222
  onEventResize,
1111
1223
  timezone,
1112
1224
  locale,
1113
- readonly
1225
+ readonly,
1226
+ onEventContextMenu
1114
1227
  }) => {
1115
1228
  const weekDays = React9.useMemo(
1116
1229
  () => getWeekDays(currentDate, resolveWeekStartsOn(locale, 1)),
@@ -1260,6 +1373,7 @@ var WeekViewImpl = ({
1260
1373
  e.stopPropagation();
1261
1374
  onEventClick?.(event);
1262
1375
  },
1376
+ onContextMenu: onEventContextMenu ? (e) => onEventContextMenu(event, e) : void 0,
1263
1377
  title: columns > 1 ? `${event.title} (${column + 1}/${columns})` : void 0
1264
1378
  },
1265
1379
  /* @__PURE__ */ React9.createElement("div", { className: "flex flex-col h-full overflow-hidden w-full" }, /* @__PURE__ */ React9.createElement("div", { className: cn(
@@ -1304,7 +1418,9 @@ var DayViewImpl = ({
1304
1418
  onEventResize,
1305
1419
  timezone,
1306
1420
  locale,
1307
- readonly
1421
+ readonly,
1422
+ onEventContextMenu,
1423
+ translations
1308
1424
  }) => {
1309
1425
  const visibleRange = React10.useMemo(() => {
1310
1426
  const start = startOfDay3(currentDate);
@@ -1328,7 +1444,7 @@ var DayViewImpl = ({
1328
1444
  const zonedNow = toDisplay(now, timezone);
1329
1445
  const nowTop = minutesSinceDisplayMidnight(now, timezone) / 60 * HOUR_HEIGHT2;
1330
1446
  const formats = timeFormats(locale);
1331
- return /* @__PURE__ */ React10.createElement("div", { className: "flex flex-col h-full bg-background border-[0.5px] border-border/50 rounded-2xl overflow-hidden shadow-sm" }, /* @__PURE__ */ React10.createElement("div", { className: "px-6 py-4 border-b-[0.5px] border-border/50 bg-gradient-to-r from-muted/20 via-background to-muted/20 text-center shrink-0" }, /* @__PURE__ */ React10.createElement("div", { className: "flex items-center justify-center gap-3" }, /* @__PURE__ */ React10.createElement("h2", { className: "text-xl font-semibold capitalize text-foreground" }, format9(currentDate, "EEEE, MMMM d, yyyy", { locale })), isToday2 && /* @__PURE__ */ React10.createElement("span", { className: "text-xs font-semibold bg-primary text-primary-foreground px-3 py-1 rounded-full shadow-md shadow-primary/20" }, "Today"))), /* @__PURE__ */ React10.createElement("div", { ref: scrollContainerRef, className: "flex-1 overflow-y-auto relative" }, /* @__PURE__ */ React10.createElement("div", { className: "flex relative", style: { height: HOURS2.length * HOUR_HEIGHT2 } }, /* @__PURE__ */ React10.createElement("div", { className: "w-20 bg-muted/5 border-r-[0.5px] border-border/30 relative" }, HOURS2.map((hour) => /* @__PURE__ */ React10.createElement(
1447
+ return /* @__PURE__ */ React10.createElement("div", { className: "flex flex-col h-full bg-background border-[0.5px] border-border/50 rounded-2xl overflow-hidden shadow-sm" }, /* @__PURE__ */ React10.createElement("div", { className: "px-6 py-4 border-b-[0.5px] border-border/50 bg-gradient-to-r from-muted/20 via-background to-muted/20 text-center shrink-0" }, /* @__PURE__ */ React10.createElement("div", { className: "flex items-center justify-center gap-3" }, /* @__PURE__ */ React10.createElement("h2", { className: "text-xl font-semibold capitalize text-foreground" }, format9(currentDate, "EEEE, MMMM d, yyyy", { locale })), isToday2 && /* @__PURE__ */ React10.createElement("span", { className: "text-xs font-semibold bg-primary text-primary-foreground px-3 py-1 rounded-full shadow-md shadow-primary/20" }, translations?.today || "Today"))), /* @__PURE__ */ React10.createElement("div", { ref: scrollContainerRef, className: "flex-1 overflow-y-auto relative" }, /* @__PURE__ */ React10.createElement("div", { className: "flex relative", style: { height: HOURS2.length * HOUR_HEIGHT2 } }, /* @__PURE__ */ React10.createElement("div", { className: "w-20 bg-muted/5 border-r-[0.5px] border-border/30 relative" }, HOURS2.map((hour) => /* @__PURE__ */ React10.createElement(
1332
1448
  "div",
1333
1449
  {
1334
1450
  key: hour,
@@ -1425,6 +1541,7 @@ var DayViewImpl = ({
1425
1541
  e.stopPropagation();
1426
1542
  onEventClick?.(event);
1427
1543
  },
1544
+ onContextMenu: onEventContextMenu ? (e) => onEventContextMenu(event, e) : void 0,
1428
1545
  title: columns > 1 ? `${event.title} (${column + 1}/${columns})` : void 0
1429
1546
  },
1430
1547
  /* @__PURE__ */ React10.createElement("div", { className: "flex flex-col h-full overflow-hidden w-full" }, /* @__PURE__ */ React10.createElement("div", { className: cn(
@@ -1487,7 +1604,8 @@ var AgendaView = ({
1487
1604
  onCreateEvent,
1488
1605
  locale,
1489
1606
  translations,
1490
- timezone
1607
+ timezone,
1608
+ onEventContextMenu
1491
1609
  }) => {
1492
1610
  const is24h = uses24HourClock(locale);
1493
1611
  const { time: timePattern, compact: compactTimePattern } = timeFormats(locale);
@@ -1544,6 +1662,7 @@ var AgendaView = ({
1544
1662
  {
1545
1663
  key: event.id,
1546
1664
  onClick: () => onEventClick?.(event),
1665
+ onContextMenu: onEventContextMenu ? (e) => onEventContextMenu(event, e) : void 0,
1547
1666
  className: cn(
1548
1667
  "group relative flex gap-4 p-4 rounded-2xl border border-border/40",
1549
1668
  "hover:border-primary/40 hover:shadow-lg hover:shadow-primary/5",
@@ -1584,7 +1703,9 @@ var ResourceView = ({
1584
1703
  onEventClick,
1585
1704
  onTimeSlotClick,
1586
1705
  locale,
1587
- timezone
1706
+ timezone,
1707
+ translations,
1708
+ onEventContextMenu
1588
1709
  }) => {
1589
1710
  const containerRef = useRef2(null);
1590
1711
  const hours = Array.from({ length: 24 }, (_, i) => i);
@@ -1604,7 +1725,7 @@ var ResourceView = ({
1604
1725
  // Min width for visibility
1605
1726
  };
1606
1727
  };
1607
- return /* @__PURE__ */ React13.createElement("div", { className: "flex flex-col h-full bg-background overflow-hidden" }, /* @__PURE__ */ React13.createElement("div", { className: "flex border-b border-border bg-muted/20" }, /* @__PURE__ */ React13.createElement("div", { className: "w-48 shrink-0 border-r border-border p-4 font-semibold text-sm bg-background sticky left-0 z-20" }, "Resources"), /* @__PURE__ */ React13.createElement("div", { className: "flex overflow-hidden" }, /* @__PURE__ */ React13.createElement("div", { className: "flex relative", style: { width: hours.length * hourWidth } }, hours.map((hour) => /* @__PURE__ */ React13.createElement(
1728
+ return /* @__PURE__ */ React13.createElement("div", { className: "flex flex-col h-full bg-background overflow-hidden" }, /* @__PURE__ */ React13.createElement("div", { className: "flex border-b border-border bg-muted/20" }, /* @__PURE__ */ React13.createElement("div", { className: "w-48 shrink-0 border-r border-border p-4 font-semibold text-sm bg-background sticky left-0 z-20" }, translations?.resource || "Resources"), /* @__PURE__ */ React13.createElement("div", { className: "flex overflow-hidden" }, /* @__PURE__ */ React13.createElement("div", { className: "flex relative", style: { width: hours.length * hourWidth } }, hours.map((hour) => /* @__PURE__ */ React13.createElement(
1608
1729
  "div",
1609
1730
  {
1610
1731
  key: hour,
@@ -1616,7 +1737,7 @@ var ResourceView = ({
1616
1737
  const resourceEvents = events.filter(
1617
1738
  (e) => e.resourceId === resource.id && isSameDisplayDay(e.start, currentDate, timezone)
1618
1739
  );
1619
- return /* @__PURE__ */ React13.createElement("div", { key: resource.id, className: "flex border-b border-border min-h-[100px]" }, /* @__PURE__ */ React13.createElement("div", { className: "w-48 shrink-0 border-r border-border p-4 bg-background sticky left-0 z-10 flex items-center gap-3" }, resource.avatar ? /* @__PURE__ */ React13.createElement("img", { src: resource.avatar, alt: resource.label, className: "w-8 h-8 rounded-full object-cover" }) : /* @__PURE__ */ React13.createElement("div", { className: "w-8 h-8 rounded-full bg-primary/10 flex items-center justify-center text-xs font-bold text-primary" }, resource.label.substring(0, 2).toUpperCase()), /* @__PURE__ */ React13.createElement("div", { className: "flex flex-col" }, /* @__PURE__ */ React13.createElement("span", { className: "text-sm font-medium" }, resource.label), /* @__PURE__ */ React13.createElement("span", { className: "text-xs text-muted-foreground" }, "ID: ", resource.id))), /* @__PURE__ */ React13.createElement("div", { className: "relative flex", style: { width: hours.length * hourWidth } }, hours.map((hour) => /* @__PURE__ */ React13.createElement(
1740
+ return /* @__PURE__ */ React13.createElement("div", { key: resource.id, className: "flex border-b border-border min-h-[100px]" }, /* @__PURE__ */ React13.createElement("div", { className: "w-48 shrink-0 border-r border-border p-4 bg-background sticky left-0 z-10 flex items-center gap-3" }, resource.avatar ? /* @__PURE__ */ React13.createElement("img", { src: resource.avatar, alt: resource.label, className: "w-8 h-8 rounded-full object-cover" }) : /* @__PURE__ */ React13.createElement("div", { className: "w-8 h-8 rounded-full bg-primary/10 flex items-center justify-center text-xs font-bold text-primary" }, resource.label.substring(0, 2).toUpperCase()), /* @__PURE__ */ React13.createElement("div", { className: "flex flex-col" }, /* @__PURE__ */ React13.createElement("span", { className: "text-sm font-medium" }, resource.label), /* @__PURE__ */ React13.createElement("span", { className: "text-xs text-muted-foreground" }, interpolate(translations?.idLabel || "ID: {id}", { id: resource.id })))), /* @__PURE__ */ React13.createElement("div", { className: "relative flex", style: { width: hours.length * hourWidth } }, hours.map((hour) => /* @__PURE__ */ React13.createElement(
1620
1741
  "div",
1621
1742
  {
1622
1743
  key: hour,
@@ -1648,6 +1769,7 @@ var ResourceView = ({
1648
1769
  e.stopPropagation();
1649
1770
  onEventClick?.(event);
1650
1771
  },
1772
+ onContextMenu: onEventContextMenu ? (e) => onEventContextMenu(event, e) : void 0,
1651
1773
  className: "absolute top-2 bottom-2 rounded-md px-2 py-1 text-xs font-medium border shadow-sm cursor-pointer overflow-hidden hover:brightness-95 transition-all z-10",
1652
1774
  style: {
1653
1775
  ...getEventStyle(event),
@@ -1662,7 +1784,7 @@ var ResourceView = ({
1662
1784
  };
1663
1785
 
1664
1786
  // src/components/EventModal.tsx
1665
- import React15, { useState as useState3, useEffect as useEffect3 } from "react";
1787
+ import React15, { useState as useState3, useEffect as useEffect3, useInsertionEffect, useRef as useRef4 } from "react";
1666
1788
 
1667
1789
  // src/components/ui/Modal.tsx
1668
1790
  import React14, { useEffect as useEffect2, useRef as useRef3 } from "react";
@@ -1673,7 +1795,8 @@ var Modal = ({
1673
1795
  title,
1674
1796
  children,
1675
1797
  className,
1676
- hideHeader = false
1798
+ hideHeader = false,
1799
+ closeLabel = "Close"
1677
1800
  }) => {
1678
1801
  const overlayRef = useRef3(null);
1679
1802
  useEffect2(() => {
@@ -1714,7 +1837,7 @@ var Modal = ({
1714
1837
  className: "h-9 w-9 p-0 rounded-xl hover:bg-accent/80 transition-all duration-200"
1715
1838
  },
1716
1839
  /* @__PURE__ */ React14.createElement(X, { className: "h-4 w-4" }),
1717
- /* @__PURE__ */ React14.createElement("span", { className: "sr-only" }, "Close")
1840
+ /* @__PURE__ */ React14.createElement("span", { className: "sr-only" }, closeLabel)
1718
1841
  )),
1719
1842
  /* @__PURE__ */ React14.createElement("div", { className: cn("overflow-y-auto flex-1", !hideHeader && "p-6") }, children)
1720
1843
  ));
@@ -1744,18 +1867,7 @@ import {
1744
1867
  Tag,
1745
1868
  ExternalLink
1746
1869
  } from "lucide-react";
1747
- var REMINDER_OPTIONS = [
1748
- { value: 0, label: "At time of event" },
1749
- { value: 5, label: "5 minutes before" },
1750
- { value: 10, label: "10 minutes before" },
1751
- { value: 15, label: "15 minutes before" },
1752
- { value: 30, label: "30 minutes before" },
1753
- { value: 60, label: "1 hour before" },
1754
- { value: 120, label: "2 hours before" },
1755
- { value: 1440, label: "1 day before" },
1756
- { value: 2880, label: "2 days before" },
1757
- { value: 10080, label: "1 week before" }
1758
- ];
1870
+ var REMINDER_OPTIONS = [0, 5, 10, 15, 30, 60, 120, 1440, 2880, 10080];
1759
1871
  var COLOR_PALETTE = [
1760
1872
  "#3b82f6",
1761
1873
  // Blue
@@ -1778,6 +1890,30 @@ var COLOR_PALETTE = [
1778
1890
  "#6366f1"
1779
1891
  // Indigo
1780
1892
  ];
1893
+ var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1894
+ var mergeGuests = (current, added) => {
1895
+ const seen = new Set(current.map((email) => email.toLowerCase()));
1896
+ const merged = [...current];
1897
+ for (const email of added) {
1898
+ if (!seen.has(email.toLowerCase())) {
1899
+ seen.add(email.toLowerCase());
1900
+ merged.push(email);
1901
+ }
1902
+ }
1903
+ return merged;
1904
+ };
1905
+ var reminderLabel = (minutes, t) => {
1906
+ if (minutes === 0) return t.reminderAtTime;
1907
+ if (minutes === 10080) return t.reminderWeek;
1908
+ if (minutes === 1440) return t.reminderDay;
1909
+ if (minutes % 1440 === 0) return interpolate(t.reminderDays, { n: minutes / 1440 });
1910
+ if (minutes === 60) return t.reminderHour;
1911
+ if (minutes % 60 === 0) return interpolate(t.reminderHours, { n: minutes / 60 });
1912
+ return interpolate(t.reminderMinutes, { n: minutes });
1913
+ };
1914
+ var avatarHue = (email) => [...email].reduce((hash, char) => (hash * 31 + char.charCodeAt(0)) % 360, 7);
1915
+ var mapsUrl = (location) => `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(location)}`;
1916
+ var isSafeUrl = (url) => /^(https?:|mailto:|blob:|\/|\.\/|\.\.\/|[^:]*$)/i.test(url.trim());
1781
1917
  var EventModal = ({
1782
1918
  isOpen,
1783
1919
  onClose,
@@ -1785,9 +1921,15 @@ var EventModal = ({
1785
1921
  initialDate,
1786
1922
  onSave,
1787
1923
  onDelete,
1788
- calendars
1924
+ calendars,
1925
+ translations: t,
1926
+ locale
1789
1927
  }) => {
1790
1928
  const [mode, setMode] = useState3("create");
1929
+ const translationsRef = useRef4(t);
1930
+ useInsertionEffect(() => {
1931
+ translationsRef.current = t;
1932
+ });
1791
1933
  const [formData, setFormData] = useState3({
1792
1934
  title: "",
1793
1935
  description: "",
@@ -1800,22 +1942,18 @@ var EventModal = ({
1800
1942
  type: void 0,
1801
1943
  recurrence: void 0,
1802
1944
  attachments: [],
1803
- reminders: [{ id: "1", type: "notification", time: 30, label: "30 minutes before" }]
1945
+ reminders: []
1804
1946
  });
1805
1947
  const [isCalendarDropdownOpen, setIsCalendarDropdownOpen] = useState3(false);
1806
1948
  const [isColorPickerOpen, setIsColorPickerOpen] = useState3(false);
1807
1949
  const [isReminderDropdownOpen, setIsReminderDropdownOpen] = useState3(false);
1808
- const [isGuestsDropdownOpen, setIsGuestsDropdownOpen] = useState3(false);
1809
- const fakeGuests = [
1810
- "alice@example.com",
1811
- "bob@example.com",
1812
- "charlie@example.com",
1813
- "diana@example.com",
1814
- "evan@example.com"
1815
- ];
1816
- const [selectedGuests, setSelectedGuests] = useState3([]);
1950
+ const [guestInput, setGuestInput] = useState3("");
1951
+ const [guestError, setGuestError] = useState3(false);
1817
1952
  useEffect3(() => {
1953
+ const defaultReminders = () => [{ id: "1", type: "notification", time: 30, label: reminderLabel(30, translationsRef.current) }];
1818
1954
  if (isOpen) {
1955
+ setGuestInput("");
1956
+ setGuestError(false);
1819
1957
  if (event) {
1820
1958
  setMode("view");
1821
1959
  setFormData({
@@ -1823,9 +1961,9 @@ var EventModal = ({
1823
1961
  start: new Date(event.start),
1824
1962
  end: new Date(event.end),
1825
1963
  attachments: event.attachments || [],
1826
- reminders: event.reminders || [{ id: "1", type: "notification", time: 30, label: "30 minutes before" }]
1964
+ reminders: event.reminders || defaultReminders(),
1965
+ guests: event.guests || []
1827
1966
  });
1828
- setSelectedGuests(event.guests || []);
1829
1967
  } else {
1830
1968
  setMode("create");
1831
1969
  const start = initialDate || /* @__PURE__ */ new Date();
@@ -1841,21 +1979,34 @@ var EventModal = ({
1841
1979
  color: "#3b82f6",
1842
1980
  calendarId: calendars?.[0]?.id,
1843
1981
  attachments: [],
1844
- reminders: [{ id: "1", type: "notification", time: 30, label: "30 minutes before" }]
1982
+ reminders: defaultReminders(),
1983
+ guests: []
1845
1984
  });
1846
- setSelectedGuests([]);
1847
1985
  }
1848
1986
  }
1849
1987
  }, [isOpen, event, initialDate, calendars]);
1850
- const toggleGuest = (email) => {
1851
- setSelectedGuests((prev) => {
1852
- const newGuests = prev.includes(email) ? prev.filter((e) => e !== email) : [...prev, email];
1853
- setFormData((curr) => ({ ...curr, guests: newGuests }));
1854
- return newGuests;
1855
- });
1988
+ const guests = formData.guests || [];
1989
+ const addGuests = (raw) => {
1990
+ const entries = raw.split(/[\s,;]+/).filter(Boolean);
1991
+ const valid = entries.filter((entry) => EMAIL.test(entry));
1992
+ const invalid = entries.filter((entry) => !EMAIL.test(entry));
1993
+ if (valid.length > 0) {
1994
+ setFormData((prev) => ({ ...prev, guests: mergeGuests(prev.guests || [], valid) }));
1995
+ }
1996
+ setGuestInput(invalid.join(", "));
1997
+ setGuestError(invalid.length > 0);
1998
+ return invalid.length === 0;
1999
+ };
2000
+ const removeGuest = (email) => {
2001
+ setFormData((prev) => ({ ...prev, guests: (prev.guests || []).filter((g) => g !== email) }));
1856
2002
  };
1857
2003
  const handleSubmit = (e) => {
1858
2004
  e.preventDefault();
2005
+ const pending = guestInput.split(/[\s,;]+/).filter(Boolean);
2006
+ if (pending.some((entry) => !EMAIL.test(entry))) {
2007
+ addGuests(guestInput);
2008
+ return;
2009
+ }
1859
2010
  let eventColor = formData.color;
1860
2011
  if (formData.calendarId && calendars) {
1861
2012
  const selectedCal = calendars.find((c) => c.id === formData.calendarId);
@@ -1863,8 +2014,10 @@ var EventModal = ({
1863
2014
  eventColor = selectedCal.color;
1864
2015
  }
1865
2016
  }
2017
+ const finalGuests = mergeGuests(guests, pending);
1866
2018
  onSave({
1867
2019
  ...formData,
2020
+ guests: finalGuests,
1868
2021
  color: eventColor,
1869
2022
  id: event?.id
1870
2023
  });
@@ -1898,12 +2051,12 @@ var EventModal = ({
1898
2051
  }
1899
2052
  }
1900
2053
  };
1901
- const addReminder = (minutes, label) => {
2054
+ const addReminder = (minutes) => {
1902
2055
  const newReminder = {
1903
- id: Math.random().toString(36).substr(2, 9),
2056
+ id: Math.random().toString(36).slice(2, 11),
1904
2057
  type: "notification",
1905
2058
  time: minutes,
1906
- label
2059
+ label: reminderLabel(minutes, t)
1907
2060
  };
1908
2061
  setFormData((prev) => ({
1909
2062
  ...prev,
@@ -1917,23 +2070,20 @@ var EventModal = ({
1917
2070
  reminders: prev.reminders?.filter((r) => r.id !== id)
1918
2071
  }));
1919
2072
  };
1920
- const handleDownload = (attachment) => {
1921
- const element = document.createElement("a");
1922
- const file = new Blob(["Dummy content for " + attachment.name], { type: "text/plain" });
1923
- element.href = URL.createObjectURL(file);
1924
- element.download = attachment.name;
1925
- document.body.appendChild(element);
1926
- element.click();
1927
- document.body.removeChild(element);
1928
- };
1929
2073
  const getDurationText = () => {
1930
2074
  if (!formData.start || !formData.end) return "";
1931
2075
  const mins = differenceInMinutes5(formData.end, formData.start);
1932
- if (mins < 60) return `${mins} min`;
2076
+ if (mins < 60) return interpolate(t.durationMinutes, { n: mins });
1933
2077
  const hours = Math.floor(mins / 60);
1934
2078
  const remainingMins = mins % 60;
1935
- if (remainingMins === 0) return `${hours} hr`;
1936
- return `${hours} hr ${remainingMins} min`;
2079
+ if (remainingMins === 0) return interpolate(t.durationHours, { n: hours });
2080
+ return interpolate(t.durationHoursMinutes, { h: hours, m: remainingMins });
2081
+ };
2082
+ const { time: timeFormat } = timeFormats(locale);
2083
+ const renderAttachment = (att) => {
2084
+ const content = /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement("div", { className: "p-2 bg-primary/10 rounded-lg" }, /* @__PURE__ */ React15.createElement(File, { className: "w-4 h-4 text-primary" })), /* @__PURE__ */ React15.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React15.createElement("p", { className: "text-sm font-medium text-foreground truncate" }, att.name), /* @__PURE__ */ React15.createElement("p", { className: "text-xs text-muted-foreground" }, att.size)), att.url && /* @__PURE__ */ React15.createElement(Download, { className: "w-4 h-4 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" }));
2085
+ const className = "flex items-center gap-3 p-3 rounded-xl border border-border/30 bg-muted/20 transition-all group";
2086
+ return att.url && isSafeUrl(att.url) ? /* @__PURE__ */ React15.createElement("a", { key: att.id, href: att.url, target: "_blank", rel: "noopener noreferrer", download: att.name, className: cn(className, "hover:bg-muted/40 cursor-pointer") }, content) : /* @__PURE__ */ React15.createElement("div", { key: att.id, className }, content);
1937
2087
  };
1938
2088
  const renderViewMode = () => /* @__PURE__ */ React15.createElement("div", { className: "flex flex-col bg-background overflow-hidden w-full max-h-[90vh]" }, /* @__PURE__ */ React15.createElement(
1939
2089
  "div",
@@ -1949,12 +2099,13 @@ var EventModal = ({
1949
2099
  className: "w-3 h-3 rounded-full",
1950
2100
  style: { backgroundColor: event?.color || "#3b82f6" }
1951
2101
  }
1952
- ), /* @__PURE__ */ React15.createElement("span", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider" }, calendars?.find((c) => c.id === event?.calendarId)?.label || "Event")), /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React15.createElement(
2102
+ ), /* @__PURE__ */ React15.createElement("span", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider" }, calendars?.find((c) => c.id === event?.calendarId)?.label || t.event)), /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React15.createElement(
1953
2103
  "button",
1954
2104
  {
1955
2105
  onClick: () => setMode("edit"),
1956
2106
  className: "p-2 hover:bg-background/80 rounded-lg transition-all text-muted-foreground hover:text-foreground",
1957
- title: "Edit"
2107
+ title: t.edit,
2108
+ "aria-label": t.edit
1958
2109
  },
1959
2110
  /* @__PURE__ */ React15.createElement(Edit2, { className: "w-4 h-4" })
1960
2111
  ), /* @__PURE__ */ React15.createElement(
@@ -1962,7 +2113,8 @@ var EventModal = ({
1962
2113
  {
1963
2114
  onClick: handleDelete,
1964
2115
  className: "p-2 hover:bg-destructive/10 rounded-lg transition-all text-muted-foreground hover:text-destructive",
1965
- title: "Delete"
2116
+ title: t.delete,
2117
+ "aria-label": t.delete
1966
2118
  },
1967
2119
  /* @__PURE__ */ React15.createElement(Trash2, { className: "w-4 h-4" })
1968
2120
  ), /* @__PURE__ */ React15.createElement(
@@ -1970,21 +2122,25 @@ var EventModal = ({
1970
2122
  {
1971
2123
  onClick: onClose,
1972
2124
  className: "p-2 hover:bg-background/80 rounded-lg transition-all text-muted-foreground hover:text-foreground",
1973
- title: "Close"
2125
+ title: t.close,
2126
+ "aria-label": t.close
1974
2127
  },
1975
2128
  /* @__PURE__ */ React15.createElement(X2, { className: "w-4 h-4" })
1976
2129
  ))),
1977
- /* @__PURE__ */ React15.createElement("h1", { className: "text-2xl font-bold text-foreground mb-3 leading-tight" }, event?.title || "(No title)"),
1978
- /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-3 text-sm" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2 px-3 py-1.5 bg-background/60 backdrop-blur-sm rounded-lg border border-border/30" }, /* @__PURE__ */ React15.createElement(Calendar2, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "font-medium" }, event?.start && format12(new Date(event.start), "EEE, MMM d"))), /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2 px-3 py-1.5 bg-background/60 backdrop-blur-sm rounded-lg border border-border/30" }, /* @__PURE__ */ React15.createElement(Clock2, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "font-medium" }, event?.start && format12(new Date(event.start), "h:mm a"), " \u2013 ", event?.end && format12(new Date(event.end), "h:mm a"))))
2130
+ /* @__PURE__ */ React15.createElement("h1", { className: "text-2xl font-bold text-foreground mb-3 leading-tight" }, event?.title || t.noTitle),
2131
+ /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-3 text-sm" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2 px-3 py-1.5 bg-background/60 backdrop-blur-sm rounded-lg border border-border/30" }, /* @__PURE__ */ React15.createElement(Calendar2, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "font-medium" }, event?.start && format12(new Date(event.start), "EEE, MMM d", { locale }))), /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2 px-3 py-1.5 bg-background/60 backdrop-blur-sm rounded-lg border border-border/30" }, /* @__PURE__ */ React15.createElement(Clock2, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "font-medium" }, event?.start && format12(new Date(event.start), timeFormat, { locale }), " \u2013 ", event?.end && format12(new Date(event.end), timeFormat, { locale }))))
1979
2132
  ), /* @__PURE__ */ React15.createElement("div", { className: "px-6 pb-6 space-y-4 overflow-y-auto flex-1" }, event?.location && /* @__PURE__ */ React15.createElement(
1980
- motion2.div,
2133
+ motion2.a,
1981
2134
  {
2135
+ href: mapsUrl(event.location),
2136
+ target: "_blank",
2137
+ rel: "noopener noreferrer",
1982
2138
  initial: { opacity: 0, y: 10 },
1983
2139
  animate: { opacity: 1, y: 0 },
1984
2140
  className: "flex items-start gap-3 p-4 rounded-2xl bg-muted/30 border border-border/30 group hover:bg-muted/50 transition-all cursor-pointer"
1985
2141
  },
1986
2142
  /* @__PURE__ */ React15.createElement("div", { className: "p-2 bg-primary/10 rounded-xl" }, /* @__PURE__ */ React15.createElement(MapPin, { className: "w-5 h-5 text-primary" })),
1987
- /* @__PURE__ */ React15.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React15.createElement("p", { className: "text-sm font-medium text-foreground" }, event.location), /* @__PURE__ */ React15.createElement("p", { className: "text-xs text-muted-foreground mt-0.5" }, "Click to open in maps")),
2143
+ /* @__PURE__ */ React15.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React15.createElement("p", { className: "text-sm font-medium text-foreground" }, event.location), /* @__PURE__ */ React15.createElement("p", { className: "text-xs text-muted-foreground mt-0.5" }, t.openInMaps)),
1988
2144
  /* @__PURE__ */ React15.createElement(ExternalLink, { className: "w-4 h-4 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" })
1989
2145
  ), event?.guests && event.guests.length > 0 && /* @__PURE__ */ React15.createElement(
1990
2146
  motion2.div,
@@ -1994,18 +2150,18 @@ var EventModal = ({
1994
2150
  transition: { delay: 0.05 },
1995
2151
  className: "space-y-3"
1996
2152
  },
1997
- /* @__PURE__ */ React15.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(Users2, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "text-sm font-medium text-foreground" }, event.guests.length, " ", event.guests.length > 1 ? "Guests" : "Guest"))),
1998
- /* @__PURE__ */ React15.createElement("div", { className: "flex flex-wrap gap-2" }, event.guests.map((email, index) => /* @__PURE__ */ React15.createElement(
2153
+ /* @__PURE__ */ React15.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(Users2, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "text-sm font-medium text-foreground" }, event.guests.length, " ", event.guests.length > 1 ? t.guests : t.guestSingular))),
2154
+ /* @__PURE__ */ React15.createElement("div", { className: "flex flex-wrap gap-2" }, event.guests.map((email) => /* @__PURE__ */ React15.createElement(
1999
2155
  "div",
2000
2156
  {
2001
- key: index,
2002
- className: "flex items-center gap-2 px-3 py-2 rounded-xl bg-muted/30 border border-border/30 hover:bg-muted/50 transition-all cursor-pointer group"
2157
+ key: email,
2158
+ className: "flex items-center gap-2 px-3 py-2 rounded-xl bg-muted/30 border border-border/30"
2003
2159
  },
2004
2160
  /* @__PURE__ */ React15.createElement(
2005
2161
  "div",
2006
2162
  {
2007
2163
  className: "w-8 h-8 rounded-full flex items-center justify-center text-sm font-semibold text-white",
2008
- style: { backgroundColor: `hsl(${index * 60}, 70%, 50%)` }
2164
+ style: { backgroundColor: `hsl(${avatarHue(email)}, 70%, 50%)` }
2009
2165
  },
2010
2166
  email[0].toUpperCase()
2011
2167
  ),
@@ -2026,7 +2182,7 @@ var EventModal = ({
2026
2182
  key: reminder.id,
2027
2183
  className: "text-xs font-medium px-2.5 py-1 rounded-full bg-amber-500/10 text-amber-600 dark:text-amber-400"
2028
2184
  },
2029
- reminder.label || `${reminder.time} min before`
2185
+ reminder.label || reminderLabel(reminder.time, t)
2030
2186
  )))
2031
2187
  ), event?.description && /* @__PURE__ */ React15.createElement(
2032
2188
  motion2.div,
@@ -2036,7 +2192,7 @@ var EventModal = ({
2036
2192
  transition: { delay: 0.15 },
2037
2193
  className: "space-y-2"
2038
2194
  },
2039
- /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(AlignLeft, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "text-sm font-medium text-foreground" }, "Notes")),
2195
+ /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(AlignLeft, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "text-sm font-medium text-foreground" }, t.notesLabel)),
2040
2196
  /* @__PURE__ */ React15.createElement("div", { className: "text-sm text-foreground/80 whitespace-pre-wrap leading-relaxed pl-6 p-3 rounded-xl bg-muted/20 border border-border/30" }, event.description)
2041
2197
  ), event?.attachments && event.attachments.length > 0 && /* @__PURE__ */ React15.createElement(
2042
2198
  motion2.div,
@@ -2046,25 +2202,16 @@ var EventModal = ({
2046
2202
  transition: { delay: 0.2 },
2047
2203
  className: "space-y-2"
2048
2204
  },
2049
- /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(Paperclip2, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "text-sm font-medium text-foreground" }, "Attachments")),
2050
- /* @__PURE__ */ React15.createElement("div", { className: "grid gap-2" }, event.attachments.map((att) => /* @__PURE__ */ React15.createElement(
2051
- "div",
2052
- {
2053
- key: att.id,
2054
- onClick: () => handleDownload(att),
2055
- className: "flex items-center gap-3 p-3 rounded-xl border border-border/30 bg-muted/20 hover:bg-muted/40 transition-all cursor-pointer group"
2056
- },
2057
- /* @__PURE__ */ React15.createElement("div", { className: "p-2 bg-primary/10 rounded-lg" }, /* @__PURE__ */ React15.createElement(File, { className: "w-4 h-4 text-primary" })),
2058
- /* @__PURE__ */ React15.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React15.createElement("p", { className: "text-sm font-medium text-foreground truncate" }, att.name), /* @__PURE__ */ React15.createElement("p", { className: "text-xs text-muted-foreground" }, att.size)),
2059
- /* @__PURE__ */ React15.createElement(Download, { className: "w-4 h-4 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" })
2060
- )))
2205
+ /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(Paperclip2, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "text-sm font-medium text-foreground" }, t.attachments)),
2206
+ /* @__PURE__ */ React15.createElement("div", { className: "grid gap-2" }, event.attachments.map(renderAttachment))
2061
2207
  )));
2062
2208
  const renderEditMode = () => /* @__PURE__ */ React15.createElement("form", { onSubmit: handleSubmit, className: "flex flex-col bg-background overflow-hidden w-full max-h-[85vh]" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center justify-between px-5 py-4 border-b border-border/50 shrink-0" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ React15.createElement(
2063
2209
  "button",
2064
2210
  {
2065
2211
  type: "button",
2066
2212
  onClick: () => setIsColorPickerOpen(!isColorPickerOpen),
2067
- className: "relative p-1"
2213
+ className: "relative p-1",
2214
+ "aria-label": t.eventColor
2068
2215
  },
2069
2216
  /* @__PURE__ */ React15.createElement(
2070
2217
  "div",
@@ -2081,6 +2228,7 @@ var EventModal = ({
2081
2228
  {
2082
2229
  key: color,
2083
2230
  type: "button",
2231
+ "aria-label": color,
2084
2232
  onClick: () => {
2085
2233
  setFormData((prev) => ({ ...prev, color }));
2086
2234
  setIsColorPickerOpen(false);
@@ -2092,12 +2240,13 @@ var EventModal = ({
2092
2240
  style: { backgroundColor: color, color }
2093
2241
  }
2094
2242
  )))
2095
- ), /* @__PURE__ */ React15.createElement("h2", { className: "text-lg font-semibold text-foreground" }, mode === "edit" ? "Edit Event" : "New Event")), /* @__PURE__ */ React15.createElement(
2243
+ ), /* @__PURE__ */ React15.createElement("h2", { className: "text-lg font-semibold text-foreground" }, mode === "edit" ? t.editEvent : t.newEvent)), /* @__PURE__ */ React15.createElement(
2096
2244
  "button",
2097
2245
  {
2098
2246
  type: "button",
2099
2247
  onClick: onClose,
2100
- className: "p-2 hover:bg-accent rounded-xl transition-all text-muted-foreground hover:text-foreground"
2248
+ className: "p-2 hover:bg-accent rounded-xl transition-all text-muted-foreground hover:text-foreground",
2249
+ "aria-label": t.close
2101
2250
  },
2102
2251
  /* @__PURE__ */ React15.createElement(X2, { className: "w-4 h-4" })
2103
2252
  )), /* @__PURE__ */ React15.createElement("div", { className: "flex-1 overflow-y-auto" }, /* @__PURE__ */ React15.createElement("div", { className: "px-5 py-4 space-y-5" }, /* @__PURE__ */ React15.createElement("div", null, /* @__PURE__ */ React15.createElement(
@@ -2109,25 +2258,28 @@ var EventModal = ({
2109
2258
  className: "w-full text-xl font-semibold border-0 border-b-2 border-transparent focus:border-primary bg-transparent px-0 py-2 placeholder-muted-foreground/50 transition-all text-foreground focus:outline-none",
2110
2259
  value: formData.title,
2111
2260
  onChange: (e) => setFormData({ ...formData, title: e.target.value }),
2112
- placeholder: "Add title"
2261
+ placeholder: t.title,
2262
+ "aria-label": t.title
2113
2263
  }
2114
- )), /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-3 p-4 rounded-2xl bg-muted/20 border border-border/30" }, /* @__PURE__ */ React15.createElement("div", { className: "p-2 bg-primary/10 rounded-xl shrink-0" }, /* @__PURE__ */ React15.createElement(Clock2, { className: "w-5 h-5 text-primary" })), /* @__PURE__ */ React15.createElement("div", { className: "flex-1 grid grid-cols-2 gap-3" }, /* @__PURE__ */ React15.createElement("div", null, /* @__PURE__ */ React15.createElement("label", { className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-wider mb-1 block" }, "Start"), /* @__PURE__ */ React15.createElement(
2264
+ )), /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-3 p-4 rounded-2xl bg-muted/20 border border-border/30" }, /* @__PURE__ */ React15.createElement("div", { className: "p-2 bg-primary/10 rounded-xl shrink-0" }, /* @__PURE__ */ React15.createElement(Clock2, { className: "w-5 h-5 text-primary" })), /* @__PURE__ */ React15.createElement("div", { className: "flex-1 grid grid-cols-2 gap-3" }, /* @__PURE__ */ React15.createElement("div", null, /* @__PURE__ */ React15.createElement("label", { htmlFor: "calendarkit-event-start", className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-wider mb-1 block" }, t.start), /* @__PURE__ */ React15.createElement(
2115
2265
  "input",
2116
2266
  {
2267
+ id: "calendarkit-event-start",
2117
2268
  type: "datetime-local",
2118
2269
  className: "w-full bg-background border border-border/50 rounded-xl px-3 py-2 text-sm font-medium text-foreground focus:border-primary focus:ring-2 focus:ring-primary/20 transition-all",
2119
2270
  value: formatDateForInput(formData.start),
2120
2271
  onChange: (e) => handleDateChange("start", e.target.value)
2121
2272
  }
2122
- )), /* @__PURE__ */ React15.createElement("div", null, /* @__PURE__ */ React15.createElement("label", { className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-wider mb-1 block" }, "End"), /* @__PURE__ */ React15.createElement(
2273
+ )), /* @__PURE__ */ React15.createElement("div", null, /* @__PURE__ */ React15.createElement("label", { htmlFor: "calendarkit-event-end", className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-wider mb-1 block" }, t.end), /* @__PURE__ */ React15.createElement(
2123
2274
  "input",
2124
2275
  {
2276
+ id: "calendarkit-event-end",
2125
2277
  type: "datetime-local",
2126
2278
  className: "w-full bg-background border border-border/50 rounded-xl px-3 py-2 text-sm font-medium text-foreground focus:border-primary focus:ring-2 focus:ring-primary/20 transition-all",
2127
2279
  value: formatDateForInput(formData.end),
2128
2280
  onChange: (e) => handleDateChange("end", e.target.value)
2129
2281
  }
2130
- )))), getDurationText() && /* @__PURE__ */ React15.createElement("div", { className: "flex justify-center" }, /* @__PURE__ */ React15.createElement("span", { className: "text-xs font-medium px-3 py-1 rounded-full bg-primary/10 text-primary" }, getDurationText())), /* @__PURE__ */ React15.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(Bell2, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "text-sm font-medium text-foreground" }, "Reminders")), /* @__PURE__ */ React15.createElement("div", { className: "relative" }, /* @__PURE__ */ React15.createElement(
2282
+ )))), getDurationText() && /* @__PURE__ */ React15.createElement("div", { className: "flex justify-center" }, /* @__PURE__ */ React15.createElement("span", { className: "text-xs font-medium px-3 py-1 rounded-full bg-primary/10 text-primary" }, getDurationText())), /* @__PURE__ */ React15.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(Bell2, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "text-sm font-medium text-foreground" }, t.reminders)), /* @__PURE__ */ React15.createElement("div", { className: "relative" }, /* @__PURE__ */ React15.createElement(
2131
2283
  "button",
2132
2284
  {
2133
2285
  type: "button",
@@ -2135,16 +2287,16 @@ var EventModal = ({
2135
2287
  className: "flex items-center gap-1 text-xs font-medium text-primary hover:text-primary/80 transition-colors"
2136
2288
  },
2137
2289
  /* @__PURE__ */ React15.createElement(Plus3, { className: "w-3.5 h-3.5" }),
2138
- "Add"
2139
- ), isReminderDropdownOpen && /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement("div", { className: "fixed inset-0 z-40", onClick: () => setIsReminderDropdownOpen(false) }), /* @__PURE__ */ React15.createElement("div", { className: "absolute right-0 top-full mt-1 w-48 bg-background border border-border rounded-xl shadow-xl z-50 py-1 max-h-60 overflow-y-auto" }, REMINDER_OPTIONS.map((option) => /* @__PURE__ */ React15.createElement(
2290
+ t.add
2291
+ ), isReminderDropdownOpen && /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement("div", { className: "fixed inset-0 z-40", onClick: () => setIsReminderDropdownOpen(false) }), /* @__PURE__ */ React15.createElement("div", { className: "absolute right-0 top-full mt-1 w-48 bg-background border border-border rounded-xl shadow-xl z-50 py-1 max-h-60 overflow-y-auto" }, REMINDER_OPTIONS.map((minutes) => /* @__PURE__ */ React15.createElement(
2140
2292
  "button",
2141
2293
  {
2142
- key: option.value,
2294
+ key: minutes,
2143
2295
  type: "button",
2144
- onClick: () => addReminder(option.value, option.label),
2296
+ onClick: () => addReminder(minutes),
2145
2297
  className: "w-full px-3 py-2 text-left text-sm hover:bg-accent transition-colors"
2146
2298
  },
2147
- option.label
2299
+ reminderLabel(minutes, t)
2148
2300
  )))))), formData.reminders && formData.reminders.length > 0 && /* @__PURE__ */ React15.createElement("div", { className: "flex flex-wrap gap-2" }, formData.reminders.map((reminder) => /* @__PURE__ */ React15.createElement(
2149
2301
  "div",
2150
2302
  {
@@ -2152,45 +2304,44 @@ var EventModal = ({
2152
2304
  className: "flex items-center gap-2 px-3 py-1.5 rounded-full bg-amber-500/10 border border-amber-500/20"
2153
2305
  },
2154
2306
  /* @__PURE__ */ React15.createElement(BellRing, { className: "w-3.5 h-3.5 text-amber-500" }),
2155
- /* @__PURE__ */ React15.createElement("span", { className: "text-xs font-medium text-amber-600 dark:text-amber-400" }, reminder.label),
2307
+ /* @__PURE__ */ React15.createElement("span", { className: "text-xs font-medium text-amber-600 dark:text-amber-400" }, reminder.label || reminderLabel(reminder.time, t)),
2156
2308
  /* @__PURE__ */ React15.createElement(
2157
2309
  "button",
2158
2310
  {
2159
2311
  type: "button",
2160
2312
  onClick: () => removeReminder(reminder.id),
2161
- className: "p-0.5 hover:bg-amber-500/20 rounded-full transition-colors"
2313
+ className: "p-0.5 hover:bg-amber-500/20 rounded-full transition-colors",
2314
+ "aria-label": t.removeReminder
2162
2315
  },
2163
2316
  /* @__PURE__ */ React15.createElement(X2, { className: "w-3 h-3 text-amber-500" })
2164
2317
  )
2165
- )))), /* @__PURE__ */ React15.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(Users2, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "text-sm font-medium text-foreground" }, "Guests")), /* @__PURE__ */ React15.createElement("div", { className: "relative" }, /* @__PURE__ */ React15.createElement(
2166
- "button",
2167
- {
2168
- type: "button",
2169
- onClick: () => setIsGuestsDropdownOpen(!isGuestsDropdownOpen),
2170
- className: "w-full flex items-center justify-between px-4 py-3 bg-muted/20 border border-border/30 rounded-xl text-sm text-left hover:bg-muted/30 transition-all"
2171
- },
2172
- /* @__PURE__ */ React15.createElement("span", { className: selectedGuests.length > 0 ? "text-foreground font-medium" : "text-muted-foreground" }, selectedGuests.length > 0 ? `${selectedGuests.length} guest${selectedGuests.length > 1 ? "s" : ""} added` : "Add guests"),
2173
- /* @__PURE__ */ React15.createElement(ChevronDown2, { className: cn("w-4 h-4 text-muted-foreground transition-transform", isGuestsDropdownOpen && "rotate-180") })
2174
- ), isGuestsDropdownOpen && /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement("div", { className: "fixed inset-0 z-40", onClick: () => setIsGuestsDropdownOpen(false) }), /* @__PURE__ */ React15.createElement("div", { className: "absolute z-50 w-full mt-2 bg-background border border-border rounded-xl shadow-xl max-h-48 overflow-y-auto" }, fakeGuests.map((email) => /* @__PURE__ */ React15.createElement(
2175
- "div",
2318
+ )))), /* @__PURE__ */ React15.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(Users2, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("label", { htmlFor: "calendarkit-event-guest", className: "text-sm font-medium text-foreground" }, t.guests)), /* @__PURE__ */ React15.createElement(
2319
+ "input",
2176
2320
  {
2177
- key: email,
2178
- className: "px-4 py-3 hover:bg-accent cursor-pointer flex items-center gap-3 transition-colors",
2179
- onClick: () => toggleGuest(email)
2180
- },
2181
- /* @__PURE__ */ React15.createElement("div", { className: cn(
2182
- "w-5 h-5 rounded-md border-2 flex items-center justify-center transition-all",
2183
- selectedGuests.includes(email) ? "bg-primary border-primary" : "border-border"
2184
- ) }, selectedGuests.includes(email) && /* @__PURE__ */ React15.createElement(Check, { className: "w-3 h-3 text-primary-foreground" })),
2185
- /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(
2186
- "div",
2187
- {
2188
- className: "w-7 h-7 rounded-full flex items-center justify-center text-xs font-semibold text-white",
2189
- style: { backgroundColor: `hsl(${fakeGuests.indexOf(email) * 60}, 70%, 50%)` }
2321
+ id: "calendarkit-event-guest",
2322
+ type: "text",
2323
+ inputMode: "email",
2324
+ autoComplete: "email",
2325
+ value: guestInput,
2326
+ onChange: (e) => {
2327
+ setGuestInput(e.target.value);
2328
+ setGuestError(false);
2190
2329
  },
2191
- email[0].toUpperCase()
2192
- ), /* @__PURE__ */ React15.createElement("span", { className: "text-sm" }, email))
2193
- ))))), selectedGuests.length > 0 && /* @__PURE__ */ React15.createElement("div", { className: "flex flex-wrap gap-2 mt-2" }, selectedGuests.map((email) => /* @__PURE__ */ React15.createElement(
2330
+ onKeyDown: (e) => {
2331
+ if (e.key === "Enter" || e.key === ",") {
2332
+ e.preventDefault();
2333
+ addGuests(guestInput);
2334
+ }
2335
+ },
2336
+ onBlur: () => guestInput.trim() && addGuests(guestInput),
2337
+ placeholder: t.addGuestPlaceholder,
2338
+ "aria-invalid": guestError,
2339
+ className: cn(
2340
+ "w-full px-4 py-3 bg-muted/20 border rounded-xl text-sm text-foreground placeholder-muted-foreground focus:outline-none focus:border-primary transition-all",
2341
+ guestError ? "border-destructive" : "border-border/30"
2342
+ )
2343
+ }
2344
+ ), guestError && /* @__PURE__ */ React15.createElement("p", { role: "alert", className: "text-xs text-destructive" }, t.invalidEmail), guests.length > 0 && /* @__PURE__ */ React15.createElement("div", { className: "flex flex-wrap gap-2 mt-2" }, guests.map((email) => /* @__PURE__ */ React15.createElement(
2194
2345
  "div",
2195
2346
  {
2196
2347
  key: email,
@@ -2200,17 +2351,18 @@ var EventModal = ({
2200
2351
  "div",
2201
2352
  {
2202
2353
  className: "w-6 h-6 rounded-full flex items-center justify-center text-xs font-semibold text-white",
2203
- style: { backgroundColor: `hsl(${fakeGuests.indexOf(email) * 60}, 70%, 50%)` }
2354
+ style: { backgroundColor: `hsl(${avatarHue(email)}, 70%, 50%)` }
2204
2355
  },
2205
2356
  email[0].toUpperCase()
2206
2357
  ),
2207
- /* @__PURE__ */ React15.createElement("span", { className: "text-xs font-medium text-foreground" }, email.split("@")[0]),
2358
+ /* @__PURE__ */ React15.createElement("span", { className: "text-xs font-medium text-foreground" }, email),
2208
2359
  /* @__PURE__ */ React15.createElement(
2209
2360
  "button",
2210
2361
  {
2211
2362
  type: "button",
2212
- onClick: () => toggleGuest(email),
2213
- className: "p-0.5 hover:bg-destructive/10 rounded-full transition-colors"
2363
+ onClick: () => removeGuest(email),
2364
+ className: "p-0.5 hover:bg-destructive/10 rounded-full transition-colors",
2365
+ "aria-label": interpolate(t.removeGuest, { email })
2214
2366
  },
2215
2367
  /* @__PURE__ */ React15.createElement(X2, { className: "w-3 h-3 text-muted-foreground" })
2216
2368
  )
@@ -2219,15 +2371,17 @@ var EventModal = ({
2219
2371
  {
2220
2372
  type: "text",
2221
2373
  className: "flex-1 bg-transparent text-sm text-foreground placeholder-muted-foreground focus:outline-none",
2222
- placeholder: "Add location",
2374
+ placeholder: t.addLocation,
2375
+ "aria-label": t.addLocation,
2223
2376
  value: formData.location || "",
2224
2377
  onChange: (e) => setFormData({ ...formData, location: e.target.value })
2225
2378
  }
2226
- )), /* @__PURE__ */ React15.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(AlignLeft, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "text-sm font-medium text-foreground" }, "Notes")), /* @__PURE__ */ React15.createElement(
2379
+ )), /* @__PURE__ */ React15.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(AlignLeft, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "text-sm font-medium text-foreground" }, t.notesLabel)), /* @__PURE__ */ React15.createElement(
2227
2380
  "textarea",
2228
2381
  {
2229
2382
  className: "w-full bg-muted/20 border border-border/30 rounded-xl px-4 py-3 text-sm min-h-[100px] resize-none text-foreground placeholder-muted-foreground focus:border-primary focus:ring-2 focus:ring-primary/20 transition-all",
2230
- placeholder: "Add description or notes...",
2383
+ placeholder: t.addNotes,
2384
+ "aria-label": t.notesLabel,
2231
2385
  value: formData.description || "",
2232
2386
  onChange: (e) => setFormData({ ...formData, description: e.target.value })
2233
2387
  }
@@ -2235,6 +2389,7 @@ var EventModal = ({
2235
2389
  "select",
2236
2390
  {
2237
2391
  className: "flex-1 bg-transparent text-sm text-foreground focus:outline-none cursor-pointer",
2392
+ "aria-label": t.repeat,
2238
2393
  value: formData.recurrence?.freq || "",
2239
2394
  onChange: (e) => {
2240
2395
  if (e.target.value === "") {
@@ -2250,12 +2405,12 @@ var EventModal = ({
2250
2405
  }
2251
2406
  }
2252
2407
  },
2253
- /* @__PURE__ */ React15.createElement("option", { value: "" }, "Does not repeat"),
2254
- /* @__PURE__ */ React15.createElement("option", { value: "DAILY" }, "Daily"),
2255
- /* @__PURE__ */ React15.createElement("option", { value: "WEEKLY" }, "Weekly"),
2256
- /* @__PURE__ */ React15.createElement("option", { value: "MONTHLY" }, "Monthly"),
2257
- /* @__PURE__ */ React15.createElement("option", { value: "YEARLY" }, "Yearly")
2258
- )), /* @__PURE__ */ React15.createElement("div", { className: "flex items-center justify-between p-3 rounded-xl bg-muted/20 border border-border/30" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(Tag, { className: "w-5 h-5 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "text-sm text-foreground" }, "Calendar")), /* @__PURE__ */ React15.createElement("div", { className: "relative" }, /* @__PURE__ */ React15.createElement(
2408
+ /* @__PURE__ */ React15.createElement("option", { value: "" }, t.doesNotRepeat),
2409
+ /* @__PURE__ */ React15.createElement("option", { value: "DAILY" }, t.daily),
2410
+ /* @__PURE__ */ React15.createElement("option", { value: "WEEKLY" }, t.weekly),
2411
+ /* @__PURE__ */ React15.createElement("option", { value: "MONTHLY" }, t.monthly),
2412
+ /* @__PURE__ */ React15.createElement("option", { value: "YEARLY" }, t.yearly)
2413
+ )), calendars && calendars.length > 0 && /* @__PURE__ */ React15.createElement("div", { className: "flex items-center justify-between p-3 rounded-xl bg-muted/20 border border-border/30" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(Tag, { className: "w-5 h-5 text-muted-foreground" }), /* @__PURE__ */ React15.createElement("span", { className: "text-sm text-foreground" }, t.calendar)), /* @__PURE__ */ React15.createElement("div", { className: "relative" }, /* @__PURE__ */ React15.createElement(
2259
2414
  "button",
2260
2415
  {
2261
2416
  type: "button",
@@ -2266,12 +2421,12 @@ var EventModal = ({
2266
2421
  "div",
2267
2422
  {
2268
2423
  className: "w-3 h-3 rounded-full",
2269
- style: { backgroundColor: calendars?.find((c) => c.id === formData.calendarId)?.color || "#3b82f6" }
2424
+ style: { backgroundColor: calendars.find((c) => c.id === formData.calendarId)?.color || "#3b82f6" }
2270
2425
  }
2271
2426
  ),
2272
- /* @__PURE__ */ React15.createElement("span", { className: "font-medium" }, calendars?.find((c) => c.id === formData.calendarId)?.label || "Select"),
2427
+ /* @__PURE__ */ React15.createElement("span", { className: "font-medium" }, calendars.find((c) => c.id === formData.calendarId)?.label || t.select),
2273
2428
  /* @__PURE__ */ React15.createElement(ChevronDown2, { className: "w-3.5 h-3.5 text-muted-foreground" })
2274
- ), isCalendarDropdownOpen && /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement("div", { className: "fixed inset-0 z-40", onClick: () => setIsCalendarDropdownOpen(false) }), /* @__PURE__ */ React15.createElement("div", { className: "absolute right-0 bottom-full mb-2 w-48 bg-background border border-border rounded-xl shadow-xl z-50 py-1" }, calendars?.map((cal) => /* @__PURE__ */ React15.createElement(
2429
+ ), isCalendarDropdownOpen && /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement("div", { className: "fixed inset-0 z-40", onClick: () => setIsCalendarDropdownOpen(false) }), /* @__PURE__ */ React15.createElement("div", { className: "absolute right-0 bottom-full mb-2 w-48 bg-background border border-border rounded-xl shadow-xl z-50 py-1" }, calendars.map((cal) => /* @__PURE__ */ React15.createElement(
2275
2430
  "button",
2276
2431
  {
2277
2432
  key: cal.id,
@@ -2297,7 +2452,7 @@ var EventModal = ({
2297
2452
  className: "flex items-center gap-2 px-4 py-2 text-destructive hover:bg-destructive/10 rounded-xl transition-all text-sm font-medium"
2298
2453
  },
2299
2454
  /* @__PURE__ */ React15.createElement(Trash2, { className: "w-4 h-4" }),
2300
- "Delete"
2455
+ t.delete
2301
2456
  ), mode === "create" && /* @__PURE__ */ React15.createElement("div", null), /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(
2302
2457
  "button",
2303
2458
  {
@@ -2305,14 +2460,14 @@ var EventModal = ({
2305
2460
  onClick: onClose,
2306
2461
  className: "px-4 py-2.5 text-muted-foreground hover:text-foreground font-medium text-sm rounded-xl transition-all hover:bg-accent"
2307
2462
  },
2308
- "Cancel"
2463
+ t.cancel
2309
2464
  ), /* @__PURE__ */ React15.createElement(
2310
2465
  "button",
2311
2466
  {
2312
2467
  type: "submit",
2313
2468
  className: "px-6 py-2.5 bg-primary hover:bg-primary/90 text-primary-foreground font-semibold text-sm rounded-xl shadow-md transition-all hover:shadow-lg hover:shadow-primary/20 active:scale-95"
2314
2469
  },
2315
- mode === "edit" ? "Save Changes" : "Create Event"
2470
+ mode === "edit" ? t.saveChanges : t.createEvent
2316
2471
  ))));
2317
2472
  return /* @__PURE__ */ React15.createElement(Modal, { isOpen, onClose, hideHeader: true, className: "p-0 overflow-hidden max-w-lg rounded-2xl shadow-2xl" }, /* @__PURE__ */ React15.createElement(AnimatePresence, { mode: "wait" }, mode === "view" && event ? /* @__PURE__ */ React15.createElement(
2318
2473
  motion2.div,
@@ -2340,7 +2495,7 @@ var EventModal = ({
2340
2495
  // src/components/Skeleton.tsx
2341
2496
  import React16 from "react";
2342
2497
  var Skeleton = ({ className, style }) => /* @__PURE__ */ React16.createElement("div", { className: cn("animate-pulse bg-muted/40 rounded", className), style });
2343
- var MonthViewSkeleton = () => /* @__PURE__ */ React16.createElement("div", { className: "h-full bg-background border border-border/50 rounded-2xl overflow-hidden" }, /* @__PURE__ */ React16.createElement("div", { className: "grid grid-cols-7 border-b border-border/50 bg-muted/10" }, Array.from({ length: 7 }).map((_, i) => /* @__PURE__ */ React16.createElement("div", { key: i, className: "py-3 px-2 text-center border-r border-border/30 last:border-r-0" }, /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-4 w-8 mx-auto" })))), /* @__PURE__ */ React16.createElement("div", { className: "grid grid-cols-7 flex-1" }, Array.from({ length: 35 }).map((_, i) => /* @__PURE__ */ React16.createElement("div", { key: i, className: "min-h-[120px] border-r border-b border-border/30 p-2" }, /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-4 w-6 mb-2" }), /* @__PURE__ */ React16.createElement("div", { className: "space-y-1" }, Math.random() > 0.5 && /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-5 w-full rounded-md" }), Math.random() > 0.7 && /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-5 w-3/4 rounded-md" }))))));
2498
+ var MonthViewSkeleton = () => /* @__PURE__ */ React16.createElement("div", { className: "h-full bg-background border border-border/50 rounded-2xl overflow-hidden" }, /* @__PURE__ */ React16.createElement("div", { className: "grid grid-cols-7 border-b border-border/50 bg-muted/10" }, Array.from({ length: 7 }).map((_, i) => /* @__PURE__ */ React16.createElement("div", { key: i, className: "py-3 px-2 text-center border-r border-border/30 last:border-r-0" }, /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-4 w-8 mx-auto" })))), /* @__PURE__ */ React16.createElement("div", { className: "grid grid-cols-7 flex-1" }, Array.from({ length: 35 }).map((_, i) => /* @__PURE__ */ React16.createElement("div", { key: i, className: "min-h-[120px] border-r border-b border-border/30 p-2" }, /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-4 w-6 mb-2" }), /* @__PURE__ */ React16.createElement("div", { className: "space-y-1" }, i * 7 % 10 > 4 && /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-5 w-full rounded-md" }), i * 3 % 10 > 6 && /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-5 w-3/4 rounded-md" }))))));
2344
2499
  var WeekViewSkeleton = () => /* @__PURE__ */ React16.createElement("div", { className: "h-full bg-background border border-border/50 rounded-2xl overflow-hidden" }, /* @__PURE__ */ React16.createElement("div", { className: "flex border-b border-border/50 bg-muted/10" }, /* @__PURE__ */ React16.createElement("div", { className: "w-16 p-3 border-r border-border/30" }, /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-8 w-10 mx-auto" })), /* @__PURE__ */ React16.createElement("div", { className: "flex-1 grid grid-cols-7" }, Array.from({ length: 7 }).map((_, i) => /* @__PURE__ */ React16.createElement("div", { key: i, className: "py-3 px-2 text-center border-r border-border/30 last:border-r-0" }, /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-3 w-8 mx-auto mb-1" }), /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-8 w-8 mx-auto rounded-xl" }))))), /* @__PURE__ */ React16.createElement("div", { className: "flex flex-1", style: { height: "600px" } }, /* @__PURE__ */ React16.createElement("div", { className: "w-16 border-r border-border/30" }, Array.from({ length: 10 }).map((_, i) => /* @__PURE__ */ React16.createElement("div", { key: i, className: "h-[60px] relative" }, /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-4 w-10 absolute right-2 -translate-y-1/2" })))), /* @__PURE__ */ React16.createElement("div", { className: "flex-1 grid grid-cols-7 relative" }, Array.from({ length: 7 }).map((_, colIdx) => /* @__PURE__ */ React16.createElement("div", { key: colIdx, className: "relative border-r border-border/30 last:border-r-0" }, Array.from({ length: 10 }).map((_2, rowIdx) => /* @__PURE__ */ React16.createElement("div", { key: rowIdx, className: "h-[60px] border-b border-dashed border-border/20" })), Math.random() > 0.3 && /* @__PURE__ */ React16.createElement(
2345
2500
  Skeleton,
2346
2501
  {
@@ -2369,7 +2524,7 @@ var DayViewSkeleton = () => /* @__PURE__ */ React16.createElement("div", { class
2369
2524
  var AgendaViewSkeleton = () => /* @__PURE__ */ React16.createElement("div", { className: "h-full bg-background border border-border/50 rounded-2xl overflow-hidden p-6" }, /* @__PURE__ */ React16.createElement("div", { className: "space-y-6" }, Array.from({ length: 4 }).map((_, dayIdx) => /* @__PURE__ */ React16.createElement("div", { key: dayIdx }, /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-5 w-40 mb-4" }), /* @__PURE__ */ React16.createElement("div", { className: "space-y-3" }, Array.from({ length: 2 + Math.floor(Math.random() * 3) }).map((_2, eventIdx) => /* @__PURE__ */ React16.createElement("div", { key: eventIdx, className: "flex items-center gap-4 p-3 rounded-xl bg-muted/10" }, /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-10 w-10 rounded-lg" }), /* @__PURE__ */ React16.createElement("div", { className: "flex-1 space-y-2" }, /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-4 w-3/4" }), /* @__PURE__ */ React16.createElement(Skeleton, { className: "h-3 w-1/2" })))))))));
2370
2525
 
2371
2526
  // src/components/ContextMenu.tsx
2372
- import React17, { useState as useState4, useCallback as useCallback2, useEffect as useEffect4, useRef as useRef4 } from "react";
2527
+ import React17, { useState as useState4, useCallback as useCallback2, useEffect as useEffect4, useRef as useRef5 } from "react";
2373
2528
  import { Edit3, Trash2 as Trash22, Copy } from "lucide-react";
2374
2529
  import { AnimatePresence as AnimatePresence2, motion as motion3 } from "framer-motion";
2375
2530
  var EventContextMenu = ({
@@ -2382,7 +2537,7 @@ var EventContextMenu = ({
2382
2537
  customActions = [],
2383
2538
  translations
2384
2539
  }) => {
2385
- const menuRef = useRef4(null);
2540
+ const menuRef = useRef5(null);
2386
2541
  useEffect4(() => {
2387
2542
  const handleKeyDown = (e) => {
2388
2543
  if (e.key === "Escape") {
@@ -2673,13 +2828,14 @@ function cachedMonth(event, monthStart, tz) {
2673
2828
  }
2674
2829
  function expandRecurrences(events, range, anchorTz) {
2675
2830
  const result = [];
2676
- const firstMonth = startOfMonth2(toDisplay(range.start, anchorTz));
2677
2831
  const lastMonth = startOfMonth2(toDisplay(range.end, anchorTz));
2678
2832
  for (const event of events) {
2679
2833
  if (!event.recurrence) {
2680
2834
  result.push(event);
2681
2835
  continue;
2682
2836
  }
2837
+ const duration = event.end.getTime() - event.start.getTime();
2838
+ const firstMonth = startOfMonth2(toDisplay(new Date(range.start.getTime() - duration), anchorTz));
2683
2839
  try {
2684
2840
  for (let month = firstMonth; month <= lastMonth; month = addMonths2(month, 1)) {
2685
2841
  for (const occurrence of cachedMonth(event, fromDisplay(month, anchorTz), anchorTz)) {
@@ -2710,6 +2866,7 @@ function shiftSeries(master, occurrence, newStart, newEnd, anchorTz) {
2710
2866
  const recurrence = master.recurrence && {
2711
2867
  ...master.recurrence,
2712
2868
  byweekday: master.recurrence.byweekday && dayDelta % 7 !== 0 ? master.recurrence.byweekday.map((d) => ((d + dayDelta) % 7 + 7) % 7) : master.recurrence.byweekday,
2869
+ until: master.recurrence.until && shiftFloating(master.recurrence.until, deltaMs, anchorTz),
2713
2870
  exdates: master.recurrence.exdates?.map((d) => shiftFloating(d, deltaMs, anchorTz))
2714
2871
  };
2715
2872
  return stripOccurrenceFields({ ...master, start, end, recurrence });
@@ -2720,10 +2877,12 @@ function excludeOccurrence(master, occurrenceStart) {
2720
2877
  }
2721
2878
 
2722
2879
  // src/hooks/useStableCallback.ts
2723
- import { useCallback as useCallback3, useRef as useRef5 } from "react";
2880
+ import { useCallback as useCallback3, useInsertionEffect as useInsertionEffect2, useRef as useRef6 } from "react";
2724
2881
  function useStableCallback(fn) {
2725
- const ref = useRef5(fn);
2726
- ref.current = fn;
2882
+ const ref = useRef6(fn);
2883
+ useInsertionEffect2(() => {
2884
+ ref.current = fn;
2885
+ });
2727
2886
  return useCallback3((...args) => ref.current(...args), []);
2728
2887
  }
2729
2888
 
@@ -2904,6 +3063,7 @@ var useCalendarLogic = ({
2904
3063
  return { original: master, updated: shiftSeries(master, event, start, end, timezone) };
2905
3064
  };
2906
3065
  const handleDragEnd = useStableCallback((dragEvent) => {
3066
+ if (readOnly) return;
2907
3067
  const { active, over } = dragEvent;
2908
3068
  if (!over) return;
2909
3069
  const dragged = active.data.current?.event;
@@ -2960,10 +3120,10 @@ var useCalendarLogic = ({
2960
3120
  };
2961
3121
 
2962
3122
  // src/index.tsx
2963
- import { differenceInMinutes as differenceInMinutes6, format as format14 } from "date-fns";
3123
+ import { differenceInMinutes as differenceInMinutes6, format as format14, startOfWeek as startOfWeek3 } from "date-fns";
2964
3124
 
2965
3125
  // src/hooks/useSwipeGesture.ts
2966
- import { useRef as useRef6, useCallback as useCallback4, useEffect as useEffect5 } from "react";
3126
+ import { useRef as useRef7, useCallback as useCallback4, useEffect as useEffect5 } from "react";
2967
3127
  var useSwipeGesture = (options) => {
2968
3128
  const {
2969
3129
  onSwipeLeft,
@@ -2975,8 +3135,8 @@ var useSwipeGesture = (options) => {
2975
3135
  allowedTime = 500,
2976
3136
  enabled = true
2977
3137
  } = options;
2978
- const touchInfoRef = useRef6(null);
2979
- const elementRef = useRef6(null);
3138
+ const touchInfoRef = useRef7(null);
3139
+ const elementRef = useRef7(null);
2980
3140
  const handleTouchStart = useCallback4((e) => {
2981
3141
  if (!enabled) return;
2982
3142
  const touch = e.touches[0];
@@ -3072,6 +3232,7 @@ var Scheduler = ({
3072
3232
  const {
3073
3233
  contextMenuEvent,
3074
3234
  contextMenuPosition,
3235
+ openContextMenu,
3075
3236
  closeContextMenu
3076
3237
  } = useEventContextMenu();
3077
3238
  const {
@@ -3159,14 +3320,15 @@ var Scheduler = ({
3159
3320
  }
3160
3321
  return "150px";
3161
3322
  };
3323
+ const activeCalendarKey = calendars ? calendars.filter((c) => c.active !== false).map((c) => c.id).join("\0") : null;
3162
3324
  const filteredEvents = useMemo4(() => {
3163
- if (!calendars) return expandedEvents;
3164
- const activeCalendarIds = calendars.filter((c) => c.active !== false).map((c) => c.id);
3165
- return expandedEvents.filter((e) => {
3166
- if (!e.calendarId) return true;
3167
- return activeCalendarIds.includes(e.calendarId);
3168
- });
3169
- }, [expandedEvents, calendars]);
3325
+ if (activeCalendarKey === null) return expandedEvents;
3326
+ const activeCalendarIds = new Set(activeCalendarKey.split("\0"));
3327
+ return expandedEvents.filter((e) => !e.calendarId || activeCalendarIds.has(String(e.calendarId)));
3328
+ }, [expandedEvents, activeCalendarKey]);
3329
+ const periodKey = view === "month" ? format14(currentDate, "yyyy-MM") : view === "week" ? format14(startOfWeek3(currentDate, { weekStartsOn: resolveWeekStartsOn(locale, 1) }), "yyyy-MM-dd") : format14(currentDate, "yyyy-MM-dd");
3330
+ const slotClickHandler = readOnly ? void 0 : handleTimeSlotClick;
3331
+ const contextMenuHandler = readOnly ? void 0 : openContextMenu;
3170
3332
  return /* @__PURE__ */ React18.createElement(
3171
3333
  DndContext,
3172
3334
  {
@@ -3237,7 +3399,7 @@ var Scheduler = ({
3237
3399
  ), /* @__PURE__ */ React18.createElement("div", { className: "flex-1 flex flex-col overflow-hidden relative" }, isLoading ? /* @__PURE__ */ React18.createElement("div", { className: "flex-1 overflow-auto p-0 md:p-4" }, /* @__PURE__ */ React18.createElement("div", { className: "h-full min-w-full" }, view === "month" && /* @__PURE__ */ React18.createElement(MonthViewSkeleton, null), view === "week" && /* @__PURE__ */ React18.createElement(WeekViewSkeleton, null), view === "day" && /* @__PURE__ */ React18.createElement(DayViewSkeleton, null), view === "agenda" && /* @__PURE__ */ React18.createElement(AgendaViewSkeleton, null), view === "resource" && /* @__PURE__ */ React18.createElement(WeekViewSkeleton, null))) : /* @__PURE__ */ React18.createElement("div", { ref: swipeRef, className: "flex-1 overflow-auto p-0 md:p-4 touch-pan-y" }, /* @__PURE__ */ React18.createElement("div", { className: "h-full min-w-full" }, /* @__PURE__ */ React18.createElement(AnimatePresence3, { mode: "wait", initial: false }, /* @__PURE__ */ React18.createElement(
3238
3400
  motion4.div,
3239
3401
  {
3240
- key: `${view}-${currentDate.toISOString()}-${timezone || "local"}`,
3402
+ key: `${view}-${periodKey}-${timezone || "local"}`,
3241
3403
  initial: { opacity: 0, scale: 0.98, y: 15 },
3242
3404
  animate: {
3243
3405
  opacity: 1,
@@ -3267,7 +3429,9 @@ var Scheduler = ({
3267
3429
  onEventClick: handleEventClickInternal,
3268
3430
  onDateClick: handleDateClick,
3269
3431
  timezone,
3270
- locale
3432
+ locale,
3433
+ translations: t,
3434
+ onEventContextMenu: contextMenuHandler
3271
3435
  }
3272
3436
  ),
3273
3437
  view === "week" && /* @__PURE__ */ React18.createElement(
@@ -3276,11 +3440,12 @@ var Scheduler = ({
3276
3440
  currentDate,
3277
3441
  events: filteredEvents,
3278
3442
  onEventClick: handleEventClickInternal,
3279
- onTimeSlotClick: handleTimeSlotClick,
3443
+ onTimeSlotClick: slotClickHandler,
3280
3444
  onEventResize: handleEventResize,
3281
3445
  timezone,
3282
3446
  locale,
3283
- readonly: readOnly
3447
+ readonly: readOnly,
3448
+ onEventContextMenu: contextMenuHandler
3284
3449
  }
3285
3450
  ),
3286
3451
  view === "day" && /* @__PURE__ */ React18.createElement(
@@ -3289,11 +3454,13 @@ var Scheduler = ({
3289
3454
  currentDate,
3290
3455
  events: filteredEvents,
3291
3456
  onEventClick: handleEventClickInternal,
3292
- onTimeSlotClick: handleTimeSlotClick,
3457
+ onTimeSlotClick: slotClickHandler,
3293
3458
  onEventResize: handleEventResize,
3294
3459
  timezone,
3295
3460
  locale,
3296
- readonly: readOnly
3461
+ readonly: readOnly,
3462
+ translations: t,
3463
+ onEventContextMenu: contextMenuHandler
3297
3464
  }
3298
3465
  ),
3299
3466
  view === "agenda" && /* @__PURE__ */ React18.createElement(
@@ -3305,7 +3472,8 @@ var Scheduler = ({
3305
3472
  onCreateEvent: handleCreateEvent,
3306
3473
  locale,
3307
3474
  translations: t,
3308
- timezone
3475
+ timezone,
3476
+ onEventContextMenu: contextMenuHandler
3309
3477
  }
3310
3478
  ),
3311
3479
  view === "resource" && resources && /* @__PURE__ */ React18.createElement(
@@ -3315,15 +3483,18 @@ var Scheduler = ({
3315
3483
  events: filteredEvents,
3316
3484
  resources,
3317
3485
  onEventClick: handleEventClickInternal,
3318
- onTimeSlotClick: handleTimeSlotClick,
3486
+ onTimeSlotClick: slotClickHandler,
3319
3487
  locale,
3320
- timezone
3488
+ timezone,
3489
+ translations: t,
3490
+ onEventContextMenu: contextMenuHandler
3321
3491
  }
3322
3492
  )
3323
- )))), /* @__PURE__ */ React18.createElement("div", { className: "md:hidden absolute bottom-6 right-6 z-50" }, /* @__PURE__ */ React18.createElement(
3493
+ )))), !readOnly && /* @__PURE__ */ React18.createElement("div", { className: "md:hidden absolute bottom-6 right-6 z-50" }, /* @__PURE__ */ React18.createElement(
3324
3494
  "button",
3325
3495
  {
3326
3496
  onClick: handleCreateEvent,
3497
+ "aria-label": t.createEvent,
3327
3498
  className: "w-14 h-14 bg-blue-600 rounded-full shadow-lg flex items-center justify-center text-white active:scale-90 transition-transform"
3328
3499
  },
3329
3500
  /* @__PURE__ */ React18.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React18.createElement("path", { d: "M5 12h14" }), /* @__PURE__ */ React18.createElement("path", { d: "M12 5v14" }))
@@ -3346,7 +3517,8 @@ var Scheduler = ({
3346
3517
  onDelete: handleModalDelete,
3347
3518
  calendars,
3348
3519
  eventTypes,
3349
- translations: t
3520
+ translations: t,
3521
+ locale
3350
3522
  }
3351
3523
  ),
3352
3524
  /* @__PURE__ */ React18.createElement(
@@ -3398,7 +3570,7 @@ var Scheduler = ({
3398
3570
  className: "absolute left-0 top-0 bottom-0 w-1 rounded-l-lg",
3399
3571
  style: { backgroundColor: activeDragEvent.color || "var(--primary)" }
3400
3572
  }
3401
- ), /* @__PURE__ */ React18.createElement("div", { className: "pl-2" }, /* @__PURE__ */ React18.createElement("div", { className: "font-semibold truncate text-sm" }, activeDragEvent.title), (view === "week" || view === "day") && getDragHeight() && getDragHeight() > 40 && /* @__PURE__ */ React18.createElement("div", { className: "text-xs opacity-80 mt-0.5 flex items-center gap-1" }, /* @__PURE__ */ React18.createElement("svg", { className: "w-3 h-3", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" }, /* @__PURE__ */ React18.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ React18.createElement("path", { d: "M12 6v6l4 2" })), format14(activeDragEvent.start, overlayTimeFormat, { locale }), " - ", format14(activeDragEvent.end, overlayTimeFormat, { locale }))), /* @__PURE__ */ React18.createElement("div", { className: "absolute bottom-1.5 right-1.5 opacity-60" }, /* @__PURE__ */ React18.createElement("svg", { className: "w-4 h-4", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React18.createElement("circle", { cx: "9", cy: "5", r: "1.5" }), /* @__PURE__ */ React18.createElement("circle", { cx: "15", cy: "5", r: "1.5" }), /* @__PURE__ */ React18.createElement("circle", { cx: "9", cy: "12", r: "1.5" }), /* @__PURE__ */ React18.createElement("circle", { cx: "15", cy: "12", r: "1.5" }), /* @__PURE__ */ React18.createElement("circle", { cx: "9", cy: "19", r: "1.5" }), /* @__PURE__ */ React18.createElement("circle", { cx: "15", cy: "19", r: "1.5" }))))
3573
+ ), /* @__PURE__ */ React18.createElement("div", { className: "pl-2" }, /* @__PURE__ */ React18.createElement("div", { className: "font-semibold truncate text-sm" }, activeDragEvent.title), (view === "week" || view === "day") && getDragHeight() && getDragHeight() > 40 && /* @__PURE__ */ React18.createElement("div", { className: "text-xs opacity-80 mt-0.5 flex items-center gap-1" }, /* @__PURE__ */ React18.createElement("svg", { className: "w-3 h-3", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" }, /* @__PURE__ */ React18.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ React18.createElement("path", { d: "M12 6v6l4 2" })), format14(toDisplay(activeDragEvent.start, timezone), overlayTimeFormat, { locale }), " - ", format14(toDisplay(activeDragEvent.end, timezone), overlayTimeFormat, { locale }))), /* @__PURE__ */ React18.createElement("div", { className: "absolute bottom-1.5 right-1.5 opacity-60" }, /* @__PURE__ */ React18.createElement("svg", { className: "w-4 h-4", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ React18.createElement("circle", { cx: "9", cy: "5", r: "1.5" }), /* @__PURE__ */ React18.createElement("circle", { cx: "15", cy: "5", r: "1.5" }), /* @__PURE__ */ React18.createElement("circle", { cx: "9", cy: "12", r: "1.5" }), /* @__PURE__ */ React18.createElement("circle", { cx: "15", cy: "12", r: "1.5" }), /* @__PURE__ */ React18.createElement("circle", { cx: "9", cy: "19", r: "1.5" }), /* @__PURE__ */ React18.createElement("circle", { cx: "15", cy: "19", r: "1.5" }))))
3402
3574
  ) : null)
3403
3575
  )
3404
3576
  );