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
@@ -18,34 +18,11 @@ var Kalendly = (() => {
18
18
  };
19
19
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
20
 
21
- // src/vanilla/index.ts
22
- var index_exports = {};
23
- __export(index_exports, {
24
- CalendarEngine: () => CalendarEngine,
25
- DAYS: () => DAYS,
26
- DEFAULT_CATEGORY_COLORS: () => DEFAULT_CATEGORY_COLORS,
27
- MONTHS: () => MONTHS,
28
- MONTHS_FULL: () => MONTHS_FULL,
29
- VanillaCalendar: () => VanillaCalendar,
30
- createCalendar: () => createCalendar,
31
- formatAttendees: () => formatAttendees,
32
- formatDateForDisplay: () => formatDateForDisplay,
33
- formatTimeRange: () => formatTimeRange,
34
- generateCalendarDates: () => generateCalendarDates,
35
- generateYears: () => generateYears,
36
- getCategoryColor: () => getCategoryColor,
37
- getCellClasses: () => getCellClasses,
38
- getDefaultEventColor: () => getDefaultEventColor,
39
- getEventsForDate: () => getEventsForDate,
40
- getMonthYearText: () => getMonthYearText,
41
- getPopupPositionClass: () => getPopupPositionClass,
42
- hasEvents: () => hasEvents,
43
- isSameDay: () => isSameDay,
44
- isToday: () => isToday,
45
- isValidHexColor: () => isValidHexColor,
46
- mergeCategoryColors: () => mergeCategoryColors,
47
- normalizeDate: () => normalizeDate,
48
- sortEventsByTime: () => sortEventsByTime
21
+ // src/web-components/CalendarElement.ts
22
+ var CalendarElement_exports = {};
23
+ __export(CalendarElement_exports, {
24
+ CalendarElement: () => CalendarElement,
25
+ defineCalendarElement: () => defineCalendarElement
49
26
  });
50
27
 
51
28
  // src/core/utils.ts
@@ -108,9 +85,6 @@ var Kalendly = (() => {
108
85
  return eventDate.getTime() === normalizedTargetDate.getTime();
109
86
  });
110
87
  }
111
- function hasEvents(events, date) {
112
- return getEventsForDate(events, date).length > 0;
113
- }
114
88
  function generateCalendarDates(year, month, events = [], weekStartsOn = 0) {
115
89
  const firstDay = new Date(year, month, 1);
116
90
  const lastDay = new Date(year, month + 1, 0);
@@ -212,16 +186,6 @@ var Kalendly = (() => {
212
186
  if (attendees.length === 2) return attendees.join(" and ");
213
187
  return `${attendees.slice(0, -1).join(", ")}, and ${attendees[attendees.length - 1]}`;
214
188
  }
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
189
  var DEFAULT_CATEGORY_COLORS = {
226
190
  work: "#3b82f6",
227
191
  personal: "#8b5cf6",
@@ -230,13 +194,6 @@ var Kalendly = (() => {
230
194
  appointment: "#f59e0b",
231
195
  other: "#6b7280"
232
196
  };
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
197
  function mergeCategoryColors(customColors) {
241
198
  return {
242
199
  ...DEFAULT_CATEGORY_COLORS,
@@ -480,78 +437,174 @@ var Kalendly = (() => {
480
437
  }
481
438
  };
482
439
 
483
- // src/vanilla/Calendar.ts
484
- var VanillaCalendar = class {
485
- constructor(props) {
440
+ // src/web-components/CalendarElement.ts
441
+ var CalendarElement = class extends HTMLElement {
442
+ constructor() {
443
+ super(...arguments);
444
+ this.engine = null;
486
445
  this.unsubscribe = null;
446
+ this.actions = null;
487
447
  this.pickerOpen = false;
488
448
  this.yearInput = "";
489
449
  this.yearInputValid = true;
450
+ this.listenersAttached = false;
490
451
  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;
452
+ this.delegatedClickHandler = null;
453
+ this.delegatedInputHandler = null;
454
+ this.delegatedBlurHandler = null;
455
+ this.delegatedKeydownHandler = null;
456
+ // Rich property backing fields
457
+ this._events = [];
458
+ this._theme = null;
459
+ this._categoryColors = null;
460
+ this._renderEvent = null;
461
+ this._renderNoEvents = null;
462
+ }
463
+ get events() {
464
+ return this._events;
465
+ }
466
+ set events(val) {
467
+ this._events = val;
468
+ if (this.engine) {
469
+ this.engine.updateEvents(val);
500
470
  }
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();
471
+ }
472
+ set theme(val) {
473
+ this._theme = val;
509
474
  this.applyTheme();
510
- this.init();
511
475
  }
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
- );
476
+ set categoryColors(val) {
477
+ this._categoryColors = val;
478
+ if (this.engine) {
479
+ this.engine.updateCategoryColors(val);
541
480
  }
542
481
  }
543
- init() {
544
- this.container.classList.add("kalendly-calendar");
545
- if (this.props.className) {
546
- this.container.classList.add(this.props.className);
547
- }
482
+ set renderEvent(val) {
483
+ this._renderEvent = val;
484
+ if (this.engine) this.render();
485
+ }
486
+ set renderNoEvents(val) {
487
+ this._renderNoEvents = val;
488
+ if (this.engine) this.render();
489
+ }
490
+ connectedCallback() {
491
+ this.classList.add("kalendly-calendar");
492
+ this.initEngine();
493
+ this.render();
494
+ }
495
+ disconnectedCallback() {
496
+ this.cleanup();
497
+ this.classList.remove("kalendly-calendar");
498
+ this.innerHTML = "";
499
+ }
500
+ attributeChangedCallback(_name, oldVal, newVal) {
501
+ if (oldVal === newVal) return;
502
+ this.reinit();
503
+ }
504
+ get minYear() {
505
+ const val = this.getAttribute("min-year");
506
+ return val ? parseInt(val, 10) : (/* @__PURE__ */ new Date()).getFullYear() - 30;
507
+ }
508
+ get maxYear() {
509
+ const val = this.getAttribute("max-year");
510
+ return val ? parseInt(val, 10) : (/* @__PURE__ */ new Date()).getFullYear() + 10;
511
+ }
512
+ initEngine() {
513
+ const initialDateAttr = this.getAttribute("initial-date");
514
+ const initialDate = initialDateAttr ? new Date(initialDateAttr) : void 0;
515
+ const weekStartsOnAttr = this.getAttribute("week-starts-on");
516
+ const weekStartsOn = weekStartsOnAttr === "1" ? 1 : 0;
517
+ this.engine = new CalendarEngine({
518
+ events: this._events,
519
+ initialDate,
520
+ minYear: this.minYear,
521
+ maxYear: this.maxYear,
522
+ weekStartsOn,
523
+ categoryColors: this._categoryColors ?? void 0
524
+ });
525
+ this.actions = this.engine.getActions();
526
+ this.applyTheme();
548
527
  this.unsubscribe = this.engine.subscribe(() => {
549
528
  this.render();
550
529
  });
530
+ }
531
+ reinit() {
532
+ if (!this.engine) return;
533
+ this.cleanup();
534
+ this.initEngine();
551
535
  this.render();
552
536
  }
537
+ cleanup() {
538
+ if (this.unsubscribe) {
539
+ this.unsubscribe();
540
+ this.unsubscribe = null;
541
+ }
542
+ if (this.delegatedClickHandler) {
543
+ this.removeEventListener("click", this.delegatedClickHandler);
544
+ this.delegatedClickHandler = null;
545
+ }
546
+ if (this.delegatedInputHandler) {
547
+ this.removeEventListener("input", this.delegatedInputHandler);
548
+ this.delegatedInputHandler = null;
549
+ }
550
+ if (this.delegatedBlurHandler) {
551
+ this.removeEventListener("blur", this.delegatedBlurHandler, true);
552
+ this.delegatedBlurHandler = null;
553
+ }
554
+ if (this.delegatedKeydownHandler) {
555
+ this.removeEventListener("keydown", this.delegatedKeydownHandler);
556
+ this.delegatedKeydownHandler = null;
557
+ }
558
+ if (this.clickOutsideHandler) {
559
+ document.removeEventListener("click", this.clickOutsideHandler);
560
+ this.clickOutsideHandler = null;
561
+ }
562
+ this.listenersAttached = false;
563
+ if (this.engine) {
564
+ this.engine.destroy();
565
+ this.engine = null;
566
+ }
567
+ this.actions = null;
568
+ }
569
+ applyTheme() {
570
+ if (!this._theme) return;
571
+ const root = document.documentElement;
572
+ const t = this._theme;
573
+ if (t.primary)
574
+ root.style.setProperty("--calendar-primary-color", t.primary);
575
+ if (t.secondary)
576
+ root.style.setProperty("--calendar-secondary-color", t.secondary);
577
+ if (t.tertiary)
578
+ root.style.setProperty("--calendar-tertiary-color", t.tertiary);
579
+ if (t.textColor)
580
+ root.style.setProperty("--calendar-text-color", t.textColor);
581
+ if (t.textLight)
582
+ root.style.setProperty("--calendar-text-light", t.textLight);
583
+ if (t.background)
584
+ root.style.setProperty("--calendar-background", t.background);
585
+ if (t.cellHover)
586
+ root.style.setProperty("--calendar-cell-hover", t.cellHover);
587
+ if (t.borderColor)
588
+ root.style.setProperty("--calendar-border-color", t.borderColor);
589
+ if (t.todayOutline)
590
+ root.style.setProperty("--calendar-today-outline", t.todayOutline);
591
+ if (t.selectedBg)
592
+ root.style.setProperty("--calendar-selected-bg", t.selectedBg);
593
+ if (t.eventIndicator)
594
+ root.style.setProperty("--calendar-event-indicator", t.eventIndicator);
595
+ }
553
596
  render() {
597
+ if (!this.engine) return;
554
598
  const viewModel = this.engine.getViewModel();
599
+ const useShortMonths = this.hasAttribute("use-short-month-names");
600
+ const title = this.getAttribute("title");
601
+ const minYear = this.minYear;
602
+ const maxYear = this.maxYear;
603
+ const today = /* @__PURE__ */ new Date();
604
+ const todayMonth = today.getMonth();
605
+ const todayYear = today.getFullYear();
606
+ const isCurrentMonth = viewModel.currentYear === todayYear && viewModel.currentMonth === todayMonth;
607
+ const showScrollHint = viewModel.tasks.length > 3;
555
608
  const defaultRenderEvent = (event) => {
556
609
  const timeRange = formatTimeRange(event);
557
610
  const attendeesList = formatAttendees(event.attendees);
@@ -597,46 +650,46 @@ var Kalendly = (() => {
597
650
  ${event.status && event.status !== "scheduled" ? `<span class="badge status-${event.status}">${getStatusLabel(event.status)}</span>` : ""}
598
651
  </div>
599
652
  </div>
600
-
653
+
601
654
  ${timeRange ? `
602
655
  <div class="event-time">
603
656
  <span class="event-time-label">Time:</span>
604
657
  <span class="event-time-value">${timeRange}</span>
605
658
  </div>
606
659
  ` : ""}
607
-
660
+
608
661
  ${event.description ? `
609
662
  <div class="event-description">${event.description}</div>
610
663
  ` : ""}
611
-
664
+
612
665
  ${event.location ? `
613
666
  <div class="event-time">
614
667
  <span class="event-time-label">Location:</span>
615
668
  <span class="event-time-value">${event.location}</span>
616
669
  </div>
617
670
  ` : ""}
618
-
671
+
619
672
  ${attendeesList ? `
620
673
  <div class="event-time">
621
674
  <span class="event-time-label">Attendees:</span>
622
675
  <span class="event-time-value">${attendeesList}</span>
623
676
  </div>
624
677
  ` : ""}
625
-
678
+
626
679
  ${event.organizer ? `
627
680
  <div class="event-time">
628
681
  <span class="event-time-label">Organizer:</span>
629
682
  <span class="event-time-value">${event.organizer}</span>
630
683
  </div>
631
684
  ` : ""}
632
-
685
+
633
686
  ${event.notes ? `
634
687
  <div class="event-time">
635
688
  <span class="event-time-label">Notes:</span>
636
689
  <span class="event-time-value">${event.notes}</span>
637
690
  </div>
638
691
  ` : ""}
639
-
692
+
640
693
  ${event.url ? `
641
694
  <div class="event-time">
642
695
  <a href="${event.url}" target="_blank" rel="noopener noreferrer" class="event-link">
@@ -644,7 +697,7 @@ var Kalendly = (() => {
644
697
  </a>
645
698
  </div>
646
699
  ` : ""}
647
-
700
+
648
701
  ${event.tags && event.tags.length > 0 ? `
649
702
  <div class="event-tags">
650
703
  ${event.tags.map((tag) => `<span class="event-tag">${tag}</span>`).join("")}
@@ -654,25 +707,17 @@ var Kalendly = (() => {
654
707
  `;
655
708
  };
656
709
  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;
710
+ const renderEvent = this._renderEvent || defaultRenderEvent;
711
+ const renderNoEvents = this._renderNoEvents || defaultRenderNoEvents;
666
712
  const html = `
667
- ${this.props.title ? `
668
- <div class="page--title">
669
- <h1>${this.props.title}</h1>
670
- </div>
713
+ ${title ? `
714
+ <div class="page--title">
715
+ <h1>${title}</h1>
716
+ </div>
671
717
  ` : ""}
672
718
 
673
719
  <div class="calendar--content">
674
720
  <div class="calendar--card">
675
- <!-- Navigation Header -->
676
721
  <div class="calendar--nav-header">
677
722
  <button type="button" class="calendar--nav-arrow" data-action="previous" aria-label="Previous month">
678
723
  &#8249;
@@ -686,7 +731,7 @@ var Kalendly = (() => {
686
731
  aria-expanded="${this.pickerOpen ? "true" : "false"}"
687
732
  aria-haspopup="true"
688
733
  >
689
- ${this.props.useShortMonthNames ? `${MONTHS[viewModel.currentMonth]} ${viewModel.currentYear}` : viewModel.monthAndYearText}
734
+ ${useShortMonths ? `${MONTHS[viewModel.currentMonth]} ${viewModel.currentYear}` : viewModel.monthAndYearText}
690
735
  <span class="calendar--picker-chevron">&#9662;</span>
691
736
  </button>
692
737
 
@@ -717,7 +762,7 @@ var Kalendly = (() => {
717
762
  </div>
718
763
 
719
764
  <div class="calendar--picker-months">
720
- ${(this.props.useShortMonthNames ? MONTHS : MONTHS_FULL).map((month, index) => {
765
+ ${(useShortMonths ? MONTHS : MONTHS_FULL).map((month, index) => {
721
766
  const isSelected = index === viewModel.currentMonth;
722
767
  const isCurrent = index === todayMonth && viewModel.currentYear === todayYear;
723
768
  return `
@@ -790,233 +835,204 @@ var Kalendly = (() => {
790
835
  ` : ""}
791
836
 
792
837
  <div class="events-container">
793
- ${viewModel.tasks.length > 0 ? `
794
- ${viewModel.tasks.map((event) => renderEvent(event)).join("")}
795
- ` : renderNoEvents()}
838
+ ${viewModel.tasks.length > 0 ? viewModel.tasks.map((event) => renderEvent(event)).join("") : renderNoEvents()}
796
839
  </div>
797
840
  </div>
798
841
  ` : ""}
799
842
  </div>
800
843
  </div>
801
844
  `;
802
- this.container.innerHTML = html;
845
+ this.innerHTML = html;
803
846
  this.attachEventListeners();
804
847
  }
805
848
  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 }
849
+ if (this.listenersAttached) return;
850
+ this.listenersAttached = true;
851
+ const minYear = this.minYear;
852
+ const maxYear = this.maxYear;
853
+ this.delegatedClickHandler = (e) => {
854
+ const target = e.target;
855
+ const cell = target.closest('td[data-clickable="true"]');
856
+ if (cell && cell.dataset.date) {
857
+ e.stopPropagation();
858
+ const date = new Date(cell.dataset.date);
859
+ const dayIndex = parseInt(cell.dataset.dayIndex || "0");
860
+ this.engine.handleDateClick(date, dayIndex);
861
+ this.dispatchEvent(
862
+ new CustomEvent("cal-date-select", {
863
+ bubbles: true,
864
+ composed: true,
865
+ detail: { date, events: this.engine.getEventsForDate(date) }
850
866
  })
851
867
  );
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);
868
+ return;
869
+ }
870
+ const actionEl = target.closest("[data-action]");
871
+ if (!actionEl) return;
872
+ const action = actionEl.dataset.action;
873
+ e.stopPropagation();
874
+ switch (action) {
875
+ case "previous":
876
+ this.actions.previous();
881
877
  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);
878
+ break;
879
+ case "next":
880
+ this.actions.next();
892
881
  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;
882
+ break;
883
+ case "today":
884
+ this.actions.goToToday();
885
+ this.pickerOpen = false;
886
+ this.dispatchMonthChange();
887
+ break;
888
+ case "toggle-picker":
889
+ this.pickerOpen = !this.pickerOpen;
890
+ if (this.pickerOpen) {
891
+ this.yearInput = String(this.engine.getViewModel().currentYear);
892
+ this.yearInputValid = true;
893
+ }
907
894
  this.render();
895
+ break;
896
+ case "year-prev": {
897
+ const vm = this.engine.getViewModel();
898
+ if (vm.currentYear > minYear) {
899
+ const newYear = vm.currentYear - 1;
900
+ this.yearInput = String(newYear);
901
+ this.updatePickerYear(newYear);
902
+ this.actions.jump(newYear, vm.currentMonth);
903
+ this.dispatchMonthChange();
904
+ }
905
+ break;
908
906
  }
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();
907
+ case "year-next": {
908
+ const vm = this.engine.getViewModel();
909
+ if (vm.currentYear < maxYear) {
910
+ const newYear = vm.currentYear + 1;
911
+ this.yearInput = String(newYear);
912
+ this.updatePickerYear(newYear);
913
+ this.actions.jump(newYear, vm.currentMonth);
914
+ this.dispatchMonthChange();
915
+ }
916
+ break;
920
917
  }
921
- this.render();
922
- });
923
- yearInputEl.addEventListener("keydown", (e) => {
924
- if (e.key === "Enter") {
925
- yearInputEl.blur();
918
+ case "select-month": {
919
+ const month = parseInt(actionEl.dataset.month || "0", 10);
920
+ this.actions.jump(this.engine.getViewModel().currentYear, month);
921
+ this.pickerOpen = false;
922
+ this.dispatchMonthChange();
923
+ break;
926
924
  }
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;
925
+ case "close-popup":
926
+ this.engine.clearSelection();
927
+ break;
928
+ }
929
+ };
930
+ this.delegatedInputHandler = (e) => {
931
+ const target = e.target;
932
+ if (!target.matches("[data-year-input]")) return;
933
+ const value = target.value;
934
+ if (value === "" || /^\d+$/.test(value)) {
935
+ this.yearInput = value;
936
+ const year = parseInt(value, 10);
937
+ this.yearInputValid = value === "" || year >= minYear && year <= maxYear;
938
+ target.classList.toggle("invalid", !this.yearInputValid);
939
+ }
940
+ };
941
+ this.delegatedBlurHandler = (e) => {
942
+ const target = e.target;
943
+ if (!target.matches("[data-year-input]")) return;
944
+ const year = parseInt(this.yearInput, 10);
945
+ const vm = this.engine.getViewModel();
946
+ if (isNaN(year) || year < minYear || year > maxYear) {
947
+ this.yearInput = String(vm.currentYear);
948
+ this.yearInputValid = true;
949
+ target.value = this.yearInput;
950
+ target.classList.remove("invalid");
951
+ } else if (year !== vm.currentYear) {
952
+ this.actions.jump(year, vm.currentMonth);
938
953
  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
- }
954
+ }
955
+ };
956
+ this.delegatedKeydownHandler = (e) => {
957
+ const target = e.target;
958
+ if (!target.matches("[data-year-input]")) return;
959
+ if (e.key === "Enter") target.blur();
960
+ };
961
+ this.addEventListener("click", this.delegatedClickHandler);
962
+ this.addEventListener("input", this.delegatedInputHandler);
963
+ this.addEventListener("blur", this.delegatedBlurHandler, true);
964
+ this.addEventListener("keydown", this.delegatedKeydownHandler);
953
965
  this.clickOutsideHandler = (e) => {
954
966
  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)) {
967
+ if (this.contains(target)) return;
968
+ if (this.pickerOpen) {
965
969
  this.pickerOpen = false;
966
970
  this.render();
967
971
  return;
968
972
  }
969
- const popup = this.container.querySelector(".date-popup");
970
- if (popup && !popup.contains(target) && !target.closest("[data-calendar-body]")) {
973
+ const popup = this.querySelector(".date-popup");
974
+ if (popup) {
971
975
  this.engine.clearSelection();
972
976
  }
973
977
  };
974
978
  document.addEventListener("click", this.clickOutsideHandler);
975
979
  }
980
+ updatePickerYear(year) {
981
+ const input = this.querySelector("[data-year-input]");
982
+ if (input) input.value = String(year);
983
+ const prevBtn = this.querySelector(
984
+ '[data-action="year-prev"]'
985
+ );
986
+ const nextBtn = this.querySelector(
987
+ '[data-action="year-next"]'
988
+ );
989
+ if (prevBtn) prevBtn.disabled = year <= this.minYear;
990
+ if (nextBtn) nextBtn.disabled = year >= this.maxYear;
991
+ }
976
992
  dispatchMonthChange() {
993
+ if (!this.engine) return;
977
994
  const vm = this.engine.getViewModel();
978
- this.container.dispatchEvent(
979
- new CustomEvent("monthChange", {
995
+ this.dispatchEvent(
996
+ new CustomEvent("cal-month-change", {
997
+ bubbles: true,
998
+ composed: true,
980
999
  detail: { year: vm.currentYear, month: vm.currentMonth }
981
1000
  })
982
1001
  );
983
1002
  }
1003
+ // Public API
984
1004
  updateEvents(events) {
985
- this.engine.updateEvents(events);
1005
+ this.events = events;
986
1006
  }
987
1007
  updateTheme(theme) {
988
- this.props.theme = theme;
989
- this.applyTheme();
1008
+ this.theme = theme;
990
1009
  }
991
1010
  getCurrentDate() {
992
- return this.engine.getViewModel().selectedDate;
1011
+ return this.engine?.getViewModel().selectedDate ?? null;
993
1012
  }
994
1013
  goToDate(date) {
995
- this.actions.jump(date.getFullYear(), date.getMonth());
1014
+ this.actions?.jump(date.getFullYear(), date.getMonth());
996
1015
  }
997
1016
  getEngine() {
1017
+ if (!this.engine)
1018
+ throw new Error("CalendarElement is not connected to the DOM");
998
1019
  return this.engine;
999
1020
  }
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
1021
  };
1017
- function createCalendar(props) {
1018
- return new VanillaCalendar(props);
1022
+ CalendarElement.observedAttributes = [
1023
+ "initial-date",
1024
+ "min-year",
1025
+ "max-year",
1026
+ "week-starts-on",
1027
+ "title",
1028
+ "use-short-month-names"
1029
+ ];
1030
+ function defineCalendarElement(tagName = "kal-calendar") {
1031
+ if (typeof customElements !== "undefined" && !customElements.get(tagName)) {
1032
+ customElements.define(tagName, CalendarElement);
1033
+ }
1019
1034
  }
1020
- return __toCommonJS(index_exports);
1035
+ return __toCommonJS(CalendarElement_exports);
1021
1036
  })();
1037
+ if (typeof customElements !== 'undefined' && !customElements.get('kal-calendar')) { customElements.define('kal-calendar', Kalendly.CalendarElement); }
1022
1038
  //# sourceMappingURL=index.umd.js.map