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