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.
@@ -51,12 +51,28 @@ function generateYears(minYear, maxYear) {
51
51
  const max = maxYear ?? currentYear + 10;
52
52
  return Array.from({ length: max - min + 1 }, (_, i) => min + i);
53
53
  }
54
+ function eventCoversDate(event, date) {
55
+ const start = normalizeDate(new Date(event.date)).getTime();
56
+ const target = normalizeDate(date).getTime();
57
+ if (Number.isNaN(start)) return false;
58
+ if (event.endDate === void 0 || event.endDate === null) {
59
+ return start === target;
60
+ }
61
+ const end = normalizeDate(new Date(event.endDate)).getTime();
62
+ if (Number.isNaN(end)) {
63
+ throw new Error(
64
+ `<kal-calendar> event ${event.id} has an unreadable endDate: ${String(event.endDate)}.`
65
+ );
66
+ }
67
+ if (end < start) {
68
+ throw new Error(
69
+ `<kal-calendar> event ${event.id} has an endDate before its date: ${String(event.endDate)} < ${String(event.date)}.`
70
+ );
71
+ }
72
+ return target >= start && target <= end;
73
+ }
54
74
  function getEventsForDate(events, date) {
55
- const normalizedTargetDate = normalizeDate(date);
56
- return events.filter((event) => {
57
- const eventDate = normalizeDate(new Date(event.date));
58
- return eventDate.getTime() === normalizedTargetDate.getTime();
59
- });
75
+ return events.filter((event) => eventCoversDate(event, date));
60
76
  }
61
77
  function hasEvents(events, date) {
62
78
  return getEventsForDate(events, date).length > 0;
@@ -115,26 +131,16 @@ function generateCalendarDates(year, month, events = [], weekStartsOn = 0) {
115
131
  }
116
132
  return dates;
117
133
  }
118
- function getPopupPositionClass(selectedDayIndex) {
119
- if (selectedDayIndex === null) return "popup-center-bottom";
120
- if (selectedDayIndex < 3) {
121
- return "popup-right";
122
- } else if (selectedDayIndex > 4) {
123
- return "popup-left";
124
- } else {
125
- return "popup-center-bottom";
126
- }
127
- }
128
134
  function getCellClasses(calendarDate) {
129
135
  const classes = [];
130
136
  if (!calendarDate.isCurrentMonth) {
131
- classes.push("other-month");
137
+ classes.push("calendar-cell-other-month");
132
138
  }
133
139
  if (calendarDate.isToday) {
134
- classes.push("schedule--current--exam");
140
+ classes.push("calendar-cell-today");
135
141
  }
136
142
  if (calendarDate.hasEvents) {
137
- classes.push("has--event");
143
+ classes.push("calendar-cell-has-event");
138
144
  }
139
145
  return classes;
140
146
  }
@@ -201,6 +207,92 @@ function getCategoryColor(category, customColors) {
201
207
  const color = colorMap[category] || colorMap.other || "#fc8917";
202
208
  return isValidHexColor(color) ? color : "#fc8917";
203
209
  }
