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,1064 +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.delegatedClickHandler = null;
492
- this.delegatedInputHandler = null;
493
- this.delegatedBlurHandler = null;
494
- this.delegatedKeydownHandler = null;
495
- this.listenersAttached = false;
496
- this.props = props;
497
- if (typeof props.container === "string") {
498
- const element = document.querySelector(props.container);
499
- if (!element) {
500
- throw new Error(`Container element "${props.container}" not found`);
501
- }
502
- this.container = element;
503
- } else {
504
- this.container = props.container;
505
- }
506
- this.engine = new CalendarEngine({
507
- events: props.events,
508
- initialDate: props.initialDate,
509
- minYear: props.minYear,
510
- maxYear: props.maxYear,
511
- weekStartsOn: props.weekStartsOn
512
- });
513
- this.actions = this.engine.getActions();
514
- this.applyTheme();
515
- this.init();
516
- }
517
- applyTheme() {
518
- if (this.props.theme) {
519
- const root = document.documentElement;
520
- const theme = this.props.theme;
521
- if (theme.primary)
522
- root.style.setProperty("--calendar-primary-color", theme.primary);
523
- if (theme.secondary)
524
- root.style.setProperty("--calendar-secondary-color", theme.secondary);
525
- if (theme.tertiary)
526
- root.style.setProperty("--calendar-tertiary-color", theme.tertiary);
527
- if (theme.textColor)
528
- root.style.setProperty("--calendar-text-color", theme.textColor);
529
- if (theme.textLight)
530
- root.style.setProperty("--calendar-text-light", theme.textLight);
531
- if (theme.background)
532
- root.style.setProperty("--calendar-background", theme.background);
533
- if (theme.cellHover)
534
- root.style.setProperty("--calendar-cell-hover", theme.cellHover);
535
- if (theme.borderColor)
536
- root.style.setProperty("--calendar-border-color", theme.borderColor);
537
- if (theme.todayOutline)
538
- root.style.setProperty("--calendar-today-outline", theme.todayOutline);
539
- if (theme.selectedBg)
540
- root.style.setProperty("--calendar-selected-bg", theme.selectedBg);
541
- if (theme.eventIndicator)
542
- root.style.setProperty(
543
- "--calendar-event-indicator",
544
- theme.eventIndicator
545
- );
546
- }
547
- }
548
- init() {
549
- this.container.classList.add("kalendly-calendar");
550
- if (this.props.className) {
551
- this.container.classList.add(this.props.className);
552
- }
553
- this.unsubscribe = this.engine.subscribe(() => {
554
- this.render();
555
- });
556
- this.render();
557
- }
558
- render() {
559
- const viewModel = this.engine.getViewModel();
560
- const defaultRenderEvent = (event) => {
561
- const timeRange = formatTimeRange(event);
562
- const attendeesList = formatAttendees(event.attendees);
563
- let borderColor = event.color || "#3b82f6";
564
- if (event.category) {
565
- borderColor = this.engine.getCategoryColor(event.category);
566
- }
567
- const getCategoryLabel = (category) => {
568
- const labels = {
569
- work: "WORK",
570
- personal: "PERSONAL",
571
- meeting: "MEETING",
572
- deadline: "DEADLINE",
573
- appointment: "APPOINTMENT",
574
- other: "OTHER"
575
- };
576
- return category ? labels[category] || category.toUpperCase() : "";
577
- };
578
- const getPriorityLabel = (priority) => {
579
- const labels = {
580
- high: "HIGH",
581
- medium: "MEDIUM",
582
- low: "LOW"
583
- };
584
- return priority ? labels[priority] || priority.toUpperCase() : "";
585
- };
586
- const getStatusLabel = (status) => {
587
- const labels = {
588
- completed: "COMPLETED",
589
- cancelled: "CANCELLED",
590
- tentative: "TENTATIVE",
591
- scheduled: "SCHEDULED"
592
- };
593
- return status ? labels[status] || status.toUpperCase() : "";
594
- };
595
- return `
596
- <div class="event-card" style="border-left-color: ${borderColor}">
597
- <div class="event-header">
598
- <div class="event-title">${event.name}</div>
599
- <div class="event-badges">
600
- ${event.category ? `<span class="badge category-${event.category}">${getCategoryLabel(event.category)}</span>` : ""}
601
- ${event.priority ? `<span class="badge priority-${event.priority}">${getPriorityLabel(event.priority)}</span>` : ""}
602
- ${event.status && event.status !== "scheduled" ? `<span class="badge status-${event.status}">${getStatusLabel(event.status)}</span>` : ""}
603
- </div>
604
- </div>
605
-
606
- ${timeRange ? `
607
- <div class="event-time">
608
- <span class="event-time-label">Time:</span>
609
- <span class="event-time-value">${timeRange}</span>
610
- </div>
611
- ` : ""}
612
-
613
- ${event.description ? `
614
- <div class="event-description">${event.description}</div>
615
- ` : ""}
616
-
617
- ${event.location ? `
618
- <div class="event-time">
619
- <span class="event-time-label">Location:</span>
620
- <span class="event-time-value">${event.location}</span>
621
- </div>
622
- ` : ""}
623
-
624
- ${attendeesList ? `
625
- <div class="event-time">
626
- <span class="event-time-label">Attendees:</span>
627
- <span class="event-time-value">${attendeesList}</span>
628
- </div>
629
- ` : ""}
630
-
631
- ${event.organizer ? `
632
- <div class="event-time">
633
- <span class="event-time-label">Organizer:</span>
634
- <span class="event-time-value">${event.organizer}</span>
635
- </div>
636
- ` : ""}
637
-
638
- ${event.notes ? `
639
- <div class="event-time">
640
- <span class="event-time-label">Notes:</span>
641
- <span class="event-time-value">${event.notes}</span>
642
- </div>
643
- ` : ""}
644
-
645
- ${event.url ? `
646
- <div class="event-time">
647
- <a href="${event.url}" target="_blank" rel="noopener noreferrer" class="event-link">
648
- View Details \u2192
649
- </a>
650
- </div>
651
- ` : ""}
652
-
653
- ${event.tags && event.tags.length > 0 ? `
654
- <div class="event-tags">
655
- ${event.tags.map((tag) => `<span class="event-tag">${tag}</span>`).join("")}
656
- </div>
657
- ` : ""}
658
- </div>
659
- `;
660
- };
661
- const defaultRenderNoEvents = () => '<div class="no-events-message">No events scheduled for this day.</div>';
662
- const renderEvent = this.props.renderEvent || defaultRenderEvent;
663
- const renderNoEvents = this.props.renderNoEvents || defaultRenderNoEvents;
664
- const showScrollHint = viewModel.tasks.length > 3;
665
- const today = /* @__PURE__ */ new Date();
666
- const todayMonth = today.getMonth();
667
- const todayYear = today.getFullYear();
668
- const minYear = this.props.minYear ?? todayYear - 30;
669
- const maxYear = this.props.maxYear ?? todayYear + 10;
670
- const isCurrentMonth = viewModel.currentYear === todayYear && viewModel.currentMonth === todayMonth;
671
- const html = `
672
- ${this.props.title ? `
673
- <div class="page--title">
674
- <h1>${this.props.title}</h1>
675
- </div>
676
- ` : ""}
677
-
678
- <div class="calendar--content">
679
- <div class="calendar--card">
680
- <!-- Navigation Header -->
681
- <div class="calendar--nav-header">
682
- <button type="button" class="calendar--nav-arrow" data-action="previous" aria-label="Previous month">
683
- &#8249;
684
- </button>
685
-
686
- <div class="calendar--picker-container" data-picker-container>
687
- <button
688
- type="button"
689
- class="calendar--picker-btn"
690
- data-action="toggle-picker"
691
- aria-expanded="${this.pickerOpen ? "true" : "false"}"
692
- aria-haspopup="true"
693
- >
694
- ${this.props.useShortMonthNames ? `${MONTHS[viewModel.currentMonth]} ${viewModel.currentYear}` : viewModel.monthAndYearText}
695
- <span class="calendar--picker-chevron">&#9662;</span>
696
- </button>
697
-
698
- ${this.pickerOpen ? `
699
- <div class="calendar--picker-dropdown">
700
- <div class="calendar--picker-year-row">
701
- <button
702
- type="button"
703
- class="calendar--picker-year-arrow"
704
- data-action="year-prev"
705
- ${viewModel.currentYear <= minYear ? "disabled" : ""}
706
- aria-label="Previous year"
707
- >&#8249;</button>
708
- <input
709
- type="text"
710
- class="calendar--picker-year-input${!this.yearInputValid ? " invalid" : ""}"
711
- value="${this.yearInput || viewModel.currentYear}"
712
- data-year-input
713
- aria-label="Year"
714
- />
715
- <button
716
- type="button"
717
- class="calendar--picker-year-arrow"
718
- data-action="year-next"
719
- ${viewModel.currentYear >= maxYear ? "disabled" : ""}
720
- aria-label="Next year"
721
- >&#8250;</button>
722
- </div>
723
-
724
- <div class="calendar--picker-months">
725
- ${(this.props.useShortMonthNames ? MONTHS : MONTHS_FULL).map((month, index) => {
726
- const isSelected = index === viewModel.currentMonth;
727
- const isCurrent = index === todayMonth && viewModel.currentYear === todayYear;
728
- return `
729
- <button
730
- type="button"
731
- class="calendar--picker-month${isSelected ? " selected" : ""}${isCurrent ? " current-month" : ""}"
732
- data-action="select-month"
733
- data-month="${index}"
734
- >${month}</button>
735
- `;
736
- }).join("")}
737
- </div>
738
- </div>
739
- ` : ""}
740
- </div>
741
-
742
- <button
743
- type="button"
744
- class="calendar--today-btn"
745
- data-action="today"
746
- ${isCurrentMonth ? "disabled" : ""}
747
- >Today</button>
748
-
749
- <button type="button" class="calendar--nav-arrow" data-action="next" aria-label="Next month">
750
- &#8250;
751
- </button>
752
- </div>
753
-
754
- <table class="calendar--table calendar--table--bordered">
755
- <thead>
756
- <tr>
757
- ${viewModel.days.map((day) => `<th>${day.slice(0, 3)}</th>`).join("")}
758
- </tr>
759
- </thead>
760
- <tbody data-calendar-body>
761
- ${viewModel.calendarDates.map(
762
- (week) => `
763
- <tr>
764
- ${week.map((calendarDate, dayIndex) => {
765
- const classes = getCellClasses(calendarDate);
766
- const dateString = calendarDate.date.toISOString();
767
- return `
768
- <td
769
- class="${classes.join(" ")}"
770
- data-date="${dateString}"
771
- data-day-index="${dayIndex}"
772
- data-clickable="true"
773
- >
774
- ${calendarDate.date.getDate()}
775
- </td>
776
- `;
777
- }).join("")}
778
- </tr>
779
- `
780
- ).join("")}
781
- </tbody>
782
- </table>
783
-
784
- ${viewModel.selectedDate ? `
785
- <div class="date-popup ${viewModel.popupPositionClass}">
786
- <div class="popup-header">
787
- <h2>${viewModel.scheduleDay}</h2>
788
- <button type="button" class="popup-close" data-action="close-popup" aria-label="Close">\u2715</button>
789
- </div>
790
-
791
- ${showScrollHint ? `
792
- <div class="scroll-hint">
793
- \u2193 Scroll to see more events \u2193
794
- </div>
795
- ` : ""}
796
-
797
- <div class="events-container">
798
- ${viewModel.tasks.length > 0 ? `
799
- ${viewModel.tasks.map((event) => renderEvent(event)).join("")}
800
- ` : renderNoEvents()}
801
- </div>
802
- </div>
803
- ` : ""}
804
- </div>
805
- </div>
806
- `;
807
- this.container.innerHTML = html;
808
- this.attachEventListeners();
809
- }
810
- attachEventListeners() {
811
- if (this.listenersAttached) return;
812
- this.listenersAttached = true;
813
- const today = /* @__PURE__ */ new Date();
814
- const todayYear = today.getFullYear();
815
- const todayMonth = today.getMonth();
816
- const minYear = this.props.minYear ?? todayYear - 30;
817
- const maxYear = this.props.maxYear ?? todayYear + 10;
818
- this.delegatedClickHandler = (e) => {
819
- const target = e.target;
820
- const cell = target.closest('td[data-clickable="true"]');
821
- if (cell && cell.dataset.date) {
822
- e.stopPropagation();
823
- const date = new Date(cell.dataset.date);
824
- const dayIndex = parseInt(cell.dataset.dayIndex || "0");
825
- this.engine.handleDateClick(date, dayIndex);
826
- this.container.dispatchEvent(
827
- new CustomEvent("dateSelect", { detail: { date, dayIndex } })
828
- );
829
- return;
830
- }
831
- const actionEl = target.closest("[data-action]");
832
- if (!actionEl) return;
833
- const action = actionEl.dataset.action;
834
- e.stopPropagation();
835
- switch (action) {
836
- case "previous":
837
- this.actions.previous();
838
- this.dispatchMonthChange();
839
- break;
840
- case "next":
841
- this.actions.next();
842
- this.dispatchMonthChange();
843
- break;
844
- case "today":
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
- break;
853
- case "toggle-picker":
854
- this.pickerOpen = !this.pickerOpen;
855
- if (this.pickerOpen) {
856
- this.yearInput = String(this.engine.getViewModel().currentYear);
857
- this.yearInputValid = true;
858
- }
859
- this.render();
860
- break;
861
- case "year-prev": {
862
- const vm = this.engine.getViewModel();
863
- if (vm.currentYear > minYear) {
864
- const newYear = vm.currentYear - 1;
865
- this.yearInput = String(newYear);
866
- this.updatePickerYear(newYear);
867
- this.actions.jump(newYear, vm.currentMonth);
868
- this.dispatchMonthChange();
869
- }
870
- break;
871
- }
872
- case "year-next": {
873
- const vm = this.engine.getViewModel();
874
- if (vm.currentYear < maxYear) {
875
- const newYear = vm.currentYear + 1;
876
- this.yearInput = String(newYear);
877
- this.updatePickerYear(newYear);
878
- this.actions.jump(newYear, vm.currentMonth);
879
- this.dispatchMonthChange();
880
- }
881
- break;
882
- }
883
- case "select-month": {
884
- const month = parseInt(actionEl.dataset.month || "0", 10);
885
- this.actions.jump(this.engine.getViewModel().currentYear, month);
886
- this.pickerOpen = false;
887
- this.dispatchMonthChange();
888
- break;
889
- }
890
- case "close-popup":
891
- this.engine.clearSelection();
892
- break;
893
- }
894
- };
895
- this.delegatedInputHandler = (e) => {
896
- const target = e.target;
897
- if (!target.matches("[data-year-input]")) return;
898
- const value = target.value;
899
- if (value === "" || /^\d+$/.test(value)) {
900
- this.yearInput = value;
901
- const year = parseInt(value, 10);
902
- this.yearInputValid = value === "" || year >= minYear && year <= maxYear;
903
- target.classList.toggle("invalid", !this.yearInputValid);
904
- }
905
- };
906
- this.delegatedBlurHandler = (e) => {
907
- const target = e.target;
908
- if (!target.matches("[data-year-input]")) return;
909
- const year = parseInt(this.yearInput, 10);
910
- const vm = this.engine.getViewModel();
911
- if (isNaN(year) || year < minYear || year > maxYear) {
912
- this.yearInput = String(vm.currentYear);
913
- this.yearInputValid = true;
914
- target.value = this.yearInput;
915
- target.classList.remove("invalid");
916
- } else if (year !== vm.currentYear) {
917
- this.actions.jump(year, vm.currentMonth);
918
- this.dispatchMonthChange();
919
- }
920
- };
921
- this.delegatedKeydownHandler = (e) => {
922
- const target = e.target;
923
- if (!target.matches("[data-year-input]")) return;
924
- if (e.key === "Enter") {
925
- target.blur();
926
- }
927
- };
928
- this.container.addEventListener("click", this.delegatedClickHandler);
929
- this.container.addEventListener("input", this.delegatedInputHandler);
930
- this.container.addEventListener("blur", this.delegatedBlurHandler, true);
931
- this.container.addEventListener("keydown", this.delegatedKeydownHandler);
932
- this.clickOutsideHandler = (e) => {
933
- const target = e.target;
934
- if (this.container.contains(target)) return;
935
- if (this.pickerOpen) {
936
- this.pickerOpen = false;
937
- this.render();
938
- return;
939
- }
940
- const popup = this.container.querySelector(".date-popup");
941
- if (popup) {
942
- this.engine.clearSelection();
943
- }
944
- };
945
- document.addEventListener("click", this.clickOutsideHandler);
946
- }
947
- // Targeted update for picker year - avoids full DOM rebuild
948
- updatePickerYear(year) {
949
- const input = this.container.querySelector(
950
- "[data-year-input]"
951
- );
952
- if (input) {
953
- input.value = String(year);
954
- }
955
- const today = /* @__PURE__ */ new Date();
956
- const todayYear = today.getFullYear();
957
- const minYear = this.props.minYear ?? todayYear - 30;
958
- const maxYear = this.props.maxYear ?? todayYear + 10;
959
- const prevBtn = this.container.querySelector(
960
- '[data-action="year-prev"]'
961
- );
962
- const nextBtn = this.container.querySelector(
963
- '[data-action="year-next"]'
964
- );
965
- if (prevBtn) prevBtn.disabled = year <= minYear;
966
- if (nextBtn) nextBtn.disabled = year >= maxYear;
967
- }
968
- dispatchMonthChange() {
969
- const vm = this.engine.getViewModel();
970
- this.container.dispatchEvent(
971
- new CustomEvent("monthChange", {
972
- detail: { year: vm.currentYear, month: vm.currentMonth }
973
- })
974
- );
975
- }
976
- updateEvents(events) {
977
- this.engine.updateEvents(events);
978
- }
979
- updateTheme(theme) {
980
- this.props.theme = theme;
981
- this.applyTheme();
982
- }
983
- getCurrentDate() {
984
- return this.engine.getViewModel().selectedDate;
985
- }
986
- goToDate(date) {
987
- this.actions.jump(date.getFullYear(), date.getMonth());
988
- }
989
- getEngine() {
990
- return this.engine;
991
- }
992
- destroy() {
993
- if (this.unsubscribe) {
994
- this.unsubscribe();
995
- this.unsubscribe = null;
996
- }
997
- if (this.delegatedClickHandler) {
998
- this.container.removeEventListener("click", this.delegatedClickHandler);
999
- this.delegatedClickHandler = null;
1000
- }
1001
- if (this.delegatedInputHandler) {
1002
- this.container.removeEventListener("input", this.delegatedInputHandler);
1003
- this.delegatedInputHandler = null;
1004
- }
1005
- if (this.delegatedBlurHandler) {
1006
- this.container.removeEventListener(
1007
- "blur",
1008
- this.delegatedBlurHandler,
1009
- true
1010
- );
1011
- this.delegatedBlurHandler = null;
1012
- }
1013
- if (this.delegatedKeydownHandler) {
1014
- this.container.removeEventListener(
1015
- "keydown",
1016
- this.delegatedKeydownHandler
1017
- );
1018
- this.delegatedKeydownHandler = null;
1019
- }
1020
- if (this.clickOutsideHandler) {
1021
- document.removeEventListener("click", this.clickOutsideHandler);
1022
- this.clickOutsideHandler = null;
1023
- }
1024
- this.listenersAttached = false;
1025
- this.engine.destroy();
1026
- this.container.innerHTML = "";
1027
- this.container.classList.remove("kalendly-calendar");
1028
- if (this.props.className) {
1029
- this.container.classList.remove(this.props.className);
1030
- }
1031
- }
1032
- };
1033
- function createCalendar(props) {
1034
- return new VanillaCalendar(props);
1035
- }
1036
- // Annotate the CommonJS export names for ESM import in node:
1037
- 0 && (module.exports = {
1038
- CalendarEngine,
1039
- DAYS,
1040
- DEFAULT_CATEGORY_COLORS,
1041
- MONTHS,
1042
- MONTHS_FULL,
1043
- VanillaCalendar,
1044
- createCalendar,
1045
- formatAttendees,
1046
- formatDateForDisplay,
1047
- formatTimeRange,
1048
- generateCalendarDates,
1049
- generateYears,
1050
- getCategoryColor,
1051
- getCellClasses,
1052
- getDefaultEventColor,
1053
- getEventsForDate,
1054
- getMonthYearText,
1055
- getPopupPositionClass,
1056
- hasEvents,
1057
- isSameDay,
1058
- isToday,
1059
- isValidHexColor,
1060
- mergeCategoryColors,
1061
- normalizeDate,
1062
- sortEventsByTime
1063
- });
1064
- //# sourceMappingURL=index.js.map