kalendly 0.3.1 → 0.3.2
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 +51 -1
- package/dist/core/index.d.mts +7 -2
- package/dist/core/index.d.ts +7 -2
- package/dist/core/index.js +54 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +51 -0
- package/dist/core/index.mjs.map +1 -1
- package/dist/index.d.mts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +137 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +134 -7
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +131 -7
- package/dist/index.umd.js.map +1 -1
- package/dist/styles/calendar.css +17 -0
- 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();
|
|
@@ -714,6 +762,41 @@ var Kalendly = (() => {
|
|
|
714
762
|
const val = this.getAttribute("max-year");
|
|
715
763
|
return val ? parseInt(val, 10) : (/* @__PURE__ */ new Date()).getFullYear() + 10;
|
|
716
764
|
}
|
|
765
|
+
boundaryDate(attr) {
|
|
766
|
+
const val = this.getAttribute(attr);
|
|
767
|
+
if (!val) return null;
|
|
768
|
+
const parsed = new Date(val);
|
|
769
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
770
|
+
throw new Error(
|
|
771
|
+
`<kal-calendar> ${attr} is unreadable: "${val}". Use a value Date can parse, the same as initial-date.`
|
|
772
|
+
);
|
|
773
|
+
}
|
|
774
|
+
return parsed;
|
|
775
|
+
}
|
|
776
|
+
get availableDays() {
|
|
777
|
+
const val = this.getAttribute("available-days");
|
|
778
|
+
if (val === null) return null;
|
|
779
|
+
const parts = val.split(",").map((part) => part.trim()).filter(Boolean);
|
|
780
|
+
if (parts.length === 0) {
|
|
781
|
+
throw new Error(
|
|
782
|
+
`<kal-calendar> available-days is empty. Omit the attribute to allow every day, or name at least one weekday.`
|
|
783
|
+
);
|
|
784
|
+
}
|
|
785
|
+
return parts.map((part) => {
|
|
786
|
+
const day = Number(part);
|
|
787
|
+
if (!Number.isInteger(day) || day < 0 || day > 6) {
|
|
788
|
+
throw new Error(
|
|
789
|
+
`<kal-calendar> available-days must list integers 0-6, 0 = Sunday. Got "${part}".`
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
return day;
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
get availableHours() {
|
|
796
|
+
const val = this.getAttribute("available-hours");
|
|
797
|
+
if (val === null) return null;
|
|
798
|
+
return parseHourRanges(val, this.slotDuration);
|
|
799
|
+
}
|
|
717
800
|
initEngine() {
|
|
718
801
|
const initialDateAttr = this.getAttribute("initial-date");
|
|
719
802
|
const initialDate = initialDateAttr ? new Date(initialDateAttr) : void 0;
|
|
@@ -832,8 +915,26 @@ var Kalendly = (() => {
|
|
|
832
915
|
if (declared.size === 0) return "open";
|
|
833
916
|
return this.knownBuckets.find((bucket) => declared.has(bucket));
|
|
834
917
|
}
|
|
918
|
+
assertHorizonOrdered() {
|
|
919
|
+
const min = this.boundaryDate("min-date");
|
|
920
|
+
const max = this.boundaryDate("max-date");
|
|
921
|
+
if (min && max && min.getTime() > max.getTime()) {
|
|
922
|
+
throw new Error(
|
|
923
|
+
`<kal-calendar> min-date is after max-date: "${this.getAttribute("min-date")}" > "${this.getAttribute("max-date")}".`
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
// Whether the vendor offers this day at all, before any event is considered.
|
|
928
|
+
isDateOffered(date) {
|
|
929
|
+
return isDateWithinWindow(
|
|
930
|
+
date,
|
|
931
|
+
this.boundaryDate("min-date"),
|
|
932
|
+
this.boundaryDate("max-date")
|
|
933
|
+
) && isDayAllowed(date, this.availableDays);
|
|
934
|
+
}
|
|
835
935
|
isDateSelectable(date) {
|
|
836
936
|
if (!this.engine) return false;
|
|
937
|
+
if (!this.isDateOffered(date)) return false;
|
|
837
938
|
if (this._selectableStatuses) {
|
|
838
939
|
return this._selectableStatuses.includes(this.resolveBucket(date));
|
|
839
940
|
}
|
|
@@ -853,6 +954,11 @@ var Kalendly = (() => {
|
|
|
853
954
|
this.assertStatusesDeclared();
|
|
854
955
|
this.assertStatusesKnown();
|
|
855
956
|
}
|
|
957
|
+
this.boundaryDate("min-date");
|
|
958
|
+
this.boundaryDate("max-date");
|
|
959
|
+
void this.availableDays;
|
|
960
|
+
void this.availableHours;
|
|
961
|
+
this.assertHorizonOrdered();
|
|
856
962
|
const today = /* @__PURE__ */ new Date();
|
|
857
963
|
const todayMonth = today.getMonth();
|
|
858
964
|
const todayYear = today.getFullYear();
|
|
@@ -896,7 +1002,7 @@ var Kalendly = (() => {
|
|
|
896
1002
|
return `
|
|
897
1003
|
<div class="event-card" style="border-left-color: ${borderColor}">
|
|
898
1004
|
<div class="event-header">
|
|
899
|
-
|
|
1005
|
+
${event.name ? `<div class="event-title">${escapeHtml(event.name)}</div>` : ""}
|
|
900
1006
|
<div class="event-badges">
|
|
901
1007
|
${event.category ? `<span class="badge category category-${slugifyToken(event.category)}">${escapeHtml(getCategoryLabel(event.category))}</span>` : ""}
|
|
902
1008
|
${event.priority ? `<span class="badge priority priority-${slugifyToken(event.priority)}">${escapeHtml(getPriorityLabel(event.priority))}</span>` : ""}
|
|
@@ -972,25 +1078,31 @@ var Kalendly = (() => {
|
|
|
972
1078
|
];
|
|
973
1079
|
this.warnMissingEndTime(events);
|
|
974
1080
|
const booked = bookedSlots(events, date, slotDuration);
|
|
1081
|
+
const hours = this.availableHours;
|
|
975
1082
|
const slots = booked.map((isBooked, index) => {
|
|
976
|
-
const
|
|
977
|
-
const
|
|
978
|
-
const
|
|
1083
|
+
const slotStart = index * slotDuration;
|
|
1084
|
+
const slotEnd = slotStart + slotDuration;
|
|
1085
|
+
const startTime = formatMinutes(slotStart);
|
|
1086
|
+
const endTime = formatMinutes(slotEnd);
|
|
1087
|
+
const offered = hours === null || hours.some(([from, to]) => slotStart >= from && slotEnd <= to);
|
|
1088
|
+
const inTimeRange = offered && !isBooked && selectable && this._timeRangeStart !== null && this._timeRangeEnd !== null && isSameDay2(this._timeRangeDate, date) && startTime >= this._timeRangeStart && endTime <= this._timeRangeEnd;
|
|
979
1089
|
const isRangeStart = inTimeRange && startTime === this._timeRangeStart;
|
|
980
1090
|
const isRangeEnd = inTimeRange && endTime === this._timeRangeEnd;
|
|
981
1091
|
const isInRange = inTimeRange && !isRangeStart && !isRangeEnd;
|
|
982
1092
|
const slotClasses = [
|
|
983
1093
|
"time-grid-slot",
|
|
1094
|
+
offered ? "" : "time-grid-slot-out-of-range",
|
|
984
1095
|
isBooked ? "time-grid-slot-blocked" : "time-grid-slot-open",
|
|
985
1096
|
isRangeStart ? "time-grid-slot-range-start" : "",
|
|
986
1097
|
isRangeEnd ? "time-grid-slot-range-end" : "",
|
|
987
1098
|
isInRange ? "time-grid-slot-in-range" : ""
|
|
988
1099
|
].filter(Boolean).join(" ");
|
|
989
|
-
const slotAttrs =
|
|
1100
|
+
const slotAttrs = `${offered ? 'data-action="select-slot" ' : ""}data-start-time="${startTime}" data-end-time="${endTime}" data-date="${date.toISOString()}" data-booked="${isBooked}"`;
|
|
1101
|
+
const status = !offered ? "Closed" : isBooked ? "Booked" : "Available";
|
|
990
1102
|
return `
|
|
991
1103
|
<div class="${slotClasses}" ${slotAttrs}>
|
|
992
1104
|
<span class="time-grid-label">${startTime}</span>
|
|
993
|
-
<span class="time-grid-status">${
|
|
1105
|
+
<span class="time-grid-status">${status}</span>
|
|
994
1106
|
</div>`;
|
|
995
1107
|
});
|
|
996
1108
|
const gridClasses = selectable ? "time-grid time-grid-selectable" : "time-grid";
|
|
@@ -1058,12 +1170,18 @@ var Kalendly = (() => {
|
|
|
1058
1170
|
}
|
|
1059
1171
|
}
|
|
1060
1172
|
const dateString = calendarDate.date.toISOString();
|
|
1173
|
+
const offered = this.isDateOffered(
|
|
1174
|
+
calendarDate.date
|
|
1175
|
+
);
|
|
1176
|
+
if (!offered) {
|
|
1177
|
+
classes.push("calendar-cell-out-of-range");
|
|
1178
|
+
}
|
|
1061
1179
|
return `
|
|
1062
1180
|
<td
|
|
1063
1181
|
class="${classes.join(" ")}"
|
|
1064
1182
|
data-date="${dateString}"
|
|
1065
1183
|
data-day-index="${dayIndex}"
|
|
1066
|
-
data-clickable="true"
|
|
1184
|
+
${offered ? 'data-clickable="true"' : ""}
|
|
1067
1185
|
${cellAttrs.join(" ")}
|
|
1068
1186
|
>
|
|
1069
1187
|
${calendarDate.date.getDate()}
|
|
@@ -1489,6 +1607,10 @@ var Kalendly = (() => {
|
|
|
1489
1607
|
"initial-date",
|
|
1490
1608
|
"min-year",
|
|
1491
1609
|
"max-year",
|
|
1610
|
+
"min-date",
|
|
1611
|
+
"max-date",
|
|
1612
|
+
"available-days",
|
|
1613
|
+
"available-hours",
|
|
1492
1614
|
"week-starts-on",
|
|
1493
1615
|
"heading",
|
|
1494
1616
|
"months",
|
|
@@ -1510,6 +1632,8 @@ var Kalendly = (() => {
|
|
|
1510
1632
|
borderColor: "--calendar-border-color",
|
|
1511
1633
|
todayOutline: "--calendar-today-outline",
|
|
1512
1634
|
selectedBg: "--calendar-selected-bg",
|
|
1635
|
+
outOfRangeBg: "--calendar-out-of-range-bg",
|
|
1636
|
+
outOfRangeFg: "--calendar-out-of-range-fg",
|
|
1513
1637
|
headerBg: "--calendar-header-bg",
|
|
1514
1638
|
popupBg: "--calendar-popup-bg",
|
|
1515
1639
|
pickerBg: "--calendar-picker-bg",
|