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.js CHANGED
@@ -128,7 +128,8 @@ var CalendarHeader = ({
128
128
  variant: "ghost",
129
129
  size: "icon",
130
130
  className: "text-muted-foreground hover:text-foreground hover:bg-accent/80 rounded-xl h-10 w-10 hidden md:inline-flex transition-all duration-200",
131
- onClick: onMenuClick
131
+ onClick: onMenuClick,
132
+ "aria-label": translations.toggleSidebar
132
133
  },
133
134
  /* @__PURE__ */ import_react.default.createElement(import_lucide_react.Menu, { className: "h-5 w-5" })
134
135
  ), /* @__PURE__ */ import_react.default.createElement(
@@ -145,7 +146,8 @@ var CalendarHeader = ({
145
146
  variant: "ghost",
146
147
  size: "icon",
147
148
  onClick: onPrev,
148
- className: "rounded-lg h-8 w-8 hover:bg-background/80 transition-all duration-200"
149
+ className: "rounded-lg h-8 w-8 hover:bg-background/80 transition-all duration-200",
150
+ "aria-label": translations.previous
149
151
  },
150
152
  /* @__PURE__ */ import_react.default.createElement(import_lucide_react.ChevronLeft, { className: "h-4 w-4 text-muted-foreground" })
151
153
  ), /* @__PURE__ */ import_react.default.createElement(
@@ -154,7 +156,8 @@ var CalendarHeader = ({
154
156
  variant: "ghost",
155
157
  size: "icon",
156
158
  onClick: onNext,
157
- className: "rounded-lg h-8 w-8 hover:bg-background/80 transition-all duration-200"
159
+ className: "rounded-lg h-8 w-8 hover:bg-background/80 transition-all duration-200",
160
+ "aria-label": translations.next
158
161
  },
159
162
  /* @__PURE__ */ import_react.default.createElement(import_lucide_react.ChevronRight, { className: "h-4 w-4 text-muted-foreground" })
160
163
  )), /* @__PURE__ */ import_react.default.createElement("div", { className: "ml-2 md:ml-4" }, /* @__PURE__ */ import_react.default.createElement("h2", { className: "text-lg md:text-xl font-semibold text-foreground whitespace-nowrap capitalize tracking-tight" }, (0, import_date_fns.format)(currentDate, "MMMM yyyy", { locale }))))), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2 md:gap-3 w-full md:w-auto justify-end" }, onLanguageChange && language && /* @__PURE__ */ import_react.default.createElement(
