kalendly 0.2.2 → 0.3.1
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 +303 -45
- package/dist/core/index.d.mts +57 -10
- package/dist/core/index.d.ts +57 -10
- package/dist/core/index.js +159 -31
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +147 -30
- package/dist/core/index.mjs.map +1 -1
- package/dist/index.d.mts +86 -10
- package/dist/index.d.ts +86 -10
- package/dist/index.js +525 -178
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +513 -177
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +499 -174
- package/dist/index.umd.js.map +1 -1
- package/dist/styles/calendar.css +457 -392
- package/package.json +1 -1
package/dist/index.umd.js
CHANGED
|
@@ -78,12 +78,28 @@ 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 eventCoversDate(event, date) {
|
|
82
|
+
const start = normalizeDate(new Date(event.date)).getTime();
|
|
83
|
+
const target = normalizeDate(date).getTime();
|
|
84
|
+
if (Number.isNaN(start)) return false;
|
|
85
|
+
if (event.endDate === void 0 || event.endDate === null) {
|
|
86
|
+
return start === target;
|
|
87
|
+
}
|
|
88
|
+
const end = normalizeDate(new Date(event.endDate)).getTime();
|
|
89
|
+
if (Number.isNaN(end)) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
`<kal-calendar> event ${event.id} has an unreadable endDate: ${String(event.endDate)}.`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
if (end < start) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`<kal-calendar> event ${event.id} has an endDate before its date: ${String(event.endDate)} < ${String(event.date)}.`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return target >= start && target <= end;
|
|
100
|
+
}
|
|
81
101
|
function getEventsForDate(events, date) {
|
|
82
|
-
|
|
83
|
-
return events.filter((event) => {
|
|
84
|
-
const eventDate = normalizeDate(new Date(event.date));
|
|
85
|
-
return eventDate.getTime() === normalizedTargetDate.getTime();
|
|
86
|
-
});
|
|
102
|
+
return events.filter((event) => eventCoversDate(event, date));
|
|
87
103
|
}
|
|
88
104
|
function generateCalendarDates(year, month, events = [], weekStartsOn = 0) {
|
|
89
105
|
const firstDay = new Date(year, month, 1);
|
|
@@ -139,26 +155,16 @@ var Kalendly = (() => {
|
|
|
139
155
|
}
|
|
140
156
|
return dates;
|
|
141
157
|
}
|
|
142
|
-
function getPopupPositionClass(selectedDayIndex) {
|
|
143
|
-
if (selectedDayIndex === null) return "popup-center-bottom";
|
|
144
|
-
if (selectedDayIndex < 3) {
|
|
145
|
-
return "popup-right";
|
|
146
|
-
} else if (selectedDayIndex > 4) {
|
|
147
|
-
return "popup-left";
|
|
148
|
-
} else {
|
|
149
|
-
return "popup-center-bottom";
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
158
|
function getCellClasses(calendarDate) {
|
|
153
159
|
const classes = [];
|
|
154
160
|
if (!calendarDate.isCurrentMonth) {
|
|
155
|
-
classes.push("other-month");
|
|
161
|
+
classes.push("calendar-cell-other-month");
|
|
156
162
|
}
|
|
157
163
|
if (calendarDate.isToday) {
|
|
158
|
-
classes.push("
|
|
164
|
+
classes.push("calendar-cell-today");
|
|
159
165
|
}
|
|
160
166
|
if (calendarDate.hasEvents) {
|
|
161
|
-
classes.push("has
|
|
167
|
+
classes.push("calendar-cell-has-event");
|
|
162
168
|
}
|
|
163
169
|
return classes;
|
|
164
170
|
}
|
|
@@ -208,6 +214,92 @@ var Kalendly = (() => {
|
|
|
208
214
|
const color = colorMap[category] || colorMap.other || "#fc8917";
|
|
209
215
|
return isValidHexColor(color) ? color : "#fc8917";
|
|
210
216
|
}
|
|
217
|
+
var HTML_ESCAPES = {
|
|
218
|
+
"&": "&",
|
|
219
|
+
"<": "<",
|
|
220
|
+
">": ">",
|
|
221
|
+
'"': """,
|
|
222
|
+
"'": "'"
|
|
223
|
+
};
|
|
224
|
+
function escapeHtml(value) {
|
|
225
|
+
if (value === null || value === void 0) return "";
|
|
226
|
+
return String(value).replace(/[&<>"']/g, (char) => HTML_ESCAPES[char]);
|
|
227
|
+
}
|
|
228
|
+
function slugifyToken(value) {
|
|
229
|
+
return String(value).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
230
|
+
}
|
|
231
|
+
function safeUrl(value) {
|
|
232
|
+
const normalized = String(value).split("").filter((char) => char.charCodeAt(0) > 32).join("");
|
|
233
|
+
if (/^(?:https?:|mailto:)/i.test(normalized)) return normalized;
|
|
234
|
+
const schemeless = !/^[^/?#]*:/.test(normalized);
|
|
235
|
+
return schemeless ? normalized : "#";
|
|
236
|
+
}
|
|
237
|
+
function safeColor(value) {
|
|
238
|
+
const trimmed = String(value).trim();
|
|
239
|
+
return isValidHexColor(trimmed) || /^[a-z]+$/i.test(trimmed) ? trimmed : "#3b82f6";
|
|
240
|
+
}
|
|
241
|
+
var MINUTES_PER_DAY = 1440;
|
|
242
|
+
var DEFAULT_SLOT_DURATION = 60;
|
|
243
|
+
function parseTimeToMinutes(time) {
|
|
244
|
+
if (typeof time !== "string") return null;
|
|
245
|
+
const match = /^(\d{1,2}):(\d{2})$/.exec(time.trim());
|
|
246
|
+
if (!match) return null;
|
|
247
|
+
const hours = Number(match[1]);
|
|
248
|
+
const mins = Number(match[2]);
|
|
249
|
+
if (hours > 24 || mins > 59 || hours === 24 && mins > 0) return null;
|
|
250
|
+
return hours * 60 + mins;
|
|
251
|
+
}
|
|
252
|
+
function formatMinutes(total) {
|
|
253
|
+
const hours = Math.floor(total / 60);
|
|
254
|
+
const mins = total % 60;
|
|
255
|
+
return `${String(hours).padStart(2, "0")}:${String(mins).padStart(2, "0")}`;
|
|
256
|
+
}
|
|
257
|
+
function eventInterval(event, slotDuration, onDay) {
|
|
258
|
+
const day = normalizeDate(onDay).getTime();
|
|
259
|
+
if (Number.isNaN(day)) return null;
|
|
260
|
+
const base = day / 6e4;
|
|
261
|
+
const wholeDay = [base, base + MINUTES_PER_DAY];
|
|
262
|
+
if (event.allDay || !event.startTime) return wholeDay;
|
|
263
|
+
const from = parseTimeToMinutes(event.startTime);
|
|
264
|
+
if (from === null) return wholeDay;
|
|
265
|
+
if (event.endTime === void 0 || event.endTime === null) {
|
|
266
|
+
return [base + from, base + from + slotDuration];
|
|
267
|
+
}
|
|
268
|
+
const to = parseTimeToMinutes(event.endTime);
|
|
269
|
+
if (to === null) return wholeDay;
|
|
270
|
+
return [base + from, base + (to <= from ? to + MINUTES_PER_DAY : to)];
|
|
271
|
+
}
|
|
272
|
+
function mergeIntervals(intervals) {
|
|
273
|
+
const sorted = [...intervals].sort((a, b) => a[0] - b[0]);
|
|
274
|
+
return sorted.reduce((merged, [start, end]) => {
|
|
275
|
+
const last = merged[merged.length - 1];
|
|
276
|
+
if (last && start <= last[1]) last[1] = Math.max(last[1], end);
|
|
277
|
+
else merged.push([start, end]);
|
|
278
|
+
return merged;
|
|
279
|
+
}, []);
|
|
280
|
+
}
|
|
281
|
+
function bookedSlots(events, date, slotDuration = DEFAULT_SLOT_DURATION) {
|
|
282
|
+
const base = normalizeDate(date).getTime() / 6e4;
|
|
283
|
+
const previousDay = new Date(date);
|
|
284
|
+
previousDay.setDate(previousDay.getDate() - 1);
|
|
285
|
+
const intervals = [];
|
|
286
|
+
for (const event of new Set(events)) {
|
|
287
|
+
for (const day of [previousDay, date]) {
|
|
288
|
+
if (!eventCoversDate(event, day)) continue;
|
|
289
|
+
const interval = eventInterval(event, slotDuration, day);
|
|
290
|
+
if (interval) intervals.push(interval);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
const merged = mergeIntervals(intervals);
|
|
294
|
+
return Array.from(
|
|
295
|
+
{ length: Math.floor(MINUTES_PER_DAY / slotDuration) },
|
|
296
|
+
(_, index) => {
|
|
297
|
+
const start = base + index * slotDuration;
|
|
298
|
+
const end = start + slotDuration;
|
|
299
|
+
return merged.some(([from, to]) => start < to && end > from);
|
|
300
|
+
}
|
|
301
|
+
);
|
|
302
|
+
}
|
|
211
303
|
|
|
212
304
|
// src/core/calendar-engine.ts
|
|
213
305
|
var CalendarEngine = class {
|
|
@@ -248,24 +340,38 @@ var Kalendly = (() => {
|
|
|
248
340
|
* Get view model with computed properties
|
|
249
341
|
*/
|
|
250
342
|
getViewModel() {
|
|
251
|
-
const
|
|
252
|
-
this.
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
343
|
+
const panes = Array.from(
|
|
344
|
+
{ length: Math.max(1, this.config.monthCount ?? 1) },
|
|
345
|
+
(_, offset) => {
|
|
346
|
+
const anchor = new Date(
|
|
347
|
+
this.state.currentYear,
|
|
348
|
+
this.state.currentMonth + offset,
|
|
349
|
+
1
|
|
350
|
+
);
|
|
351
|
+
const year = anchor.getFullYear();
|
|
352
|
+
const month = anchor.getMonth();
|
|
353
|
+
return {
|
|
354
|
+
year,
|
|
355
|
+
month,
|
|
356
|
+
monthAndYearText: getMonthYearText(year, month),
|
|
357
|
+
calendarDates: generateCalendarDates(
|
|
358
|
+
year,
|
|
359
|
+
month,
|
|
360
|
+
this.config.events,
|
|
361
|
+
this.config.weekStartsOn
|
|
362
|
+
)
|
|
363
|
+
};
|
|
364
|
+
}
|
|
256
365
|
);
|
|
257
366
|
return {
|
|
258
367
|
...this.state,
|
|
259
368
|
months: MONTHS,
|
|
260
369
|
days: DAYS,
|
|
261
370
|
years: generateYears(this.config.minYear, this.config.maxYear),
|
|
262
|
-
monthAndYearText:
|
|
263
|
-
this.state.currentYear,
|
|
264
|
-
this.state.currentMonth
|
|
265
|
-
),
|
|
371
|
+
monthAndYearText: panes[0].monthAndYearText,
|
|
266
372
|
scheduleDay: this.state.selectedDate ? formatDateForDisplay(this.state.selectedDate) : "",
|
|
267
|
-
|
|
268
|
-
|
|
373
|
+
panes,
|
|
374
|
+
calendarDates: panes[0].calendarDates
|
|
269
375
|
};
|
|
270
376
|
}
|
|
271
377
|
/**
|
|
@@ -439,7 +545,7 @@ var Kalendly = (() => {
|
|
|
439
545
|
|
|
440
546
|
// src/web-components/CalendarElement.ts
|
|
441
547
|
var isSameDay2 = (a, b) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
442
|
-
var
|
|
548
|
+
var _CalendarElement = class _CalendarElement extends HTMLElement {
|
|
443
549
|
constructor() {
|
|
444
550
|
super(...arguments);
|
|
445
551
|
this.engine = null;
|
|
@@ -460,6 +566,10 @@ var Kalendly = (() => {
|
|
|
460
566
|
this._categoryColors = null;
|
|
461
567
|
this._renderEvent = null;
|
|
462
568
|
this._renderNoEvents = null;
|
|
569
|
+
this._availabilityColors = null;
|
|
570
|
+
this._selectableStatuses = null;
|
|
571
|
+
this._initError = null;
|
|
572
|
+
this.pendingUpdate = null;
|
|
463
573
|
// Selection state (availability mode — range)
|
|
464
574
|
this._rangeStart = null;
|
|
465
575
|
this._rangeEnd = null;
|
|
@@ -472,6 +582,7 @@ var Kalendly = (() => {
|
|
|
472
582
|
return this._events;
|
|
473
583
|
}
|
|
474
584
|
set events(val) {
|
|
585
|
+
this._initError = null;
|
|
475
586
|
this._events = val;
|
|
476
587
|
if (this.engine) {
|
|
477
588
|
this.engine.updateEvents(val);
|
|
@@ -487,13 +598,28 @@ var Kalendly = (() => {
|
|
|
487
598
|
this.engine.updateCategoryColors(val);
|
|
488
599
|
}
|
|
489
600
|
}
|
|
601
|
+
get availabilityColors() {
|
|
602
|
+
return this._availabilityColors ?? {};
|
|
603
|
+
}
|
|
604
|
+
set availabilityColors(val) {
|
|
605
|
+
this._initError = null;
|
|
606
|
+
this._availabilityColors = val;
|
|
607
|
+
if (this.engine) this.scheduleRender();
|
|
608
|
+
}
|
|
609
|
+
get selectableStatuses() {
|
|
610
|
+
return this._selectableStatuses ?? [];
|
|
611
|
+
}
|
|
612
|
+
set selectableStatuses(val) {
|
|
613
|
+
this._selectableStatuses = val;
|
|
614
|
+
if (this.engine) this.scheduleRender();
|
|
615
|
+
}
|
|
490
616
|
set renderEvent(val) {
|
|
491
617
|
this._renderEvent = val;
|
|
492
|
-
if (this.engine) this.
|
|
618
|
+
if (this.engine) this.scheduleRender();
|
|
493
619
|
}
|
|
494
620
|
set renderNoEvents(val) {
|
|
495
621
|
this._renderNoEvents = val;
|
|
496
|
-
if (this.engine) this.
|
|
622
|
+
if (this.engine) this.scheduleRender();
|
|
497
623
|
}
|
|
498
624
|
get loading() {
|
|
499
625
|
return this.hasAttribute("loading");
|
|
@@ -502,10 +628,22 @@ var Kalendly = (() => {
|
|
|
502
628
|
if (val) this.setAttribute("loading", "");
|
|
503
629
|
else this.removeAttribute("loading");
|
|
504
630
|
}
|
|
631
|
+
// Custom element reactions report rather than propagate, so a failure here
|
|
632
|
+
// is kept and resurfaced at the next call the integrator makes
|
|
633
|
+
reaction(run) {
|
|
634
|
+
try {
|
|
635
|
+
run();
|
|
636
|
+
} catch (error) {
|
|
637
|
+
this._initError = error;
|
|
638
|
+
throw error;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
505
641
|
connectedCallback() {
|
|
506
642
|
this.classList.add("kalendly-calendar");
|
|
507
|
-
this.
|
|
508
|
-
|
|
643
|
+
this.reaction(() => {
|
|
644
|
+
this.initEngine();
|
|
645
|
+
this.render();
|
|
646
|
+
});
|
|
509
647
|
}
|
|
510
648
|
disconnectedCallback() {
|
|
511
649
|
this.cleanup();
|
|
@@ -515,7 +653,7 @@ var Kalendly = (() => {
|
|
|
515
653
|
attributeChangedCallback(name, oldVal, newVal) {
|
|
516
654
|
if (oldVal === newVal) return;
|
|
517
655
|
if (name === "loading") {
|
|
518
|
-
this.
|
|
656
|
+
this.scheduleRender();
|
|
519
657
|
return;
|
|
520
658
|
}
|
|
521
659
|
if (name === "selectable" && newVal === null) {
|
|
@@ -528,6 +666,46 @@ var Kalendly = (() => {
|
|
|
528
666
|
}
|
|
529
667
|
this.reinit();
|
|
530
668
|
}
|
|
669
|
+
get headingText() {
|
|
670
|
+
const heading = this.getAttribute("heading");
|
|
671
|
+
if (heading !== null) return heading;
|
|
672
|
+
const title = this.getAttribute("title");
|
|
673
|
+
if (title !== null && !_CalendarElement.titleDeprecationWarned) {
|
|
674
|
+
_CalendarElement.titleDeprecationWarned = true;
|
|
675
|
+
console.warn(
|
|
676
|
+
`<kal-calendar> the title attribute is deprecated and will be removed in a future release \u2014 use heading instead. title is a global HTML attribute, so the browser also renders it as a tooltip over the whole calendar.`
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
return title;
|
|
680
|
+
}
|
|
681
|
+
get slotDuration() {
|
|
682
|
+
const raw = this.getAttribute("slot-duration");
|
|
683
|
+
if (raw === null) return DEFAULT_SLOT_DURATION;
|
|
684
|
+
const minutes = Number(raw);
|
|
685
|
+
if (Number.isInteger(minutes) && minutes > 0 && MINUTES_PER_DAY % minutes === 0) {
|
|
686
|
+
return minutes;
|
|
687
|
+
}
|
|
688
|
+
console.warn(
|
|
689
|
+
`<kal-calendar> slot-duration="${raw}" must be a positive whole number of minutes that divides ${MINUTES_PER_DAY} \u2014 falling back to ${DEFAULT_SLOT_DURATION}.`
|
|
690
|
+
);
|
|
691
|
+
return DEFAULT_SLOT_DURATION;
|
|
692
|
+
}
|
|
693
|
+
warnMissingEndTime(events) {
|
|
694
|
+
for (const event of events) {
|
|
695
|
+
if (!event.startTime || event.endTime) continue;
|
|
696
|
+
const key = String(event.id);
|
|
697
|
+
if (_CalendarElement.warnedMissingEnd.has(key)) continue;
|
|
698
|
+
_CalendarElement.warnedMissingEnd.add(key);
|
|
699
|
+
console.warn(
|
|
700
|
+
`<kal-calendar> event ${key} has startTime but no endTime \u2014 occupying one ${this.slotDuration}-minute slot. Send an endTime to say how long it runs.`
|
|
701
|
+
);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
get monthCount() {
|
|
705
|
+
const requested = Number(this.getAttribute("months") ?? 1);
|
|
706
|
+
if (!Number.isInteger(requested) || requested < 1) return 1;
|
|
707
|
+
return Math.min(requested, 2);
|
|
708
|
+
}
|
|
531
709
|
get minYear() {
|
|
532
710
|
const val = this.getAttribute("min-year");
|
|
533
711
|
return val ? parseInt(val, 10) : (/* @__PURE__ */ new Date()).getFullYear() - 30;
|
|
@@ -547,19 +725,20 @@ var Kalendly = (() => {
|
|
|
547
725
|
minYear: this.minYear,
|
|
548
726
|
maxYear: this.maxYear,
|
|
549
727
|
weekStartsOn,
|
|
550
|
-
categoryColors: this._categoryColors ?? void 0
|
|
728
|
+
categoryColors: this._categoryColors ?? void 0,
|
|
729
|
+
monthCount: this.monthCount
|
|
551
730
|
});
|
|
552
731
|
this.actions = this.engine.getActions();
|
|
553
732
|
this.applyTheme();
|
|
554
733
|
this.unsubscribe = this.engine.subscribe(() => {
|
|
555
|
-
this.
|
|
734
|
+
this.scheduleRender();
|
|
556
735
|
});
|
|
557
736
|
}
|
|
558
737
|
reinit() {
|
|
559
738
|
if (!this.engine) return;
|
|
560
739
|
this.cleanup();
|
|
561
740
|
this.initEngine();
|
|
562
|
-
this.
|
|
741
|
+
this.scheduleRender();
|
|
563
742
|
}
|
|
564
743
|
cleanup() {
|
|
565
744
|
if (this.unsubscribe) {
|
|
@@ -596,42 +775,84 @@ var Kalendly = (() => {
|
|
|
596
775
|
applyTheme() {
|
|
597
776
|
if (!this._theme) return;
|
|
598
777
|
const root = document.documentElement;
|
|
599
|
-
const
|
|
600
|
-
|
|
601
|
-
root.style.setProperty(
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
if (
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
778
|
+
for (const key of Object.keys(_CalendarElement.themeMap)) {
|
|
779
|
+
const value = this._theme[key];
|
|
780
|
+
if (value) root.style.setProperty(_CalendarElement.themeMap[key], value);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
get knownBuckets() {
|
|
784
|
+
return [
|
|
785
|
+
..._CalendarElement.BUILT_IN_BUCKETS,
|
|
786
|
+
...Object.keys(this._availabilityColors ?? {})
|
|
787
|
+
];
|
|
788
|
+
}
|
|
789
|
+
get updateComplete() {
|
|
790
|
+
return this.pendingUpdate ?? Promise.resolve();
|
|
791
|
+
}
|
|
792
|
+
scheduleRender() {
|
|
793
|
+
if (this.pendingUpdate) return;
|
|
794
|
+
this.pendingUpdate = Promise.resolve().then(() => {
|
|
795
|
+
this.pendingUpdate = null;
|
|
796
|
+
try {
|
|
797
|
+
this.render();
|
|
798
|
+
} catch (error) {
|
|
799
|
+
this._initError = error;
|
|
800
|
+
throw error;
|
|
801
|
+
}
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
rethrowInitError() {
|
|
805
|
+
if (this._initError) throw this._initError;
|
|
806
|
+
}
|
|
807
|
+
assertStatusesDeclared() {
|
|
808
|
+
if (!this.getAttribute("availability-mode")) return;
|
|
809
|
+
const missing = this._events.filter(
|
|
810
|
+
(event) => typeof event.availabilityStatus !== "string" || event.availabilityStatus === ""
|
|
811
|
+
).map((event) => String(event.id));
|
|
812
|
+
if (missing.length) {
|
|
813
|
+
throw new Error(
|
|
814
|
+
`<kal-calendar> availability-mode requires availabilityStatus on every event. Missing on: ${missing.join(", ")}.`
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
assertStatusesKnown() {
|
|
819
|
+
const known = new Set(this.knownBuckets);
|
|
820
|
+
const unknown = this._events.filter((event) => !known.has(event.availabilityStatus)).map((event) => `${event.id} (${event.availabilityStatus})`);
|
|
821
|
+
if (unknown.length) {
|
|
822
|
+
throw new Error(
|
|
823
|
+
`<kal-calendar> availabilityStatus must name a built-in bucket (${_CalendarElement.BUILT_IN_BUCKETS.join(", ")}) or a key of availabilityColors. Unrecognised on: ${unknown.join(", ")}.`
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
resolveBucket(date) {
|
|
828
|
+
if (!this.engine) return "open";
|
|
829
|
+
const declared = new Set(
|
|
830
|
+
this.engine.getEventsForDate(date).map((event) => event.availabilityStatus)
|
|
831
|
+
);
|
|
832
|
+
if (declared.size === 0) return "open";
|
|
833
|
+
return this.knownBuckets.find((bucket) => declared.has(bucket));
|
|
834
|
+
}
|
|
835
|
+
isDateSelectable(date) {
|
|
836
|
+
if (!this.engine) return false;
|
|
837
|
+
if (this._selectableStatuses) {
|
|
838
|
+
return this._selectableStatuses.includes(this.resolveBucket(date));
|
|
839
|
+
}
|
|
840
|
+
return this.engine.getEventsForDate(date).length === 0;
|
|
624
841
|
}
|
|
625
842
|
render() {
|
|
626
843
|
if (!this.engine) return;
|
|
627
844
|
const viewModel = this.engine.getViewModel();
|
|
628
845
|
const useShortMonths = this.hasAttribute("use-short-month-names");
|
|
629
|
-
const title = this.
|
|
846
|
+
const title = this.headingText;
|
|
630
847
|
const minYear = this.minYear;
|
|
631
848
|
const maxYear = this.maxYear;
|
|
632
849
|
const availabilityMode = this.getAttribute("availability-mode");
|
|
633
850
|
const selectable = this.hasAttribute("selectable");
|
|
634
851
|
const isLoading = this.loading;
|
|
852
|
+
if (availabilityMode) {
|
|
853
|
+
this.assertStatusesDeclared();
|
|
854
|
+
this.assertStatusesKnown();
|
|
855
|
+
}
|
|
635
856
|
const today = /* @__PURE__ */ new Date();
|
|
636
857
|
const todayMonth = today.getMonth();
|
|
637
858
|
const todayYear = today.getFullYear();
|
|
@@ -640,7 +861,7 @@ var Kalendly = (() => {
|
|
|
640
861
|
const defaultRenderEvent = (event) => {
|
|
641
862
|
const timeRange = formatTimeRange(event);
|
|
642
863
|
const attendeesList = formatAttendees(event.attendees);
|
|
643
|
-
let borderColor = event.color || "#3b82f6";
|
|
864
|
+
let borderColor = safeColor(event.color || "#3b82f6");
|
|
644
865
|
if (event.category) {
|
|
645
866
|
borderColor = this.engine.getCategoryColor(event.category);
|
|
646
867
|
}
|
|
@@ -675,56 +896,56 @@ var Kalendly = (() => {
|
|
|
675
896
|
return `
|
|
676
897
|
<div class="event-card" style="border-left-color: ${borderColor}">
|
|
677
898
|
<div class="event-header">
|
|
678
|
-
<div class="event-title">${event.name}</div>
|
|
899
|
+
<div class="event-title">${escapeHtml(event.name)}</div>
|
|
679
900
|
<div class="event-badges">
|
|
680
|
-
${event.category ? `<span class="badge category-${event.category}">${getCategoryLabel(event.category)}</span>` : ""}
|
|
681
|
-
${event.priority ? `<span class="badge priority-${event.priority}">${getPriorityLabel(event.priority)}</span>` : ""}
|
|
682
|
-
${event.status && event.status !== "scheduled" ? `<span class="badge status-${event.status}">${getStatusLabel(event.status)}</span>` : ""}
|
|
901
|
+
${event.category ? `<span class="badge category category-${slugifyToken(event.category)}">${escapeHtml(getCategoryLabel(event.category))}</span>` : ""}
|
|
902
|
+
${event.priority ? `<span class="badge priority priority-${slugifyToken(event.priority)}">${escapeHtml(getPriorityLabel(event.priority))}</span>` : ""}
|
|
903
|
+
${event.status && event.status !== "scheduled" ? `<span class="badge status status-${slugifyToken(event.status)}">${escapeHtml(getStatusLabel(event.status))}</span>` : ""}
|
|
683
904
|
</div>
|
|
684
905
|
</div>
|
|
685
906
|
|
|
686
907
|
${timeRange ? `
|
|
687
908
|
<div class="event-time">
|
|
688
909
|
<span class="event-time-label">Time:</span>
|
|
689
|
-
<span class="event-time-value">${timeRange}</span>
|
|
910
|
+
<span class="event-time-value">${escapeHtml(timeRange)}</span>
|
|
690
911
|
</div>
|
|
691
912
|
` : ""}
|
|
692
913
|
|
|
693
914
|
${event.description ? `
|
|
694
|
-
<div class="event-description">${event.description}</div>
|
|
915
|
+
<div class="event-description">${escapeHtml(event.description)}</div>
|
|
695
916
|
` : ""}
|
|
696
917
|
|
|
697
918
|
${event.location ? `
|
|
698
919
|
<div class="event-time">
|
|
699
920
|
<span class="event-time-label">Location:</span>
|
|
700
|
-
<span class="event-time-value">${event.location}</span>
|
|
921
|
+
<span class="event-time-value">${escapeHtml(event.location)}</span>
|
|
701
922
|
</div>
|
|
702
923
|
` : ""}
|
|
703
924
|
|
|
704
925
|
${attendeesList ? `
|
|
705
926
|
<div class="event-time">
|
|
706
927
|
<span class="event-time-label">Attendees:</span>
|
|
707
|
-
<span class="event-time-value">${attendeesList}</span>
|
|
928
|
+
<span class="event-time-value">${escapeHtml(attendeesList)}</span>
|
|
708
929
|
</div>
|
|
709
930
|
` : ""}
|
|
710
931
|
|
|
711
932
|
${event.organizer ? `
|
|
712
933
|
<div class="event-time">
|
|
713
934
|
<span class="event-time-label">Organizer:</span>
|
|
714
|
-
<span class="event-time-value">${event.organizer}</span>
|
|
935
|
+
<span class="event-time-value">${escapeHtml(event.organizer)}</span>
|
|
715
936
|
</div>
|
|
716
937
|
` : ""}
|
|
717
938
|
|
|
718
939
|
${event.notes ? `
|
|
719
940
|
<div class="event-time">
|
|
720
941
|
<span class="event-time-label">Notes:</span>
|
|
721
|
-
<span class="event-time-value">${event.notes}</span>
|
|
942
|
+
<span class="event-time-value">${escapeHtml(event.notes)}</span>
|
|
722
943
|
</div>
|
|
723
944
|
` : ""}
|
|
724
945
|
|
|
725
946
|
${event.url ? `
|
|
726
947
|
<div class="event-time">
|
|
727
|
-
<a href="${event.url}" target="_blank" rel="noopener noreferrer" class="event-link">
|
|
948
|
+
<a href="${escapeHtml(safeUrl(event.url))}" target="_blank" rel="noopener noreferrer" class="event-link">
|
|
728
949
|
View Details \u2192
|
|
729
950
|
</a>
|
|
730
951
|
</div>
|
|
@@ -732,7 +953,7 @@ var Kalendly = (() => {
|
|
|
732
953
|
|
|
733
954
|
${event.tags && event.tags.length > 0 ? `
|
|
734
955
|
<div class="event-tags">
|
|
735
|
-
${event.tags.map((tag) => `<span class="event-tag">${tag}</span>`).join("")}
|
|
956
|
+
${event.tags.map((tag) => `<span class="event-tag">${escapeHtml(tag)}</span>`).join("")}
|
|
736
957
|
</div>
|
|
737
958
|
` : ""}
|
|
738
959
|
</div>
|
|
@@ -741,97 +962,181 @@ var Kalendly = (() => {
|
|
|
741
962
|
const defaultRenderNoEvents = () => '<div class="no-events-message">No events scheduled for this day.</div>';
|
|
742
963
|
const renderEvent = this._renderEvent || defaultRenderEvent;
|
|
743
964
|
const renderNoEvents = this._renderNoEvents || defaultRenderNoEvents;
|
|
744
|
-
const renderTimeGrid = (
|
|
745
|
-
const
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
const
|
|
965
|
+
const renderTimeGrid = (date) => {
|
|
966
|
+
const slotDuration = this.slotDuration;
|
|
967
|
+
const previousDay = new Date(date);
|
|
968
|
+
previousDay.setDate(previousDay.getDate() - 1);
|
|
969
|
+
const events = [
|
|
970
|
+
...this.engine.getEventsForDate(previousDay),
|
|
971
|
+
...this.engine.getEventsForDate(date)
|
|
972
|
+
];
|
|
973
|
+
this.warnMissingEndTime(events);
|
|
974
|
+
const booked = bookedSlots(events, date, slotDuration);
|
|
975
|
+
const slots = booked.map((isBooked, index) => {
|
|
976
|
+
const startTime = formatMinutes(index * slotDuration);
|
|
977
|
+
const endTime = formatMinutes((index + 1) * slotDuration);
|
|
978
|
+
const inTimeRange = !isBooked && selectable && this._timeRangeStart !== null && this._timeRangeEnd !== null && isSameDay2(this._timeRangeDate, date) && startTime >= this._timeRangeStart && endTime <= this._timeRangeEnd;
|
|
756
979
|
const isRangeStart = inTimeRange && startTime === this._timeRangeStart;
|
|
757
980
|
const isRangeEnd = inTimeRange && endTime === this._timeRangeEnd;
|
|
758
981
|
const isInRange = inTimeRange && !isRangeStart && !isRangeEnd;
|
|
759
982
|
const slotClasses = [
|
|
760
|
-
"time-
|
|
761
|
-
|
|
762
|
-
isRangeStart ? "time-
|
|
763
|
-
isRangeEnd ? "time-
|
|
764
|
-
isInRange ? "time-
|
|
983
|
+
"time-grid-slot",
|
|
984
|
+
isBooked ? "time-grid-slot-blocked" : "time-grid-slot-open",
|
|
985
|
+
isRangeStart ? "time-grid-slot-range-start" : "",
|
|
986
|
+
isRangeEnd ? "time-grid-slot-range-end" : "",
|
|
987
|
+
isInRange ? "time-grid-slot-in-range" : ""
|
|
765
988
|
].filter(Boolean).join(" ");
|
|
766
|
-
const slotAttrs =
|
|
989
|
+
const slotAttrs = `data-action="select-slot" data-start-time="${startTime}" data-end-time="${endTime}" data-date="${date.toISOString()}" data-booked="${isBooked}"`;
|
|
767
990
|
return `
|
|
768
991
|
<div class="${slotClasses}" ${slotAttrs}>
|
|
769
|
-
<span class="time-
|
|
770
|
-
<span class="time-
|
|
992
|
+
<span class="time-grid-label">${startTime}</span>
|
|
993
|
+
<span class="time-grid-status">${isBooked ? "Booked" : "Available"}</span>
|
|
771
994
|
</div>`;
|
|
772
995
|
});
|
|
773
|
-
|
|
996
|
+
const gridClasses = selectable ? "time-grid time-grid-selectable" : "time-grid";
|
|
997
|
+
return `<div class="${gridClasses}">${slots.join("")}</div>`;
|
|
774
998
|
};
|
|
999
|
+
const multiMonth = viewModel.panes.length > 1;
|
|
1000
|
+
const renderPane = (pane) => `
|
|
1001
|
+
<div class="calendar-pane">
|
|
1002
|
+
${multiMonth ? `<div class="calendar-pane-caption">${escapeHtml(pane.monthAndYearText)}</div>` : ""}
|
|
1003
|
+
<table class="calendar-table calendar-table-bordered">
|
|
1004
|
+
<thead>
|
|
1005
|
+
<tr>
|
|
1006
|
+
${viewModel.days.map((day) => `<th>${day.slice(0, 3)}</th>`).join("")}
|
|
1007
|
+
</tr>
|
|
1008
|
+
</thead>
|
|
1009
|
+
<tbody data-calendar-body>
|
|
1010
|
+
${isLoading ? Array.from(
|
|
1011
|
+
{ length: 6 },
|
|
1012
|
+
() => `<tr>${Array.from(
|
|
1013
|
+
{ length: 7 },
|
|
1014
|
+
() => `<td class="calendar-skeleton" aria-hidden="true"></td>`
|
|
1015
|
+
).join("")}</tr>`
|
|
1016
|
+
).join("") : pane.calendarDates.map(
|
|
1017
|
+
(week) => `
|
|
1018
|
+
<tr>
|
|
1019
|
+
${week.map((calendarDate, dayIndex) => {
|
|
1020
|
+
const classes = getCellClasses(calendarDate);
|
|
1021
|
+
const cellAttrs = [];
|
|
1022
|
+
if (viewModel.selectedDate && isSameDay2(
|
|
1023
|
+
calendarDate.date,
|
|
1024
|
+
viewModel.selectedDate
|
|
1025
|
+
)) {
|
|
1026
|
+
classes.push("calendar-cell-selected");
|
|
1027
|
+
}
|
|
1028
|
+
if (availabilityMode && calendarDate.isCurrentMonth) {
|
|
1029
|
+
const bucket = this.resolveBucket(
|
|
1030
|
+
calendarDate.date
|
|
1031
|
+
);
|
|
1032
|
+
const custom = (this._availabilityColors ?? {})[bucket];
|
|
1033
|
+
classes.push(
|
|
1034
|
+
`availability-${slugifyToken(bucket)}`
|
|
1035
|
+
);
|
|
1036
|
+
if (custom) {
|
|
1037
|
+
classes.push("availability-status");
|
|
1038
|
+
cellAttrs.push(
|
|
1039
|
+
`style="--availability-color: ${escapeHtml(safeColor(custom))}"`
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
if (!this.isDateSelectable(calendarDate.date)) {
|
|
1043
|
+
classes.push("availability-unselectable");
|
|
1044
|
+
}
|
|
1045
|
+
cellAttrs.push(
|
|
1046
|
+
`aria-label="${escapeHtml(bucket)}"`
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
1049
|
+
if (availabilityMode === "day" && selectable && calendarDate.isCurrentMonth) {
|
|
1050
|
+
const d = calendarDate.date;
|
|
1051
|
+
if (this._rangeStart && isSameDay2(d, this._rangeStart))
|
|
1052
|
+
classes.push("availability-range-start");
|
|
1053
|
+
if (this._rangeEnd && isSameDay2(d, this._rangeEnd))
|
|
1054
|
+
classes.push("availability-range-end");
|
|
1055
|
+
if (this._rangeStart && this._rangeEnd) {
|
|
1056
|
+
if (d > this._rangeStart && d < this._rangeEnd)
|
|
1057
|
+
classes.push("availability-in-range");
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
const dateString = calendarDate.date.toISOString();
|
|
1061
|
+
return `
|
|
1062
|
+
<td
|
|
1063
|
+
class="${classes.join(" ")}"
|
|
1064
|
+
data-date="${dateString}"
|
|
1065
|
+
data-day-index="${dayIndex}"
|
|
1066
|
+
data-clickable="true"
|
|
1067
|
+
${cellAttrs.join(" ")}
|
|
1068
|
+
>
|
|
1069
|
+
${calendarDate.date.getDate()}
|
|
1070
|
+
</td>
|
|
1071
|
+
`;
|
|
1072
|
+
}).join("")}
|
|
1073
|
+
</tr>
|
|
1074
|
+
`
|
|
1075
|
+
).join("")}
|
|
1076
|
+
</tbody>
|
|
1077
|
+
</table>
|
|
1078
|
+
</div>
|
|
1079
|
+
`;
|
|
775
1080
|
const html = `
|
|
776
1081
|
${title ? `
|
|
777
|
-
<div class="
|
|
778
|
-
<h1>${title}</h1>
|
|
1082
|
+
<div class="calendar-title">
|
|
1083
|
+
<h1>${escapeHtml(title)}</h1>
|
|
779
1084
|
</div>
|
|
780
1085
|
` : ""}
|
|
781
1086
|
|
|
782
|
-
<div class="calendar
|
|
783
|
-
<div class="calendar
|
|
784
|
-
<div class="calendar
|
|
785
|
-
<button type="button" class="calendar
|
|
1087
|
+
<div class="calendar-content">
|
|
1088
|
+
<div class="calendar-card">
|
|
1089
|
+
<div class="calendar-nav-header">
|
|
1090
|
+
<button type="button" class="calendar-nav-arrow" data-action="previous" aria-label="Previous month">
|
|
786
1091
|
‹
|
|
787
1092
|
</button>
|
|
788
1093
|
|
|
789
|
-
<div class="calendar
|
|
1094
|
+
<div class="calendar-picker-container" data-picker-container>
|
|
790
1095
|
<button
|
|
791
1096
|
type="button"
|
|
792
|
-
class="calendar
|
|
1097
|
+
class="calendar-picker-btn"
|
|
793
1098
|
data-action="toggle-picker"
|
|
794
1099
|
aria-expanded="${this.pickerOpen ? "true" : "false"}"
|
|
795
1100
|
aria-haspopup="true"
|
|
796
1101
|
>
|
|
797
1102
|
${useShortMonths ? `${MONTHS[viewModel.currentMonth]} ${viewModel.currentYear}` : viewModel.monthAndYearText}
|
|
798
|
-
<span class="calendar
|
|
1103
|
+
<span class="calendar-picker-chevron">▾</span>
|
|
799
1104
|
</button>
|
|
800
1105
|
|
|
801
1106
|
${this.pickerOpen ? `
|
|
802
|
-
<div class="calendar
|
|
803
|
-
<div class="calendar
|
|
1107
|
+
<div class="calendar-picker-dropdown">
|
|
1108
|
+
<div class="calendar-picker-year-row">
|
|
804
1109
|
<button
|
|
805
1110
|
type="button"
|
|
806
|
-
class="calendar
|
|
1111
|
+
class="calendar-picker-year-arrow"
|
|
807
1112
|
data-action="year-prev"
|
|
808
1113
|
${viewModel.currentYear <= minYear ? "disabled" : ""}
|
|
809
1114
|
aria-label="Previous year"
|
|
810
1115
|
>‹</button>
|
|
811
1116
|
<input
|
|
812
1117
|
type="text"
|
|
813
|
-
class="calendar
|
|
814
|
-
value="${this.yearInput || viewModel.currentYear}"
|
|
1118
|
+
class="calendar-picker-year-input${!this.yearInputValid ? " invalid" : ""}"
|
|
1119
|
+
value="${escapeHtml(this.yearInput || viewModel.currentYear)}"
|
|
815
1120
|
data-year-input
|
|
816
1121
|
aria-label="Year"
|
|
817
1122
|
/>
|
|
818
1123
|
<button
|
|
819
1124
|
type="button"
|
|
820
|
-
class="calendar
|
|
1125
|
+
class="calendar-picker-year-arrow"
|
|
821
1126
|
data-action="year-next"
|
|
822
1127
|
${viewModel.currentYear >= maxYear ? "disabled" : ""}
|
|
823
1128
|
aria-label="Next year"
|
|
824
1129
|
>›</button>
|
|
825
1130
|
</div>
|
|
826
1131
|
|
|
827
|
-
<div class="calendar
|
|
1132
|
+
<div class="calendar-picker-months">
|
|
828
1133
|
${(useShortMonths ? MONTHS : MONTHS_FULL).map((month, index) => {
|
|
829
1134
|
const isSelected = index === viewModel.currentMonth;
|
|
830
1135
|
const isCurrent = index === todayMonth && viewModel.currentYear === todayYear;
|
|
831
1136
|
return `
|
|
832
1137
|
<button
|
|
833
1138
|
type="button"
|
|
834
|
-
class="calendar
|
|
1139
|
+
class="calendar-picker-month${isSelected ? " selected" : ""}${isCurrent ? " current-month" : ""}"
|
|
835
1140
|
data-action="select-month"
|
|
836
1141
|
data-month="${index}"
|
|
837
1142
|
>${month}</button>
|
|
@@ -844,70 +1149,22 @@ var Kalendly = (() => {
|
|
|
844
1149
|
|
|
845
1150
|
<button
|
|
846
1151
|
type="button"
|
|
847
|
-
class="calendar
|
|
1152
|
+
class="calendar-today-btn"
|
|
848
1153
|
data-action="today"
|
|
849
1154
|
${isCurrentMonth ? "disabled" : ""}
|
|
850
1155
|
>Today</button>
|
|
851
1156
|
|
|
852
|
-
<button type="button" class="calendar
|
|
1157
|
+
<button type="button" class="calendar-nav-arrow" data-action="next" aria-label="Next month">
|
|
853
1158
|
›
|
|
854
1159
|
</button>
|
|
855
1160
|
</div>
|
|
856
1161
|
|
|
857
|
-
<
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
${viewModel.days.map((day) => `<th>${day.slice(0, 3)}</th>`).join("")}
|
|
861
|
-
</tr>
|
|
862
|
-
</thead>
|
|
863
|
-
<tbody data-calendar-body>
|
|
864
|
-
${isLoading ? Array.from(
|
|
865
|
-
{ length: 6 },
|
|
866
|
-
() => `<tr>${Array.from(
|
|
867
|
-
{ length: 7 },
|
|
868
|
-
() => `<td class="calendar--skeleton" aria-hidden="true"></td>`
|
|
869
|
-
).join("")}</tr>`
|
|
870
|
-
).join("") : viewModel.calendarDates.map(
|
|
871
|
-
(week) => `
|
|
872
|
-
<tr>
|
|
873
|
-
${week.map((calendarDate, dayIndex) => {
|
|
874
|
-
const classes = getCellClasses(calendarDate);
|
|
875
|
-
if (availabilityMode && calendarDate.isCurrentMonth) {
|
|
876
|
-
classes.push(
|
|
877
|
-
calendarDate.hasEvents ? "availability--booked" : "availability--free"
|
|
878
|
-
);
|
|
879
|
-
}
|
|
880
|
-
if (availabilityMode === "day" && selectable && calendarDate.isCurrentMonth) {
|
|
881
|
-
const d = calendarDate.date;
|
|
882
|
-
if (this._rangeStart && isSameDay2(d, this._rangeStart))
|
|
883
|
-
classes.push("availability--range-start");
|
|
884
|
-
if (this._rangeEnd && isSameDay2(d, this._rangeEnd))
|
|
885
|
-
classes.push("availability--range-end");
|
|
886
|
-
if (this._rangeStart && this._rangeEnd) {
|
|
887
|
-
if (d > this._rangeStart && d < this._rangeEnd)
|
|
888
|
-
classes.push("availability--in-range");
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
const dateString = calendarDate.date.toISOString();
|
|
892
|
-
return `
|
|
893
|
-
<td
|
|
894
|
-
class="${classes.join(" ")}"
|
|
895
|
-
data-date="${dateString}"
|
|
896
|
-
data-day-index="${dayIndex}"
|
|
897
|
-
data-clickable="true"
|
|
898
|
-
>
|
|
899
|
-
${calendarDate.date.getDate()}
|
|
900
|
-
</td>
|
|
901
|
-
`;
|
|
902
|
-
}).join("")}
|
|
903
|
-
</tr>
|
|
904
|
-
`
|
|
905
|
-
).join("")}
|
|
906
|
-
</tbody>
|
|
907
|
-
</table>
|
|
1162
|
+
<div class="calendar-panes">
|
|
1163
|
+
${viewModel.panes.map(renderPane).join("")}
|
|
1164
|
+
</div>
|
|
908
1165
|
|
|
909
1166
|
${!isLoading && availabilityMode !== "day" && viewModel.selectedDate ? `
|
|
910
|
-
<div class="date-popup
|
|
1167
|
+
<div class="date-popup">
|
|
911
1168
|
<div class="popup-header">
|
|
912
1169
|
<h2>${viewModel.scheduleDay}</h2>
|
|
913
1170
|
<button type="button" class="popup-close" data-action="close-popup" aria-label="Close">\u2715</button>
|
|
@@ -920,7 +1177,7 @@ var Kalendly = (() => {
|
|
|
920
1177
|
` : ""}
|
|
921
1178
|
|
|
922
1179
|
<div class="events-container">
|
|
923
|
-
${availabilityMode === "time" ? renderTimeGrid(viewModel.
|
|
1180
|
+
${availabilityMode === "time" ? renderTimeGrid(viewModel.selectedDate) : viewModel.tasks.length > 0 ? viewModel.tasks.map((event) => renderEvent(event)).join("") : renderNoEvents()}
|
|
924
1181
|
</div>
|
|
925
1182
|
</div>
|
|
926
1183
|
` : ""}
|
|
@@ -945,8 +1202,7 @@ var Kalendly = (() => {
|
|
|
945
1202
|
const availMode = this.getAttribute("availability-mode");
|
|
946
1203
|
const isSelectable = this.hasAttribute("selectable");
|
|
947
1204
|
if (availMode === "day" && isSelectable) {
|
|
948
|
-
|
|
949
|
-
if (!isBooked) {
|
|
1205
|
+
if (this.isDateSelectable(date)) {
|
|
950
1206
|
let startDate;
|
|
951
1207
|
let endDate;
|
|
952
1208
|
if (this._rangeEnd !== null) {
|
|
@@ -964,7 +1220,7 @@ var Kalendly = (() => {
|
|
|
964
1220
|
cursor.setDate(cursor.getDate() + 1);
|
|
965
1221
|
let blocked = false;
|
|
966
1222
|
while (cursor < e2) {
|
|
967
|
-
if (this.
|
|
1223
|
+
if (!this.isDateSelectable(cursor)) {
|
|
968
1224
|
blocked = true;
|
|
969
1225
|
break;
|
|
970
1226
|
}
|
|
@@ -1082,6 +1338,20 @@ var Kalendly = (() => {
|
|
|
1082
1338
|
const slotStart = actionEl.dataset.startTime;
|
|
1083
1339
|
const slotEnd = actionEl.dataset.endTime;
|
|
1084
1340
|
const slotDate = new Date(actionEl.dataset.date);
|
|
1341
|
+
const slotBooked = actionEl.dataset.booked === "true";
|
|
1342
|
+
this.dispatchEvent(
|
|
1343
|
+
new CustomEvent("cal-slot-select", {
|
|
1344
|
+
bubbles: true,
|
|
1345
|
+
composed: true,
|
|
1346
|
+
detail: {
|
|
1347
|
+
date: slotDate,
|
|
1348
|
+
startTime: slotStart,
|
|
1349
|
+
endTime: slotEnd,
|
|
1350
|
+
booked: slotBooked
|
|
1351
|
+
}
|
|
1352
|
+
})
|
|
1353
|
+
);
|
|
1354
|
+
if (slotBooked || !this.hasAttribute("selectable")) break;
|
|
1085
1355
|
if (this._timeRangeComplete) {
|
|
1086
1356
|
this._timeRangeDate = slotDate;
|
|
1087
1357
|
this._timeRangeStart = slotStart;
|
|
@@ -1198,31 +1468,86 @@ var Kalendly = (() => {
|
|
|
1198
1468
|
this.theme = theme;
|
|
1199
1469
|
}
|
|
1200
1470
|
getCurrentDate() {
|
|
1471
|
+
this.rethrowInitError();
|
|
1201
1472
|
return this.engine?.getViewModel().selectedDate ?? null;
|
|
1202
1473
|
}
|
|
1203
1474
|
goToDate(date) {
|
|
1475
|
+
this.rethrowInitError();
|
|
1204
1476
|
const year = date.getFullYear();
|
|
1205
1477
|
const month = date.getMonth();
|
|
1206
1478
|
this.dispatchMonthChange(year, month);
|
|
1207
1479
|
this.actions?.jump(year, month);
|
|
1208
1480
|
}
|
|
1209
1481
|
getEngine() {
|
|
1482
|
+
this.rethrowInitError();
|
|
1210
1483
|
if (!this.engine)
|
|
1211
1484
|
throw new Error("CalendarElement is not connected to the DOM");
|
|
1212
1485
|
return this.engine;
|
|
1213
1486
|
}
|
|
1214
1487
|
};
|
|
1215
|
-
|
|
1488
|
+
_CalendarElement.observedAttributes = [
|
|
1216
1489
|
"initial-date",
|
|
1217
1490
|
"min-year",
|
|
1218
1491
|
"max-year",
|
|
1219
1492
|
"week-starts-on",
|
|
1493
|
+
"heading",
|
|
1494
|
+
"months",
|
|
1495
|
+
"slot-duration",
|
|
1220
1496
|
"title",
|
|
1221
1497
|
"use-short-month-names",
|
|
1222
1498
|
"availability-mode",
|
|
1223
1499
|
"selectable",
|
|
1224
1500
|
"loading"
|
|
1225
1501
|
];
|
|
1502
|
+
_CalendarElement.themeMap = {
|
|
1503
|
+
primary: "--calendar-primary-color",
|
|
1504
|
+
secondary: "--calendar-secondary-color",
|
|
1505
|
+
tertiary: "--calendar-tertiary-color",
|
|
1506
|
+
textColor: "--calendar-text-color",
|
|
1507
|
+
textLight: "--calendar-text-light",
|
|
1508
|
+
background: "--calendar-background",
|
|
1509
|
+
cellHover: "--calendar-cell-hover",
|
|
1510
|
+
borderColor: "--calendar-border-color",
|
|
1511
|
+
todayOutline: "--calendar-today-outline",
|
|
1512
|
+
selectedBg: "--calendar-selected-bg",
|
|
1513
|
+
headerBg: "--calendar-header-bg",
|
|
1514
|
+
popupBg: "--calendar-popup-bg",
|
|
1515
|
+
pickerBg: "--calendar-picker-bg",
|
|
1516
|
+
pickerShadow: "--calendar-picker-shadow",
|
|
1517
|
+
eventIndicator: "--calendar-event-indicator",
|
|
1518
|
+
onAccent: "--calendar-on-accent",
|
|
1519
|
+
link: "--calendar-link",
|
|
1520
|
+
openBg: "--calendar-open-bg",
|
|
1521
|
+
openFg: "--calendar-open-fg",
|
|
1522
|
+
conditionalBg: "--calendar-conditional-bg",
|
|
1523
|
+
conditionalFg: "--calendar-conditional-fg",
|
|
1524
|
+
blockedBg: "--calendar-blocked-bg",
|
|
1525
|
+
blockedFg: "--calendar-blocked-fg",
|
|
1526
|
+
rangeBg: "--calendar-range-bg",
|
|
1527
|
+
rangeOutline: "--calendar-range-outline",
|
|
1528
|
+
inRangeBg: "--calendar-in-range-bg",
|
|
1529
|
+
inRangeOutline: "--calendar-in-range-outline",
|
|
1530
|
+
badgeBg: "--calendar-badge-bg",
|
|
1531
|
+
badgeText: "--calendar-badge-text",
|
|
1532
|
+
badgeSuccessBg: "--calendar-badge-success-bg",
|
|
1533
|
+
badgeSuccessText: "--calendar-badge-success-text",
|
|
1534
|
+
badgeInfoBg: "--calendar-badge-info-bg",
|
|
1535
|
+
badgeInfoText: "--calendar-badge-info-text",
|
|
1536
|
+
badgeWarningBg: "--calendar-badge-warning-bg",
|
|
1537
|
+
badgeWarningText: "--calendar-badge-warning-text",
|
|
1538
|
+
badgeDangerBg: "--calendar-badge-danger-bg",
|
|
1539
|
+
badgeDangerText: "--calendar-badge-danger-text",
|
|
1540
|
+
badgeNeutralBg: "--calendar-badge-neutral-bg",
|
|
1541
|
+
badgeNeutralText: "--calendar-badge-neutral-text",
|
|
1542
|
+
badgePositiveBg: "--calendar-badge-positive-bg",
|
|
1543
|
+
badgePositiveText: "--calendar-badge-positive-text",
|
|
1544
|
+
badgeTentativeBg: "--calendar-badge-tentative-bg",
|
|
1545
|
+
badgeTentativeText: "--calendar-badge-tentative-text"
|
|
1546
|
+
};
|
|
1547
|
+
_CalendarElement.warnedMissingEnd = /* @__PURE__ */ new Set();
|
|
1548
|
+
_CalendarElement.BUILT_IN_BUCKETS = ["blocked", "conditional", "open"];
|
|
1549
|
+
_CalendarElement.titleDeprecationWarned = false;
|
|
1550
|
+
var CalendarElement = _CalendarElement;
|
|
1226
1551
|
function defineCalendarElement(tagName = "kal-calendar") {
|
|
1227
1552
|
if (typeof customElements !== "undefined" && !customElements.get(tagName)) {
|
|
1228
1553
|
customElements.define(tagName, CalendarElement);
|