kalendly 0.1.7 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +317 -681
  2. package/dist/core/index.js +3 -3
  3. package/dist/index.d.mts +48 -602
  4. package/dist/index.d.ts +48 -602
  5. package/dist/index.js +753 -2359
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +727 -2348
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/{vanilla/index.umd.js → index.umd.js} +218 -218
  10. package/dist/index.umd.js.map +1 -0
  11. package/package.json +41 -91
  12. package/dist/react/index.d.mts +0 -251
  13. package/dist/react/index.d.ts +0 -251
  14. package/dist/react/index.js +0 -969
  15. package/dist/react/index.js.map +0 -1
  16. package/dist/react/index.mjs +0 -924
  17. package/dist/react/index.mjs.map +0 -1
  18. package/dist/react-native/index.d.mts +0 -642
  19. package/dist/react-native/index.d.ts +0 -642
  20. package/dist/react-native/index.js +0 -1649
  21. package/dist/react-native/index.js.map +0 -1
  22. package/dist/react-native/index.mjs +0 -1615
  23. package/dist/react-native/index.mjs.map +0 -1
  24. package/dist/vanilla/index.d.mts +0 -271
  25. package/dist/vanilla/index.d.ts +0 -271
  26. package/dist/vanilla/index.js +0 -1064
  27. package/dist/vanilla/index.js.map +0 -1
  28. package/dist/vanilla/index.mjs +0 -1013
  29. package/dist/vanilla/index.mjs.map +0 -1
  30. package/dist/vanilla/index.umd.js.map +0 -1
  31. package/dist/vue/components/Calendar.vue.d.ts +0 -43
  32. package/dist/vue/components/Calendar.vue.d.ts.map +0 -1
  33. package/dist/vue/index.d.ts +0 -134
  34. package/dist/vue/index.d.ts.map +0 -1
  35. package/dist/vue/index.js +0 -1
  36. package/dist/vue/index.mjs +0 -717
  37. package/dist/vue/types.d.ts +0 -21
  38. package/dist/vue/types.d.ts.map +0 -1