210
+ var HTML_ESCAPES = {
211
+ "&": "&amp;",
212
+ "<": "&lt;",
213
+ ">": "&gt;",
214
+ '"': "&quot;",
215
+ "'": "&#39;"
216
+ };
217
+ function escapeHtml(value) {
218
+ if (value === null || value === void 0) return "";
219
+ return String(value).replace(/[&<>"']/g, (char) => HTML_ESCAPES[char]);
220
+ }
221
+ function slugifyToken(value) {
222
+ return String(value).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
223
+ }
224
+ function safeUrl(value) {
225
+ const normalized = String(value).split("").filter((char) => char.charCodeAt(0) > 32).join("");
226
+ if (/^(?:https?:|mailto:)/i.test(normalized)) return normalized;
227
+ const schemeless = !/^[^/?#]*:/.test(normalized);
228
+ return schemeless ? normalized : "#";
229
+ }
230
+ function safeColor(value) {
231
+ const trimmed = String(value).trim();
232
+ return isValidHexColor(trimmed) || /^[a-z]+$/i.test(trimmed) ? trimmed : "#3b82f6";
233
+ }
234
+ var MINUTES_PER_DAY = 1440;
235
+ var DEFAULT_SLOT_DURATION = 60;
236
+ function parseTimeToMinutes(time) {
237
+ if (typeof time !== "string") return null;
238
+ const match = /^(\d{1,2}):(\d{2})$/.exec(time.trim());
239
+ if (!match) return null;
240
+ const hours = Number(match[1]);
241
+ const mins = Number(match[2]);
242
+ if (hours > 24 || mins > 59 || hours === 24 && mins > 0) return null;
243
+ return hours * 60 + mins;
244
+ }
245
+ function formatMinutes(total) {
246
+ const hours = Math.floor(total / 60);
247
+ const mins = total % 60;
248
+ return `${String(hours).padStart(2, "0")}:${String(mins).padStart(2, "0")}`;
249
+ }
250
+ function eventInterval(event, slotDuration, onDay) {
251
+ const day = normalizeDate(onDay).getTime();
252
+ if (Number.isNaN(day)) return null;
253
+ const base = day / 6e4;
254
+ const wholeDay = [base, base + MINUTES_PER_DAY];
255
+ if (event.allDay || !event.startTime) return wholeDay;
256
+ const from = parseTimeToMinutes(event.startTime);
257
+ if (from === null) return wholeDay;
258
+ if (event.endTime === void 0 || event.endTime === null) {
259
+ return [base + from, base + from + slotDuration];
260
+ }
261
+ const to = parseTimeToMinutes(event.endTime);
262
+ if (to === null) return wholeDay;
263
+ return [base + from, base + (to <= from ? to + MINUTES_PER_DAY : to)];
264
+ }
265
+ function mergeIntervals(intervals) {
266
+ const sorted = [...intervals].sort((a, b) => a[0] - b[0]);
267
+ return sorted.reduce((merged, [start, end]) => {
268
+ const last = merged[merged.length - 1];
269
+ if (last && start <= last[1]) last[1] = Math.max(last[1], end);
270
+ else merged.push([start, end]);
271
+ return merged;
272
+ }, []);
273
+ }
274
+ function bookedSlots(events, date, slotDuration = DEFAULT_SLOT_DURATION) {
275
+ const base = normalizeDate(date).getTime() / 6e4;
276
+ const previousDay = new Date(date);
277
+ previousDay.setDate(previousDay.getDate() - 1);
278
+ const intervals = [];
279
+ for (const event of new Set(events)) {
280
+ for (const day of [previousDay, date]) {
281
+ if (!eventCoversDate(event, day)) continue;
282
+ const interval = eventInterval(event, slotDuration, day);
283
+ if (interval) intervals.push(interval);
284
+ }
285
+ }
286
+ const merged = mergeIntervals(intervals);
287
+ return Array.from(
288
+ { length: Math.floor(MINUTES_PER_DAY / slotDuration) },
289
+ (_, index) => {
290
+ const start = base + index * slotDuration;
291
+ const end = start + slotDuration;
292
+ return merged.some(([from, to]) => start < to && end > from);
293
+ }
294
+ );
295
+ }
204
296
 
205
297
  // src/core/calendar-engine.ts
206
298
  var CalendarEngine = class {
@@ -241,24 +333,38 @@ var CalendarEngine = class {
241
333
  * Get view model with computed properties
242
334
  */
243
335
  getViewModel() {
244
- const calendarDates = generateCalendarDates(
245
- this.state.currentYear,
246
- this.state.currentMonth,
247
- this.config.events,
248
- this.config.weekStartsOn
336
+ const panes = Array.from(
337
+ { length: Math.max(1, this.config.monthCount ?? 1) },
338
+ (_, offset) => {
339
+ const anchor = new Date(
340
+ this.state.currentYear,
341
+ this.state.currentMonth + offset,
342
+ 1
343
+ );
344
+ const year = anchor.getFullYear();
345
+ const month = anchor.getMonth();
346
+ return {
347
+ year,
348
+ month,
349
+ monthAndYearText: getMonthYearText(year, month),
350
+ calendarDates: generateCalendarDates(
351
+ year,
352
+ month,
353
+ this.config.events,
354
+ this.config.weekStartsOn
355
+ )
356
+ };
357
+ }
249
358
  );
250
359
  return {
251
360
  ...this.state,
252
361
  months: MONTHS,
253
362
  days: DAYS,
254
363
  years: generateYears(this.config.minYear, this.config.maxYear),
255
- monthAndYearText: getMonthYearText(
256
- this.state.currentYear,
257
- this.state.currentMonth
258
- ),
364
+ monthAndYearText: panes[0].monthAndYearText,
259
365
  scheduleDay: this.state.selectedDate ? formatDateForDisplay(this.state.selectedDate) : "",
260
- calendarDates,
261
- popupPositionClass: getPopupPositionClass(this.state.selectedDayIndex)
366
+ panes,
367
+ calendarDates: panes[0].calendarDates
262
368
  };
263
369
  }
264
370
  /**
@@ -433,10 +539,17 @@ export {
433
539
  CalendarEngine,
434
540
  DAYS,
435
541
  DEFAULT_CATEGORY_COLORS,
542
+ DEFAULT_SLOT_DURATION,
543
+ MINUTES_PER_DAY,
436
544
  MONTHS,
437
545
  MONTHS_FULL,
546
+ bookedSlots,
547
+ escapeHtml,
548
+ eventCoversDate,
549
+ eventInterval,
438
550
  formatAttendees,
439
551
  formatDateForDisplay,
552
+ formatMinutes,
440
553
  formatTimeRange,
441
554
  generateCalendarDates,
442
555
  generateYears,
@@ -445,13 +558,17 @@ export {
445
558
  getDefaultEventColor,
446
559
  getEventsForDate,
447
560
  getMonthYearText,
448
- getPopupPositionClass,
449
561
  hasEvents,
450
562
  isSameDay,
451
563
  isToday,
452
564
  isValidHexColor,
453
565
  mergeCategoryColors,
566
+ mergeIntervals,
454
567
  normalizeDate,
568
+ parseTimeToMinutes,
569
+ safeColor,
570
+ safeUrl,
571
+ slugifyToken,
455
572
  sortEventsByTime
456
573
  };
457
574
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/utils.ts","../../src/core/calendar-engine.ts"],"sourcesContent":["import { CalendarEvent, CalendarDate, CategoryColorMap } from './types';\n\nexport const MONTHS = [\n 'Jan',\n 'Feb',\n 'Mar',\n 'Apr',\n 'May',\n 'Jun',\n 'Jul',\n 'Aug',\n 'Sep',\n 'Oct',\n 'Nov',\n 'Dec',\n];\n\nexport const MONTHS_FULL = [\n 'January',\n 'February',\n 'March',\n 'April',\n 'May',\n 'June',\n 'July',\n 'August',\n 'September',\n 'October',\n 'November',\n 'December',\n];\n\nexport const DAYS = [\n 'Sunday',\n 'Monday',\n 'Tuesday',\n 'Wednesday',\n 'Thursday',\n 'Friday',\n 'Saturday',\n];\n\nexport function normalizeDate(date: Date): Date {\n return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);\n}\n\nexport function isSameDay(date1: Date, date2: Date): boolean {\n return normalizeDate(date1).getTime() === normalizeDate(date2).getTime();\n}\n\nexport function isToday(date: Date): boolean {\n return isSameDay(date, new Date());\n}\n\nexport function generateYears(minYear?: number, maxYear?: number): number[] {\n const currentYear = new Date().getFullYear();\n const min = minYear ?? currentYear - 30;\n const max = maxYear ?? currentYear + 10;\n\n return Array.from({ length: max - min + 1 }, (_, i) => min + i);\n}\n\nexport function getEventsForDate(\n events: CalendarEvent[],\n date: Date\n): CalendarEvent[] {\n const normalizedTargetDate = normalizeDate(date);\n\n return events.filter(event => {\n const eventDate = normalizeDate(new Date(event.date));\n return eventDate.getTime() === normalizedTargetDate.getTime();\n });\n}\n\nexport function hasEvents(events: CalendarEvent[], date: Date): boolean {\n return getEventsForDate(events, date).length > 0;\n}\n\nexport function generateCalendarDates(\n year: number,\n month: number,\n events: CalendarEvent[] = [],\n weekStartsOn: 0 | 1 = 0\n): CalendarDate[][] {\n const firstDay = new Date(year, month, 1);\n const lastDay = new Date(year, month + 1, 0);\n const daysInMonth = lastDay.getDate();\n const prevMonthLastDay = new Date(year, month, 0).getDate();\n\n let firstDayOfWeek = firstDay.getDay();\n if (weekStartsOn === 1) {\n firstDayOfWeek = firstDayOfWeek === 0 ? 6 : firstDayOfWeek - 1;\n }\n\n const dates: CalendarDate[][] = [];\n let day = 1;\n let nextMonthDay = 1;\n\n for (let week = 0; week < 6; week++) {\n const weekDates: CalendarDate[] = [];\n\n for (let dayOfWeek = 0; dayOfWeek < 7; dayOfWeek++) {\n if (week === 0 && dayOfWeek < firstDayOfWeek) {\n // Previous month days\n const prevDay = prevMonthLastDay - firstDayOfWeek + dayOfWeek + 1;\n const prevDate = new Date(year, month - 1, prevDay);\n const dateEvents = getEventsForDate(events, prevDate);\n weekDates.push({\n date: prevDate,\n isCurrentMonth: false,\n isToday: isToday(prevDate),\n hasEvents: dateEvents.length > 0,\n events: dateEvents,\n });\n } else if (day > daysInMonth) {\n // Next month days\n const nextDate = new Date(year, month + 1, nextMonthDay);\n const dateEvents = getEventsForDate(events, nextDate);\n weekDates.push({\n date: nextDate,\n isCurrentMonth: false,\n isToday: isToday(nextDate),\n hasEvents: dateEvents.length > 0,\n events: dateEvents,\n });\n nextMonthDay++;\n } else {\n // Current month days\n const currentDate = new Date(year, month, day);\n const dateEvents = getEventsForDate(events, currentDate);\n weekDates.push({\n date: currentDate,\n isCurrentMonth: true,\n isToday: isToday(currentDate),\n hasEvents: dateEvents.length > 0,\n events: dateEvents,\n });\n day++;\n }\n }\n\n dates.push(weekDates);\n }\n\n return dates;\n}\n\nexport function getPopupPositionClass(selectedDayIndex: number | null): string {\n if (selectedDayIndex === null) return 'popup-center-bottom';\n\n if (selectedDayIndex < 3) {\n return 'popup-right';\n } else if (selectedDayIndex > 4) {\n return 'popup-left';\n } else {\n return 'popup-center-bottom';\n }\n}\n\nexport function getCellClasses(calendarDate: CalendarDate): string[] {\n const classes: string[] = [];\n\n if (!calendarDate.isCurrentMonth) {\n classes.push('other-month');\n }\n\n if (calendarDate.isToday) {\n classes.push('schedule--current--exam');\n }\n\n if (calendarDate.hasEvents) {\n classes.push('has--event');\n }\n\n return classes;\n}\n\nexport function formatDateForDisplay(date: Date): string {\n return `${DAYS[date.getDay()]} ${date.getDate()}`;\n}\n\nexport function getMonthYearText(year: number, month: number): string {\n return `${MONTHS_FULL[month]} ${year}`;\n}\n\nexport function formatTimeRange(event: CalendarEvent): string {\n if (event.allDay || (!event.startTime && !event.endTime)) {\n return 'All day';\n }\n\n if (event.startTime && event.endTime) {\n return `${event.startTime} - ${event.endTime}`;\n }\n\n if (event.startTime) {\n return `${event.startTime}`;\n }\n\n return '';\n}\n\nexport function formatAttendees(attendees?: string[]): string {\n if (!attendees || attendees.length === 0) return '';\n\n if (attendees.length === 1) return attendees[0];\n if (attendees.length === 2) return attendees.join(' and ');\n\n return `${attendees.slice(0, -1).join(', ')}, and ${attendees[attendees.length - 1]}`;\n}\n\nexport function sortEventsByTime(events: CalendarEvent[]): CalendarEvent[] {\n return [...events].sort((a, b) => {\n const aAllDay = a.allDay || (!a.startTime && !a.endTime);\n const bAllDay = b.allDay || (!b.startTime && !b.endTime);\n\n if (aAllDay && !bAllDay) return -1;\n if (!aAllDay && bAllDay) return 1;\n\n if (!a.startTime || !b.startTime) return 0;\n return a.startTime.localeCompare(b.startTime);\n });\n}\n\nexport const DEFAULT_CATEGORY_COLORS: CategoryColorMap = {\n work: '#3b82f6',\n personal: '#8b5cf6',\n meeting: '#10b981',\n deadline: '#ef4444',\n appointment: '#f59e0b',\n other: '#6b7280',\n};\n\n// Dynamic function that accepts custom colors\nexport function getDefaultEventColor(\n category?: string,\n customColors?: CategoryColorMap\n): string {\n const colorMap = customColors || DEFAULT_CATEGORY_COLORS;\n\n if (category && colorMap[category]) {\n return colorMap[category];\n }\n\n // Default fallback color\n return '#fc8917';\n}\n\n// Helper to merge custom colors with defaults\nexport function mergeCategoryColors(\n customColors?: CategoryColorMap\n): CategoryColorMap {\n return {\n ...DEFAULT_CATEGORY_COLORS,\n ...customColors,\n };\n}\n\n// Validate if a color is a valid hex color\nexport function isValidHexColor(color: string): boolean {\n return /^#([0-9A-F]{3}){1,2}$/i.test(color);\n}\n\n// Get color for a category with validation\nexport function getCategoryColor(\n category: string,\n customColors?: CategoryColorMap\n): string {\n const colorMap = mergeCategoryColors(customColors);\n const color = colorMap[category] || colorMap.other || '#fc8917';\n\n return isValidHexColor(color) ? color : '#fc8917';\n}\n","import {\n CalendarEvent,\n CalendarState,\n CalendarConfig,\n CalendarActions,\n CalendarViewModel,\n CategoryColorMap,\n} from './types';\nimport {\n generateCalendarDates,\n getEventsForDate,\n generateYears,\n getPopupPositionClass,\n getMonthYearText,\n formatDateForDisplay,\n mergeCategoryColors,\n getCategoryColor,\n MONTHS,\n DAYS,\n} from './utils';\n\nexport class CalendarEngine {\n private state: CalendarState;\n private config: CalendarConfig;\n private listeners: Set<() => void> = new Set();\n private categoryColors: CategoryColorMap;\n\n constructor(config: CalendarConfig) {\n this.config = config;\n this.categoryColors = mergeCategoryColors(config.categoryColors);\n\n const initialDate = config.initialDate || new Date();\n this.state = {\n currentYear: initialDate.getFullYear(),\n currentMonth: initialDate.getMonth(),\n currentDate: initialDate.getDate(),\n selectedDate: null,\n selectedDayIndex: null,\n tasks: [],\n };\n }\n\n /**\n * Subscribe to state changes\n */\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Notify all listeners of state changes\n */\n private notify(): void {\n this.listeners.forEach(listener => listener());\n }\n\n /**\n * Get current state\n */\n getState(): CalendarState {\n return { ...this.state };\n }\n\n /**\n * Get view model with computed properties\n */\n getViewModel(): CalendarViewModel {\n const calendarDates = generateCalendarDates(\n this.state.currentYear,\n this.state.currentMonth,\n this.config.events,\n this.config.weekStartsOn\n );\n\n return {\n ...this.state,\n months: MONTHS,\n days: DAYS,\n years: generateYears(this.config.minYear, this.config.maxYear),\n monthAndYearText: getMonthYearText(\n this.state.currentYear,\n this.state.currentMonth\n ),\n scheduleDay: this.state.selectedDate\n ? formatDateForDisplay(this.state.selectedDate)\n : '',\n calendarDates,\n popupPositionClass: getPopupPositionClass(this.state.selectedDayIndex),\n };\n }\n\n /**\n * Get actions object\n */\n getActions(): CalendarActions {\n return {\n next: this.next.bind(this),\n previous: this.previous.bind(this),\n jump: this.jump.bind(this),\n goToToday: this.goToToday.bind(this),\n isCurrentMonth: this.isCurrentMonth.bind(this),\n selectDate: this.selectDate.bind(this),\n updateTasks: this.updateTasks.bind(this),\n };\n }\n\n /**\n * Navigate to next month\n */\n private next(): void {\n if (this.state.currentMonth === 11) {\n this.state.currentMonth = 0;\n this.state.currentYear++;\n } else {\n this.state.currentMonth++;\n }\n\n this.state.selectedDate = null;\n this.state.selectedDayIndex = null;\n this.updateTasks();\n this.notify();\n }\n\n /**\n * Navigate to previous month\n */\n private previous(): void {\n if (this.state.currentMonth === 0) {\n this.state.currentMonth = 11;\n this.state.currentYear--;\n } else {\n this.state.currentMonth--;\n }\n\n this.state.selectedDate = null;\n this.state.selectedDayIndex = null;\n this.updateTasks();\n this.notify();\n }\n\n /**\n * Jump to specific month and year\n */\n private jump(year: number, month: number): void {\n this.state.currentYear = year;\n this.state.currentMonth = month;\n this.state.selectedDate = null;\n this.state.selectedDayIndex = null;\n this.updateTasks();\n this.notify();\n }\n\n /**\n * Navigate to current month (today)\n */\n private goToToday(): void {\n const today = new Date();\n this.state.currentYear = today.getFullYear();\n this.state.currentMonth = today.getMonth();\n this.state.selectedDate = null;\n this.state.selectedDayIndex = null;\n this.updateTasks();\n this.notify();\n }\n\n /**\n * Check if currently viewing today's month\n */\n private isCurrentMonth(): boolean {\n const today = new Date();\n return (\n this.state.currentYear === today.getFullYear() &&\n this.state.currentMonth === today.getMonth()\n );\n }\n\n /**\n * Select a specific date\n */\n private selectDate(date: Date, dayIndex?: number): void {\n this.state.selectedDate = date;\n this.state.selectedDayIndex = dayIndex ?? null;\n this.state.currentDate = date.getDate();\n this.state.currentMonth = date.getMonth();\n this.state.currentYear = date.getFullYear();\n this.updateTasks();\n this.notify();\n }\n\n /**\n * Update tasks for the currently selected date\n */\n private updateTasks(): void {\n if (!this.state.selectedDate) {\n this.state.tasks = [];\n return;\n }\n\n this.state.tasks = getEventsForDate(\n this.config.events,\n this.state.selectedDate\n );\n }\n\n /**\n * Update events configuration\n */\n updateEvents(events: CalendarEvent[]): void {\n this.config.events = events;\n this.updateTasks();\n this.notify();\n }\n\n /**\n * Handle date cell click\n */\n handleDateClick(date: Date, dayIndex?: number): void {\n this.selectDate(date, dayIndex);\n }\n\n /**\n * Check if date has events\n */\n hasEventsForDate(date: Date): boolean {\n return getEventsForDate(this.config.events, date).length > 0;\n }\n\n /**\n * Get events for a specific date\n */\n getEventsForDate(date: Date): CalendarEvent[] {\n return getEventsForDate(this.config.events, date);\n }\n\n /**\n * Clear selected date\n */\n clearSelection(): void {\n this.state.selectedDate = null;\n this.state.selectedDayIndex = null;\n this.state.tasks = [];\n this.notify();\n }\n\n /**\n * Get category color (with custom colors support)\n */\n getCategoryColor(category?: string): string {\n if (!category) return '#fc8917';\n return getCategoryColor(category, this.categoryColors);\n }\n\n /**\n * Update category colors dynamically\n */\n updateCategoryColors(colors: CategoryColorMap): void {\n this.categoryColors = mergeCategoryColors(colors);\n this.notify();\n }\n\n /**\n * Get all category colors\n */\n getCategoryColors(): CategoryColorMap {\n return { ...this.categoryColors };\n }\n\n /**\n * Register a new category with color\n */\n registerCategory(name: string, color: string): void {\n this.categoryColors[name] = color;\n this.notify();\n }\n\n /**\n * Destroy the engine and cleanup listeners\n */\n destroy(): void {\n this.listeners.clear();\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/core/utils.ts","../../src/core/calendar-engine.ts"],"sourcesContent":["import { CalendarEvent, CalendarDate, CategoryColorMap } from './types';\n\nexport const MONTHS = [\n 'Jan',\n 'Feb',\n 'Mar',\n 'Apr',\n 'May',\n 'Jun',\n 'Jul',\n 'Aug',\n 'Sep',\n 'Oct',\n 'Nov',\n 'Dec',\n];\n\nexport const MONTHS_FULL = [\n 'January',\n 'February',\n 'March',\n 'April',\n 'May',\n 'June',\n 'July',\n 'August',\n 'September',\n 'October',\n 'November',\n 'December',\n];\n\nexport const DAYS = [\n 'Sunday',\n 'Monday',\n 'Tuesday',\n 'Wednesday',\n 'Thursday',\n 'Friday',\n 'Saturday',\n];\n\nexport function normalizeDate(date: Date): Date {\n return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);\n}\n\nexport function isSameDay(date1: Date, date2: Date): boolean {\n return normalizeDate(date1).getTime() === normalizeDate(date2).getTime();\n}\n\nexport function isToday(date: Date): boolean {\n return isSameDay(date, new Date());\n}\n\nexport function generateYears(minYear?: number, maxYear?: number): number[] {\n const currentYear = new Date().getFullYear();\n const min = minYear ?? currentYear - 30;\n const max = maxYear ?? currentYear + 10;\n\n return Array.from({ length: max - min + 1 }, (_, i) => min + i);\n}\n\n// endDate is inclusive, matching the endDate cal-availability-select emits, so\n// a selection can be handed straight back as one event.\nexport function eventCoversDate(event: CalendarEvent, date: Date): boolean {\n const start = normalizeDate(new Date(event.date)).getTime();\n const target = normalizeDate(date).getTime();\n if (Number.isNaN(start)) return false;\n\n if (event.endDate === undefined || event.endDate === null) {\n return start === target;\n }\n\n const end = normalizeDate(new Date(event.endDate)).getTime();\n if (Number.isNaN(end)) {\n throw new Error(\n `<kal-calendar> event ${event.id} has an unreadable endDate: ` +\n `${String(event.endDate)}.`\n );\n }\n if (end < start) {\n throw new Error(\n `<kal-calendar> event ${event.id} has an endDate before its date: ` +\n `${String(event.endDate)} < ${String(event.date)}.`\n );\n }\n\n return target >= start && target <= end;\n}\n\nexport function getEventsForDate(\n events: CalendarEvent[],\n date: Date\n): CalendarEvent[] {\n return events.filter(event => eventCoversDate(event, date));\n}\n\nexport function hasEvents(events: CalendarEvent[], date: Date): boolean {\n return getEventsForDate(events, date).length > 0;\n}\n\nexport function generateCalendarDates(\n year: number,\n month: number,\n events: CalendarEvent[] = [],\n weekStartsOn: 0 | 1 = 0\n): CalendarDate[][] {\n const firstDay = new Date(year, month, 1);\n const lastDay = new Date(year, month + 1, 0);\n const daysInMonth = lastDay.getDate();\n const prevMonthLastDay = new Date(year, month, 0).getDate();\n\n let firstDayOfWeek = firstDay.getDay();\n if (weekStartsOn === 1) {\n firstDayOfWeek = firstDayOfWeek === 0 ? 6 : firstDayOfWeek - 1;\n }\n\n const dates: CalendarDate[][] = [];\n let day = 1;\n let nextMonthDay = 1;\n\n for (let week = 0; week < 6; week++) {\n const weekDates: CalendarDate[] = [];\n\n for (let dayOfWeek = 0; dayOfWeek < 7; dayOfWeek++) {\n if (week === 0 && dayOfWeek < firstDayOfWeek) {\n // Previous month days\n const prevDay = prevMonthLastDay - firstDayOfWeek + dayOfWeek + 1;\n const prevDate = new Date(year, month - 1, prevDay);\n const dateEvents = getEventsForDate(events, prevDate);\n weekDates.push({\n date: prevDate,\n isCurrentMonth: false,\n isToday: isToday(prevDate),\n hasEvents: dateEvents.length > 0,\n events: dateEvents,\n });\n } else if (day > daysInMonth) {\n // Next month days\n const nextDate = new Date(year, month + 1, nextMonthDay);\n const dateEvents = getEventsForDate(events, nextDate);\n weekDates.push({\n date: nextDate,\n isCurrentMonth: false,\n isToday: isToday(nextDate),\n hasEvents: dateEvents.length > 0,\n events: dateEvents,\n });\n nextMonthDay++;\n } else {\n // Current month days\n const currentDate = new Date(year, month, day);\n const dateEvents = getEventsForDate(events, currentDate);\n weekDates.push({\n date: currentDate,\n isCurrentMonth: true,\n isToday: isToday(currentDate),\n hasEvents: dateEvents.length > 0,\n events: dateEvents,\n });\n day++;\n }\n }\n\n dates.push(weekDates);\n }\n\n return dates;\n}\n\nexport function getCellClasses(calendarDate: CalendarDate): string[] {\n const classes: string[] = [];\n\n if (!calendarDate.isCurrentMonth) {\n classes.push('calendar-cell-other-month');\n }\n\n if (calendarDate.isToday) {\n classes.push('calendar-cell-today');\n }\n\n if (calendarDate.hasEvents) {\n classes.push('calendar-cell-has-event');\n }\n\n return classes;\n}\n\nexport function formatDateForDisplay(date: Date): string {\n return `${DAYS[date.getDay()]} ${date.getDate()}`;\n}\n\nexport function getMonthYearText(year: number, month: number): string {\n return `${MONTHS_FULL[month]} ${year}`;\n}\n\nexport function formatTimeRange(event: CalendarEvent): string {\n if (event.allDay || (!event.startTime && !event.endTime)) {\n return 'All day';\n }\n\n if (event.startTime && event.endTime) {\n return `${event.startTime} - ${event.endTime}`;\n }\n\n if (event.startTime) {\n return `${event.startTime}`;\n }\n\n return '';\n}\n\nexport function formatAttendees(attendees?: string[]): string {\n if (!attendees || attendees.length === 0) return '';\n\n if (attendees.length === 1) return attendees[0];\n if (attendees.length === 2) return attendees.join(' and ');\n\n return `${attendees.slice(0, -1).join(', ')}, and ${attendees[attendees.length - 1]}`;\n}\n\nexport function sortEventsByTime(events: CalendarEvent[]): CalendarEvent[] {\n return [...events].sort((a, b) => {\n const aAllDay = a.allDay || (!a.startTime && !a.endTime);\n const bAllDay = b.allDay || (!b.startTime && !b.endTime);\n\n if (aAllDay && !bAllDay) return -1;\n if (!aAllDay && bAllDay) return 1;\n\n if (!a.startTime || !b.startTime) return 0;\n return a.startTime.localeCompare(b.startTime);\n });\n}\n\nexport const DEFAULT_CATEGORY_COLORS: CategoryColorMap = {\n work: '#3b82f6',\n personal: '#8b5cf6',\n meeting: '#10b981',\n deadline: '#ef4444',\n appointment: '#f59e0b',\n other: '#6b7280',\n};\n\n// Dynamic function that accepts custom colors\nexport function getDefaultEventColor(\n category?: string,\n customColors?: CategoryColorMap\n): string {\n const colorMap = customColors || DEFAULT_CATEGORY_COLORS;\n\n if (category && colorMap[category]) {\n return colorMap[category];\n }\n\n // Default fallback color\n return '#fc8917';\n}\n\n// Helper to merge custom colors with defaults\nexport function mergeCategoryColors(\n customColors?: CategoryColorMap\n): CategoryColorMap {\n return {\n ...DEFAULT_CATEGORY_COLORS,\n ...customColors,\n };\n}\n\n// Validate if a color is a valid hex color\nexport function isValidHexColor(color: string): boolean {\n return /^#([0-9A-F]{3}){1,2}$/i.test(color);\n}\n\n// Get color for a category with validation\nexport function getCategoryColor(\n category: string,\n customColors?: CategoryColorMap\n): string {\n const colorMap = mergeCategoryColors(customColors);\n const color = colorMap[category] || colorMap.other || '#fc8917';\n\n return isValidHexColor(color) ? color : '#fc8917';\n}\n\nconst HTML_ESCAPES: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\n};\n\n// Escape a value for interpolation into HTML text or a quoted attribute\nexport function escapeHtml(value: unknown): string {\n if (value === null || value === undefined) return '';\n\n return String(value).replace(/[&<>\"']/g, char => HTML_ESCAPES[char]);\n}\n\n// Reduce a caller-supplied value to a single safe CSS class token\nexport function slugifyToken(value: string): string {\n return String(value)\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\n// Allow http(s), mailto and relative URLs; anything else (javascript:, data:,\n// vbscript:) collapses to a harmless anchor\nexport function safeUrl(value: string): string {\n // Browsers ignore control characters when resolving a scheme, so\n // `java\\0script:` and `java\\tscript:` both navigate. Strip them before\n // testing rather than after.\n const normalized = String(value)\n .split('')\n .filter(char => char.charCodeAt(0) > 0x20)\n .join('');\n\n if (/^(?:https?:|mailto:)/i.test(normalized)) return normalized;\n\n // Relative URLs have no scheme at all — a colon before the first slash,\n // question mark or hash means someone is naming one\n const schemeless = !/^[^/?#]*:/.test(normalized);\n\n return schemeless ? normalized : '#';\n}\n\n// Accept hex colors and bare CSS color keywords; reject anything that could\n// terminate the declaration or escape the style attribute\nexport function safeColor(value: string): string {\n const trimmed = String(value).trim();\n\n return isValidHexColor(trimmed) || /^[a-z]+$/i.test(trimmed)\n ? trimmed\n : '#3b82f6';\n}\n\nexport const MINUTES_PER_DAY = 1440;\nexport const DEFAULT_SLOT_DURATION = 60;\n\nexport function parseTimeToMinutes(time: unknown): number | null {\n if (typeof time !== 'string') return null;\n\n const match = /^(\\d{1,2}):(\\d{2})$/.exec(time.trim());\n if (!match) return null;\n\n const hours = Number(match[1]);\n const mins = Number(match[2]);\n if (hours > 24 || mins > 59 || (hours === 24 && mins > 0)) return null;\n\n return hours * 60 + mins;\n}\n\nexport function formatMinutes(total: number): string {\n const hours = Math.floor(total / 60);\n const mins = total % 60;\n return `${String(hours).padStart(2, '0')}:${String(mins).padStart(2, '0')}`;\n}\n\n// Absolute [start, end) in minutes. An end at or before the start is on the\n// following day, so a booking that crosses midnight is one interval rather than\n// two half-days. An unreadable time occupies the whole day — a booking surface\n// that cannot parse a time must not offer the slot.\nexport function eventInterval(\n event: CalendarEvent,\n slotDuration: number,\n onDay: Date\n): [number, number] | null {\n const day = normalizeDate(onDay).getTime();\n if (Number.isNaN(day)) return null;\n\n const base = day / 60000;\n const wholeDay: [number, number] = [base, base + MINUTES_PER_DAY];\n\n if (event.allDay || !event.startTime) return wholeDay;\n\n const from = parseTimeToMinutes(event.startTime);\n if (from === null) return wholeDay;\n\n if (event.endTime === undefined || event.endTime === null) {\n return [base + from, base + from + slotDuration];\n }\n\n const to = parseTimeToMinutes(event.endTime);\n if (to === null) return wholeDay;\n\n return [base + from, base + (to <= from ? to + MINUTES_PER_DAY : to)];\n}\n\nexport function mergeIntervals(\n intervals: Array<[number, number]>\n): Array<[number, number]> {\n const sorted = [...intervals].sort((a, b) => a[0] - b[0]);\n\n return sorted.reduce<Array<[number, number]>>((merged, [start, end]) => {\n const last = merged[merged.length - 1];\n if (last && start <= last[1]) last[1] = Math.max(last[1], end);\n else merged.push([start, end]);\n return merged;\n }, []);\n}\n\n// Which of the day's slots any booking overlaps. Events from the previous day\n// belong in `events` so a booking crossing midnight marks the morning it runs into.\nexport function bookedSlots(\n events: CalendarEvent[],\n date: Date,\n slotDuration: number = DEFAULT_SLOT_DURATION\n): boolean[] {\n const base = normalizeDate(date).getTime() / 60000;\n\n const previousDay = new Date(date);\n previousDay.setDate(previousDay.getDate() - 1);\n\n // A span's times repeat on each day it covers, so the window is anchored to\n // the day being drawn rather than the day the span began\n const intervals: Array<[number, number]> = [];\n for (const event of new Set(events)) {\n for (const day of [previousDay, date]) {\n if (!eventCoversDate(event, day)) continue;\n const interval = eventInterval(event, slotDuration, day);\n if (interval) intervals.push(interval);\n }\n }\n\n const merged = mergeIntervals(intervals);\n\n return Array.from(\n { length: Math.floor(MINUTES_PER_DAY / slotDuration) },\n (_, index) => {\n const start = base + index * slotDuration;\n const end = start + slotDuration;\n return merged.some(([from, to]) => start < to && end > from);\n }\n );\n}\n","import {\n CalendarEvent,\n CalendarState,\n CalendarConfig,\n CalendarActions,\n CalendarViewModel,\n CalendarPane,\n CategoryColorMap,\n} from './types';\nimport {\n generateCalendarDates,\n getEventsForDate,\n generateYears,\n getMonthYearText,\n formatDateForDisplay,\n mergeCategoryColors,\n getCategoryColor,\n MONTHS,\n DAYS,\n} from './utils';\n\nexport class CalendarEngine {\n private state: CalendarState;\n private config: CalendarConfig;\n private listeners: Set<() => void> = new Set();\n private categoryColors: CategoryColorMap;\n\n constructor(config: CalendarConfig) {\n this.config = config;\n this.categoryColors = mergeCategoryColors(config.categoryColors);\n\n const initialDate = config.initialDate || new Date();\n this.state = {\n currentYear: initialDate.getFullYear(),\n currentMonth: initialDate.getMonth(),\n currentDate: initialDate.getDate(),\n selectedDate: null,\n selectedDayIndex: null,\n tasks: [],\n };\n }\n\n /**\n * Subscribe to state changes\n */\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Notify all listeners of state changes\n */\n private notify(): void {\n this.listeners.forEach(listener => listener());\n }\n\n /**\n * Get current state\n */\n getState(): CalendarState {\n return { ...this.state };\n }\n\n /**\n * Get view model with computed properties\n */\n getViewModel(): CalendarViewModel {\n const panes: CalendarPane[] = Array.from(\n { length: Math.max(1, this.config.monthCount ?? 1) },\n (_, offset) => {\n const anchor = new Date(\n this.state.currentYear,\n this.state.currentMonth + offset,\n 1\n );\n const year = anchor.getFullYear();\n const month = anchor.getMonth();\n\n return {\n year,\n month,\n monthAndYearText: getMonthYearText(year, month),\n calendarDates: generateCalendarDates(\n year,\n month,\n this.config.events,\n this.config.weekStartsOn\n ),\n };\n }\n );\n\n return {\n ...this.state,\n months: MONTHS,\n days: DAYS,\n years: generateYears(this.config.minYear, this.config.maxYear),\n monthAndYearText: panes[0].monthAndYearText,\n scheduleDay: this.state.selectedDate\n ? formatDateForDisplay(this.state.selectedDate)\n : '',\n panes,\n calendarDates: panes[0].calendarDates,\n };\n }\n\n /**\n * Get actions object\n */\n getActions(): CalendarActions {\n return {\n next: this.next.bind(this),\n previous: this.previous.bind(this),\n jump: this.jump.bind(this),\n goToToday: this.goToToday.bind(this),\n isCurrentMonth: this.isCurrentMonth.bind(this),\n selectDate: this.selectDate.bind(this),\n updateTasks: this.updateTasks.bind(this),\n };\n }\n\n /**\n * Navigate to next month\n */\n private next(): void {\n if (this.state.currentMonth === 11) {\n this.state.currentMonth = 0;\n this.state.currentYear++;\n } else {\n this.state.currentMonth++;\n }\n\n this.state.selectedDate = null;\n this.state.selectedDayIndex = null;\n this.updateTasks();\n this.notify();\n }\n\n /**\n * Navigate to previous month\n */\n private previous(): void {\n if (this.state.currentMonth === 0) {\n this.state.currentMonth = 11;\n this.state.currentYear--;\n } else {\n this.state.currentMonth--;\n }\n\n this.state.selectedDate = null;\n this.state.selectedDayIndex = null;\n this.updateTasks();\n this.notify();\n }\n\n /**\n * Jump to specific month and year\n */\n private jump(year: number, month: number): void {\n this.state.currentYear = year;\n this.state.currentMonth = month;\n this.state.selectedDate = null;\n this.state.selectedDayIndex = null;\n this.updateTasks();\n this.notify();\n }\n\n /**\n * Navigate to current month (today)\n */\n private goToToday(): void {\n const today = new Date();\n this.state.currentYear = today.getFullYear();\n this.state.currentMonth = today.getMonth();\n this.state.selectedDate = null;\n this.state.selectedDayIndex = null;\n this.updateTasks();\n this.notify();\n }\n\n /**\n * Check if currently viewing today's month\n */\n private isCurrentMonth(): boolean {\n const today = new Date();\n return (\n this.state.currentYear === today.getFullYear() &&\n this.state.currentMonth === today.getMonth()\n );\n }\n\n /**\n * Select a specific date\n */\n private selectDate(date: Date, dayIndex?: number): void {\n this.state.selectedDate = date;\n this.state.selectedDayIndex = dayIndex ?? null;\n this.state.currentDate = date.getDate();\n this.state.currentMonth = date.getMonth();\n this.state.currentYear = date.getFullYear();\n this.updateTasks();\n this.notify();\n }\n\n /**\n * Update tasks for the currently selected date\n */\n private updateTasks(): void {\n if (!this.state.selectedDate) {\n this.state.tasks = [];\n return;\n }\n\n this.state.tasks = getEventsForDate(\n this.config.events,\n this.state.selectedDate\n );\n }\n\n /**\n * Update events configuration\n */\n updateEvents(events: CalendarEvent[]): void {\n this.config.events = events;\n this.updateTasks();\n this.notify();\n }\n\n /**\n * Handle date cell click\n */\n handleDateClick(date: Date, dayIndex?: number): void {\n this.selectDate(date, dayIndex);\n }\n\n /**\n * Check if date has events\n */\n hasEventsForDate(date: Date): boolean {\n return getEventsForDate(this.config.events, date).length > 0;\n }\n\n /**\n * Get events for a specific date\n */\n getEventsForDate(date: Date): CalendarEvent[] {\n return getEventsForDate(this.config.events, date);\n }\n\n /**\n * Clear selected date\n */\n clearSelection(): void {\n this.state.selectedDate = null;\n this.state.selectedDayIndex = null;\n this.state.tasks = [];\n this.notify();\n }\n\n /**\n * Get category color (with custom colors support)\n */\n getCategoryColor(category?: string): string {\n if (!category) return '#fc8917';\n return getCategoryColor(category, this.categoryColors);\n }\n\n /**\n * Update category colors dynamically\n */\n updateCategoryColors(colors: CategoryColorMap): void {\n this.categoryColors = mergeCategoryColors(colors);\n this.notify();\n }\n\n /**\n * Get all category colors\n */\n getCategoryColors(): CategoryColorMap {\n return { ...this.categoryColors };\n }\n\n /**\n * Register a new category with color\n */\n registerCategory(name: string, color: string): void {\n this.categoryColors[name] = color;\n this.notify();\n }\n\n /**\n * Destroy the engine and cleanup listeners\n */\n destroy(): void {\n this.listeners.clear();\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.mts CHANGED
@@ -1,17 +1,20 @@
1
+ type Open<T extends string> = T | (string & {});
1
2
  interface CalendarEvent {
2
3
  id: string | number;
3
4
  name: string;
4
5
  date: string | Date;
6
+ endDate?: string | Date;
5
7
  startTime?: string;
6
8
  endTime?: string;
7
9
  allDay?: boolean;
8
10
  description?: string;
9
11
  color?: string;
10
- category?: 'work' | 'personal' | 'meeting' | 'deadline' | 'appointment' | 'other';
12
+ category?: Open<'work' | 'personal' | 'meeting' | 'deadline' | 'appointment' | 'other'>;
11
13
  location?: string;
12
14
  url?: string;
13
- status?: 'scheduled' | 'completed' | 'cancelled' | 'tentative';
14
- priority?: 'low' | 'medium' | 'high';
15
+ status?: Open<'scheduled' | 'completed' | 'cancelled' | 'tentative'>;
16
+ priority?: Open<'low' | 'medium' | 'high'>;
17
+ availabilityStatus?: Open<'open' | 'conditional' | 'blocked'>;
15
18
  attendees?: string[];
16
19
  organizer?: string;
17
20
  reminders?: number[];
@@ -52,6 +55,13 @@ interface CalendarConfig {
52
55
  maxYear?: number;
53
56
  weekStartsOn?: 0 | 1;
54
57
  categoryColors?: CategoryColorMap;
58
+ monthCount?: number;
59
+ }
60
+ interface CalendarPane {
61
+ year: number;
62
+ month: number;
63
+ monthAndYearText: string;
64
+ calendarDates: CalendarDate[][];
55
65
  }
56
66
  interface CalendarActions {
57
67
  next: () => void;
@@ -62,18 +72,14 @@ interface CalendarActions {
62
72
  selectDate: (date: Date) => void;
63
73
  updateTasks: () => void;
64
74
  }
65
- interface PopupPosition {
66
- class: 'popup-left' | 'popup-right' | 'popup-center-top' | 'popup-center-bottom';
67
- style?: Record<string, string | number>;
68
- }
69
75
  interface CalendarViewModel extends CalendarState {
70
76
  months: string[];
71
77
  days: string[];
72
78
  years: number[];
73
79
  monthAndYearText: string;
74
80
  scheduleDay: string;
81
+ panes: CalendarPane[];
75
82
  calendarDates: CalendarDate[][];
76
- popupPositionClass: string;
77
83
  }
78
84
  type CalendarEventHandler = (event: CalendarEvent) => void;
79
85
  interface CalendarProps {
@@ -100,7 +106,37 @@ interface CalendarTheme {
100
106
  selectedBg?: string;
101
107
  headerBg?: string;
102
108
  popupBg?: string;
109
+ pickerBg?: string;
110
+ pickerShadow?: string;
103
111
  eventIndicator?: string;
112
+ onAccent?: string;
113
+ link?: string;
114
+ openBg?: string;
115
+ openFg?: string;
116
+ conditionalBg?: string;
117
+ conditionalFg?: string;
118
+ blockedBg?: string;
119
+ blockedFg?: string;
120
+ rangeBg?: string;
121
+ rangeOutline?: string;
122
+ inRangeBg?: string;
123
+ inRangeOutline?: string;
124
+ badgeBg?: string;
125
+ badgeText?: string;
126
+ badgeSuccessBg?: string;
127
+ badgeSuccessText?: string;
128
+ badgeInfoBg?: string;
129
+ badgeInfoText?: string;
130
+ badgeWarningBg?: string;
131
+ badgeWarningText?: string;
132
+ badgeDangerBg?: string;
133
+ badgeDangerText?: string;
134
+ badgeNeutralBg?: string;
135
+ badgeNeutralText?: string;
136
+ badgePositiveBg?: string;
137
+ badgePositiveText?: string;
138
+ badgeTentativeBg?: string;
139
+ badgeTentativeText?: string;
104
140
  }
105
141
 
106
142
  declare const MONTHS: string[];
@@ -110,10 +146,10 @@ declare function normalizeDate(date: Date): Date;
110
146
  declare function isSameDay(date1: Date, date2: Date): boolean;
111
147
  declare function isToday(date: Date): boolean;
112
148
  declare function generateYears(minYear?: number, maxYear?: number): number[];
149
+ declare function eventCoversDate(event: CalendarEvent, date: Date): boolean;
113
150
  declare function getEventsForDate(events: CalendarEvent[], date: Date): CalendarEvent[];
114
151
  declare function hasEvents(events: CalendarEvent[], date: Date): boolean;
115
152
  declare function generateCalendarDates(year: number, month: number, events?: CalendarEvent[], weekStartsOn?: 0 | 1): CalendarDate[][];
116
- declare function getPopupPositionClass(selectedDayIndex: number | null): string;
117
153
  declare function getCellClasses(calendarDate: CalendarDate): string[];
118
154
  declare function formatDateForDisplay(date: Date): string;
119
155
  declare function getMonthYearText(year: number, month: number): string;
@@ -125,6 +161,17 @@ declare function getDefaultEventColor(category?: string, customColors?: Category
125
161
  declare function mergeCategoryColors(customColors?: CategoryColorMap): CategoryColorMap;
126
162
  declare function isValidHexColor(color: string): boolean;
127
163
  declare function getCategoryColor(category: string, customColors?: CategoryColorMap): string;
164
+ declare function escapeHtml(value: unknown): string;
165
+ declare function slugifyToken(value: string): string;
166
+ declare function safeUrl(value: string): string;
167
+ declare function safeColor(value: string): string;
168
+ declare const MINUTES_PER_DAY = 1440;
169
+ declare const DEFAULT_SLOT_DURATION = 60;
170
+ declare function parseTimeToMinutes(time: unknown): number | null;
171
+ declare function formatMinutes(total: number): string;
172
+ declare function eventInterval(event: CalendarEvent, slotDuration: number, onDay: Date): [number, number] | null;
173
+ declare function mergeIntervals(intervals: Array<[number, number]>): Array<[number, number]>;
174
+ declare function bookedSlots(events: CalendarEvent[], date: Date, slotDuration?: number): boolean[];
128
175
 
129
176
  declare class CalendarEngine {
130
177
  private state;
@@ -224,6 +271,7 @@ declare class CalendarEngine {
224
271
 
225
272
  declare class CalendarElement extends HTMLElement {
226
273
  static observedAttributes: string[];
274
+ private static readonly themeMap;
227
275
  private engine;
228
276
  private unsubscribe;
229
277
  private actions;
@@ -241,6 +289,10 @@ declare class CalendarElement extends HTMLElement {
241
289
  private _categoryColors;
242
290
  private _renderEvent;
243
291
  private _renderNoEvents;
292
+ private _availabilityColors;
293
+ private _selectableStatuses;
294
+ private _initError;
295
+ private pendingUpdate;
244
296
  private _rangeStart;
245
297
  private _rangeEnd;
246
298
  private _timeRangeDate;
@@ -251,19 +303,39 @@ declare class CalendarElement extends HTMLElement {
251
303
  set events(val: CalendarEvent[]);
252
304
  set theme(val: CalendarTheme);
253
305
  set categoryColors(val: CategoryColorMap);
306
+ get availabilityColors(): Record<string, string>;
307
+ set availabilityColors(val: Record<string, string>);
308
+ get selectableStatuses(): string[];
309
+ set selectableStatuses(val: string[]);
254
310
  set renderEvent(val: (e: CalendarEvent) => string);
255
311
  set renderNoEvents(val: () => string);
256
312
  get loading(): boolean;
257
313
  set loading(val: boolean);
314
+ private reaction;
258
315
  connectedCallback(): void;
259
316
  disconnectedCallback(): void;
260
317
  attributeChangedCallback(name: string, oldVal: string | null, newVal: string | null): void;
318
+ private get headingText();
319
+ private static warnedMissingEnd;
320
+ private get slotDuration();
321
+ private warnMissingEndTime;
322
+ private get monthCount();
261
323
  private get minYear();
262
324
  private get maxYear();
263
325
  private initEngine;
264
326
  private reinit;
265
327
  private cleanup;
266
328
  private applyTheme;
329
+ private static readonly BUILT_IN_BUCKETS;
330
+ private static titleDeprecationWarned;
331
+ private get knownBuckets();
332
+ get updateComplete(): Promise<void>;
333
+ private scheduleRender;
334
+ private rethrowInitError;
335
+ private assertStatusesDeclared;
336
+ private assertStatusesKnown;
337
+ private resolveBucket;
338
+ private isDateSelectable;
267
339
  private render;
268
340
  private attachEventListeners;
269
341
  private updatePickerYear;
@@ -293,11 +365,15 @@ interface CalendarElementProps {
293
365
  categoryColors?: CategoryColorMap;
294
366
  renderEvent?: (event: CalendarEvent) => string;
295
367
  renderNoEvents?: () => string;
368
+ availabilityColors?: Record<string, string>;
369
+ selectableStatuses?: string[];
296
370
  }
297
371
  interface CalendarElementInstance {
298
372
  events: CalendarEvent[];
299
373
  theme: CalendarTheme;
300
374
  categoryColors: CategoryColorMap;
375
+ availabilityColors: Record<string, string>;
376
+ selectableStatuses: string[];
301
377
  renderEvent: (event: CalendarEvent) => string;
302
378
  renderNoEvents: () => string;
303
379
  updateEvents: (events: CalendarEvent[]) => void;
@@ -307,4 +383,4 @@ interface CalendarElementInstance {
307
383
  getEngine: () => CalendarEngine;
308
384
  }
309
385
 
310
- export { type CalendarActions, type CalendarConfig, type CalendarDate, CalendarElement, type CalendarElementInstance, type CalendarElementProps, CalendarEngine, type CalendarEvent, type CalendarEventHandler, type CalendarProps, type CalendarState, type CalendarTheme, type CalendarViewModel, type CategoryColorMap, DAYS, DEFAULT_CATEGORY_COLORS, MONTHS, MONTHS_FULL, type PopupPosition, defineCalendarElement, formatAttendees, formatDateForDisplay, formatTimeRange, generateCalendarDates, generateYears, getCategoryColor, getCellClasses, getDefaultEventColor, getEventsForDate, getMonthYearText, getPopupPositionClass, hasEvents, isSameDay, isToday, isValidHexColor, mergeCategoryColors, normalizeDate, sortEventsByTime };
386
+ export { type CalendarActions, type CalendarConfig, type CalendarDate, CalendarElement, type CalendarElementInstance, type CalendarElementProps, CalendarEngine, type CalendarEvent, type CalendarEventHandler, type CalendarPane, type CalendarProps, type CalendarState, type CalendarTheme, type CalendarViewModel, type CategoryColorMap, DAYS, DEFAULT_CATEGORY_COLORS, DEFAULT_SLOT_DURATION, MINUTES_PER_DAY, MONTHS, MONTHS_FULL, bookedSlots, defineCalendarElement, escapeHtml, eventCoversDate, eventInterval, formatAttendees, formatDateForDisplay, formatMinutes, formatTimeRange, generateCalendarDates, generateYears, getCategoryColor, getCellClasses, getDefaultEventColor, getEventsForDate, getMonthYearText, hasEvents, isSameDay, isToday, isValidHexColor, mergeCategoryColors, mergeIntervals, normalizeDate, parseTimeToMinutes, safeColor, safeUrl, slugifyToken, sortEventsByTime };