@@ -163,6 +166,7 @@ var CalendarHeader = ({
163
166
  variant: "ghost",
164
167
  size: "sm",
165
168
  onClick: () => onLanguageChange(language === "en" ? "fr" : "en"),
169
+ "aria-label": translations.switchLanguage,
166
170
  className: "h-9 px-3 rounded-xl text-xs font-semibold text-muted-foreground hover:text-foreground hover:bg-accent/80 transition-all duration-200 uppercase tracking-wider"
167
171
  },
168
172
  language
@@ -172,7 +176,8 @@ var CalendarHeader = ({
172
176
  variant: "ghost",
173
177
  size: "icon",
174
178
  className: "rounded-xl h-9 w-9 hover:bg-accent/80 transition-all duration-200",
175
- onClick: onThemeToggle
179
+ onClick: onThemeToggle,
180
+ "aria-label": translations.toggleTheme
176
181
  },
177
182
  isDarkMode ? /* @__PURE__ */ import_react.default.createElement(import_lucide_react.Sun, { className: "h-4 w-4 text-amber-500" }) : /* @__PURE__ */ import_react.default.createElement(import_lucide_react.Moon, { className: "h-4 w-4 text-muted-foreground" })
178
183
  ), !hideViewSwitcher && /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center bg-muted/50 backdrop-blur-sm rounded-xl p-1" }, viewConfig.map(({ key, icon: Icon }) => /* @__PURE__ */ import_react.default.createElement(
@@ -182,6 +187,8 @@ var CalendarHeader = ({
182
187
  variant: "ghost",
183
188
  size: "sm",
184
189
  onClick: () => onViewChange(key),
190
+ "aria-pressed": view === key,
191
+ "aria-label": translations[key],
185
192
  className: cn(
186
193
  "h-8 px-3 text-xs rounded-lg transition-all duration-200 gap-1.5",
187
194
  view === key ? "bg-background shadow-sm text-foreground font-medium border-[0.5px] border-border/50" : "text-muted-foreground hover:text-foreground hover:bg-background/50"
@@ -226,7 +233,8 @@ var MiniCalendar = ({
226
233
  onDateChange,
227
234
  onViewChange,
228
235
  className,
229
- locale
236
+ locale,
237
+ labels
230
238
  }) => {
231
239
  const [viewDate, setViewDate] = import_react2.default.useState(currentDate);
232
240
  import_react2.default.useEffect(() => {
@@ -264,7 +272,7 @@ var MiniCalendar = ({
264
272
  size: "icon",
265
273
  className: "h-7 w-7 rounded-md hover:bg-background/80 transition-all",
266
274
  onClick: handlePrev,
267
- "aria-label": "Previous month"
275
+ "aria-label": labels?.previousMonth || "Previous month"
268
276
  },
269
277
  /* @__PURE__ */ import_react2.default.createElement(import_lucide_react2.ChevronLeft, { className: "h-4 w-4" })
270
278
  ), /* @__PURE__ */ import_react2.default.createElement(
@@ -274,7 +282,7 @@ var MiniCalendar = ({
274
282
  size: "icon",
275
283
  className: "h-7 w-7 rounded-md hover:bg-background/80 transition-all",
276
284
  onClick: handleNext,
277
- "aria-label": "Next month"
285
+ "aria-label": labels?.nextMonth || "Next month"
278
286
  },
279
287
  /* @__PURE__ */ import_react2.default.createElement(import_lucide_react2.ChevronRight, { className: "h-4 w-4" })
280
288
  ))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "grid grid-cols-7 gap-y-2 text-center mb-2" }, weekDays.map((day, i) => /* @__PURE__ */ import_react2.default.createElement("div", { key: `${day}-${i}`, className: "text-[10px] text-muted-foreground/70 font-semibold uppercase" }, day))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "grid grid-cols-7 gap-y-1 text-center" }, days.map((day) => {
@@ -374,7 +382,45 @@ var en = {
374
382
  allCaughtUp: "All caught up!",
375
383
  noEventsScheduled: "No events scheduled",
376
384
  noUpcoming: "Your schedule is clear",
377
- noUpcomingHint: "No upcoming events to show. Create a new event to start planning."
385
+ noUpcomingHint: "No upcoming events to show. Create a new event to start planning.",
386
+ newEvent: "New Event",
387
+ saveChanges: "Save Changes",
388
+ close: "Close",
389
+ noTitle: "(No title)",
390
+ openInMaps: "Open in maps",
391
+ notesLabel: "Notes",
392
+ addNotes: "Add description or notes...",
393
+ attachments: "Attachments",
394
+ reminders: "Reminders",
395
+ add: "Add",
396
+ calendar: "Calendar",
397
+ select: "Select",
398
+ addLocation: "Add location",
399
+ guestSingular: "Guest",
400
+ addGuestPlaceholder: "Add a guest email and press Enter",
401
+ invalidEmail: "Enter a valid email address",
402
+ removeGuest: "Remove {email}",
403
+ reminderAtTime: "At time of event",
404
+ reminderMinutes: "{n} minutes before",
405
+ reminderHour: "1 hour before",
406
+ reminderHours: "{n} hours before",
407
+ reminderDay: "1 day before",
408
+ reminderDays: "{n} days before",
409
+ reminderWeek: "1 week before",
410
+ removeReminder: "Remove reminder",
411
+ durationMinutes: "{n} min",
412
+ durationHours: "{n} hr",
413
+ durationHoursMinutes: "{h} hr {m} min",
414
+ moreEvents: "+{n} more",
415
+ idLabel: "ID: {id}",
416
+ previous: "Previous",
417
+ next: "Next",
418
+ previousMonth: "Previous month",
419
+ nextMonth: "Next month",
420
+ toggleSidebar: "Toggle sidebar",
421
+ toggleTheme: "Toggle dark mode",
422
+ switchLanguage: "Switch language",
423
+ eventColor: "Event color"
378
424
  };
379
425
  var fr = {
380
426
  today: "Aujourd'hui",
@@ -443,7 +489,45 @@ var fr = {
443
489
  allCaughtUp: "Tout est \xE0 jour !",
444
490
  noEventsScheduled: "Aucun \xE9v\xE9nement pr\xE9vu",
445
491
  noUpcoming: "Votre planning est libre",
446
- noUpcomingHint: "Aucun \xE9v\xE9nement \xE0 venir. Cr\xE9ez un nouvel \xE9v\xE9nement pour commencer \xE0 planifier."
492
+ noUpcomingHint: "Aucun \xE9v\xE9nement \xE0 venir. Cr\xE9ez un nouvel \xE9v\xE9nement pour commencer \xE0 planifier.",
493
+ newEvent: "Nouvel \xE9v\xE9nement",
494
+ saveChanges: "Enregistrer",
495
+ close: "Fermer",
496
+ noTitle: "(Sans titre)",
497
+ openInMaps: "Ouvrir dans Maps",
498
+ notesLabel: "Notes",
499
+ addNotes: "Ajouter une description ou des notes\u2026",
500
+ attachments: "Pi\xE8ces jointes",
501
+ reminders: "Rappels",
502
+ add: "Ajouter",
503
+ calendar: "Calendrier",
504
+ select: "Choisir",
505
+ addLocation: "Ajouter un lieu",
506
+ guestSingular: "Invit\xE9",
507
+ addGuestPlaceholder: "Ajoutez l'e-mail d'un invit\xE9 puis Entr\xE9e",
508
+ invalidEmail: "Saisissez une adresse e-mail valide",
509
+ removeGuest: "Retirer {email}",
510
+ reminderAtTime: "Au moment de l'\xE9v\xE9nement",
511
+ reminderMinutes: "{n} minutes avant",
512
+ reminderHour: "1 heure avant",
513
+ reminderHours: "{n} heures avant",
514
+ reminderDay: "1 jour avant",
515
+ reminderDays: "{n} jours avant",
516
+ reminderWeek: "1 semaine avant",
517
+ removeReminder: "Supprimer le rappel",
518
+ durationMinutes: "{n} min",
519
+ durationHours: "{n} h",
520
+ durationHoursMinutes: "{h} h {m} min",
521
+ moreEvents: "+{n} de plus",
522
+ idLabel: "ID : {id}",
523
+ previous: "Pr\xE9c\xE9dent",
524
+ next: "Suivant",
525
+ previousMonth: "Mois pr\xE9c\xE9dent",
526
+ nextMonth: "Mois suivant",
527
+ toggleSidebar: "Afficher ou masquer la barre lat\xE9rale",
528
+ toggleTheme: "Basculer le th\xE8me sombre",
529
+ switchLanguage: "Changer de langue",
530
+ eventColor: "Couleur de l'\xE9v\xE9nement"
447
531
  };
448
532
 
449
533
  // src/core/format.ts
@@ -486,7 +570,18 @@ function timeZoneAbbreviation(date, timeZone) {
486
570
  }
487
571
  }
488
572
  function resolveTranslations(language, overrides) {
489
- return { ...language === "fr" ? fr : en, ...overrides };
573
+ const defined = Object.fromEntries(Object.entries(overrides ?? {}).filter(([, value]) => value !== void 0));
574
+ return { ...language === "fr" ? fr : en, ...defined };
575
+ }
576
+ function interpolate(template, values) {
577
+ return template.replace(/\{(\w+)\}/g, (match, key) => key in values ? String(values[key]) : match);
578
+ }
579
+
580
+ // src/core/color.ts
581
+ var SAFE_COLOR = /^[#\w\s%.,/()+-]+$/;
582
+ var FORBIDDEN = /url\(|expression\(/i;
583
+ function safeColor(value, fallback) {
584
+ return value && SAFE_COLOR.test(value.trim()) && !FORBIDDEN.test(value) ? value.trim() : fallback;
490
585
  }
491
586
 
492
587
  // src/components/Sidebar.tsx
@@ -514,12 +609,6 @@ var Sidebar = ({
514
609
  const timer = setInterval(() => setNow(/* @__PURE__ */ new Date()), 6e4);
515
610
  return () => clearInterval(timer);
516
611
  }, []);
517
- const defaultCalendars = [
518
- { id: "1", label: "My Calendar", color: "#3b82f6", active: true },
519
- { id: "2", label: "Birthdays", color: "#10b981", active: true },
520
- { id: "3", label: "Tasks", color: "#6366f1", active: true }
521
- ];
522
- const displayCalendars = calendars || defaultCalendars;
523
612
  const getAcronym = (tz) => !tz || !now ? "LOC" : timeZoneAbbreviation(now, tz);
524
613
  const timezones = [
525
614
  { value: "", label: translations?.localTime || "Local Time", acronym: "LOC" },
@@ -569,7 +658,16 @@ var Sidebar = ({
569
658
  },
570
659
  /* @__PURE__ */ import_react3.default.createElement(import_lucide_react3.Plus, { className: "w-5 h-5" }),
571
660
  /* @__PURE__ */ import_react3.default.createElement("span", { className: "text-sm" }, translations?.create || "Create")
572
- )), /* @__PURE__ */ import_react3.default.createElement(MiniCalendar, { currentDate, onDateChange, onViewChange, locale }), /* @__PURE__ */ import_react3.default.createElement("div", { className: "flex-1 px-4 space-y-5 mt-5" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "bg-muted/20 rounded-2xl p-3 border-[0px] border-border/30" }, /* @__PURE__ */ import_react3.default.createElement(
661
+ )), /* @__PURE__ */ import_react3.default.createElement(
662
+ MiniCalendar,
663
+ {
664
+ currentDate,
665
+ onDateChange,
666
+ onViewChange,
667
+ locale,
668
+ labels: { previousMonth: translations?.previousMonth, nextMonth: translations?.nextMonth }
669
+ }
670
+ ), /* @__PURE__ */ import_react3.default.createElement("div", { className: "flex-1 px-4 space-y-5 mt-5" }, calendars && calendars.length > 0 && /* @__PURE__ */ import_react3.default.createElement("div", { className: "bg-muted/20 rounded-2xl p-3 border-[0px] border-border/30" }, /* @__PURE__ */ import_react3.default.createElement(
573
671
  "div",
574
672
  {
575
673
  className: "flex items-center justify-between cursor-pointer hover:bg-accent/50 p-2 -m-1 rounded-xl mb-2 transition-all duration-200",
@@ -577,41 +675,40 @@ var Sidebar = ({
577
675
  },
578
676
  /* @__PURE__ */ import_react3.default.createElement("span", { className: "text-sm font-semibold text-foreground" }, translations?.calendars || "Calendars"),
579
677
  /* @__PURE__ */ import_react3.default.createElement(import_lucide_react3.ChevronDown, { className: cn("w-4 h-4 text-muted-foreground transition-transform duration-200", calendarsOpen && "rotate-180") })
580
- ), calendarsOpen && /* @__PURE__ */ import_react3.default.createElement("div", { className: "space-y-1" }, displayCalendars.map((cal) => /* @__PURE__ */ import_react3.default.createElement(
678
+ ), calendarsOpen && /* @__PURE__ */ import_react3.default.createElement("div", { className: "space-y-1" }, calendars.map((cal) => /* @__PURE__ */ import_react3.default.createElement(
581
679
  "div",
582
680
  {
583
681
  key: cal.id,
584
682
  className: "flex items-center gap-3 py-2 px-2 hover:bg-accent/60 rounded-xl cursor-pointer group transition-all duration-200",
585
683
  onClick: () => onCalendarToggle?.(cal.id, !(cal.active ?? true))
586
684
  },
587
- /* @__PURE__ */ import_react3.default.createElement("div", { className: "relative flex items-center justify-center" }, /* @__PURE__ */ import_react3.default.createElement(
588
- "input",
685
+ /* @__PURE__ */ import_react3.default.createElement(
686
+ "div",
589
687
  {
590
- type: "checkbox",
591
- checked: cal.active ?? true,
592
- onChange: (e) => {
593
- e.stopPropagation();
594
- onCalendarToggle?.(cal.id, e.target.checked);
595
- },
596
- className: "peer h-5 w-5 rounded-md border-2 border-border/60 cursor-pointer appearance-none checked:border-transparent transition-all duration-200",
597
- style: { "--primary-color": cal.color },
598
- "data-cal-id": cal.id
599
- }
600
- ), /* @__PURE__ */ import_react3.default.createElement("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none opacity-0 peer-checked:opacity-100 transition-opacity" }, /* @__PURE__ */ import_react3.default.createElement("svg", { className: "w-3.5 h-3.5 text-white", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 3 }, /* @__PURE__ */ import_react3.default.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" }))), /* @__PURE__ */ import_react3.default.createElement("style", null, `
601
- input[type="checkbox"][data-cal-id="${cal.id}"]:checked {
602
- background-color: ${cal.color} !important;
603
- border-color: ${cal.color} !important;
604
- }
605
- input[type="checkbox"][data-cal-id="${cal.id}"]:focus {
606
- --tw-ring-color: ${cal.color}40 !important;
607
- }
608
- `)),
688
+ className: "relative flex items-center justify-center",
689
+ style: { "--cal-color": safeColor(cal.color, "hsl(var(--primary))") }
690
+ },
691
+ /* @__PURE__ */ import_react3.default.createElement(
692
+ "input",
693
+ {
694
+ type: "checkbox",
695
+ checked: cal.active ?? true,
696
+ onChange: (e) => {
697
+ e.stopPropagation();
698
+ onCalendarToggle?.(cal.id, e.target.checked);
699
+ },
700
+ "aria-label": cal.label,
701
+ className: "peer h-5 w-5 rounded-md border-2 border-border/60 cursor-pointer appearance-none checked:border-transparent checked:bg-[var(--cal-color)] focus:ring-2 focus:ring-[var(--cal-color)] transition-all duration-200"
702
+ }
703
+ ),
704
+ /* @__PURE__ */ import_react3.default.createElement("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none opacity-0 peer-checked:opacity-100 transition-opacity" }, /* @__PURE__ */ import_react3.default.createElement("svg", { className: "w-3.5 h-3.5 text-white", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 3 }, /* @__PURE__ */ import_react3.default.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" })))
705
+ ),
609
706
  /* @__PURE__ */ import_react3.default.createElement("div", { className: "flex items-center gap-2 flex-1 min-w-0" }, /* @__PURE__ */ import_react3.default.createElement("span", { className: "text-sm text-foreground/90 truncate font-medium" }, cal.label)),
610
707
  /* @__PURE__ */ import_react3.default.createElement(
611
708
  "div",
612
709
  {
613
710
  className: "w-2 h-2 rounded-full opacity-60 group-hover:opacity-100 transition-opacity",
614
- style: { backgroundColor: cal.color }
711
+ style: { backgroundColor: safeColor(cal.color, "hsl(var(--primary))") }
615
712
  }
616
713
  )
617
714
  ))))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mt-auto px-4 pt-5" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "bg-muted/20 rounded-2xl p-3" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "flex items-center gap-2 mb-3" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "p-1.5 bg-primary/10 rounded-lg" }, /* @__PURE__ */ import_react3.default.createElement(import_lucide_react3.Globe, { className: "w-4 h-4 text-primary" })), /* @__PURE__ */ import_react3.default.createElement("span", { className: "text-sm font-semibold text-foreground" }, translations?.timezone || "Timezone")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react3.default.createElement(
@@ -898,7 +995,7 @@ var DroppableCell = import_react5.default.memo(
898
995
  );
899
996
 
900
997
  // src/views/MonthView.tsx
901
- var EventItem = import_react6.default.memo(({ event, onEventClick }) => /* @__PURE__ */ import_react6.default.createElement(DraggableEvent, { event }, /* @__PURE__ */ import_react6.default.createElement(
998
+ var EventItem = import_react6.default.memo(({ event, onEventClick, onEventContextMenu }) => /* @__PURE__ */ import_react6.default.createElement(DraggableEvent, { event }, /* @__PURE__ */ import_react6.default.createElement(
902
999
  "div",
903
1000
  {
904
1001
  className: cn(
@@ -914,7 +1011,8 @@ var EventItem = import_react6.default.memo(({ event, onEventClick }) => /* @__PU
914
1011
  onClick: (e) => {
915
1012
  e.stopPropagation();
916
1013
  onEventClick?.(event);
917
- }
1014
+ },
1015
+ onContextMenu: onEventContextMenu ? (e) => onEventContextMenu(event, e) : void 0
918
1016
  },
919
1017
  /* @__PURE__ */ import_react6.default.createElement("span", { className: "font-medium" }, event.title)
920
1018
  )));
@@ -925,7 +1023,9 @@ var MonthView = ({
925
1023
  onEventClick,
926
1024
  onDateClick,
927
1025
  timezone,
928
- locale
1026
+ locale,
1027
+ translations,
1028
+ onEventContextMenu
929
1029
  }) => {
930
1030
  const weekStartsOn = resolveWeekStartsOn(locale, 1);
931
1031
  const days = (0, import_react6.useMemo)(() => getMonthGrid(currentDate, weekStartsOn), [currentDate, weekStartsOn]);
@@ -961,7 +1061,19 @@ var MonthView = ({
961
1061
  "text-sm font-semibold w-7 h-7 flex items-center justify-center rounded-full transition-all duration-200",
962
1062
  (0, import_date_fns8.isSameDay)(day, today) ? "bg-primary text-primary-foreground shadow-md shadow-primary/30" : "group-hover:bg-accent"
963
1063
  ) }, (0, import_date_fns8.format)(day, "d", { locale })), dayEvents.length > 0 && /* @__PURE__ */ import_react6.default.createElement("div", { className: "text-[10px] font-medium text-muted-foreground/60 bg-muted/50 px-1.5 py-0.5 rounded-full" }, dayEvents.length)),
964
- /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex-1 flex flex-col gap-1 scrollbar-hide overflow-y-auto overflow-x-hidden" }, dayEvents.slice(0, 4).map((event) => /* @__PURE__ */ import_react6.default.createElement(EventItem, { key: `${event.id}-${dayKey2}`, event, onEventClick })), dayEvents.length > 4 && /* @__PURE__ */ import_react6.default.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"))
1064
+ /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex-1 flex flex-col gap-1 scrollbar-hide overflow-y-auto overflow-x-hidden" }, dayEvents.slice(0, 4).map((event) => /* @__PURE__ */ import_react6.default.createElement(EventItem, { key: `${event.id}-${dayKey2}`, event, onEventClick, onEventContextMenu })), dayEvents.length > 4 && // U7: "+N more" opens the day instead of doing nothing.
1065
+ /* @__PURE__ */ import_react6.default.createElement(
1066
+ "button",
1067
+ {
1068
+ type: "button",
1069
+ className: "text-[10px] text-primary font-semibold text-center py-1 px-2 rounded-md bg-primary/5 hover:bg-primary/10 cursor-pointer transition-colors",
1070
+ onClick: (e) => {
1071
+ e.stopPropagation();
1072
+ onDateClick?.(day);
1073
+ }
1074
+ },
1075
+ interpolate(translations?.moreEvents || "+{n} more", { n: dayEvents.length - 4 })
1076
+ ))
965
1077
  );
966
1078
  }))));
967
1079
  };
@@ -1144,7 +1256,8 @@ var WeekViewImpl = ({
1144
1256
  onEventResize,
1145
1257
  timezone,
1146
1258
  locale,
1147
- readonly
1259
+ readonly,
1260
+ onEventContextMenu
1148
1261
  }) => {
1149
1262
  const weekDays = import_react8.default.useMemo(
1150
1263
  () => getWeekDays(currentDate, resolveWeekStartsOn(locale, 1)),
@@ -1294,6 +1407,7 @@ var WeekViewImpl = ({
1294
1407
  e.stopPropagation();
1295
1408
  onEventClick?.(event);
1296
1409
  },
1410
+ onContextMenu: onEventContextMenu ? (e) => onEventContextMenu(event, e) : void 0,
1297
1411
  title: columns > 1 ? `${event.title} (${column + 1}/${columns})` : void 0
1298
1412
  },
1299
1413
  /* @__PURE__ */ import_react8.default.createElement("div", { className: "flex flex-col h-full overflow-hidden w-full" }, /* @__PURE__ */ import_react8.default.createElement("div", { className: cn(
@@ -1338,7 +1452,9 @@ var DayViewImpl = ({
1338
1452
  onEventResize,
1339
1453
  timezone,
1340
1454
  locale,
1341
- readonly
1455
+ readonly,
1456
+ onEventContextMenu,
1457
+ translations
1342
1458
  }) => {
1343
1459
  const visibleRange = import_react9.default.useMemo(() => {
1344
1460
  const start = (0, import_date_fns10.startOfDay)(currentDate);
@@ -1362,7 +1478,7 @@ var DayViewImpl = ({
1362
1478
  const zonedNow = toDisplay(now, timezone);
1363
1479
  const nowTop = minutesSinceDisplayMidnight(now, timezone) / 60 * HOUR_HEIGHT2;
1364
1480
  const formats = timeFormats(locale);
1365
- return /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex flex-col h-full bg-background border-[0.5px] border-border/50 rounded-2xl overflow-hidden shadow-sm" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "px-6 py-4 border-b-[0.5px] border-border/50 bg-gradient-to-r from-muted/20 via-background to-muted/20 text-center shrink-0" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex items-center justify-center gap-3" }, /* @__PURE__ */ import_react9.default.createElement("h2", { className: "text-xl font-semibold capitalize text-foreground" }, (0, import_date_fns10.format)(currentDate, "EEEE, MMMM d, yyyy", { locale })), isToday2 && /* @__PURE__ */ import_react9.default.createElement("span", { className: "text-xs font-semibold bg-primary text-primary-foreground px-3 py-1 rounded-full shadow-md shadow-primary/20" }, "Today"))), /* @__PURE__ */ import_react9.default.createElement("div", { ref: scrollContainerRef, className: "flex-1 overflow-y-auto relative" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex relative", style: { height: HOURS2.length * HOUR_HEIGHT2 } }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "w-20 bg-muted/5 border-r-[0.5px] border-border/30 relative" }, HOURS2.map((hour) => /* @__PURE__ */ import_react9.default.createElement(
1481
+ return /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex flex-col h-full bg-background border-[0.5px] border-border/50 rounded-2xl overflow-hidden shadow-sm" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "px-6 py-4 border-b-[0.5px] border-border/50 bg-gradient-to-r from-muted/20 via-background to-muted/20 text-center shrink-0" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex items-center justify-center gap-3" }, /* @__PURE__ */ import_react9.default.createElement("h2", { className: "text-xl font-semibold capitalize text-foreground" }, (0, import_date_fns10.format)(currentDate, "EEEE, MMMM d, yyyy", { locale })), isToday2 && /* @__PURE__ */ import_react9.default.createElement("span", { className: "text-xs font-semibold bg-primary text-primary-foreground px-3 py-1 rounded-full shadow-md shadow-primary/20" }, translations?.today || "Today"))), /* @__PURE__ */ import_react9.default.createElement("div", { ref: scrollContainerRef, className: "flex-1 overflow-y-auto relative" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex relative", style: { height: HOURS2.length * HOUR_HEIGHT2 } }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "w-20 bg-muted/5 border-r-[0.5px] border-border/30 relative" }, HOURS2.map((hour) => /* @__PURE__ */ import_react9.default.createElement(
1366
1482
  "div",
1367
1483
  {
1368
1484
  key: hour,
@@ -1459,6 +1575,7 @@ var DayViewImpl = ({
1459
1575
  e.stopPropagation();
1460
1576
  onEventClick?.(event);
1461
1577
  },
1578
+ onContextMenu: onEventContextMenu ? (e) => onEventContextMenu(event, e) : void 0,
1462
1579
  title: columns > 1 ? `${event.title} (${column + 1}/${columns})` : void 0
1463
1580
  },
1464
1581
  /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex flex-col h-full overflow-hidden w-full" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: cn(
@@ -1521,7 +1638,8 @@ var AgendaView = ({
1521
1638
  onCreateEvent,
1522
1639
  locale,
1523
1640
  translations,
1524
- timezone
1641
+ timezone,
1642
+ onEventContextMenu
1525
1643
  }) => {
1526
1644
  const is24h = uses24HourClock(locale);
1527
1645
  const { time: timePattern, compact: compactTimePattern } = timeFormats(locale);
@@ -1578,6 +1696,7 @@ var AgendaView = ({
1578
1696
  {
1579
1697
  key: event.id,
1580
1698
  onClick: () => onEventClick?.(event),
1699
+ onContextMenu: onEventContextMenu ? (e) => onEventContextMenu(event, e) : void 0,
1581
1700
  className: cn(
1582
1701
  "group relative flex gap-4 p-4 rounded-2xl border border-border/40",
1583
1702
  "hover:border-primary/40 hover:shadow-lg hover:shadow-primary/5",
@@ -1618,7 +1737,9 @@ var ResourceView = ({
1618
1737
  onEventClick,
1619
1738
  onTimeSlotClick,
1620
1739
  locale,
1621
- timezone
1740
+ timezone,
1741
+ translations,
1742
+ onEventContextMenu
1622
1743
  }) => {
1623
1744
  const containerRef = (0, import_react12.useRef)(null);
1624
1745
  const hours = Array.from({ length: 24 }, (_, i) => i);
@@ -1638,7 +1759,7 @@ var ResourceView = ({
1638
1759
  // Min width for visibility
1639
1760
  };
1640
1761
  };
1641
- return /* @__PURE__ */ import_react12.default.createElement("div", { className: "flex flex-col h-full bg-background overflow-hidden" }, /* @__PURE__ */ import_react12.default.createElement("div", { className: "flex border-b border-border bg-muted/20" }, /* @__PURE__ */ import_react12.default.createElement("div", { className: "w-48 shrink-0 border-r border-border p-4 font-semibold text-sm bg-background sticky left-0 z-20" }, "Resources"), /* @__PURE__ */ import_react12.default.createElement("div", { className: "flex overflow-hidden" }, /* @__PURE__ */ import_react12.default.createElement("div", { className: "flex relative", style: { width: hours.length * hourWidth } }, hours.map((hour) => /* @__PURE__ */ import_react12.default.createElement(
1762
+ return /* @__PURE__ */ import_react12.default.createElement("div", { className: "flex flex-col h-full bg-background overflow-hidden" }, /* @__PURE__ */ import_react12.default.createElement("div", { className: "flex border-b border-border bg-muted/20" }, /* @__PURE__ */ import_react12.default.createElement("div", { className: "w-48 shrink-0 border-r border-border p-4 font-semibold text-sm bg-background sticky left-0 z-20" }, translations?.resource || "Resources"), /* @__PURE__ */ import_react12.default.createElement("div", { className: "flex overflow-hidden" }, /* @__PURE__ */ import_react12.default.createElement("div", { className: "flex relative", style: { width: hours.length * hourWidth } }, hours.map((hour) => /* @__PURE__ */ import_react12.default.createElement(
1642
1763
  "div",
1643
1764
  {
1644
1765
  key: hour,
@@ -1650,7 +1771,7 @@ var ResourceView = ({
1650
1771
  const resourceEvents = events.filter(
1651
1772
  (e) => e.resourceId === resource.id && isSameDisplayDay(e.start, currentDate, timezone)
1652
1773
  );
1653
- return /* @__PURE__ */ import_react12.default.createElement("div", { key: resource.id, className: "flex border-b border-border min-h-[100px]" }, /* @__PURE__ */ import_react12.default.createElement("div", { className: "w-48 shrink-0 border-r border-border p-4 bg-background sticky left-0 z-10 flex items-center gap-3" }, resource.avatar ? /* @__PURE__ */ import_react12.default.createElement("img", { src: resource.avatar, alt: resource.label, className: "w-8 h-8 rounded-full object-cover" }) : /* @__PURE__ */ import_react12.default.createElement("div", { className: "w-8 h-8 rounded-full bg-primary/10 flex items-center justify-center text-xs font-bold text-primary" }, resource.label.substring(0, 2).toUpperCase()), /* @__PURE__ */ import_react12.default.createElement("div", { className: "flex flex-col" }, /* @__PURE__ */ import_react12.default.createElement("span", { className: "text-sm font-medium" }, resource.label), /* @__PURE__ */ import_react12.default.createElement("span", { className: "text-xs text-muted-foreground" }, "ID: ", resource.id))), /* @__PURE__ */ import_react12.default.createElement("div", { className: "relative flex", style: { width: hours.length * hourWidth } }, hours.map((hour) => /* @__PURE__ */ import_react12.default.createElement(
1774
+ return /* @__PURE__ */ import_react12.default.createElement("div", { key: resource.id, className: "flex border-b border-border min-h-[100px]" }, /* @__PURE__ */ import_react12.default.createElement("div", { className: "w-48 shrink-0 border-r border-border p-4 bg-background sticky left-0 z-10 flex items-center gap-3" }, resource.avatar ? /* @__PURE__ */ import_react12.default.createElement("img", { src: resource.avatar, alt: resource.label, className: "w-8 h-8 rounded-full object-cover" }) : /* @__PURE__ */ import_react12.default.createElement("div", { className: "w-8 h-8 rounded-full bg-primary/10 flex items-center justify-center text-xs font-bold text-primary" }, resource.label.substring(0, 2).toUpperCase()), /* @__PURE__ */ import_react12.default.createElement("div", { className: "flex flex-col" }, /* @__PURE__ */ import_react12.default.createElement("span", { className: "text-sm font-medium" }, resource.label), /* @__PURE__ */ import_react12.default.createElement("span", { className: "text-xs text-muted-foreground" }, interpolate(translations?.idLabel || "ID: {id}", { id: resource.id })))), /* @__PURE__ */ import_react12.default.createElement("div", { className: "relative flex", style: { width: hours.length * hourWidth } }, hours.map((hour) => /* @__PURE__ */ import_react12.default.createElement(
1654
1775
  "div",
1655
1776
  {
1656
1777
  key: hour,
@@ -1682,6 +1803,7 @@ var ResourceView = ({
1682
1803
  e.stopPropagation();
1683
1804
  onEventClick?.(event);
1684
1805
  },
1806
+ onContextMenu: onEventContextMenu ? (e) => onEventContextMenu(event, e) : void 0,
1685
1807
  className: "absolute top-2 bottom-2 rounded-md px-2 py-1 text-xs font-medium border shadow-sm cursor-pointer overflow-hidden hover:brightness-95 transition-all z-10",
1686
1808
  style: {
1687
1809
  ...getEventStyle(event),
@@ -1707,7 +1829,8 @@ var Modal = ({
1707
1829
  title,
1708
1830
  children,
1709
1831
  className,
1710
- hideHeader = false
1832
+ hideHeader = false,
1833
+ closeLabel = "Close"
1711
1834
  }) => {
1712
1835
  const overlayRef = (0, import_react13.useRef)(null);
1713
1836
  (0, import_react13.useEffect)(() => {
@@ -1748,7 +1871,7 @@ var Modal = ({
1748
1871
  className: "h-9 w-9 p-0 rounded-xl hover:bg-accent/80 transition-all duration-200"
1749
1872
  },
1750
1873
  /* @__PURE__ */ import_react13.default.createElement(import_lucide_react6.X, { className: "h-4 w-4" }),
1751
- /* @__PURE__ */ import_react13.default.createElement("span", { className: "sr-only" }, "Close")
1874
+ /* @__PURE__ */ import_react13.default.createElement("span", { className: "sr-only" }, closeLabel)
1752
1875
  )),
1753
1876
  /* @__PURE__ */ import_react13.default.createElement("div", { className: cn("overflow-y-auto flex-1", !hideHeader && "p-6") }, children)
1754
1877
  ));
@@ -1758,18 +1881,7 @@ var Modal = ({
1758
1881
  var import_date_fns13 = require("date-fns");
1759
1882
  var import_framer_motion2 = require("framer-motion");
1760
1883
  var import_lucide_react7 = require("lucide-react");
1761
- var REMINDER_OPTIONS = [
1762
- { value: 0, label: "At time of event" },
1763
- { value: 5, label: "5 minutes before" },
1764
- { value: 10, label: "10 minutes before" },
1765
- { value: 15, label: "15 minutes before" },
1766
- { value: 30, label: "30 minutes before" },
1767
- { value: 60, label: "1 hour before" },
1768
- { value: 120, label: "2 hours before" },
1769
- { value: 1440, label: "1 day before" },
1770
- { value: 2880, label: "2 days before" },
1771
- { value: 10080, label: "1 week before" }
1772
- ];
1884
+ var REMINDER_OPTIONS = [0, 5, 10, 15, 30, 60, 120, 1440, 2880, 10080];
1773
1885
  var COLOR_PALETTE = [
1774
1886
  "#3b82f6",
1775
1887
  // Blue
@@ -1792,6 +1904,30 @@ var COLOR_PALETTE = [
1792
1904
  "#6366f1"
1793
1905
  // Indigo
1794
1906
  ];
1907
+ var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1908
+ var mergeGuests = (current, added) => {
1909
+ const seen = new Set(current.map((email) => email.toLowerCase()));
1910
+ const merged = [...current];
1911
+ for (const email of added) {
1912
+ if (!seen.has(email.toLowerCase())) {
1913
+ seen.add(email.toLowerCase());
1914
+ merged.push(email);
1915
+ }
1916
+ }
1917
+ return merged;
1918
+ };
1919
+ var reminderLabel = (minutes, t) => {
1920
+ if (minutes === 0) return t.reminderAtTime;
1921
+ if (minutes === 10080) return t.reminderWeek;
1922
+ if (minutes === 1440) return t.reminderDay;
1923
+ if (minutes % 1440 === 0) return interpolate(t.reminderDays, { n: minutes / 1440 });
1924
+ if (minutes === 60) return t.reminderHour;
1925
+ if (minutes % 60 === 0) return interpolate(t.reminderHours, { n: minutes / 60 });
1926
+ return interpolate(t.reminderMinutes, { n: minutes });
1927
+ };
1928
+ var avatarHue = (email) => [...email].reduce((hash, char) => (hash * 31 + char.charCodeAt(0)) % 360, 7);
1929
+ var mapsUrl = (location) => `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(location)}`;
1930
+ var isSafeUrl = (url) => /^(https?:|mailto:|blob:|\/|\.\/|\.\.\/|[^:]*$)/i.test(url.trim());
1795
1931
  var EventModal = ({
1796
1932
  isOpen,
1797
1933
  onClose,
@@ -1799,9 +1935,15 @@ var EventModal = ({
1799
1935
  initialDate,
1800
1936
  onSave,
1801
1937
  onDelete,
1802
- calendars
1938
+ calendars,
1939
+ translations: t,
1940
+ locale
1803
1941
  }) => {
1804
1942
  const [mode, setMode] = (0, import_react14.useState)("create");
1943
+ const translationsRef = (0, import_react14.useRef)(t);
1944
+ (0, import_react14.useInsertionEffect)(() => {
1945
+ translationsRef.current = t;
1946
+ });
1805
1947
  const [formData, setFormData] = (0, import_react14.useState)({
1806
1948
  title: "",
1807
1949
  description: "",
@@ -1814,22 +1956,18 @@ var EventModal = ({
1814
1956
  type: void 0,
1815
1957
  recurrence: void 0,
1816
1958
  attachments: [],
1817
- reminders: [{ id: "1", type: "notification", time: 30, label: "30 minutes before" }]
1959
+ reminders: []
1818
1960
  });
1819
1961
  const [isCalendarDropdownOpen, setIsCalendarDropdownOpen] = (0, import_react14.useState)(false);
1820
1962
  const [isColorPickerOpen, setIsColorPickerOpen] = (0, import_react14.useState)(false);
1821
1963
  const [isReminderDropdownOpen, setIsReminderDropdownOpen] = (0, import_react14.useState)(false);
1822
- const [isGuestsDropdownOpen, setIsGuestsDropdownOpen] = (0, import_react14.useState)(false);
1823
- const fakeGuests = [
1824
- "alice@example.com",
1825
- "bob@example.com",
1826
- "charlie@example.com",
1827
- "diana@example.com",
1828
- "evan@example.com"
1829
- ];
1830
- const [selectedGuests, setSelectedGuests] = (0, import_react14.useState)([]);
1964
+ const [guestInput, setGuestInput] = (0, import_react14.useState)("");
1965
+ const [guestError, setGuestError] = (0, import_react14.useState)(false);
1831
1966
  (0, import_react14.useEffect)(() => {
1967
+ const defaultReminders = () => [{ id: "1", type: "notification", time: 30, label: reminderLabel(30, translationsRef.current) }];
1832
1968
  if (isOpen) {
1969
+ setGuestInput("");
1970
+ setGuestError(false);
1833
1971
  if (event) {
1834
1972
  setMode("view");
1835
1973
  setFormData({
@@ -1837,9 +1975,9 @@ var EventModal = ({
1837
1975
  start: new Date(event.start),
1838
1976
  end: new Date(event.end),
1839
1977
  attachments: event.attachments || [],
1840
- reminders: event.reminders || [{ id: "1", type: "notification", time: 30, label: "30 minutes before" }]
1978
+ reminders: event.reminders || defaultReminders(),
1979
+ guests: event.guests || []
1841
1980
  });
1842
- setSelectedGuests(event.guests || []);
1843
1981
  } else {
1844
1982
  setMode("create");
1845
1983
  const start = initialDate || /* @__PURE__ */ new Date();
@@ -1855,21 +1993,34 @@ var EventModal = ({
1855
1993
  color: "#3b82f6",
1856
1994
  calendarId: calendars?.[0]?.id,
1857
1995
  attachments: [],
1858
- reminders: [{ id: "1", type: "notification", time: 30, label: "30 minutes before" }]
1996
+ reminders: defaultReminders(),
1997
+ guests: []
1859
1998
  });
1860
- setSelectedGuests([]);
1861
1999
  }
1862
2000
  }
1863
2001
  }, [isOpen, event, initialDate, calendars]);
1864
- const toggleGuest = (email) => {
1865
- setSelectedGuests((prev) => {
1866
- const newGuests = prev.includes(email) ? prev.filter((e) => e !== email) : [...prev, email];
1867
- setFormData((curr) => ({ ...curr, guests: newGuests }));
1868
- return newGuests;
1869
- });
2002
+ const guests = formData.guests || [];
2003
+ const addGuests = (raw) => {
2004
+ const entries = raw.split(/[\s,;]+/).filter(Boolean);
2005
+ const valid = entries.filter((entry) => EMAIL.test(entry));
2006
+ const invalid = entries.filter((entry) => !EMAIL.test(entry));
2007
+ if (valid.length > 0) {
2008
+ setFormData((prev) => ({ ...prev, guests: mergeGuests(prev.guests || [], valid) }));
2009
+ }
2010
+ setGuestInput(invalid.join(", "));
2011
+ setGuestError(invalid.length > 0);
2012
+ return invalid.length === 0;
2013
+ };
2014
+ const removeGuest = (email) => {
2015
+ setFormData((prev) => ({ ...prev, guests: (prev.guests || []).filter((g) => g !== email) }));
1870
2016
  };
1871
2017
  const handleSubmit = (e) => {
1872
2018
  e.preventDefault();
2019
+ const pending = guestInput.split(/[\s,;]+/).filter(Boolean);
2020
+ if (pending.some((entry) => !EMAIL.test(entry))) {
2021
+ addGuests(guestInput);
2022
+ return;
2023
+ }
1873
2024
  let eventColor = formData.color;
1874
2025
  if (formData.calendarId && calendars) {
1875
2026
  const selectedCal = calendars.find((c) => c.id === formData.calendarId);
@@ -1877,8 +2028,10 @@ var EventModal = ({
1877
2028
  eventColor = selectedCal.color;
1878
2029
  }
1879
2030
  }
2031
+ const finalGuests = mergeGuests(guests, pending);
1880
2032
  onSave({
1881
2033
  ...formData,
2034
+ guests: finalGuests,
1882
2035
  color: eventColor,
1883
2036
  id: event?.id
1884
2037
  });
@@ -1912,12 +2065,12 @@ var EventModal = ({
1912
2065
  }
1913
2066
  }
1914
2067
  };
1915
- const addReminder = (minutes, label) => {
2068
+ const addReminder = (minutes) => {
1916
2069
  const newReminder = {
1917
- id: Math.random().toString(36).substr(2, 9),
2070
+ id: Math.random().toString(36).slice(2, 11),
1918
2071
  type: "notification",
1919
2072
  time: minutes,
1920
- label
2073
+ label: reminderLabel(minutes, t)
1921
2074
  };
1922
2075
  setFormData((prev) => ({
1923
2076
  ...prev,
@@ -1931,23 +2084,20 @@ var EventModal = ({
1931
2084
  reminders: prev.reminders?.filter((r) => r.id !== id)
1932
2085
  }));
1933
2086
  };
1934
- const handleDownload = (attachment) => {
1935
- const element = document.createElement("a");
1936
- const file = new Blob(["Dummy content for " + attachment.name], { type: "text/plain" });
1937
- element.href = URL.createObjectURL(file);
1938
- element.download = attachment.name;
1939
- document.body.appendChild(element);
1940
- element.click();
1941
- document.body.removeChild(element);
1942
- };
1943
2087
  const getDurationText = () => {
1944
2088
  if (!formData.start || !formData.end) return "";
1945
2089
  const mins = (0, import_date_fns13.differenceInMinutes)(formData.end, formData.start);
1946
- if (mins < 60) return `${mins} min`;
2090
+ if (mins < 60) return interpolate(t.durationMinutes, { n: mins });
1947
2091
  const hours = Math.floor(mins / 60);
1948
2092
  const remainingMins = mins % 60;
1949
- if (remainingMins === 0) return `${hours} hr`;
1950
- return `${hours} hr ${remainingMins} min`;
2093
+ if (remainingMins === 0) return interpolate(t.durationHours, { n: hours });
2094
+ return interpolate(t.durationHoursMinutes, { h: hours, m: remainingMins });
2095
+ };
2096
+ const { time: timeFormat } = timeFormats(locale);
2097
+ const renderAttachment = (att) => {
2098
+ const content = /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, null, /* @__PURE__ */ import_react14.default.createElement("div", { className: "p-2 bg-primary/10 rounded-lg" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.File, { className: "w-4 h-4 text-primary" })), /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ import_react14.default.createElement("p", { className: "text-sm font-medium text-foreground truncate" }, att.name), /* @__PURE__ */ import_react14.default.createElement("p", { className: "text-xs text-muted-foreground" }, att.size)), att.url && /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Download, { className: "w-4 h-4 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" }));
2099
+ const className = "flex items-center gap-3 p-3 rounded-xl border border-border/30 bg-muted/20 transition-all group";
2100
+ return att.url && isSafeUrl(att.url) ? /* @__PURE__ */ import_react14.default.createElement("a", { key: att.id, href: att.url, target: "_blank", rel: "noopener noreferrer", download: att.name, className: cn(className, "hover:bg-muted/40 cursor-pointer") }, content) : /* @__PURE__ */ import_react14.default.createElement("div", { key: att.id, className }, content);
1951
2101
  };
1952
2102
  const renderViewMode = () => /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex flex-col bg-background overflow-hidden w-full max-h-[90vh]" }, /* @__PURE__ */ import_react14.default.createElement(
1953
2103
  "div",
@@ -1963,12 +2113,13 @@ var EventModal = ({
1963
2113
  className: "w-3 h-3 rounded-full",
1964
2114
  style: { backgroundColor: event?.color || "#3b82f6" }
1965
2115
  }
1966
- ), /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider" }, calendars?.find((c) => c.id === event?.calendarId)?.label || "Event")), /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ import_react14.default.createElement(
2116
+ ), /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider" }, calendars?.find((c) => c.id === event?.calendarId)?.label || t.event)), /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ import_react14.default.createElement(
1967
2117
  "button",
1968
2118
  {
1969
2119
  onClick: () => setMode("edit"),
1970
2120
  className: "p-2 hover:bg-background/80 rounded-lg transition-all text-muted-foreground hover:text-foreground",
1971
- title: "Edit"
2121
+ title: t.edit,
2122
+ "aria-label": t.edit
1972
2123
  },
1973
2124
  /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Edit2, { className: "w-4 h-4" })
1974
2125
  ), /* @__PURE__ */ import_react14.default.createElement(
@@ -1976,7 +2127,8 @@ var EventModal = ({
1976
2127
  {
1977
2128
  onClick: handleDelete,
1978
2129
  className: "p-2 hover:bg-destructive/10 rounded-lg transition-all text-muted-foreground hover:text-destructive",
1979
- title: "Delete"
2130
+ title: t.delete,
2131
+ "aria-label": t.delete
1980
2132
  },
1981
2133
  /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Trash2, { className: "w-4 h-4" })
1982
2134
  ), /* @__PURE__ */ import_react14.default.createElement(
@@ -1984,21 +2136,25 @@ var EventModal = ({
1984
2136
  {
1985
2137
  onClick: onClose,
1986
2138
  className: "p-2 hover:bg-background/80 rounded-lg transition-all text-muted-foreground hover:text-foreground",
1987
- title: "Close"
2139
+ title: t.close,
2140
+ "aria-label": t.close
1988
2141
  },
1989
2142
  /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.X, { className: "w-4 h-4" })
1990
2143
  ))),
1991
- /* @__PURE__ */ import_react14.default.createElement("h1", { className: "text-2xl font-bold text-foreground mb-3 leading-tight" }, event?.title || "(No title)"),
1992
- /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-3 text-sm" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-2 px-3 py-1.5 bg-background/60 backdrop-blur-sm rounded-lg border border-border/30" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Calendar, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "font-medium" }, event?.start && (0, import_date_fns13.format)(new Date(event.start), "EEE, MMM d"))), /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-2 px-3 py-1.5 bg-background/60 backdrop-blur-sm rounded-lg border border-border/30" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Clock, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "font-medium" }, event?.start && (0, import_date_fns13.format)(new Date(event.start), "h:mm a"), " \u2013 ", event?.end && (0, import_date_fns13.format)(new Date(event.end), "h:mm a"))))
2144
+ /* @__PURE__ */ import_react14.default.createElement("h1", { className: "text-2xl font-bold text-foreground mb-3 leading-tight" }, event?.title || t.noTitle),
2145
+ /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-3 text-sm" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-2 px-3 py-1.5 bg-background/60 backdrop-blur-sm rounded-lg border border-border/30" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Calendar, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "font-medium" }, event?.start && (0, import_date_fns13.format)(new Date(event.start), "EEE, MMM d", { locale }))), /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-2 px-3 py-1.5 bg-background/60 backdrop-blur-sm rounded-lg border border-border/30" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Clock, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "font-medium" }, event?.start && (0, import_date_fns13.format)(new Date(event.start), timeFormat, { locale }), " \u2013 ", event?.end && (0, import_date_fns13.format)(new Date(event.end), timeFormat, { locale }))))
1993
2146
  ), /* @__PURE__ */ import_react14.default.createElement("div", { className: "px-6 pb-6 space-y-4 overflow-y-auto flex-1" }, event?.location && /* @__PURE__ */ import_react14.default.createElement(
1994
- import_framer_motion2.motion.div,
2147
+ import_framer_motion2.motion.a,
1995
2148
  {
2149
+ href: mapsUrl(event.location),
2150
+ target: "_blank",
2151
+ rel: "noopener noreferrer",
1996
2152
  initial: { opacity: 0, y: 10 },
1997
2153
  animate: { opacity: 1, y: 0 },
1998
2154
  className: "flex items-start gap-3 p-4 rounded-2xl bg-muted/30 border border-border/30 group hover:bg-muted/50 transition-all cursor-pointer"
1999
2155
  },
2000
2156
  /* @__PURE__ */ import_react14.default.createElement("div", { className: "p-2 bg-primary/10 rounded-xl" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.MapPin, { className: "w-5 h-5 text-primary" })),
2001
- /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex-1" }, /* @__PURE__ */ import_react14.default.createElement("p", { className: "text-sm font-medium text-foreground" }, event.location), /* @__PURE__ */ import_react14.default.createElement("p", { className: "text-xs text-muted-foreground mt-0.5" }, "Click to open in maps")),
2157
+ /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex-1" }, /* @__PURE__ */ import_react14.default.createElement("p", { className: "text-sm font-medium text-foreground" }, event.location), /* @__PURE__ */ import_react14.default.createElement("p", { className: "text-xs text-muted-foreground mt-0.5" }, t.openInMaps)),
2002
2158
  /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.ExternalLink, { className: "w-4 h-4 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" })
2003
2159
  ), event?.guests && event.guests.length > 0 && /* @__PURE__ */ import_react14.default.createElement(
2004
2160
  import_framer_motion2.motion.div,
@@ -2008,18 +2164,18 @@ var EventModal = ({
2008
2164
  transition: { delay: 0.05 },
2009
2165
  className: "space-y-3"
2010
2166
  },
2011
- /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Users, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-sm font-medium text-foreground" }, event.guests.length, " ", event.guests.length > 1 ? "Guests" : "Guest"))),
2012
- /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex flex-wrap gap-2" }, event.guests.map((email, index) => /* @__PURE__ */ import_react14.default.createElement(
2167
+ /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Users, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-sm font-medium text-foreground" }, event.guests.length, " ", event.guests.length > 1 ? t.guests : t.guestSingular))),
2168
+ /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex flex-wrap gap-2" }, event.guests.map((email) => /* @__PURE__ */ import_react14.default.createElement(
2013
2169
  "div",
2014
2170
  {
2015
- key: index,
2016
- 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"
2171
+ key: email,
2172
+ className: "flex items-center gap-2 px-3 py-2 rounded-xl bg-muted/30 border border-border/30"
2017
2173
  },
2018
2174
  /* @__PURE__ */ import_react14.default.createElement(
2019
2175
  "div",
2020
2176
  {
2021
2177
  className: "w-8 h-8 rounded-full flex items-center justify-center text-sm font-semibold text-white",
2022
- style: { backgroundColor: `hsl(${index * 60}, 70%, 50%)` }
2178
+ style: { backgroundColor: `hsl(${avatarHue(email)}, 70%, 50%)` }
2023
2179
  },
2024
2180
  email[0].toUpperCase()
2025
2181
  ),
@@ -2040,7 +2196,7 @@ var EventModal = ({
2040
2196
  key: reminder.id,
2041
2197
  className: "text-xs font-medium px-2.5 py-1 rounded-full bg-amber-500/10 text-amber-600 dark:text-amber-400"
2042
2198
  },
2043
- reminder.label || `${reminder.time} min before`
2199
+ reminder.label || reminderLabel(reminder.time, t)
2044
2200
  )))
2045
2201
  ), event?.description && /* @__PURE__ */ import_react14.default.createElement(
2046
2202
  import_framer_motion2.motion.div,
@@ -2050,7 +2206,7 @@ var EventModal = ({
2050
2206
  transition: { delay: 0.15 },
2051
2207
  className: "space-y-2"
2052
2208
  },
2053
- /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.AlignLeft, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-sm font-medium text-foreground" }, "Notes")),
2209
+ /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.AlignLeft, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-sm font-medium text-foreground" }, t.notesLabel)),
2054
2210
  /* @__PURE__ */ import_react14.default.createElement("div", { className: "text-sm text-foreground/80 whitespace-pre-wrap leading-relaxed pl-6 p-3 rounded-xl bg-muted/20 border border-border/30" }, event.description)
2055
2211
  ), event?.attachments && event.attachments.length > 0 && /* @__PURE__ */ import_react14.default.createElement(
2056
2212
  import_framer_motion2.motion.div,
@@ -2060,25 +2216,16 @@ var EventModal = ({
2060
2216
  transition: { delay: 0.2 },
2061
2217
  className: "space-y-2"
2062
2218
  },
2063
- /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Paperclip, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-sm font-medium text-foreground" }, "Attachments")),
2064
- /* @__PURE__ */ import_react14.default.createElement("div", { className: "grid gap-2" }, event.attachments.map((att) => /* @__PURE__ */ import_react14.default.createElement(
2065
- "div",
2066
- {
2067
- key: att.id,
2068
- onClick: () => handleDownload(att),
2069
- 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"
2070
- },
2071
- /* @__PURE__ */ import_react14.default.createElement("div", { className: "p-2 bg-primary/10 rounded-lg" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.File, { className: "w-4 h-4 text-primary" })),
2072
- /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ import_react14.default.createElement("p", { className: "text-sm font-medium text-foreground truncate" }, att.name), /* @__PURE__ */ import_react14.default.createElement("p", { className: "text-xs text-muted-foreground" }, att.size)),
2073
- /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Download, { className: "w-4 h-4 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" })
2074
- )))
2219
+ /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Paperclip, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-sm font-medium text-foreground" }, t.attachments)),
2220
+ /* @__PURE__ */ import_react14.default.createElement("div", { className: "grid gap-2" }, event.attachments.map(renderAttachment))
2075
2221
  )));
2076
2222
  const renderEditMode = () => /* @__PURE__ */ import_react14.default.createElement("form", { onSubmit: handleSubmit, className: "flex flex-col bg-background overflow-hidden w-full max-h-[85vh]" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center justify-between px-5 py-4 border-b border-border/50 shrink-0" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ import_react14.default.createElement(
2077
2223
  "button",
2078
2224
  {
2079
2225
  type: "button",
2080
2226
  onClick: () => setIsColorPickerOpen(!isColorPickerOpen),
2081
- className: "relative p-1"
2227
+ className: "relative p-1",
2228
+ "aria-label": t.eventColor
2082
2229
  },
2083
2230
  /* @__PURE__ */ import_react14.default.createElement(
2084
2231
  "div",
@@ -2095,6 +2242,7 @@ var EventModal = ({
2095
2242
  {
2096
2243
  key: color,
2097
2244
  type: "button",
2245
+ "aria-label": color,
2098
2246
  onClick: () => {
2099
2247
  setFormData((prev) => ({ ...prev, color }));
2100
2248
  setIsColorPickerOpen(false);
@@ -2106,12 +2254,13 @@ var EventModal = ({
2106
2254
  style: { backgroundColor: color, color }
2107
2255
  }
2108
2256
  )))
2109
- ), /* @__PURE__ */ import_react14.default.createElement("h2", { className: "text-lg font-semibold text-foreground" }, mode === "edit" ? "Edit Event" : "New Event")), /* @__PURE__ */ import_react14.default.createElement(
2257
+ ), /* @__PURE__ */ import_react14.default.createElement("h2", { className: "text-lg font-semibold text-foreground" }, mode === "edit" ? t.editEvent : t.newEvent)), /* @__PURE__ */ import_react14.default.createElement(
2110
2258
  "button",
2111
2259
  {
2112
2260
  type: "button",
2113
2261
  onClick: onClose,
2114
- className: "p-2 hover:bg-accent rounded-xl transition-all text-muted-foreground hover:text-foreground"
2262
+ className: "p-2 hover:bg-accent rounded-xl transition-all text-muted-foreground hover:text-foreground",
2263
+ "aria-label": t.close
2115
2264
  },
2116
2265
  /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.X, { className: "w-4 h-4" })
2117
2266
  )), /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex-1 overflow-y-auto" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "px-5 py-4 space-y-5" }, /* @__PURE__ */ import_react14.default.createElement("div", null, /* @__PURE__ */ import_react14.default.createElement(
@@ -2123,25 +2272,28 @@ var EventModal = ({
2123
2272
  className: "w-full text-xl font-semibold border-0 border-b-2 border-transparent focus:border-primary bg-transparent px-0 py-2 placeholder-muted-foreground/50 transition-all text-foreground focus:outline-none",
2124
2273
  value: formData.title,
2125
2274
  onChange: (e) => setFormData({ ...formData, title: e.target.value }),
2126
- placeholder: "Add title"
2275
+ placeholder: t.title,
2276
+ "aria-label": t.title
2127
2277
  }
2128
- )), /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-3 p-4 rounded-2xl bg-muted/20 border border-border/30" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "p-2 bg-primary/10 rounded-xl shrink-0" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Clock, { className: "w-5 h-5 text-primary" })), /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex-1 grid grid-cols-2 gap-3" }, /* @__PURE__ */ import_react14.default.createElement("div", null, /* @__PURE__ */ import_react14.default.createElement("label", { className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-wider mb-1 block" }, "Start"), /* @__PURE__ */ import_react14.default.createElement(
2278
+ )), /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-3 p-4 rounded-2xl bg-muted/20 border border-border/30" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "p-2 bg-primary/10 rounded-xl shrink-0" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Clock, { className: "w-5 h-5 text-primary" })), /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex-1 grid grid-cols-2 gap-3" }, /* @__PURE__ */ import_react14.default.createElement("div", null, /* @__PURE__ */ import_react14.default.createElement("label", { htmlFor: "calendarkit-event-start", className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-wider mb-1 block" }, t.start), /* @__PURE__ */ import_react14.default.createElement(
2129
2279
  "input",
2130
2280
  {
2281
+ id: "calendarkit-event-start",
2131
2282
  type: "datetime-local",
2132
2283
  className: "w-full bg-background border border-border/50 rounded-xl px-3 py-2 text-sm font-medium text-foreground focus:border-primary focus:ring-2 focus:ring-primary/20 transition-all",
2133
2284
  value: formatDateForInput(formData.start),
2134
2285
  onChange: (e) => handleDateChange("start", e.target.value)
2135
2286
  }
2136
- )), /* @__PURE__ */ import_react14.default.createElement("div", null, /* @__PURE__ */ import_react14.default.createElement("label", { className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-wider mb-1 block" }, "End"), /* @__PURE__ */ import_react14.default.createElement(
2287
+ )), /* @__PURE__ */ import_react14.default.createElement("div", null, /* @__PURE__ */ import_react14.default.createElement("label", { htmlFor: "calendarkit-event-end", className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-wider mb-1 block" }, t.end), /* @__PURE__ */ import_react14.default.createElement(
2137
2288
  "input",
2138
2289
  {
2290
+ id: "calendarkit-event-end",
2139
2291
  type: "datetime-local",
2140
2292
  className: "w-full bg-background border border-border/50 rounded-xl px-3 py-2 text-sm font-medium text-foreground focus:border-primary focus:ring-2 focus:ring-primary/20 transition-all",
2141
2293
  value: formatDateForInput(formData.end),
2142
2294
  onChange: (e) => handleDateChange("end", e.target.value)
2143
2295
  }
2144
- )))), getDurationText() && /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex justify-center" }, /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-xs font-medium px-3 py-1 rounded-full bg-primary/10 text-primary" }, getDurationText())), /* @__PURE__ */ import_react14.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Bell, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-sm font-medium text-foreground" }, "Reminders")), /* @__PURE__ */ import_react14.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react14.default.createElement(
2296
+ )))), getDurationText() && /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex justify-center" }, /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-xs font-medium px-3 py-1 rounded-full bg-primary/10 text-primary" }, getDurationText())), /* @__PURE__ */ import_react14.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Bell, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-sm font-medium text-foreground" }, t.reminders)), /* @__PURE__ */ import_react14.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react14.default.createElement(
2145
2297
  "button",
2146
2298
  {
2147
2299
  type: "button",
@@ -2149,16 +2301,16 @@ var EventModal = ({
2149
2301
  className: "flex items-center gap-1 text-xs font-medium text-primary hover:text-primary/80 transition-colors"
2150
2302
  },
2151
2303
  /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Plus, { className: "w-3.5 h-3.5" }),
2152
- "Add"
2153
- ), isReminderDropdownOpen && /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, null, /* @__PURE__ */ import_react14.default.createElement("div", { className: "fixed inset-0 z-40", onClick: () => setIsReminderDropdownOpen(false) }), /* @__PURE__ */ import_react14.default.createElement("div", { className: "absolute right-0 top-full mt-1 w-48 bg-background border border-border rounded-xl shadow-xl z-50 py-1 max-h-60 overflow-y-auto" }, REMINDER_OPTIONS.map((option) => /* @__PURE__ */ import_react14.default.createElement(
2304
+ t.add
2305
+ ), isReminderDropdownOpen && /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, null, /* @__PURE__ */ import_react14.default.createElement("div", { className: "fixed inset-0 z-40", onClick: () => setIsReminderDropdownOpen(false) }), /* @__PURE__ */ import_react14.default.createElement("div", { className: "absolute right-0 top-full mt-1 w-48 bg-background border border-border rounded-xl shadow-xl z-50 py-1 max-h-60 overflow-y-auto" }, REMINDER_OPTIONS.map((minutes) => /* @__PURE__ */ import_react14.default.createElement(
2154
2306
  "button",
2155
2307
  {
2156
- key: option.value,
2308
+ key: minutes,
2157
2309
  type: "button",
2158
- onClick: () => addReminder(option.value, option.label),
2310
+ onClick: () => addReminder(minutes),
2159
2311
  className: "w-full px-3 py-2 text-left text-sm hover:bg-accent transition-colors"
2160
2312
  },
2161
- option.label
2313
+ reminderLabel(minutes, t)
2162
2314
  )))))), formData.reminders && formData.reminders.length > 0 && /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex flex-wrap gap-2" }, formData.reminders.map((reminder) => /* @__PURE__ */ import_react14.default.createElement(
2163
2315
  "div",
2164
2316
  {
@@ -2166,45 +2318,44 @@ var EventModal = ({
2166
2318
  className: "flex items-center gap-2 px-3 py-1.5 rounded-full bg-amber-500/10 border border-amber-500/20"
2167
2319
  },
2168
2320
  /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.BellRing, { className: "w-3.5 h-3.5 text-amber-500" }),
2169
- /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-xs font-medium text-amber-600 dark:text-amber-400" }, reminder.label),
2321
+ /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-xs font-medium text-amber-600 dark:text-amber-400" }, reminder.label || reminderLabel(reminder.time, t)),
2170
2322
  /* @__PURE__ */ import_react14.default.createElement(
2171
2323
  "button",
2172
2324
  {
2173
2325
  type: "button",
2174
2326
  onClick: () => removeReminder(reminder.id),
2175
- className: "p-0.5 hover:bg-amber-500/20 rounded-full transition-colors"
2327
+ className: "p-0.5 hover:bg-amber-500/20 rounded-full transition-colors",
2328
+ "aria-label": t.removeReminder
2176
2329
  },
2177
2330
  /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.X, { className: "w-3 h-3 text-amber-500" })
2178
2331
  )
2179
- )))), /* @__PURE__ */ import_react14.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Users, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-sm font-medium text-foreground" }, "Guests")), /* @__PURE__ */ import_react14.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react14.default.createElement(
2180
- "button",
2181
- {
2182
- type: "button",
2183
- onClick: () => setIsGuestsDropdownOpen(!isGuestsDropdownOpen),
2184
- 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"
2185
- },
2186
- /* @__PURE__ */ import_react14.default.createElement("span", { className: selectedGuests.length > 0 ? "text-foreground font-medium" : "text-muted-foreground" }, selectedGuests.length > 0 ? `${selectedGuests.length} guest${selectedGuests.length > 1 ? "s" : ""} added` : "Add guests"),
2187
- /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.ChevronDown, { className: cn("w-4 h-4 text-muted-foreground transition-transform", isGuestsDropdownOpen && "rotate-180") })
2188
- ), isGuestsDropdownOpen && /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, null, /* @__PURE__ */ import_react14.default.createElement("div", { className: "fixed inset-0 z-40", onClick: () => setIsGuestsDropdownOpen(false) }), /* @__PURE__ */ import_react14.default.createElement("div", { className: "absolute z-50 w-full mt-2 bg-background border border-border rounded-xl shadow-xl max-h-48 overflow-y-auto" }, fakeGuests.map((email) => /* @__PURE__ */ import_react14.default.createElement(
2189
- "div",
2332
+ )))), /* @__PURE__ */ import_react14.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Users, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ import_react14.default.createElement("label", { htmlFor: "calendarkit-event-guest", className: "text-sm font-medium text-foreground" }, t.guests)), /* @__PURE__ */ import_react14.default.createElement(
2333
+ "input",
2190
2334
  {
2191
- key: email,
2192
- className: "px-4 py-3 hover:bg-accent cursor-pointer flex items-center gap-3 transition-colors",
2193
- onClick: () => toggleGuest(email)
2194
- },
2195
- /* @__PURE__ */ import_react14.default.createElement("div", { className: cn(
2196
- "w-5 h-5 rounded-md border-2 flex items-center justify-center transition-all",
2197
- selectedGuests.includes(email) ? "bg-primary border-primary" : "border-border"
2198
- ) }, selectedGuests.includes(email) && /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Check, { className: "w-3 h-3 text-primary-foreground" })),
2199
- /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react14.default.createElement(
2200
- "div",
2201
- {
2202
- className: "w-7 h-7 rounded-full flex items-center justify-center text-xs font-semibold text-white",
2203
- style: { backgroundColor: `hsl(${fakeGuests.indexOf(email) * 60}, 70%, 50%)` }
2335
+ id: "calendarkit-event-guest",
2336
+ type: "text",
2337
+ inputMode: "email",
2338
+ autoComplete: "email",
2339
+ value: guestInput,
2340
+ onChange: (e) => {
2341
+ setGuestInput(e.target.value);
2342
+ setGuestError(false);
2204
2343
  },
2205
- email[0].toUpperCase()
2206
- ), /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-sm" }, email))
2207
- ))))), selectedGuests.length > 0 && /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex flex-wrap gap-2 mt-2" }, selectedGuests.map((email) => /* @__PURE__ */ import_react14.default.createElement(
2344
+ onKeyDown: (e) => {
2345
+ if (e.key === "Enter" || e.key === ",") {
2346
+ e.preventDefault();
2347
+ addGuests(guestInput);
2348
+ }
2349
+ },
2350
+ onBlur: () => guestInput.trim() && addGuests(guestInput),
2351
+ placeholder: t.addGuestPlaceholder,
2352
+ "aria-invalid": guestError,
2353
+ className: cn(
2354
+ "w-full px-4 py-3 bg-muted/20 border rounded-xl text-sm text-foreground placeholder-muted-foreground focus:outline-none focus:border-primary transition-all",
2355
+ guestError ? "border-destructive" : "border-border/30"
2356
+ )
2357
+ }
2358
+ ), guestError && /* @__PURE__ */ import_react14.default.createElement("p", { role: "alert", className: "text-xs text-destructive" }, t.invalidEmail), guests.length > 0 && /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex flex-wrap gap-2 mt-2" }, guests.map((email) => /* @__PURE__ */ import_react14.default.createElement(
2208
2359
  "div",
2209
2360
  {
2210
2361
  key: email,
@@ -2214,17 +2365,18 @@ var EventModal = ({
2214
2365
  "div",
2215
2366
  {
2216
2367
  className: "w-6 h-6 rounded-full flex items-center justify-center text-xs font-semibold text-white",
2217
- style: { backgroundColor: `hsl(${fakeGuests.indexOf(email) * 60}, 70%, 50%)` }
2368
+ style: { backgroundColor: `hsl(${avatarHue(email)}, 70%, 50%)` }
2218
2369
  },
2219
2370
  email[0].toUpperCase()
2220
2371
  ),
2221
- /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-xs font-medium text-foreground" }, email.split("@")[0]),
2372
+ /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-xs font-medium text-foreground" }, email),
2222
2373
  /* @__PURE__ */ import_react14.default.createElement(
2223
2374
  "button",
2224
2375
  {
2225
2376
  type: "button",
2226
- onClick: () => toggleGuest(email),
2227
- className: "p-0.5 hover:bg-destructive/10 rounded-full transition-colors"
2377
+ onClick: () => removeGuest(email),
2378
+ className: "p-0.5 hover:bg-destructive/10 rounded-full transition-colors",
2379
+ "aria-label": interpolate(t.removeGuest, { email })
2228
2380
  },
2229
2381
  /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.X, { className: "w-3 h-3 text-muted-foreground" })
2230
2382
  )
@@ -2233,15 +2385,17 @@ var EventModal = ({
2233
2385
  {
2234
2386
  type: "text",
2235
2387
  className: "flex-1 bg-transparent text-sm text-foreground placeholder-muted-foreground focus:outline-none",
2236
- placeholder: "Add location",
2388
+ placeholder: t.addLocation,
2389
+ "aria-label": t.addLocation,
2237
2390
  value: formData.location || "",
2238
2391
  onChange: (e) => setFormData({ ...formData, location: e.target.value })
2239
2392
  }
2240
- )), /* @__PURE__ */ import_react14.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.AlignLeft, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-sm font-medium text-foreground" }, "Notes")), /* @__PURE__ */ import_react14.default.createElement(
2393
+ )), /* @__PURE__ */ import_react14.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.AlignLeft, { className: "w-4 h-4 text-muted-foreground" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-sm font-medium text-foreground" }, t.notesLabel)), /* @__PURE__ */ import_react14.default.createElement(
2241
2394
  "textarea",
2242
2395
  {
2243
2396
  className: "w-full bg-muted/20 border border-border/30 rounded-xl px-4 py-3 text-sm min-h-[100px] resize-none text-foreground placeholder-muted-foreground focus:border-primary focus:ring-2 focus:ring-primary/20 transition-all",
2244
- placeholder: "Add description or notes...",
2397
+ placeholder: t.addNotes,
2398
+ "aria-label": t.notesLabel,
2245
2399
  value: formData.description || "",
2246
2400
  onChange: (e) => setFormData({ ...formData, description: e.target.value })
2247
2401
  }
@@ -2249,6 +2403,7 @@ var EventModal = ({
2249
2403
  "select",
2250
2404
  {
2251
2405
  className: "flex-1 bg-transparent text-sm text-foreground focus:outline-none cursor-pointer",
2406
+ "aria-label": t.repeat,
2252
2407
  value: formData.recurrence?.freq || "",
2253
2408
  onChange: (e) => {
2254
2409
  if (e.target.value === "") {
@@ -2264,12 +2419,12 @@ var EventModal = ({
2264
2419
  }
2265
2420
  }
2266
2421
  },
2267
- /* @__PURE__ */ import_react14.default.createElement("option", { value: "" }, "Does not repeat"),
2268
- /* @__PURE__ */ import_react14.default.createElement("option", { value: "DAILY" }, "Daily"),
2269
- /* @__PURE__ */ import_react14.default.createElement("option", { value: "WEEKLY" }, "Weekly"),
2270
- /* @__PURE__ */ import_react14.default.createElement("option", { value: "MONTHLY" }, "Monthly"),
2271
- /* @__PURE__ */ import_react14.default.createElement("option", { value: "YEARLY" }, "Yearly")
2272
- )), /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center justify-between p-3 rounded-xl bg-muted/20 border border-border/30" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Tag, { className: "w-5 h-5 text-muted-foreground" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-sm text-foreground" }, "Calendar")), /* @__PURE__ */ import_react14.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react14.default.createElement(
2422
+ /* @__PURE__ */ import_react14.default.createElement("option", { value: "" }, t.doesNotRepeat),
2423
+ /* @__PURE__ */ import_react14.default.createElement("option", { value: "DAILY" }, t.daily),
2424
+ /* @__PURE__ */ import_react14.default.createElement("option", { value: "WEEKLY" }, t.weekly),
2425
+ /* @__PURE__ */ import_react14.default.createElement("option", { value: "MONTHLY" }, t.monthly),
2426
+ /* @__PURE__ */ import_react14.default.createElement("option", { value: "YEARLY" }, t.yearly)
2427
+ )), calendars && calendars.length > 0 && /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center justify-between p-3 rounded-xl bg-muted/20 border border-border/30" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Tag, { className: "w-5 h-5 text-muted-foreground" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "text-sm text-foreground" }, t.calendar)), /* @__PURE__ */ import_react14.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react14.default.createElement(
2273
2428
  "button",
2274
2429
  {
2275
2430
  type: "button",
@@ -2280,12 +2435,12 @@ var EventModal = ({
2280
2435
  "div",
2281
2436
  {
2282
2437
  className: "w-3 h-3 rounded-full",
2283
- style: { backgroundColor: calendars?.find((c) => c.id === formData.calendarId)?.color || "#3b82f6" }
2438
+ style: { backgroundColor: calendars.find((c) => c.id === formData.calendarId)?.color || "#3b82f6" }
2284
2439
  }
2285
2440
  ),
2286
- /* @__PURE__ */ import_react14.default.createElement("span", { className: "font-medium" }, calendars?.find((c) => c.id === formData.calendarId)?.label || "Select"),
2441
+ /* @__PURE__ */ import_react14.default.createElement("span", { className: "font-medium" }, calendars.find((c) => c.id === formData.calendarId)?.label || t.select),
2287
2442
  /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.ChevronDown, { className: "w-3.5 h-3.5 text-muted-foreground" })
2288
- ), isCalendarDropdownOpen && /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, null, /* @__PURE__ */ import_react14.default.createElement("div", { className: "fixed inset-0 z-40", onClick: () => setIsCalendarDropdownOpen(false) }), /* @__PURE__ */ import_react14.default.createElement("div", { className: "absolute right-0 bottom-full mb-2 w-48 bg-background border border-border rounded-xl shadow-xl z-50 py-1" }, calendars?.map((cal) => /* @__PURE__ */ import_react14.default.createElement(
2443
+ ), isCalendarDropdownOpen && /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, null, /* @__PURE__ */ import_react14.default.createElement("div", { className: "fixed inset-0 z-40", onClick: () => setIsCalendarDropdownOpen(false) }), /* @__PURE__ */ import_react14.default.createElement("div", { className: "absolute right-0 bottom-full mb-2 w-48 bg-background border border-border rounded-xl shadow-xl z-50 py-1" }, calendars.map((cal) => /* @__PURE__ */ import_react14.default.createElement(
2289
2444
  "button",
2290
2445
  {
2291
2446
  key: cal.id,
@@ -2311,7 +2466,7 @@ var EventModal = ({
2311
2466
  className: "flex items-center gap-2 px-4 py-2 text-destructive hover:bg-destructive/10 rounded-xl transition-all text-sm font-medium"
2312
2467
  },
2313
2468
  /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Trash2, { className: "w-4 h-4" }),
2314
- "Delete"
2469
+ t.delete
2315
2470
  ), mode === "create" && /* @__PURE__ */ import_react14.default.createElement("div", null), /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react14.default.createElement(
2316
2471
  "button",
2317
2472
  {
@@ -2319,14 +2474,14 @@ var EventModal = ({
2319
2474
  onClick: onClose,
2320
2475
  className: "px-4 py-2.5 text-muted-foreground hover:text-foreground font-medium text-sm rounded-xl transition-all hover:bg-accent"
2321
2476
  },
2322
- "Cancel"
2477
+ t.cancel
2323
2478
  ), /* @__PURE__ */ import_react14.default.createElement(
2324
2479
  "button",
2325
2480
  {
2326
2481
  type: "submit",
2327
2482
  className: "px-6 py-2.5 bg-primary hover:bg-primary/90 text-primary-foreground font-semibold text-sm rounded-xl shadow-md transition-all hover:shadow-lg hover:shadow-primary/20 active:scale-95"
2328
2483
  },
2329
- mode === "edit" ? "Save Changes" : "Create Event"
2484
+ mode === "edit" ? t.saveChanges : t.createEvent
2330
2485
  ))));
2331
2486
  return /* @__PURE__ */ import_react14.default.createElement(Modal, { isOpen, onClose, hideHeader: true, className: "p-0 overflow-hidden max-w-lg rounded-2xl shadow-2xl" }, /* @__PURE__ */ import_react14.default.createElement(import_framer_motion2.AnimatePresence, { mode: "wait" }, mode === "view" && event ? /* @__PURE__ */ import_react14.default.createElement(
2332
2487
  import_framer_motion2.motion.div,
@@ -2354,7 +2509,7 @@ var EventModal = ({
2354
2509
  // src/components/Skeleton.tsx
2355
2510
  var import_react15 = __toESM(require("react"));
2356
2511
  var Skeleton = ({ className, style }) => /* @__PURE__ */ import_react15.default.createElement("div", { className: cn("animate-pulse bg-muted/40 rounded", className), style });
2357
- var MonthViewSkeleton = () => /* @__PURE__ */ import_react15.default.createElement("div", { className: "h-full bg-background border border-border/50 rounded-2xl overflow-hidden" }, /* @__PURE__ */ import_react15.default.createElement("div", { className: "grid grid-cols-7 border-b border-border/50 bg-muted/10" }, Array.from({ length: 7 }).map((_, i) => /* @__PURE__ */ import_react15.default.createElement("div", { key: i, className: "py-3 px-2 text-center border-r border-border/30 last:border-r-0" }, /* @__PURE__ */ import_react15.default.createElement(Skeleton, { className: "h-4 w-8 mx-auto" })))), /* @__PURE__ */ import_react15.default.createElement("div", { className: "grid grid-cols-7 flex-1" }, Array.from({ length: 35 }).map((_, i) => /* @__PURE__ */ import_react15.default.createElement("div", { key: i, className: "min-h-[120px] border-r border-b border-border/30 p-2" }, /* @__PURE__ */ import_react15.default.createElement(Skeleton, { className: "h-4 w-6 mb-2" }), /* @__PURE__ */ import_react15.default.createElement("div", { className: "space-y-1" }, Math.random() > 0.5 && /* @__PURE__ */ import_react15.default.createElement(Skeleton, { className: "h-5 w-full rounded-md" }), Math.random() > 0.7 && /* @__PURE__ */ import_react15.default.createElement(Skeleton, { className: "h-5 w-3/4 rounded-md" }))))));
2512
+ var MonthViewSkeleton = () => /* @__PURE__ */ import_react15.default.createElement("div", { className: "h-full bg-background border border-border/50 rounded-2xl overflow-hidden" }, /* @__PURE__ */ import_react15.default.createElement("div", { className: "grid grid-cols-7 border-b border-border/50 bg-muted/10" }, Array.from({ length: 7 }).map((_, i) => /* @__PURE__ */ import_react15.default.createElement("div", { key: i, className: "py-3 px-2 text-center border-r border-border/30 last:border-r-0" }, /* @__PURE__ */ import_react15.default.createElement(Skeleton, { className: "h-4 w-8 mx-auto" })))), /* @__PURE__ */ import_react15.default.createElement("div", { className: "grid grid-cols-7 flex-1" }, Array.from({ length: 35 }).map((_, i) => /* @__PURE__ */ import_react15.default.createElement("div", { key: i, className: "min-h-[120px] border-r border-b border-border/30 p-2" }, /* @__PURE__ */ import_react15.default.createElement(Skeleton, { className: "h-4 w-6 mb-2" }), /* @__PURE__ */ import_react15.default.createElement("div", { className: "space-y-1" }, i * 7 % 10 > 4 && /* @__PURE__ */ import_react15.default.createElement(Skeleton, { className: "h-5 w-full rounded-md" }), i * 3 % 10 > 6 && /* @__PURE__ */ import_react15.default.createElement(Skeleton, { className: "h-5 w-3/4 rounded-md" }))))));
2358
2513
  var WeekViewSkeleton = () => /* @__PURE__ */ import_react15.default.createElement("div", { className: "h-full bg-background border border-border/50 rounded-2xl overflow-hidden" }, /* @__PURE__ */ import_react15.default.createElement("div", { className: "flex border-b border-border/50 bg-muted/10" }, /* @__PURE__ */ import_react15.default.createElement("div", { className: "w-16 p-3 border-r border-border/30" }, /* @__PURE__ */ import_react15.default.createElement(Skeleton, { className: "h-8 w-10 mx-auto" })), /* @__PURE__ */ import_react15.default.createElement("div", { className: "flex-1 grid grid-cols-7" }, Array.from({ length: 7 }).map((_, i) => /* @__PURE__ */ import_react15.default.createElement("div", { key: i, className: "py-3 px-2 text-center border-r border-border/30 last:border-r-0" }, /* @__PURE__ */ import_react15.default.createElement(Skeleton, { className: "h-3 w-8 mx-auto mb-1" }), /* @__PURE__ */ import_react15.default.createElement(Skeleton, { className: "h-8 w-8 mx-auto rounded-xl" }))))), /* @__PURE__ */ import_react15.default.createElement("div", { className: "flex flex-1", style: { height: "600px" } }, /* @__PURE__ */ import_react15.default.createElement("div", { className: "w-16 border-r border-border/30" }, Array.from({ length: 10 }).map((_, i) => /* @__PURE__ */ import_react15.default.createElement("div", { key: i, className: "h-[60px] relative" }, /* @__PURE__ */ import_react15.default.createElement(Skeleton, { className: "h-4 w-10 absolute right-2 -translate-y-1/2" })))), /* @__PURE__ */ import_react15.default.createElement("div", { className: "flex-1 grid grid-cols-7 relative" }, Array.from({ length: 7 }).map((_, colIdx) => /* @__PURE__ */ import_react15.default.createElement("div", { key: colIdx, className: "relative border-r border-border/30 last:border-r-0" }, Array.from({ length: 10 }).map((_2, rowIdx) => /* @__PURE__ */ import_react15.default.createElement("div", { key: rowIdx, className: "h-[60px] border-b border-dashed border-border/20" })), Math.random() > 0.3 && /* @__PURE__ */ import_react15.default.createElement(
2359
2514
  Skeleton,
2360
2515
  {
@@ -2676,13 +2831,14 @@ function cachedMonth(event, monthStart, tz) {
2676
2831
  }
2677
2832
  function expandRecurrences(events, range, anchorTz) {
2678
2833
  const result = [];
2679
- const firstMonth = (0, import_date_fns14.startOfMonth)(toDisplay(range.start, anchorTz));
2680
2834
  const lastMonth = (0, import_date_fns14.startOfMonth)(toDisplay(range.end, anchorTz));
2681
2835
  for (const event of events) {
2682
2836
  if (!event.recurrence) {
2683
2837
  result.push(event);
2684
2838
  continue;
2685
2839
  }
2840
+ const duration = event.end.getTime() - event.start.getTime();
2841
+ const firstMonth = (0, import_date_fns14.startOfMonth)(toDisplay(new Date(range.start.getTime() - duration), anchorTz));
2686
2842
  try {
2687
2843
  for (let month = firstMonth; month <= lastMonth; month = (0, import_date_fns14.addMonths)(month, 1)) {
2688
2844
  for (const occurrence of cachedMonth(event, fromDisplay(month, anchorTz), anchorTz)) {
@@ -2713,6 +2869,7 @@ function shiftSeries(master, occurrence, newStart, newEnd, anchorTz) {
2713
2869
  const recurrence = master.recurrence && {
2714
2870
  ...master.recurrence,
2715
2871
  byweekday: master.recurrence.byweekday && dayDelta % 7 !== 0 ? master.recurrence.byweekday.map((d) => ((d + dayDelta) % 7 + 7) % 7) : master.recurrence.byweekday,
2872
+ until: master.recurrence.until && shiftFloating(master.recurrence.until, deltaMs, anchorTz),
2716
2873
  exdates: master.recurrence.exdates?.map((d) => shiftFloating(d, deltaMs, anchorTz))
2717
2874
  };
2718
2875
  return stripOccurrenceFields({ ...master, start, end, recurrence });
@@ -2726,7 +2883,9 @@ function excludeOccurrence(master, occurrenceStart) {
2726
2883
  var import_react17 = require("react");
2727
2884
  function useStableCallback(fn) {
2728
2885
  const ref = (0, import_react17.useRef)(fn);
2729
- ref.current = fn;
2886
+ (0, import_react17.useInsertionEffect)(() => {
2887
+ ref.current = fn;
2888
+ });
2730
2889
  return (0, import_react17.useCallback)((...args) => ref.current(...args), []);
2731
2890
  }
2732
2891
 
@@ -2907,6 +3066,7 @@ var useCalendarLogic = ({
2907
3066
  return { original: master, updated: shiftSeries(master, event, start, end, timezone) };
2908
3067
  };
2909
3068
  const handleDragEnd = useStableCallback((dragEvent) => {
3069
+ if (readOnly) return;
2910
3070
  const { active, over } = dragEvent;
2911
3071
  if (!over) return;
2912
3072
  const dragged = active.data.current?.event;
@@ -3075,6 +3235,7 @@ var Scheduler = ({
3075
3235
  const {
3076
3236
  contextMenuEvent,
3077
3237
  contextMenuPosition,
3238
+ openContextMenu,
3078
3239
  closeContextMenu
3079
3240
  } = useEventContextMenu();
3080
3241
  const {
@@ -3162,14 +3323,15 @@ var Scheduler = ({
3162
3323
  }
3163
3324
  return "150px";
3164
3325
  };
3326
+ const activeCalendarKey = calendars ? calendars.filter((c) => c.active !== false).map((c) => c.id).join("\0") : null;
3165
3327
  const filteredEvents = (0, import_react20.useMemo)(() => {
3166
- if (!calendars) return expandedEvents;
3167
- const activeCalendarIds = calendars.filter((c) => c.active !== false).map((c) => c.id);
3168
- return expandedEvents.filter((e) => {
3169
- if (!e.calendarId) return true;
3170
- return activeCalendarIds.includes(e.calendarId);
3171
- });
3172
- }, [expandedEvents, calendars]);
3328
+ if (activeCalendarKey === null) return expandedEvents;
3329
+ const activeCalendarIds = new Set(activeCalendarKey.split("\0"));
3330
+ return expandedEvents.filter((e) => !e.calendarId || activeCalendarIds.has(String(e.calendarId)));
3331
+ }, [expandedEvents, activeCalendarKey]);
3332
+ const periodKey = view === "month" ? (0, import_date_fns16.format)(currentDate, "yyyy-MM") : view === "week" ? (0, import_date_fns16.format)((0, import_date_fns16.startOfWeek)(currentDate, { weekStartsOn: resolveWeekStartsOn(locale, 1) }), "yyyy-MM-dd") : (0, import_date_fns16.format)(currentDate, "yyyy-MM-dd");
3333
+ const slotClickHandler = readOnly ? void 0 : handleTimeSlotClick;
3334
+ const contextMenuHandler = readOnly ? void 0 : openContextMenu;
3173
3335
  return /* @__PURE__ */ import_react20.default.createElement(
3174
3336
  import_core3.DndContext,
3175
3337
  {
@@ -3240,7 +3402,7 @@ var Scheduler = ({
3240
3402
  ), /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex-1 flex flex-col overflow-hidden relative" }, isLoading ? /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex-1 overflow-auto p-0 md:p-4" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "h-full min-w-full" }, view === "month" && /* @__PURE__ */ import_react20.default.createElement(MonthViewSkeleton, null), view === "week" && /* @__PURE__ */ import_react20.default.createElement(WeekViewSkeleton, null), view === "day" && /* @__PURE__ */ import_react20.default.createElement(DayViewSkeleton, null), view === "agenda" && /* @__PURE__ */ import_react20.default.createElement(AgendaViewSkeleton, null), view === "resource" && /* @__PURE__ */ import_react20.default.createElement(WeekViewSkeleton, null))) : /* @__PURE__ */ import_react20.default.createElement("div", { ref: swipeRef, className: "flex-1 overflow-auto p-0 md:p-4 touch-pan-y" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "h-full min-w-full" }, /* @__PURE__ */ import_react20.default.createElement(import_framer_motion4.AnimatePresence, { mode: "wait", initial: false }, /* @__PURE__ */ import_react20.default.createElement(
3241
3403
  import_framer_motion4.motion.div,
3242
3404
  {
3243
- key: `${view}-${currentDate.toISOString()}-${timezone || "local"}`,
3405
+ key: `${view}-${periodKey}-${timezone || "local"}`,
3244
3406
  initial: { opacity: 0, scale: 0.98, y: 15 },
3245
3407
  animate: {
3246
3408
  opacity: 1,
@@ -3270,7 +3432,9 @@ var Scheduler = ({
3270
3432
  onEventClick: handleEventClickInternal,
3271
3433
  onDateClick: handleDateClick,
3272
3434
  timezone,
3273
- locale
3435
+ locale,
3436
+ translations: t,
3437
+ onEventContextMenu: contextMenuHandler
3274
3438
  }
3275
3439
  ),
3276
3440
  view === "week" && /* @__PURE__ */ import_react20.default.createElement(
@@ -3279,11 +3443,12 @@ var Scheduler = ({
3279
3443
  currentDate,
3280
3444
  events: filteredEvents,
3281
3445
  onEventClick: handleEventClickInternal,
3282
- onTimeSlotClick: handleTimeSlotClick,
3446
+ onTimeSlotClick: slotClickHandler,
3283
3447
  onEventResize: handleEventResize,
3284
3448
  timezone,
3285
3449
  locale,
3286
- readonly: readOnly
3450
+ readonly: readOnly,
3451
+ onEventContextMenu: contextMenuHandler
3287
3452
  }
3288
3453
  ),
3289
3454
  view === "day" && /* @__PURE__ */ import_react20.default.createElement(
@@ -3292,11 +3457,13 @@ var Scheduler = ({
3292
3457
  currentDate,
3293
3458
  events: filteredEvents,
3294
3459
  onEventClick: handleEventClickInternal,
3295
- onTimeSlotClick: handleTimeSlotClick,
3460
+ onTimeSlotClick: slotClickHandler,
3296
3461
  onEventResize: handleEventResize,
3297
3462
  timezone,
3298
3463
  locale,
3299
- readonly: readOnly
3464
+ readonly: readOnly,
3465
+ translations: t,
3466
+ onEventContextMenu: contextMenuHandler
3300
3467
  }
3301
3468
  ),
3302
3469
  view === "agenda" && /* @__PURE__ */ import_react20.default.createElement(
@@ -3308,7 +3475,8 @@ var Scheduler = ({
3308
3475
  onCreateEvent: handleCreateEvent,
3309
3476
  locale,
3310
3477
  translations: t,
3311
- timezone
3478
+ timezone,
3479
+ onEventContextMenu: contextMenuHandler
3312
3480
  }
3313
3481
  ),
3314
3482
  view === "resource" && resources && /* @__PURE__ */ import_react20.default.createElement(
@@ -3318,15 +3486,18 @@ var Scheduler = ({
3318
3486
  events: filteredEvents,
3319
3487
  resources,
3320
3488
  onEventClick: handleEventClickInternal,
3321
- onTimeSlotClick: handleTimeSlotClick,
3489
+ onTimeSlotClick: slotClickHandler,
3322
3490
  locale,
3323
- timezone
3491
+ timezone,
3492
+ translations: t,
3493
+ onEventContextMenu: contextMenuHandler
3324
3494
  }
3325
3495
  )
3326
- )))), /* @__PURE__ */ import_react20.default.createElement("div", { className: "md:hidden absolute bottom-6 right-6 z-50" }, /* @__PURE__ */ import_react20.default.createElement(
3496
+ )))), !readOnly && /* @__PURE__ */ import_react20.default.createElement("div", { className: "md:hidden absolute bottom-6 right-6 z-50" }, /* @__PURE__ */ import_react20.default.createElement(
3327
3497
  "button",
3328
3498
  {
3329
3499
  onClick: handleCreateEvent,
3500
+ "aria-label": t.createEvent,
3330
3501
  className: "w-14 h-14 bg-blue-600 rounded-full shadow-lg flex items-center justify-center text-white active:scale-90 transition-transform"
3331
3502
  },
3332
3503
  /* @__PURE__ */ import_react20.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react20.default.createElement("path", { d: "M5 12h14" }), /* @__PURE__ */ import_react20.default.createElement("path", { d: "M12 5v14" }))
@@ -3349,7 +3520,8 @@ var Scheduler = ({
3349
3520
  onDelete: handleModalDelete,
3350
3521
  calendars,
3351
3522
  eventTypes,
3352
- translations: t
3523
+ translations: t,
3524
+ locale
3353
3525
  }
3354
3526
  ),
3355
3527
  /* @__PURE__ */ import_react20.default.createElement(
@@ -3401,7 +3573,7 @@ var Scheduler = ({
3401
3573
  className: "absolute left-0 top-0 bottom-0 w-1 rounded-l-lg",
3402
3574
  style: { backgroundColor: activeDragEvent.color || "var(--primary)" }
3403
3575
  }
3404
- ), /* @__PURE__ */ import_react20.default.createElement("div", { className: "pl-2" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "font-semibold truncate text-sm" }, activeDragEvent.title), (view === "week" || view === "day") && getDragHeight() && getDragHeight() > 40 && /* @__PURE__ */ import_react20.default.createElement("div", { className: "text-xs opacity-80 mt-0.5 flex items-center gap-1" }, /* @__PURE__ */ import_react20.default.createElement("svg", { className: "w-3 h-3", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" }, /* @__PURE__ */ import_react20.default.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ import_react20.default.createElement("path", { d: "M12 6v6l4 2" })), (0, import_date_fns16.format)(activeDragEvent.start, overlayTimeFormat, { locale }), " - ", (0, import_date_fns16.format)(activeDragEvent.end, overlayTimeFormat, { locale }))), /* @__PURE__ */ import_react20.default.createElement("div", { className: "absolute bottom-1.5 right-1.5 opacity-60" }, /* @__PURE__ */ import_react20.default.createElement("svg", { className: "w-4 h-4", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ import_react20.default.createElement("circle", { cx: "9", cy: "5", r: "1.5" }), /* @__PURE__ */ import_react20.default.createElement("circle", { cx: "15", cy: "5", r: "1.5" }), /* @__PURE__ */ import_react20.default.createElement("circle", { cx: "9", cy: "12", r: "1.5" }), /* @__PURE__ */ import_react20.default.createElement("circle", { cx: "15", cy: "12", r: "1.5" }), /* @__PURE__ */ import_react20.default.createElement("circle", { cx: "9", cy: "19", r: "1.5" }), /* @__PURE__ */ import_react20.default.createElement("circle", { cx: "15", cy: "19", r: "1.5" }))))
3576
+ ), /* @__PURE__ */ import_react20.default.createElement("div", { className: "pl-2" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "font-semibold truncate text-sm" }, activeDragEvent.title), (view === "week" || view === "day") && getDragHeight() && getDragHeight() > 40 && /* @__PURE__ */ import_react20.default.createElement("div", { className: "text-xs opacity-80 mt-0.5 flex items-center gap-1" }, /* @__PURE__ */ import_react20.default.createElement("svg", { className: "w-3 h-3", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" }, /* @__PURE__ */ import_react20.default.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ import_react20.default.createElement("path", { d: "M12 6v6l4 2" })), (0, import_date_fns16.format)(toDisplay(activeDragEvent.start, timezone), overlayTimeFormat, { locale }), " - ", (0, import_date_fns16.format)(toDisplay(activeDragEvent.end, timezone), overlayTimeFormat, { locale }))), /* @__PURE__ */ import_react20.default.createElement("div", { className: "absolute bottom-1.5 right-1.5 opacity-60" }, /* @__PURE__ */ import_react20.default.createElement("svg", { className: "w-4 h-4", viewBox: "0 0 24 24", fill: "currentColor" }, /* @__PURE__ */ import_react20.default.createElement("circle", { cx: "9", cy: "5", r: "1.5" }), /* @__PURE__ */ import_react20.default.createElement("circle", { cx: "15", cy: "5", r: "1.5" }), /* @__PURE__ */ import_react20.default.createElement("circle", { cx: "9", cy: "12", r: "1.5" }), /* @__PURE__ */ import_react20.default.createElement("circle", { cx: "15", cy: "12", r: "1.5" }), /* @__PURE__ */ import_react20.default.createElement("circle", { cx: "9", cy: "19", r: "1.5" }), /* @__PURE__ */ import_react20.default.createElement("circle", { cx: "15", cy: "19", r: "1.5" }))))
3405
3577
  ) : null)
3406
3578
  )
3407
3579
  );