kalendly 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -2
- package/dist/core/index.d.mts +20 -3
- package/dist/core/index.d.ts +20 -3
- package/dist/core/index.js +61 -5
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +58 -5
- package/dist/core/index.mjs.map +1 -1
- package/dist/index.d.mts +25 -3
- package/dist/index.d.ts +25 -3
- package/dist/index.js +157 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +154 -15
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +151 -15
- package/dist/index.umd.js.map +1 -1
- package/dist/styles/calendar.css +39 -11
- package/package.json +1 -1
package/dist/index.umd.js
CHANGED
|
@@ -78,6 +78,54 @@ var Kalendly = (() => {
|
|
|
78
78
|
const max = maxYear ?? currentYear + 10;
|
|
79
79
|
return Array.from({ length: max - min + 1 }, (_, i) => min + i);
|
|
80
80
|
}
|
|
81
|
+
function parseHourRanges(value, slotDuration) {
|
|
82
|
+
const parts = value.split(",").map((part) => part.trim()).filter(Boolean);
|
|
83
|
+
if (parts.length === 0) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`<kal-calendar> available-hours is empty. Omit the attribute to allow every hour, or name at least one HH:MM-HH:MM range.`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
const ranges = parts.map((part) => {
|
|
89
|
+
const halves = part.split("-");
|
|
90
|
+
if (halves.length !== 2) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`<kal-calendar> available-hours range "${part}" is not HH:MM-HH:MM.`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
const [start, end] = halves.map((half) => parseTimeToMinutes(half.trim()));
|
|
96
|
+
if (start === null || end === null) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`<kal-calendar> available-hours range "${part}" has an unreadable time. Use 24-hour HH:MM.`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
if (start >= end) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`<kal-calendar> available-hours range "${part}" starts at or after it ends.`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
if (start % slotDuration !== 0 || end % slotDuration !== 0) {
|
|
107
|
+
throw new Error(
|
|
108
|
+
`<kal-calendar> available-hours range "${part}" does not land on a ${slotDuration}-minute slot boundary.`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
return [start, end];
|
|
112
|
+
});
|
|
113
|
+
if (mergeIntervals(ranges).length !== ranges.length) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`<kal-calendar> available-hours ranges overlap or touch: "${value}". Write each bookable window once.`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
return ranges;
|
|
119
|
+
}
|
|
120
|
+
function isDateWithinWindow(date, min, max) {
|
|
121
|
+
const target = normalizeDate(date).getTime();
|
|
122
|
+
if (min && target < normalizeDate(min).getTime()) return false;
|
|
123
|
+
if (max && target > normalizeDate(max).getTime()) return false;
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
function isDayAllowed(date, days) {
|
|
127
|
+
return days === null || days.includes(date.getDay());
|
|
128
|
+
}
|
|
81
129
|
function eventCoversDate(event, date) {
|
|
82
130
|
const start = normalizeDate(new Date(event.date)).getTime();
|
|
83
131
|
const target = normalizeDate(date).getTime();
|
|
@@ -451,12 +499,14 @@ var Kalendly = (() => {
|
|
|
451
499
|
/**
|
|
452
500
|
* Select a specific date
|
|
453
501
|
*/
|
|
454
|
-
selectDate(date, dayIndex) {
|
|
502
|
+
selectDate(date, dayIndex, navigate = true) {
|
|
455
503
|
this.state.selectedDate = date;
|
|
456
504
|
this.state.selectedDayIndex = dayIndex ?? null;
|
|
457
505
|
this.state.currentDate = date.getDate();
|
|
458
|
-
|
|
459
|
-
|
|
506
|
+
if (navigate) {
|
|
507
|
+
this.state.currentMonth = date.getMonth();
|
|
508
|
+
this.state.currentYear = date.getFullYear();
|
|
509
|
+
}
|
|
460
510
|
this.updateTasks();
|
|
461
511
|
this.notify();
|
|
462
512
|
}
|
|
@@ -484,8 +534,8 @@ var Kalendly = (() => {
|
|
|
484
534
|
/**
|
|
485
535
|
* Handle date cell click
|
|
486
536
|
*/
|
|
487
|
-
handleDateClick(date, dayIndex) {
|
|
488
|
-
this.selectDate(date, dayIndex);
|
|
537
|
+
handleDateClick(date, dayIndex, options) {
|
|
538
|
+
this.selectDate(date, dayIndex, options?.navigate !== false);
|
|
489
539
|
}
|
|
490
540
|
/**
|
|
491
541
|
* Check if date has events
|
|
@@ -714,6 +764,41 @@ var Kalendly = (() => {
|
|
|
714
764
|
const val = this.getAttribute("max-year");
|
|
715
765
|
return val ? parseInt(val, 10) : (/* @__PURE__ */ new Date()).getFullYear() + 10;
|
|
716
766
|
}
|
|
767
|
+
boundaryDate(attr) {
|
|
768
|
+
const val = this.getAttribute(attr);
|
|
769
|
+
if (!val) return null;
|
|
770
|
+
const parsed = new Date(val);
|
|
771
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
772
|
+
throw new Error(
|
|
773
|
+
`<kal-calendar> ${attr} is unreadable: "${val}". Use a value Date can parse, the same as initial-date.`
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
return parsed;
|
|
777
|
+
}
|
|
778
|
+
get availableDays() {
|
|
779
|
+
const val = this.getAttribute("available-days");
|
|
780
|
+
if (val === null) return null;
|
|
781
|
+
const parts = val.split(",").map((part) => part.trim()).filter(Boolean);
|
|
782
|
+
if (parts.length === 0) {
|
|
783
|
+
throw new Error(
|
|
784
|
+
`<kal-calendar> available-days is empty. Omit the attribute to allow every day, or name at least one weekday.`
|
|
785
|
+
);
|
|
786
|
+
}
|
|
787
|
+
return parts.map((part) => {
|
|
788
|
+
const day = Number(part);
|
|
789
|
+
if (!Number.isInteger(day) || day < 0 || day > 6) {
|
|
790
|
+
throw new Error(
|
|
791
|
+
`<kal-calendar> available-days must list integers 0-6, 0 = Sunday. Got "${part}".`
|
|
792
|
+
);
|
|
793
|
+
}
|
|
794
|
+
return day;
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
get availableHours() {
|
|
798
|
+
const val = this.getAttribute("available-hours");
|
|
799
|
+
if (val === null) return null;
|
|
800
|
+
return parseHourRanges(val, this.slotDuration);
|
|
801
|
+
}
|
|
717
802
|
initEngine() {
|
|
718
803
|
const initialDateAttr = this.getAttribute("initial-date");
|
|
719
804
|
const initialDate = initialDateAttr ? new Date(initialDateAttr) : void 0;
|
|
@@ -832,8 +917,26 @@ var Kalendly = (() => {
|
|
|
832
917
|
if (declared.size === 0) return "open";
|
|
833
918
|
return this.knownBuckets.find((bucket) => declared.has(bucket));
|
|
834
919
|
}
|
|
920
|
+
assertHorizonOrdered() {
|
|
921
|
+
const min = this.boundaryDate("min-date");
|
|
922
|
+
const max = this.boundaryDate("max-date");
|
|
923
|
+
if (min && max && min.getTime() > max.getTime()) {
|
|
924
|
+
throw new Error(
|
|
925
|
+
`<kal-calendar> min-date is after max-date: "${this.getAttribute("min-date")}" > "${this.getAttribute("max-date")}".`
|
|
926
|
+
);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
// Whether the vendor offers this day at all, before any event is considered.
|
|
930
|
+
isDateOffered(date) {
|
|
931
|
+
return isDateWithinWindow(
|
|
932
|
+
date,
|
|
933
|
+
this.boundaryDate("min-date"),
|
|
934
|
+
this.boundaryDate("max-date")
|
|
935
|
+
) && isDayAllowed(date, this.availableDays);
|
|
936
|
+
}
|
|
835
937
|
isDateSelectable(date) {
|
|
836
938
|
if (!this.engine) return false;
|
|
939
|
+
if (!this.isDateOffered(date)) return false;
|
|
837
940
|
if (this._selectableStatuses) {
|
|
838
941
|
return this._selectableStatuses.includes(this.resolveBucket(date));
|
|
839
942
|
}
|
|
@@ -853,6 +956,11 @@ var Kalendly = (() => {
|
|
|
853
956
|
this.assertStatusesDeclared();
|
|
854
957
|
this.assertStatusesKnown();
|
|
855
958
|
}
|
|
959
|
+
this.boundaryDate("min-date");
|
|
960
|
+
this.boundaryDate("max-date");
|
|
961
|
+
void this.availableDays;
|
|
962
|
+
void this.availableHours;
|
|
963
|
+
this.assertHorizonOrdered();
|
|
856
964
|
const today = /* @__PURE__ */ new Date();
|
|
857
965
|
const todayMonth = today.getMonth();
|
|
858
966
|
const todayYear = today.getFullYear();
|
|
@@ -896,7 +1004,7 @@ var Kalendly = (() => {
|
|
|
896
1004
|
return `
|
|
897
1005
|
<div class="event-card" style="border-left-color: ${borderColor}">
|
|
898
1006
|
<div class="event-header">
|
|
899
|
-
|
|
1007
|
+
${event.name ? `<div class="event-title">${escapeHtml(event.name)}</div>` : ""}
|
|
900
1008
|
<div class="event-badges">
|
|
901
1009
|
${event.category ? `<span class="badge category category-${slugifyToken(event.category)}">${escapeHtml(getCategoryLabel(event.category))}</span>` : ""}
|
|
902
1010
|
${event.priority ? `<span class="badge priority priority-${slugifyToken(event.priority)}">${escapeHtml(getPriorityLabel(event.priority))}</span>` : ""}
|
|
@@ -972,25 +1080,31 @@ var Kalendly = (() => {
|
|
|
972
1080
|
];
|
|
973
1081
|
this.warnMissingEndTime(events);
|
|
974
1082
|
const booked = bookedSlots(events, date, slotDuration);
|
|
1083
|
+
const hours = this.availableHours;
|
|
975
1084
|
const slots = booked.map((isBooked, index) => {
|
|
976
|
-
const
|
|
977
|
-
const
|
|
978
|
-
const
|
|
1085
|
+
const slotStart = index * slotDuration;
|
|
1086
|
+
const slotEnd = slotStart + slotDuration;
|
|
1087
|
+
const startTime = formatMinutes(slotStart);
|
|
1088
|
+
const endTime = formatMinutes(slotEnd);
|
|
1089
|
+
const offered = hours === null || hours.some(([from, to]) => slotStart >= from && slotEnd <= to);
|
|
1090
|
+
const inTimeRange = offered && !isBooked && selectable && this._timeRangeStart !== null && this._timeRangeEnd !== null && isSameDay2(this._timeRangeDate, date) && startTime >= this._timeRangeStart && endTime <= this._timeRangeEnd;
|
|
979
1091
|
const isRangeStart = inTimeRange && startTime === this._timeRangeStart;
|
|
980
1092
|
const isRangeEnd = inTimeRange && endTime === this._timeRangeEnd;
|
|
981
1093
|
const isInRange = inTimeRange && !isRangeStart && !isRangeEnd;
|
|
982
1094
|
const slotClasses = [
|
|
983
1095
|
"time-grid-slot",
|
|
1096
|
+
offered ? "" : "time-grid-slot-out-of-range",
|
|
984
1097
|
isBooked ? "time-grid-slot-blocked" : "time-grid-slot-open",
|
|
985
1098
|
isRangeStart ? "time-grid-slot-range-start" : "",
|
|
986
1099
|
isRangeEnd ? "time-grid-slot-range-end" : "",
|
|
987
1100
|
isInRange ? "time-grid-slot-in-range" : ""
|
|
988
1101
|
].filter(Boolean).join(" ");
|
|
989
|
-
const slotAttrs =
|
|
1102
|
+
const slotAttrs = `${offered ? 'data-action="select-slot" ' : ""}data-start-time="${startTime}" data-end-time="${endTime}" data-date="${date.toISOString()}" data-booked="${isBooked}"`;
|
|
1103
|
+
const status = !offered ? "Closed" : isBooked ? "Booked" : "Available";
|
|
990
1104
|
return `
|
|
991
1105
|
<div class="${slotClasses}" ${slotAttrs}>
|
|
992
1106
|
<span class="time-grid-label">${startTime}</span>
|
|
993
|
-
<span class="time-grid-status">${
|
|
1107
|
+
<span class="time-grid-status">${status}</span>
|
|
994
1108
|
</div>`;
|
|
995
1109
|
});
|
|
996
1110
|
const gridClasses = selectable ? "time-grid time-grid-selectable" : "time-grid";
|
|
@@ -1025,7 +1139,7 @@ var Kalendly = (() => {
|
|
|
1025
1139
|
)) {
|
|
1026
1140
|
classes.push("calendar-cell-selected");
|
|
1027
1141
|
}
|
|
1028
|
-
if (availabilityMode
|
|
1142
|
+
if (availabilityMode) {
|
|
1029
1143
|
const bucket = this.resolveBucket(
|
|
1030
1144
|
calendarDate.date
|
|
1031
1145
|
);
|
|
@@ -1046,7 +1160,7 @@ var Kalendly = (() => {
|
|
|
1046
1160
|
`aria-label="${escapeHtml(bucket)}"`
|
|
1047
1161
|
);
|
|
1048
1162
|
}
|
|
1049
|
-
if (availabilityMode === "day" && selectable
|
|
1163
|
+
if (availabilityMode === "day" && selectable) {
|
|
1050
1164
|
const d = calendarDate.date;
|
|
1051
1165
|
if (this._rangeStart && isSameDay2(d, this._rangeStart))
|
|
1052
1166
|
classes.push("availability-range-start");
|
|
@@ -1058,12 +1172,18 @@ var Kalendly = (() => {
|
|
|
1058
1172
|
}
|
|
1059
1173
|
}
|
|
1060
1174
|
const dateString = calendarDate.date.toISOString();
|
|
1175
|
+
const offered = this.isDateOffered(
|
|
1176
|
+
calendarDate.date
|
|
1177
|
+
);
|
|
1178
|
+
if (!offered) {
|
|
1179
|
+
classes.push("calendar-cell-out-of-range");
|
|
1180
|
+
}
|
|
1061
1181
|
return `
|
|
1062
1182
|
<td
|
|
1063
1183
|
class="${classes.join(" ")}"
|
|
1064
1184
|
data-date="${dateString}"
|
|
1065
1185
|
data-day-index="${dayIndex}"
|
|
1066
|
-
data-clickable="true"
|
|
1186
|
+
${offered ? 'data-clickable="true"' : ""}
|
|
1067
1187
|
${cellAttrs.join(" ")}
|
|
1068
1188
|
>
|
|
1069
1189
|
${calendarDate.date.getDate()}
|
|
@@ -1251,7 +1371,7 @@ var Kalendly = (() => {
|
|
|
1251
1371
|
this._timeRangeEnd = null;
|
|
1252
1372
|
this._timeRangeComplete = false;
|
|
1253
1373
|
}
|
|
1254
|
-
this.engine.handleDateClick(date, dayIndex);
|
|
1374
|
+
this.engine.handleDateClick(date, dayIndex, { navigate: false });
|
|
1255
1375
|
this.dispatchEvent(
|
|
1256
1376
|
new CustomEvent("cal-date-select", {
|
|
1257
1377
|
bubbles: true,
|
|
@@ -1489,6 +1609,10 @@ var Kalendly = (() => {
|
|
|
1489
1609
|
"initial-date",
|
|
1490
1610
|
"min-year",
|
|
1491
1611
|
"max-year",
|
|
1612
|
+
"min-date",
|
|
1613
|
+
"max-date",
|
|
1614
|
+
"available-days",
|
|
1615
|
+
"available-hours",
|
|
1492
1616
|
"week-starts-on",
|
|
1493
1617
|
"heading",
|
|
1494
1618
|
"months",
|
|
@@ -1510,6 +1634,18 @@ var Kalendly = (() => {
|
|
|
1510
1634
|
borderColor: "--calendar-border-color",
|
|
1511
1635
|
todayOutline: "--calendar-today-outline",
|
|
1512
1636
|
selectedBg: "--calendar-selected-bg",
|
|
1637
|
+
outOfRangeBg: "--calendar-out-of-range-bg",
|
|
1638
|
+
outOfRangeFg: "--calendar-out-of-range-fg",
|
|
1639
|
+
navArrowFg: "--calendar-nav-arrow-fg",
|
|
1640
|
+
navArrowBg: "--calendar-nav-arrow-bg",
|
|
1641
|
+
navArrowBorder: "--calendar-nav-arrow-border",
|
|
1642
|
+
navArrowHoverFg: "--calendar-nav-arrow-hover-fg",
|
|
1643
|
+
navArrowHoverBg: "--calendar-nav-arrow-hover-bg",
|
|
1644
|
+
inputInvalidBg: "--calendar-input-invalid-bg",
|
|
1645
|
+
popupHeaderFg: "--calendar-popup-header-fg",
|
|
1646
|
+
popupCloseFg: "--calendar-popup-close-fg",
|
|
1647
|
+
popupCloseBg: "--calendar-popup-close-bg",
|
|
1648
|
+
popupCloseHoverBg: "--calendar-popup-close-hover-bg",
|
|
1513
1649
|
headerBg: "--calendar-header-bg",
|
|
1514
1650
|
popupBg: "--calendar-popup-bg",
|
|
1515
1651
|
pickerBg: "--calendar-picker-bg",
|