@@ -1,924 +0,0 @@
1
- // src/react/components/Calendar.tsx
2
- import {
3
- useEffect,
4
- useState,
5
- useCallback,
6
- useMemo,
7
- useRef
8
- } from "react";
9
-
10
- // src/core/utils.ts
11
- var MONTHS = [
12
- "Jan",
13
- "Feb",
14
- "Mar",
15
- "Apr",
16
- "May",
17
- "Jun",
18
- "Jul",
19
- "Aug",
20
- "Sep",
21
- "Oct",
22
- "Nov",
23
- "Dec"
24
- ];
25
- var MONTHS_FULL = [
26
- "January",
27
- "February",
28
- "March",
29
- "April",
30
- "May",
31
- "June",
32
- "July",
33
- "August",
34
- "September",
35
- "October",
36
- "November",
37
- "December"
38
- ];
39
- var DAYS = [
40
- "Sunday",
41
- "Monday",
42
- "Tuesday",
43
- "Wednesday",
44
- "Thursday",
45
- "Friday",
46
- "Saturday"
47
- ];
48
- function normalizeDate(date) {
49
- return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
50
- }
51
- function isSameDay(date1, date2) {
52
- return normalizeDate(date1).getTime() === normalizeDate(date2).getTime();
53
- }
54
- function isToday(date) {
55
- return isSameDay(date, /* @__PURE__ */ new Date());
56
- }
57
- function generateYears(minYear, maxYear) {
58
- const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
59
- const min = minYear ?? currentYear - 30;
60
- const max = maxYear ?? currentYear + 10;
61
- return Array.from({ length: max - min + 1 }, (_, i) => min + i);
62
- }
63
- function getEventsForDate(events, date) {
64
- const normalizedTargetDate = normalizeDate(date);
65
- return events.filter((event) => {
66
- const eventDate = normalizeDate(new Date(event.date));
67
- return eventDate.getTime() === normalizedTargetDate.getTime();
68
- });
69
- }
70
- function hasEvents(events, date) {
71
- return getEventsForDate(events, date).length > 0;
72
- }
73
- function generateCalendarDates(year, month, events = [], weekStartsOn = 0) {
74
- const firstDay = new Date(year, month, 1);
75
- const lastDay = new Date(year, month + 1, 0);
76
- const daysInMonth = lastDay.getDate();
77
- const prevMonthLastDay = new Date(year, month, 0).getDate();
78
- let firstDayOfWeek = firstDay.getDay();
79
- if (weekStartsOn === 1) {
80
- firstDayOfWeek = firstDayOfWeek === 0 ? 6 : firstDayOfWeek - 1;
81
- }
82
- const dates = [];
83
- let day = 1;
84
- let nextMonthDay = 1;
85
- for (let week = 0; week < 6; week++) {
86
- const weekDates = [];
87
- for (let dayOfWeek = 0; dayOfWeek < 7; dayOfWeek++) {
88
- if (week === 0 && dayOfWeek < firstDayOfWeek) {
89
- const prevDay = prevMonthLastDay - firstDayOfWeek + dayOfWeek + 1;
90
- const prevDate = new Date(year, month - 1, prevDay);
91
- const dateEvents = getEventsForDate(events, prevDate);
92
- weekDates.push({
93
- date: prevDate,
94
- isCurrentMonth: false,
95
- isToday: isToday(prevDate),
96
- hasEvents: dateEvents.length > 0,
97
- events: dateEvents
98
- });
99
- } else if (day > daysInMonth) {
100
- const nextDate = new Date(year, month + 1, nextMonthDay);
101
- const dateEvents = getEventsForDate(events, nextDate);
102
- weekDates.push({
103
- date: nextDate,
104
- isCurrentMonth: false,
105
- isToday: isToday(nextDate),
106
- hasEvents: dateEvents.length > 0,
107
- events: dateEvents
108
- });
109
- nextMonthDay++;
110
- } else {
111
- const currentDate = new Date(year, month, day);
112
- const dateEvents = getEventsForDate(events, currentDate);
113
- weekDates.push({
114
- date: currentDate,
115
- isCurrentMonth: true,
116
- isToday: isToday(currentDate),
117
- hasEvents: dateEvents.length > 0,
118
- events: dateEvents
119
- });
120
- day++;
121
- }
122
- }
123
- dates.push(weekDates);
124
- }
125
- return dates;
126
- }
127
- function getPopupPositionClass(selectedDayIndex) {
128
- if (selectedDayIndex === null) return "popup-center-bottom";
129
- if (selectedDayIndex < 3) {
130
- return "popup-right";
131
- } else if (selectedDayIndex > 4) {
132
- return "popup-left";
133
- } else {
134
- return "popup-center-bottom";
135
- }
136
- }
137
- function getCellClasses(calendarDate) {
138
- const classes = [];
139
- if (!calendarDate.isCurrentMonth) {
140
- classes.push("other-month");
141
- }
142
- if (calendarDate.isToday) {
143
- classes.push("schedule--current--exam");
144
- }
145
- if (calendarDate.hasEvents) {
146
- classes.push("has--event");
147
- }
148
- return classes;
149
- }
150
- function formatDateForDisplay(date) {
151
- return `${DAYS[date.getDay()]} ${date.getDate()}`;
152
- }
153
- function getMonthYearText(year, month) {
154
- return `${MONTHS_FULL[month]} ${year}`;
155
- }
156
- function formatTimeRange(event) {
157
- if (event.allDay || !event.startTime && !event.endTime) {
158
- return "All day";
159
- }
160
- if (event.startTime && event.endTime) {
161
- return `${event.startTime} - ${event.endTime}`;
162
- }
163
- if (event.startTime) {
164
- return `${event.startTime}`;
165
- }
166
- return "";
167
- }
168
- function formatAttendees(attendees) {
169
- if (!attendees || attendees.length === 0) return "";
170
- if (attendees.length === 1) return attendees[0];
171
- if (attendees.length === 2) return attendees.join(" and ");
172
- return `${attendees.slice(0, -1).join(", ")}, and ${attendees[attendees.length - 1]}`;
173
- }
174
- function sortEventsByTime(events) {
175
- return [...events].sort((a, b) => {
176
- const aAllDay = a.allDay || !a.startTime && !a.endTime;
177
- const bAllDay = b.allDay || !b.startTime && !b.endTime;
178
- if (aAllDay && !bAllDay) return -1;
179
- if (!aAllDay && bAllDay) return 1;
180
- if (!a.startTime || !b.startTime) return 0;
181
- return a.startTime.localeCompare(b.startTime);
182
- });
183
- }
184
- var DEFAULT_CATEGORY_COLORS = {
185
- work: "#3b82f6",
186
- personal: "#8b5cf6",
187
- meeting: "#10b981",
188
- deadline: "#ef4444",
189
- appointment: "#f59e0b",
190
- other: "#6b7280"
191
- };
192
- function getDefaultEventColor(category, customColors) {
193
- const colorMap = customColors || DEFAULT_CATEGORY_COLORS;
194
- if (category && colorMap[category]) {
195
- return colorMap[category];
196
- }
197
- return "#fc8917";
198
- }
199
- function mergeCategoryColors(customColors) {
200
- return {
201
- ...DEFAULT_CATEGORY_COLORS,
202
- ...customColors
203
- };
204
- }
205
- function isValidHexColor(color) {
206
- return /^#([0-9A-F]{3}){1,2}$/i.test(color);
207
- }
208
- function getCategoryColor(category, customColors) {
209
- const colorMap = mergeCategoryColors(customColors);
210
- const color = colorMap[category] || colorMap.other || "#fc8917";
211
- return isValidHexColor(color) ? color : "#fc8917";
212
- }
213
-
214
- // src/core/calendar-engine.ts
215
- var CalendarEngine = class {
216
- constructor(config) {
217
- this.listeners = /* @__PURE__ */ new Set();
218
- this.config = config;
219
- this.categoryColors = mergeCategoryColors(config.categoryColors);
220
- const initialDate = config.initialDate || /* @__PURE__ */ new Date();
221
- this.state = {
222
- currentYear: initialDate.getFullYear(),
223
- currentMonth: initialDate.getMonth(),
224
- currentDate: initialDate.getDate(),
225
- selectedDate: null,
226
- selectedDayIndex: null,
227
- tasks: []
228
- };
229
- }
230
- /**
231
- * Subscribe to state changes
232
- */
233
- subscribe(listener) {
234
- this.listeners.add(listener);
235
- return () => this.listeners.delete(listener);
236
- }
237
- /**
238
- * Notify all listeners of state changes
239
- */
240
- notify() {
241
- this.listeners.forEach((listener) => listener());
242
- }
243
- /**
244
- * Get current state
245
- */
246
- getState() {
247
- return { ...this.state };
248
- }
249
- /**
250
- * Get view model with computed properties
251
- */
252
- getViewModel() {
253
- const calendarDates = generateCalendarDates(
254
- this.state.currentYear,
255
- this.state.currentMonth,
256
- this.config.events,
257
- this.config.weekStartsOn
258
- );
259
- return {
260
- ...this.state,
261
- months: MONTHS,
262
- days: DAYS,
263
- years: generateYears(this.config.minYear, this.config.maxYear),
264
- monthAndYearText: getMonthYearText(
265
- this.state.currentYear,
266
- this.state.currentMonth
267
- ),
268
- scheduleDay: this.state.selectedDate ? formatDateForDisplay(this.state.selectedDate) : "",
269
- calendarDates,
270
- popupPositionClass: getPopupPositionClass(this.state.selectedDayIndex)
271
- };
272
- }
273
- /**
274
- * Get actions object
275
- */
276
- getActions() {
277
- return {
278
- next: this.next.bind(this),
279
- previous: this.previous.bind(this),
280
- jump: this.jump.bind(this),
281
- goToToday: this.goToToday.bind(this),
282
- isCurrentMonth: this.isCurrentMonth.bind(this),
283
- selectDate: this.selectDate.bind(this),
284
- updateTasks: this.updateTasks.bind(this)
285
- };
286
- }
287
- /**
288
- * Navigate to next month
289
- */
290
- next() {
291
- if (this.state.currentMonth === 11) {
292
- this.state.currentMonth = 0;
293
- this.state.currentYear++;
294
- } else {
295
- this.state.currentMonth++;
296
- }
297
- this.state.selectedDate = null;
298
- this.state.selectedDayIndex = null;
299
- this.updateTasks();
300
- this.notify();
301
- }
302
- /**
303
- * Navigate to previous month
304
- */
305
- previous() {
306
- if (this.state.currentMonth === 0) {
307
- this.state.currentMonth = 11;
308
- this.state.currentYear--;
309
- } else {
310
- this.state.currentMonth--;
311
- }
312
- this.state.selectedDate = null;
313
- this.state.selectedDayIndex = null;
314
- this.updateTasks();
315
- this.notify();
316
- }
317
- /**
318
- * Jump to specific month and year
319
- */
320
- jump(year, month) {
321
- this.state.currentYear = year;
322
- this.state.currentMonth = month;
323
- this.state.selectedDate = null;
324
- this.state.selectedDayIndex = null;
325
- this.updateTasks();
326
- this.notify();
327
- }
328
- /**
329
- * Navigate to current month (today)
330
- */
331
- goToToday() {
332
- const today = /* @__PURE__ */ new Date();
333
- this.state.currentYear = today.getFullYear();
334
- this.state.currentMonth = today.getMonth();
335
- this.state.selectedDate = null;
336
- this.state.selectedDayIndex = null;
337
- this.updateTasks();
338
- this.notify();
339
- }
340
- /**
341
- * Check if currently viewing today's month
342
- */
343
- isCurrentMonth() {
344
- const today = /* @__PURE__ */ new Date();
345
- return this.state.currentYear === today.getFullYear() && this.state.currentMonth === today.getMonth();
346
- }
347
- /**
348
- * Select a specific date
349
- */
350
- selectDate(date, dayIndex) {
351
- this.state.selectedDate = date;
352
- this.state.selectedDayIndex = dayIndex ?? null;
353
- this.state.currentDate = date.getDate();
354
- this.state.currentMonth = date.getMonth();
355
- this.state.currentYear = date.getFullYear();
356
- this.updateTasks();
357
- this.notify();
358
- }
359
- /**
360
- * Update tasks for the currently selected date
361
- */
362
- updateTasks() {
363
- if (!this.state.selectedDate) {
364
- this.state.tasks = [];
365
- return;
366
- }
367
- this.state.tasks = getEventsForDate(
368
- this.config.events,
369
- this.state.selectedDate
370
- );
371
- }
372
- /**
373
- * Update events configuration
374
- */
375
- updateEvents(events) {
376
- this.config.events = events;
377
- this.updateTasks();
378
- this.notify();
379
- }
380
- /**
381
- * Handle date cell click
382
- */
383
- handleDateClick(date, dayIndex) {
384
- this.selectDate(date, dayIndex);
385
- }
386
- /**
387
- * Check if date has events
388
- */
389
- hasEventsForDate(date) {
390
- return getEventsForDate(this.config.events, date).length > 0;
391
- }
392
- /**
393
- * Get events for a specific date
394
- */
395
- getEventsForDate(date) {
396
- return getEventsForDate(this.config.events, date);
397
- }
398
- /**
399
- * Clear selected date
400
- */
401
- clearSelection() {
402
- this.state.selectedDate = null;
403
- this.state.selectedDayIndex = null;
404
- this.state.tasks = [];
405
- this.notify();
406
- }
407
- /**
408
- * Get category color (with custom colors support)
409
- */
410
- getCategoryColor(category) {
411
- if (!category) return "#fc8917";
412
- return getCategoryColor(category, this.categoryColors);
413
- }
414
- /**
415
- * Update category colors dynamically
416
- */
417
- updateCategoryColors(colors) {
418
- this.categoryColors = mergeCategoryColors(colors);
419
- this.notify();
420
- }
421
- /**
422
- * Get all category colors
423
- */
424
- getCategoryColors() {
425
- return { ...this.categoryColors };
426
- }
427
- /**
428
- * Register a new category with color
429
- */
430
- registerCategory(name, color) {
431
- this.categoryColors[name] = color;
432
- this.notify();
433
- }
434
- /**
435
- * Destroy the engine and cleanup listeners
436
- */
437
- destroy() {
438
- this.listeners.clear();
439
- }
440
- };
441
-
442
- // src/react/components/DatePopup.tsx
443
- import { jsx, jsxs } from "react/jsx-runtime";
444
- var PriorityBadge = ({ priority }) => {
445
- if (!priority) return null;
446
- const priorityConfig = {
447
- high: { label: "HIGH", className: "badge priority-high" },
448
- medium: { label: "MEDIUM", className: "badge priority-medium" },
449
- low: { label: "LOW", className: "badge priority-low" }
450
- };
451
- const config = priorityConfig[priority];
452
- if (!config) return null;
453
- return /* @__PURE__ */ jsx("span", { className: config.className, children: config.label });
454
- };
455
- var StatusBadge = ({ status }) => {
456
- if (!status || status === "scheduled") return null;
457
- const statusConfig = {
458
- completed: { label: "COMPLETED", className: "badge status-completed" },
459
- cancelled: { label: "CANCELLED", className: "badge status-cancelled" },
460
- tentative: { label: "TENTATIVE", className: "badge status-tentative" }
461
- };
462
- const config = statusConfig[status];
463
- if (!config) return null;
464
- return /* @__PURE__ */ jsx("span", { className: config.className, children: config.label });
465
- };
466
- var CategoryBadge = ({ category }) => {
467
- if (!category) return null;
468
- const categoryConfig = {
469
- work: { label: "WORK", className: "badge category-work" },
470
- personal: { label: "PERSONAL", className: "badge category-personal" },
471
- meeting: { label: "MEETING", className: "badge category-meeting" },
472
- deadline: { label: "DEADLINE", className: "badge category-deadline" },
473
- appointment: {
474
- label: "APPOINTMENT",
475
- className: "badge category-appointment"
476
- },
477
- other: { label: "OTHER", className: "badge category-other" }
478
- };
479
- const config = categoryConfig[category];
480
- if (!config) return null;
481
- return /* @__PURE__ */ jsx("span", { className: config.className, children: config.label });
482
- };
483
- var DatePopup = ({
484
- isVisible,
485
- selectedDate,
486
- events,
487
- scheduleDay,
488
- popupPositionClass,
489
- onEventClick,
490
- onClose,
491
- renderEvent,
492
- renderNoEvents
493
- }) => {
494
- if (!isVisible || !selectedDate) return null;
495
- const handleEventClick = (event) => {
496
- onEventClick?.(event);
497
- };
498
- const showScrollHint = events.length > 3;
499
- const defaultRenderEvent = (event) => {
500
- const timeRange = formatTimeRange(event);
501
- const attendeesList = formatAttendees(event.attendees);
502
- return /* @__PURE__ */ jsxs(
503
- "div",
504
- {
505
- className: `event-card${onEventClick ? " clickable" : ""}`,
506
- onClick: () => handleEventClick(event),
507
- style: {
508
- borderLeftColor: event.color || void 0
509
- },
510
- children: [
511
- /* @__PURE__ */ jsxs("div", { className: "event-header", children: [
512
- /* @__PURE__ */ jsx("div", { className: "event-title", children: event.name }),
513
- /* @__PURE__ */ jsxs("div", { className: "event-badges", children: [
514
- /* @__PURE__ */ jsx(CategoryBadge, { category: event.category }),
515
- /* @__PURE__ */ jsx(PriorityBadge, { priority: event.priority }),
516
- /* @__PURE__ */ jsx(StatusBadge, { status: event.status })
517
- ] })
518
- ] }),
519
- timeRange && /* @__PURE__ */ jsxs("div", { className: "event-time", children: [
520
- /* @__PURE__ */ jsx("span", { className: "event-time-label", children: "Time:" }),
521
- /* @__PURE__ */ jsx("span", { className: "event-time-value", children: timeRange })
522
- ] }),
523
- event.description && /* @__PURE__ */ jsx("div", { className: "event-description", children: event.description }),
524
- event.location && /* @__PURE__ */ jsxs("div", { className: "event-time", children: [
525
- /* @__PURE__ */ jsx("span", { className: "event-time-label", children: "Location:" }),
526
- /* @__PURE__ */ jsx("span", { className: "event-time-value", children: event.location })
527
- ] }),
528
- attendeesList && /* @__PURE__ */ jsxs("div", { className: "event-time", children: [
529
- /* @__PURE__ */ jsx("span", { className: "event-time-label", children: "Attendees:" }),
530
- /* @__PURE__ */ jsx("span", { className: "event-time-value", children: attendeesList })
531
- ] }),
532
- event.organizer && /* @__PURE__ */ jsxs("div", { className: "event-time", children: [
533
- /* @__PURE__ */ jsx("span", { className: "event-time-label", children: "Organizer:" }),
534
- /* @__PURE__ */ jsx("span", { className: "event-time-value", children: event.organizer })
535
- ] }),
536
- event.notes && /* @__PURE__ */ jsxs("div", { className: "event-time", children: [
537
- /* @__PURE__ */ jsx("span", { className: "event-time-label", children: "Notes:" }),
538
- /* @__PURE__ */ jsx("span", { className: "event-time-value", children: event.notes })
539
- ] }),
540
- event.url && /* @__PURE__ */ jsx("div", { className: "event-time", children: /* @__PURE__ */ jsx(
541
- "a",
542
- {
543
- href: event.url,
544
- target: "_blank",
545
- rel: "noopener noreferrer",
546
- className: "event-link",
547
- onClick: (e) => e.stopPropagation(),
548
- children: "View Details \u2192"
549
- }
550
- ) }),
551
- event.tags && event.tags.length > 0 && /* @__PURE__ */ jsx("div", { className: "event-tags", children: event.tags.map((tag, idx) => /* @__PURE__ */ jsx("span", { className: "event-tag", children: tag }, idx)) })
552
- ]
553
- },
554
- event.id || event.name
555
- );
556
- };
557
- const defaultRenderNoEvents = () => /* @__PURE__ */ jsx("div", { className: "no-events-message", children: "No events scheduled for this day." });
558
- return /* @__PURE__ */ jsxs("div", { className: `date-popup ${popupPositionClass}`, children: [
559
- /* @__PURE__ */ jsxs("div", { className: "popup-header", children: [
560
- /* @__PURE__ */ jsx("h2", { children: scheduleDay }),
561
- /* @__PURE__ */ jsx(
562
- "button",
563
- {
564
- type: "button",
565
- className: "popup-close",
566
- onClick: onClose,
567
- "aria-label": "Close",
568
- children: "\u2715"
569
- }
570
- )
571
- ] }),
572
- showScrollHint && /* @__PURE__ */ jsx("div", { className: "scroll-hint", children: "\u2193 Scroll to see more events \u2193" }),
573
- /* @__PURE__ */ jsx("div", { className: "events-container", children: events.length > 0 ? events.map(
574
- (event) => renderEvent ? /* @__PURE__ */ jsx(
575
- "div",
576
- {
577
- className: `event-card${onEventClick ? " clickable" : ""}`,
578
- onClick: () => handleEventClick(event),
579
- children: renderEvent(event)
580
- },
581
- event.id || event.name
582
- ) : defaultRenderEvent(event)
583
- ) : renderNoEvents ? renderNoEvents() : defaultRenderNoEvents() })
584
- ] });
585
- };
586
-
587
- // src/react/components/Calendar.tsx
588
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
589
- var Calendar = ({
590
- events,
591
- initialDate,
592
- minYear,
593
- maxYear,
594
- weekStartsOn = 0,
595
- useShortMonthNames = false,
596
- onDateSelect,
597
- onEventClick,
598
- onMonthChange,
599
- className = "",
600
- style,
601
- renderEvent,
602
- renderNoEvents,
603
- title,
604
- theme
605
- }) => {
606
- const engine = useMemo(
607
- () => new CalendarEngine({
608
- events,
609
- initialDate,
610
- minYear,
611
- maxYear,
612
- weekStartsOn
613
- }),
614
- [events, initialDate, minYear, maxYear, weekStartsOn]
615
- );
616
- const [, forceUpdate] = useState({});
617
- const rerender = useCallback(() => forceUpdate({}), []);
618
- const [pickerOpen, setPickerOpen] = useState(false);
619
- const [yearInput, setYearInput] = useState("");
620
- const [yearInputValid, setYearInputValid] = useState(true);
621
- const pickerRef = useRef(null);
622
- useEffect(() => {
623
- const unsubscribe = engine.subscribe(rerender);
624
- return unsubscribe;
625
- }, [engine, rerender]);
626
- useEffect(() => {
627
- engine.updateEvents(events);
628
- }, [engine, events]);
629
- useEffect(() => {
630
- return () => {
631
- engine.destroy();
632
- };
633
- }, [engine]);
634
- useEffect(() => {
635
- if (theme) {
636
- const root = document.documentElement;
637
- if (theme.primary)
638
- root.style.setProperty("--calendar-primary-color", theme.primary);
639
- if (theme.secondary)
640
- root.style.setProperty("--calendar-secondary-color", theme.secondary);
641
- if (theme.tertiary)
642
- root.style.setProperty("--calendar-tertiary-color", theme.tertiary);
643
- if (theme.textColor)
644
- root.style.setProperty("--calendar-text-color", theme.textColor);
645
- if (theme.textLight)
646
- root.style.setProperty("--calendar-text-light", theme.textLight);
647
- if (theme.background)
648
- root.style.setProperty("--calendar-background", theme.background);
649
- if (theme.cellHover)
650
- root.style.setProperty("--calendar-cell-hover", theme.cellHover);
651
- if (theme.borderColor)
652
- root.style.setProperty("--calendar-border-color", theme.borderColor);
653
- if (theme.todayOutline)
654
- root.style.setProperty("--calendar-today-outline", theme.todayOutline);
655
- if (theme.selectedBg)
656
- root.style.setProperty("--calendar-selected-bg", theme.selectedBg);
657
- if (theme.eventIndicator)
658
- root.style.setProperty(
659
- "--calendar-event-indicator",
660
- theme.eventIndicator
661
- );
662
- }
663
- }, [theme]);
664
- const viewModel = engine.getViewModel();
665
- const actions = engine.getActions();
666
- const { selectedDate, tasks } = viewModel;
667
- const handleDateClick = (event) => {
668
- const td = event.target.closest("td");
669
- if (!td) return;
670
- const tr = td.parentElement;
671
- if (!tr) return;
672
- const weekIndex = tr.rowIndex - 1;
673
- const dayIndex = Array.from(tr.children).indexOf(td);
674
- const calendarDate = viewModel.calendarDates[weekIndex]?.[dayIndex];
675
- if (!calendarDate) return;
676
- engine.handleDateClick(calendarDate.date, dayIndex);
677
- onDateSelect?.(calendarDate.date);
678
- };
679
- const handleMonthChange = useCallback(() => {
680
- onMonthChange?.(viewModel.currentYear, viewModel.currentMonth);
681
- }, [onMonthChange, viewModel.currentYear, viewModel.currentMonth]);
682
- const handleNext = () => {
683
- actions.next();
684
- handleMonthChange();
685
- };
686
- const handlePrevious = () => {
687
- actions.previous();
688
- handleMonthChange();
689
- };
690
- const handleGoToToday = () => {
691
- actions.goToToday();
692
- setPickerOpen(false);
693
- const today2 = /* @__PURE__ */ new Date();
694
- onMonthChange?.(today2.getFullYear(), today2.getMonth());
695
- };
696
- const togglePicker = () => {
697
- const newOpen = !pickerOpen;
698
- setPickerOpen(newOpen);
699
- if (newOpen) {
700
- setYearInput(String(viewModel.currentYear));
701
- setYearInputValid(true);
702
- }
703
- };
704
- const today = /* @__PURE__ */ new Date();
705
- const computedMinYear = minYear ?? today.getFullYear() - 30;
706
- const computedMaxYear = maxYear ?? today.getFullYear() + 10;
707
- const handleYearInputChange = (e) => {
708
- const value = e.target.value;
709
- if (value === "" || /^\d+$/.test(value)) {
710
- setYearInput(value);
711
- const year = parseInt(value, 10);
712
- setYearInputValid(
713
- value === "" || year >= computedMinYear && year <= computedMaxYear
714
- );
715
- }
716
- };
717
- const handleYearInputBlur = () => {
718
- const year = parseInt(yearInput, 10);
719
- if (isNaN(year) || year < computedMinYear || year > computedMaxYear) {
720
- setYearInput(String(viewModel.currentYear));
721
- setYearInputValid(true);
722
- } else {
723
- actions.jump(year, viewModel.currentMonth);
724
- handleMonthChange();
725
- }
726
- };
727
- const handleYearPrev = () => {
728
- const newYear = viewModel.currentYear - 1;
729
- if (newYear >= computedMinYear) {
730
- actions.jump(newYear, viewModel.currentMonth);
731
- setYearInput(String(newYear));
732
- handleMonthChange();
733
- }
734
- };
735
- const handleYearNext = () => {
736
- const newYear = viewModel.currentYear + 1;
737
- if (newYear <= computedMaxYear) {
738
- actions.jump(newYear, viewModel.currentMonth);
739
- setYearInput(String(newYear));
740
- handleMonthChange();
741
- }
742
- };
743
- const handleMonthSelect = (month) => {
744
- actions.jump(viewModel.currentYear, month);
745
- setPickerOpen(false);
746
- handleMonthChange();
747
- };
748
- useEffect(() => {
749
- const handleClickOutside = (event) => {
750
- if (pickerRef.current && !pickerRef.current.contains(event.target)) {
751
- setPickerOpen(false);
752
- }
753
- };
754
- if (pickerOpen) {
755
- document.addEventListener("mousedown", handleClickOutside);
756
- }
757
- return () => document.removeEventListener("mousedown", handleClickOutside);
758
- }, [pickerOpen]);
759
- return /* @__PURE__ */ jsxs2("div", { className: `kalendly-calendar ${className}`, style, children: [
760
- title && /* @__PURE__ */ jsx2("div", { className: "page--title", children: /* @__PURE__ */ jsx2("h1", { children: title }) }),
761
- /* @__PURE__ */ jsx2("div", { className: "calendar--content", children: /* @__PURE__ */ jsxs2("div", { className: "calendar--card", children: [
762
- /* @__PURE__ */ jsxs2("div", { className: "calendar--nav-header", children: [
763
- /* @__PURE__ */ jsx2(
764
- "button",
765
- {
766
- type: "button",
767
- className: "calendar--nav-arrow",
768
- onClick: handlePrevious,
769
- "aria-label": "Previous month",
770
- children: "\u2039"
771
- }
772
- ),
773
- /* @__PURE__ */ jsxs2("div", { className: "calendar--picker-container", ref: pickerRef, children: [
774
- /* @__PURE__ */ jsxs2(
775
- "button",
776
- {
777
- type: "button",
778
- className: "calendar--picker-btn",
779
- onClick: togglePicker,
780
- ...{ "aria-expanded": pickerOpen },
781
- "aria-haspopup": "true",
782
- children: [
783
- useShortMonthNames ? `${MONTHS[viewModel.currentMonth]} ${viewModel.currentYear}` : viewModel.monthAndYearText,
784
- /* @__PURE__ */ jsx2("span", { className: "calendar--picker-chevron", children: "\u25BE" })
785
- ]
786
- }
787
- ),
788
- pickerOpen && /* @__PURE__ */ jsxs2("div", { className: "calendar--picker-dropdown", children: [
789
- /* @__PURE__ */ jsxs2("div", { className: "calendar--picker-year-row", children: [
790
- /* @__PURE__ */ jsx2(
791
- "button",
792
- {
793
- type: "button",
794
- className: "calendar--picker-year-arrow",
795
- onClick: handleYearPrev,
796
- disabled: viewModel.currentYear <= computedMinYear,
797
- "aria-label": "Previous year",
798
- children: "\u2039"
799
- }
800
- ),
801
- /* @__PURE__ */ jsx2(
802
- "input",
803
- {
804
- type: "text",
805
- className: `calendar--picker-year-input${!yearInputValid ? " invalid" : ""}`,
806
- value: yearInput,
807
- onChange: handleYearInputChange,
808
- onBlur: handleYearInputBlur,
809
- onKeyDown: (e) => {
810
- if (e.key === "Enter") handleYearInputBlur();
811
- },
812
- "aria-label": "Year"
813
- }
814
- ),
815
- /* @__PURE__ */ jsx2(
816
- "button",
817
- {
818
- type: "button",
819
- className: "calendar--picker-year-arrow",
820
- onClick: handleYearNext,
821
- disabled: viewModel.currentYear >= computedMaxYear,
822
- "aria-label": "Next year",
823
- children: "\u203A"
824
- }
825
- )
826
- ] }),
827
- /* @__PURE__ */ jsx2("div", { className: "calendar--picker-months", children: (useShortMonthNames ? MONTHS : MONTHS_FULL).map(
828
- (month, index) => {
829
- const isSelected = index === viewModel.currentMonth;
830
- const isCurrentMonth = index === today.getMonth() && viewModel.currentYear === today.getFullYear();
831
- return /* @__PURE__ */ jsx2(
832
- "button",
833
- {
834
- type: "button",
835
- className: `calendar--picker-month${isSelected ? " selected" : ""}${isCurrentMonth ? " current-month" : ""}`,
836
- onClick: () => handleMonthSelect(index),
837
- children: month
838
- },
839
- month
840
- );
841
- }
842
- ) })
843
- ] })
844
- ] }),
845
- /* @__PURE__ */ jsx2(
846
- "button",
847
- {
848
- type: "button",
849
- className: "calendar--today-btn",
850
- onClick: handleGoToToday,
851
- disabled: actions.isCurrentMonth(),
852
- children: "Today"
853
- }
854
- ),
855
- /* @__PURE__ */ jsx2(
856
- "button",
857
- {
858
- type: "button",
859
- className: "calendar--nav-arrow",
860
- onClick: handleNext,
861
- "aria-label": "Next month",
862
- children: "\u203A"
863
- }
864
- )
865
- ] }),
866
- /* @__PURE__ */ jsxs2("table", { className: "calendar--table calendar--table--bordered", children: [
867
- /* @__PURE__ */ jsx2("thead", { children: /* @__PURE__ */ jsx2("tr", { children: viewModel.days.map((day) => /* @__PURE__ */ jsx2("th", { children: day.slice(0, 3) }, day)) }) }),
868
- /* @__PURE__ */ jsx2("tbody", { onClick: handleDateClick, children: viewModel.calendarDates.map((week, weekIndex) => /* @__PURE__ */ jsx2("tr", { children: week.map((calendarDate, dayIndex) => {
869
- const cellClasses = getCellClasses(calendarDate);
870
- return /* @__PURE__ */ jsx2(
871
- "td",
872
- {
873
- className: cellClasses.join(" "),
874
- children: calendarDate.date.getDate()
875
- },
876
- `${weekIndex}-${dayIndex}`
877
- );
878
- }) }, weekIndex)) })
879
- ] }),
880
- /* @__PURE__ */ jsx2(
881
- DatePopup,
882
- {
883
- isVisible: !!selectedDate,
884
- selectedDate,
885
- events: tasks,
886
- scheduleDay: viewModel.scheduleDay,
887
- popupPositionClass: viewModel.popupPositionClass,
888
- onClose: () => engine.clearSelection(),
889
- onEventClick,
890
- renderEvent,
891
- renderNoEvents
892
- }
893
- )
894
- ] }) })
895
- ] });
896
- };
897
- export {
898
- Calendar,
899
- CalendarEngine,
900
- DAYS,
901
- DEFAULT_CATEGORY_COLORS,
902
- DatePopup,
903
- MONTHS,
904
- MONTHS_FULL,
905
- formatAttendees,
906
- formatDateForDisplay,
907
- formatTimeRange,
908
- generateCalendarDates,
909
- generateYears,
910
- getCategoryColor,
911
- getCellClasses,
912
- getDefaultEventColor,
913
- getEventsForDate,
914
- getMonthYearText,
915
- getPopupPositionClass,
916
- hasEvents,
917
- isSameDay,
918
- isToday,
919
- isValidHexColor,
920
- mergeCategoryColors,
921
- normalizeDate,
922
- sortEventsByTime
923
- };
924
- //# sourceMappingURL=index.mjs.map